編輯:關於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) { ListsubscriberMethods = 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 CopyOnWriteArrayListsubscriptions = 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) { CopyOnWriteArrayListsubscriptions; 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開發過程中,拍照或者從相冊中選擇圖片是很常見的功能。下面要說得這個案例比較簡單,用戶點擊按鈕選擇拍照或者打開相冊選擇圖片,然後將選中的圖片顯示在手機上
一、materializeMaterialize將數據項和事件通知都當做數據項發射,Dematerialize剛好相反。一個合法的有限的Obversable將調用它的觀察
前言:在還沒有做任何一件事情之前,千萬不要覺得這件事情很難,因為還沒有開始做內心就已經對這件事情產生了恐懼,這將會阻止你的進步,也許當你動手開始做了這件事後發現其實並不是
如今,大部分的AR都是基於移動設備的,所以這裡我們嘗試在Android中調用OpenCV的功能來做一些相關的測試。由於OpenCV是基於C語言和部分C++語言開發的,而A