編輯:關於Android編程
先上github 地址:eventbus
其為android中的事件總線框架,用於在app內不同模塊間通信消息傳遞的框架。簡單的說,就是一個地方發送一個廣播,在注冊了該廣播的地方接受到廣播(在對應的函數中)。
1.需要創建一個類,該類對應著一類事件。(相當於廣播中的,意圖過濾器中的條件)
class MyEvent{} // 代表一類事件。 即一類的廣播。
2.在需要相應的類中,進行注冊。(對應的要在該類銷毀的時候,進行注銷操作)
EventBus.getDefault().register(this);// 注冊該類為接受者 EventBus.getDefault().unregister(this);// 注銷該類。
3.在注冊的類中,添加對應接受事件的函數。比如
public void onEvent(MyEvent event) {
// TODO 在接收到該事件時,我們要進行的相應處理。
}
4.發送一個事件。也可以理解為發送一個廣播。
EventBus.getDefault().post(new MyEvent());// 發送的MyEvent對象,會被注冊了該事件的方法中得到回調。
簡單的用法就是這些了。
我們就從其暴露給我們的接口來一步步看起源碼。
首先看從getDefault方法開始。
// 這裡可以看到一個典型的創建的單例
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
在eventbus的構造函數中
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap, CopyOnWriteArrayList>();// key 為所以Event的class,value為 該注冊了該event的所以類。
typesBySubscriber = new HashMap
然後我們在看一下register(Object subscriber)方法,都會調用一個方法。
//subscriber 為注冊的類,sticky 是否是粘性事件,priority為優先級 ,會影響存入map的順序,進而影響回調的順序。
private synchronized void register(Object subscriber, boolean sticky, int priority) {
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
首先,我們看到首先調用了SubscriberMethodFinder的findSubscriberMethods方法。給類做的事情就是 把 訂閱了事件的類中的所有的onEvent回調函數給返回出去。
ListfindSubscriberMethods(Class subscriberClass) { String key = subscriberClass.getName(); List subscriberMethods; synchronized (methodCache) { subscriberMethods = methodCache.get(key);// methodCache 為一個static變量,注冊的類為key,以該類中所有注冊函數的list為value,相當於一級緩存。將所有的注冊類中的注冊方法保存下來。避免多次遍歷類中方法的操作。 } if (subscriberMethods != null) { return subscriberMethods; // 如果已經處理過該類,直接使用緩存下來的數據返回。 } subscriberMethods = new ArrayList (); Class clazz = subscriberClass; HashSet eventTypesFound = new HashSet (); // 保存已經添加的方法和該方法的參數拼接的字符串,用於防止重復添加。 StringBuilder methodKeyBuilder = new StringBuilder(); while (clazz != null) { // 循環遍歷該類,以及其父類 String name = clazz.getName(); //忽略掉系統中的類 if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) { // Skip system classes, this just degrades performance break; } // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again) Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { String methodName = method.getName(); if (methodName.startsWith(ON_EVENT_METHOD_NAME)) { // 以OnEvent開始的方法 int modifiers = method.getModifiers(); if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //訪問修飾符判斷 public且非Ignone方法 Class[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == 1) {// 只有一個參數的方法 String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length()); ThreadMode threadMode; if (modifierString.length() == 0) { threadMode = ThreadMode.PostThread; } else if (modifierString.equals("MainThread")) { threadMode = ThreadMode.MainThread; } else if (modifierString.equals("BackgroundThread")) { threadMode = ThreadMode.BackgroundThread; } else if (modifierString.equals("Async")) { threadMode = ThreadMode.Async; } else { if (skipMethodVerificationForClasses.containsKey(clazz)) { continue; } else { throw new EventBusException("Illegal onEvent method, check for typos: " + method); } } Class eventType = parameterTypes[0]; methodKeyBuilder.setLength(0); methodKeyBuilder.append(methodName); methodKeyBuilder.append('>').append(eventType.getName()); String methodKey = methodKeyBuilder.toString(); if (eventTypesFound.add(methodKey)) { // Only add if not already found in a sub class SubscriberMethod類為封裝了一個類中接收函數信息的封裝類。其中包含了方法method、該方法需要回調的線程threadmode、對應的事件類型eventtype。 subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType)); } } } else if (!skipMethodVerificationForClasses.containsKey(clazz)) { Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "." + methodName); } } } clazz = clazz.getSuperclass(); // 獲取父類的class,繼續循環 } if (subscriberMethods.isEmpty()) { throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called " + ON_EVENT_METHOD_NAME); } else { synchronized (methodCache) { methodCache.put(key, subscriberMethods);//將該類中的所有對應的響應方法的集合緩存下來。 } return subscriberMethods; } }
然後,遍歷得到集合,調用subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)方法。該方法 主要功能就是為了將我們在EventBus 構造函數中創建的subscriptionsByEventType、typesBySubscriber和stickyEvents這三個集合,將對應的數據保存在他們中。前面已經說過他們對應保存的數據,這裡就不重復了。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
Class eventType = subscriberMethod.eventType; // 事件的對應的class
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType); // 先從集合中獲取以前保存的。
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);// subscription 類為對接受事件的類的包裝類,裡面包含 subscribe r 就是我們register(Object o)時傳入的this。一般就是activity或fragment。subscribermethod 就是該類中的onevent方法的封裝,priority
if (subscriptions == null) {//如果為空,代表第一次處理該event。創建list,並且將list添加到subscriptionsByEventType中。
subscriptions = new CopyOnWriteArrayList();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
//將newSubscription按照優先級添加到中。
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
subscriptions.add(i, newSubscription);
break;
}
}// 到此,subscriptionsByEventType這個集合填充完畢。
List> subscribedEvents = typesBySubscriber.get(subscriber);//同樣的,先將這個類中之前event.class的集合取出。
if (subscribedEvents == null) { // 如果為空,代表是第一次,創建list集合,並且將其放到typesBySubscriber這個map中
subscribedEvents = new ArrayList>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);// 將event加到剛才創建的list中。
if (sticky) { // 是否是粘性事件。如果不是的話,register方法到此就結束了。如果是注冊的是粘性事件監聽,就會在注冊的時候,執行以下代碼。
Object stickyEvent;
synchronized (stickyEvents) {
stickyEvent = stickyEvents.get(eventType); // 該集合中的stickyEvent是在postSticky(Object o)的時候,添加到集合中的。所以所謂的粘性事件就是在這類 注冊粘性事件監聽之前,就已經被post過的事件,在注冊的時候會被直接調用,並將之前的Object作為參數傳入。
}
if (stickyEvent != null) {
// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
// --> Strange corner case, which we don't take care of here.
postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper()); // 這裡是post粘性事件。該方法是post事件的核心方 法。之後在分析post模塊的時候,會繼續分析。
}
}
}
到這裡,我們就算走完了全部register的流程。我們現在回顧一下注冊所做的事情。其實,最主要的事情就是將注冊的類(比如activity)中的對應的onevent回調回調函數(比如 OnEvent(MyEvent myevent)),以及其參數,通過自己定義的包裝類,保存到相應的集合之中(subscriptionsByEventType,typesBySubscriber)。
然後,我們再看一看,我們是如何post一個事件是如何實現的。
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();//獲得在和該線程有關的postingState對象。
List
首先,我們先看一下PostingThreadState這個類。它是EventBus的內部類。其中主要是保存事件的一些信息,就是一個bean對象。這裡需要特別關注一下currentPostingThreadState這個對象。它是ThreadLocal的實例。ThreadLocal可以理解一個用於在不同線程中保存數據的一個容器。比如你在主線程中添加了一條數據,又在子線程中添加了一條數據,然後,當你在主線程中去get的時候,拿到的就是你之前在主線程中保存的數據,如果你在子線程中去get,那麼拿到的就是你在子線程中保存的數據。對於具體如何實現的,有興趣的同學可以自己去研究研究,其中有用到很巧妙的算法。
然後我們在看看postSingleEvent(Object event, PostingThreadState postingState)方法.
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class eventClass = event.getClass(); // event對應的class
boolean subscriptionFound = false;
if (eventInheritance) { // 代表是否將event的父類,也進行處理。
List> eventTypes = lookupAllEventTypes(eventClass); // 在該方法中,通過循環,將eventClass的所有父類找到並且返回出來。
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
}
然後,其都會調用postSingleEventForEventType方法,返回的boolean代表是否存在對應的注冊者類。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class eventClass) {
CopyOnWriteArrayList subscriptions;
synchronized (this) { // 這裡我們看到,我們通過eventClass從subscriptionsByEventType中拿到我們之間在注冊的時候添加進去的該event的所對應的注冊者的集合。
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {// 遍歷該event 所有的注冊者
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false; // 將信息保存到postingState對象中
try {
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
在這裡然後調用 postToSubscription方法。該方法在之前就有遇到過。實在register時,對於粘性事件我們調用了該方法。現在我們就來看看到底該方法干了什麼。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
invokeSubscriber(subscription, event);
break;
case MainThread:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BackgroundThread:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case Async:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
我們看到了,主要是通過區分subscription.subscriberMethod.threadMode的類型,以及當前方法執行的線程(也就是post方法調用的時候的線程),來調用不同方法。
這裡主要有四種方式。 一種是invokeSubscriber(subscription, event) (其實最後調用的都是這個方法,不過是在不同線程中調用而已)代表在當前線程中調用。第二種 mainThreadPoster.enqueue(subscription, event),在主線程中調用。第三種 backgroundPoster.enqueue(subscription, event) 會在子線程中調用,而且該線程是維護了一個隊列,依次處理所有的加入的數據。第四種是asyncPoster.enqueue(subscription, event)會開辟新的子線程在其中執行。
正如我剛才說的,其實最後調用的都是invokeSubscriber(Subscription subscription, Object event)這個方法。 只是通過在不同線程中調用,來實現在不同線程中回調。
void invokeSubscriber(Subscription subscription, Object event) {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
// 實現功能的就是該方法,通過反射調用該方法。
.......
}
現在我們來看看如何實現在不同線程中調用該方法。如果還記得的話,我們在eventbus的構造函數中,曾經創建過三個對象
mainThreadPoster,backgroundPoster,asyncPoster這三個對象,對應於不同線程。
首先,mainThreadPoster其實就是一個handler,而且是主線程中的handler,所以其對應的handleMessage方法會在主線程中執行。
首先調用了enqueue(Subscription subscription, Object event)方法。
首先,我們看PendingPost類,該也是一個封裝event和subscription的類,但是不同的是其中包含一個pendingPostPool的存儲集合,該集合裡面添加進去的是我們之前創建過的PendingPost的對象。其作用是復用以前創建過的對象,減少對象創建的開銷。
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null; // 將使用完畢的PendingPost中的數據清空
synchronized (pendingPostPool) {
// Don't let the pool grow indefinitely
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost); 添加到集合中
}
}
}
在我們需要一個新的PendingPost的對象的時候
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
if (size > 0) { //如果集合中有不用的PendingPost,將其取出,並且將對應的數據添加到其中。
PendingPost pendingPost = pendingPostPool.remove(size - 1);
pendingPost.event = event;
pendingPost.subscription = subscription;
pendingPost.next = null;
return pendingPost;
}
}
return new PendingPost(event, subscription);
}
然後,回到enqueue方法中,
void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); // 獲得封裝了subscription和event的pendingpost對象
synchronized (this) {
queue.enqueue(pendingPost); // 將其添加到自己定義的隊列中
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) { // 調用sendmessage方法發送一個空消息,會調用handler的handlemessage方法中。
throw new EventBusException("Could not send handler message");
}
}
}
}
然後,來到handlemessage方法
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis(); //獲取系統時間,相當於進入循環是的時間,因為我們看到下面是一個死循環,會用這個時間做一個限制。
while (true) {
PendingPost pendingPost = queue.poll(); //從隊列中取出在enqueue中添加的數據。
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
} // 以上部分是為了判斷隊列是否為空的考慮線程安全的方法。
eventBus.invokeSubscriber(pendingPost); // 這裡,就是實現真正功能的代碼。我們看到,它又回到了EventBus類中,只不過,它是在主線程中調用的。因為這是在主線程的handler的handlemessage方法中。
long timeInMethod = SystemClock.uptimeMillis() - started; //又獲取了一次系統時間,通過時間差,來控制循環。(當隊列中數據好多時,才會用到)
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) { //如果此次循環已經超過規定時間,發送一個消息,會重新調用handlemessage方法。
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
我們再回到eventbus中看看invokeSubscriber方法
void invokeSubscriber(PendingPost pendingPost) {
Object event = pendingPost.event;
Subscription subscription = pendingPost.subscription; // 將封裝在PendingPost中的數據取出來。
PendingPost.releasePendingPost(pendingPost);// 會將PendingPost對象中的數據清空,放到其內部的集合中,以便將來再用。
if (subscription.active) {
invokeSubscriber(subscription, event);// 該方法,以前已經分析過了,就是通過反射回調到相應的方法中,完成post的實現。
}
}
到這裡,我們就看完了在主線程中實現回調的實現了。對於BackgroundPoster和AsyncPoster其實現是類似的,唯一的區別就是其是實現Runnable接口,其是運行在子線程中的。其中也是先將數據添加到一個隊列中,然後再其run方法中調用eventBus.invokeSubscriber(pendingPost)方法。這兩個的區別就是 一個線程安全,一個線程不安全。
到此。post方法我們也算分析完畢了。對於unregister()方法,如果前面的都看懂了,那麼unregister就很簡單了,就是將之前register時保存到集合中的數據刪除掉就好了。這裡就不再累贅了。相信大家都可以輕松看懂的。
Android拍照,相冊選擇圖片以及Android6.0權限管理
概述在android開發過程中,拍照或者從相冊中選擇圖片是很常見的功能。下面要說得這個案例比較簡單,用戶點擊按鈕選擇拍照或者打開相冊選擇圖片,然後將選中的圖片顯示在手機上
RX操作符之輔助操作
一、materializeMaterialize將數據項和事件通知都當做數據項發射,Dematerialize剛好相反。一個合法的有限的Obversable將調用它的觀察
android使用AIDL實現跨進程通訊(IPC)
前言:在還沒有做任何一件事情之前,千萬不要覺得這件事情很難,因為還沒有開始做內心就已經對這件事情產生了恐懼,這將會阻止你的進步,也許當你動手開始做了這件事後發現其實並不是
OpenCV學習筆記(四)—— 移植到Android平台
如今,大部分的AR都是基於移動設備的,所以這裡我們嘗試在Android中調用OpenCV的功能來做一些相關的測試。由於OpenCV是基於C語言和部分C++語言開發的,而A