《Android開發(fā)藝術(shù)探索》讀書筆記

Activity 的生命周期和啟動模式

Tips

  • 新Activity是透明主題時,舊Activity不會走onStop;
  • Activity切換時,舊Activity的onPause會先執(zhí)行,然后才會啟動新的Activity;
  • Activity在異常情況下被回收時,onSaveInstanceState方法會被回調(diào),回調(diào)時機(jī)是在onStop之前,當(dāng)Activity被重新創(chuàng)建的時 候,onRestoreInstanceState方法會被回調(diào),時序在onStart之后;
  • 標(biāo)識Activity任務(wù)棧名稱的屬性:TaskAffinity,默認(rèn)為應(yīng)用包名。

Activity的LaunchMode

  1. standard 系統(tǒng)默認(rèn)。每次啟動會重新創(chuàng)建新的實例,誰啟動了這個Activity,這個Activity就在誰的棧里。
  2. singleTop 棧頂復(fù)用模式。該Activity的onNewIntent方法會被回調(diào),onCreate和onStart并不會被調(diào)用。
  3. singleTask 棧內(nèi)復(fù)用模式。只要該Activity在一個棧中存在,都不會重新創(chuàng)建,onNewIntent會被回調(diào)。如果不存在,系統(tǒng)會先尋找是否存在需要的棧,如果不存在該棧,就創(chuàng)建一個任務(wù)棧,然后把這個Activity放進(jìn)去;如果存在,就會創(chuàng)建到已經(jīng)存在的這個棧中。
  4. singleInstance。具有此種模式的Activity只能單獨存在于一個任務(wù)棧。

IntentFilter匹配規(guī)則。

  1. action匹配規(guī)則:要求intent中的action 存在 且 必須和過濾規(guī)則中的其中一個相同 區(qū)分大小寫;
  2. category匹配規(guī)則:系統(tǒng)會默認(rèn)加上一個android.intent.category.DEAFAULT,所以intent中可以不存在category,但如果存在就必須匹配其中一個;
  3. data匹配規(guī)則:data由兩部分組成,mimeType和URI,要求和action相似。如果沒有指定URI,URI但默認(rèn)值為content和file(schema)

IPC 機(jī)制

使用android:process會帶來的問題

  1. 靜態(tài)成員和單例模式完全失效;
  2. SharedPreferences可靠性下降;
  3. Application會多次創(chuàng)建;

Binder的工作機(jī)制

Binder的工作機(jī)制.png

Android中的IPC方式

  1. Bundle

  2. 文件共享(不建議使用系統(tǒng)的SharedPreferences)

  3. Messenger(輕量級IPC,底層依然是AIDL)工作原理(Page70 & 代碼)

  4. AIDL
    4.1. AIDL支持的數(shù)據(jù)類型:基本數(shù)據(jù)類型;String和CharSequence;List只支持ArrayList,里面每個元素都必須被AIDL支持;Map只支持HashMap,里面每個元素都必須被AIDL支持(包括key和value);Parcelable;AIDL接口本身;
    4.2. 服務(wù)端可以使用CopyOnWriteArrayList和ConcurrentHashMap來進(jìn)行自動線程同步,客戶端拿到的依然是ArrayList和HashMap;
    4.3. 服務(wù)端和客戶端之間做監(jiān)聽器,服務(wù)端需要使用RemoteCallbackList,否則客戶端的監(jiān)聽器無法收到通知(因為服務(wù)端實質(zhì)還是一份新的序列化后的監(jiān)聽器實例,并不是客戶端那份);
    dd. 客戶端調(diào)用遠(yuǎn)程服務(wù)方法時,因為遠(yuǎn)程方法運行在服務(wù)端的binder線程池中,同時客戶端線程會被掛起,所以如果該方法過于耗時,而客戶端又是UI線程,會導(dǎo)致ANR,所以當(dāng)確認(rèn)該遠(yuǎn)程方法是耗時操作時,應(yīng)避免客戶端在UI線程中調(diào)用該方法。同理,當(dāng)服務(wù)器調(diào)用客戶端的listener方法時,該方法也運行在客戶端的binder線程池中,所以如果該方法也是耗時操作,請確認(rèn)運行在服務(wù)端的非UI線程中。另外,因為客戶端的回調(diào)listener運行在binder線程池中,所以更新UI需要用到handler。
    4.4. 客戶端通過IBinder.DeathRecipient來監(jiān)聽Binder死亡,也可以在onServiceDisconnected中監(jiān)聽并重連服務(wù)端。區(qū)別在于前者是在binder線程池中,訪問UI需要用Handler,后者則是UI線程。
    4.5. 可通過自定義權(quán)限在onBind或者onTransact中進(jìn)行權(quán)限驗證。

  5. ContentProvider

  6. Socket 一般用于網(wǎng)絡(luò)通信,AIDL用這種方式會過于繁瑣,不建議。

  7. Binder連接池,通過BinderPool的方式將Binder的控制與Service本身解耦,同時只需要維護(hù)一份Service即可。這里用到了CountDownLatch,大概解釋下用意:線程在await后等待,直到CountDownLatch的計數(shù)為0,BinderPool里使用它的目的是為了保證Activity獲取BinderPool的時候Service已確定bind完成~

View 的事件體系

View的定義

可以把View理解成組合模式里的葉子結(jié)點和有枝節(jié)點的關(guān)系,本質(zhì)都是Composite,而這里本質(zhì)ViewGroup和View都是View,組合模式最大的好處就是遍歷的時候不用關(guān)注是怎樣的結(jié)點,因為抽象都是一樣的。

View的位置參數(shù)

  1. View的寬高和坐標(biāo)關(guān)系:width = right - left,height = top - bottom。
  2. View在平移過程中,top和left表示的是原始左上角的位置信息,其值不會改變,發(fā)生改變的是x、y、translationX、translationY這四個參數(shù),x是View左上角的坐標(biāo),translation是view移動后相對于父容器(這里其實就是剛才說的左上角)的偏移量,所以有x = left + translationX。y的原理相同。

MotionEvent典型事件:ACTION_DOWN, ACTION_MOVE, ACTION_UP。

TouchSlop:系統(tǒng)所能識別的被認(rèn)為是滑動的最小距離,我們可以用這個常量來判斷用戶的滑動是否達(dá)到閾值,提升用戶體驗。獲取方法:ViewConfiguration.get(getContext()).getScaledTouchSlop()。

VelocityTracker加速度追蹤:使用很簡單,看書就好。經(jīng)過測試一般建議類似ViewPager這樣的控件,將時間間隔設(shè)置為1000(也就是1秒)時,加速度閾值設(shè)為1000-2000左右體驗較好,各位可自行測試。

View的滑動

  1. ScrollBy和ScrollTo,簡單歸納下: ScrollBy的0點在一般情況下均為可見的top那條線,有一種特殊情況就是(書中未提及)當(dāng)某個ViewGroup在內(nèi)部的layout的時候設(shè)置margin為負(fù)值的View時,0點會在可見top上方的高度(或?qū)挾龋閙argin的地方,這個鬼東西實在是太繞口,具體的參考blog,參考headerView的設(shè)置。豎向滑動時,上滑ScrollY不斷增加(所以應(yīng)該傳正值),下滑時ScrollY不斷減少(所以應(yīng)該傳負(fù)值);同理,橫向滑動時,左滑ScrollX不斷增加,右滑不斷減少。 ScrollTo可以理解為把View滑動到ScrollX或ScrollY為指定值的位置
  2. 動畫:注意View動畫的View移動只是位置移動,其本身還是在原來位置,會導(dǎo)致一些bug。
  3. 通過LayoutParams

Scoller

  1. TouchEvent的ACTION_UP事件中,用戶滑動速度很快,但是滑動距離又不足以“翻頁”的時候,通過scroller來幫助用戶scrollBy掉滑動一頁還需要的dx或dy。
  2. 當(dāng)用戶滑動到最上端或最下端時,我們?nèi)匀辉试S用戶繼續(xù)滑動,但是一旦松手,就把頁面彈回到最上端和最下端的位置,用IOS的用戶都知道IOS幾乎所有頁面都有這個彈性效果,用戶體驗非常好,其實我們用scroller也能輕松實現(xiàn)。
  3. 第三種場景和第一種類似,大家依然可以參考上面發(fā)的那篇仿網(wǎng)易的blog,現(xiàn)在不是翻頁,當(dāng)用戶滑動的加速度很大的時候,我們認(rèn)為用戶需要滑動的距離肯定是不只他手從放下到松開的那段距離的,所以這種情況下我們需要通過scroller幫助用戶去多滑一段,這個距離具體設(shè)置一般需要交互給出,設(shè)置的是滑加速度的十分之一,感覺還是太少,各位可以自行設(shè)置。

View事件的分發(fā)機(jī)制

  1. 三大方法關(guān)系的偽代碼

    public boolean dispatchTouchEvent(MotionEvent ev) { 
        boolean consume = false;
        if(onInterceptTouchEvent(ev)) { 
            consume = onTouchEvent(ev);
        } else { 
            consume = child.dispatchTouchEvent(ev); 
        }
        return consume; 
    } 
    

關(guān)系很清楚了,如果當(dāng)前View攔截事件,就交給自己的onTouchEvent去處理,否則就丟給子View繼續(xù)走相同的流程。

  1. onTouchListener優(yōu)先級高于onTouchEvent。
  2. 事件傳遞順序:Activity -> Window -> View,如果View都不處理,最終將由Activity的onTouchEvent處理。
  3. 一些結(jié)論:攔截的一定是事件序列;不消耗ACTION_DOWN,則事件序列都會由其父元素處理;只消耗ACTION_DOWN事件,該事件會消失,消失的事件最終會交給Activity來處理;requestDisallowInterceptTouchEvent方法可以在子元素中干預(yù)父元素的事件分發(fā)過程,除了ACTION_DOWN;

事件分發(fā)源碼解析

  1. Window的實現(xiàn)類為PhoneWindow。

  2. 獲取Activity的contentView的方法
    ((ViewGroup)getWindow().getDecorView().findViewById(android.R.id.content)).getChildAt(0);

  3. View的滑動沖突處理,普通需求基本直接復(fù)用代碼就能搞定。但是如果想要深刻理解,只有自己多寫多測多讀代碼,才能很好的掌握,對于自定義View來說,掌握這個專題將對你的功力有大幅的提升。

View 的工作原理

ViewRoot和DecorView

  1. ViewRoot對應(yīng)ViewRootImpl類,它是連接WindowManager和DecorView的紐帶,View的三大流程均通過ViewRoot來完成。
  2. ActivityThread中,Activity創(chuàng)建完成后,會將DecorView添加到Window中,同時創(chuàng)建ViewRootImpl對象,并建立兩者的關(guān)聯(lián)。
  3. View的繪制流程從ViewRoot的performTraversals方法開始,經(jīng)過measure、layout和draw三大流程。

MeasureSpec

  1. 復(fù)習(xí)下位運算…… a) <<左移,>>右移,所以源碼中EXACTLY = 1 << MODE_SHIFT就相當(dāng)于010000……..0000(30個0),其他可類推; b) &運算 0011 & 1100 = 0000,按位與;|或運算 0011 | 1100 = 1111,按位或; c)~取反運算,~0000 = 1111,所以(size & ~MODE_MASK) | (mode & MODE_MASK) 就好理解了;
  2. 三類specMode:UNSPECIFIED,基本無視;EXACTLY,精確大小或match_parent;AT_MOST,warp_content;
  3. 子View和父容器的MeasureSpec關(guān)系歸納:
    a. 子View為精確寬高,無論父容器的MeasureSpec,子View的MeasureSpec都為精確值且遵循LayoutParams中的值。
    b. 子View為match_parent時,如果父容器是精確模式,則子View也為精確模式且為父容器的剩余空間大小;如果父容器是wrap_content,則子View也是wrap_content且不會超過父容器的剩余空間。
    c. 子View為wrap_content時,無論父View是精確還是wrap_content,子View的模式總是wrap_content,且不會超過父容器的剩余空間。

View的工作流程:

onMeasure, onLayout, onDraw

  1. getSuggestedMinimumWidth的邏輯:View如果沒有背景,那么返回android:minWidth這個屬性指定的值,這個值可以為0;如果設(shè)置了背景,則返回背景的最小寬度和minWidth中的較大值。
  2. 一個比較好的習(xí)慣是在onLayout方法中去獲取View的測量寬高或最終寬高。
  3. 如何在Activity初始化時獲取View的寬高:
    a. Activity或者View的onWindowFocusChanged方法(注意該方法會在Activity Pause和resume時被多次調(diào)用)。
    b. view.post(new Runnable( {@Overiddepublic void run(){})});在run方法中獲取。
    c. ViewTreeObserver中的onGlobalLayoutListener中。
    d. view.measure手動獲取: match_parent:無法測量; 精確值:int wMeasureSpec = MeasureSpec.makeMeasureSpec(exactlyValue, MeasureSpec.EXACTLY); wrap_content:int wMeasureSpec = MeasureSpec.makeMeasureSpec((1 << 30) - 1, MeasureSpec.AT_MOST); PS一下,還不懂(1<<30) - 1的,再多一句嘴,其實就是1111….11111(30個)
  4. layout中可能會使view的大小大于測量的大小
  5. draw的過程:繪制背景(background.draw(canvas)),繪制自己(onDraw()),繪制chidren(dispatchDraw),繪制裝飾(onDrawScrollBars)
  6. setWillNotDraw方法用于在一個View不需要繪制時的優(yōu)化(設(shè)置為true時)。

自定義View:

  1. 直接繼承View或ViewGroup的需要自己處理wrap_content。
  2. View要在onDraw方法中要處理padding,而ViewGroup要在onMeasure和onLayout中處理padding和margin。
  3. View中的post方法可以取代handler。
  4. 在View的onDetachedFromWindow中停止動畫,線程或回收其他資源。
  5. 滑動沖突處理。

理解RemoteViews

Notification

Notification的自定義View只能使用setTextViewText, setImageViewResource, setOnClickPendingIntent等固定方法來設(shè)置View,不能像操作普通View的方式來操作。

AppWidgetProvider

  1. 本質(zhì)是一個廣播,配置步驟:定義界面xml,定義配置信息xml,定義實現(xiàn)類(繼承AppWidgetProvider),AndroidManifest中聲明。
  2. 重要回調(diào):onEnable,第一次被添加時調(diào)用,只有一次;onUpdate,添加或更新時回調(diào);onDelete,每次刪除時回調(diào);onDisable,最后一次刪除時回調(diào);onReceive,接收廣播的action。

PendingIntent

  1. 典型的使用場景就是和RemoteViews的點擊事件配合使用;
  2. 支持三種待定Intent:Activity,Service和Broadcast
  3. PendingIntent相同的定義:內(nèi)部的Intent和requestCode都相同。Intent相同的定義:兩個Intent的componentName和intent-filter相同(不包括extras)
  4. flag定義:FLAG_NO_CREATE,基本不使用;FLAG_ONE_SHOT,以第一個為準(zhǔn),后續(xù)的會全部和第一條保持一致,任意一條被觸發(fā),其他的都cancel;FLAG_CANCEL_CURRENT,前面的相同的PendingIntent都會被cancel,只有最新的可用;FLAG_UDPATE_CURRENT,前面的PendingIntent都會被更新(它們Intent中的extras都會被更新)

RemoteViews的內(nèi)部機(jī)制

  1. 支持的View類型:Layout:FrameLayout, LinearLayout, RelativeLayout, GridLayout; View: AnalogClock, Button, Chronometer, ImageButton, ImageView, ProgressBar, TextView, ViewFlipper, ListView, GridView, StackView, AdapterViewFlipper, ViewStub。
  2. 簡單歸納,客戶端的remoteViews通過action對象,由binder機(jī)制來更新服務(wù)端的remoteViews,所以RemoteViews本身也實現(xiàn)了Parcelable接口(參考圖Page233)
  3. RemoteViews中真正操作View的方法apply和reapply,前者會加載布局并更新界面,后者則只更新界面。

跨進(jìn)程的RemoteViews傳遞

模擬通知欄的實現(xiàn)。注意不同應(yīng)用間RemoteViews的id不同,需要約定名稱然后重新通過Resource.getIdentifier來獲取。

Android 的 Drawable

Android 動畫深入分析

理解 Window 和 WindowManager

一些基礎(chǔ)知識:

  1. Window的實現(xiàn)類是PhoneWindow。
  2. Window的具體實現(xiàn)位于WindowManagerService中,WindowManager和WindowManagerService的交互是一個IPC過程。
  3. Window實際是View的直接管理者。

常用的WindowManager.LayoutParams的Flag和Type

  1. FLAG: FLAG_NOT_FOCUSABLE,當(dāng)前Window不獲取焦點,也不接收各種輸入事件,會同時啟用FLAG_NOT_TOUCH_MODAL,事件會傳遞給下層具有焦點的Window。 FLAG_NOT_TOUCH_MODAL,當(dāng)前Window區(qū)域外的單擊事件傳遞給底層,區(qū)域內(nèi)的單擊事件自己處理,一般都需要開啟。 FLAG_SHOW_WHEN_LOCKED,可以讓W(xué)indow顯示在鎖屏界面上。
  2. Type: 應(yīng)用Window,一般對應(yīng)一個Activity。層級范圍1~99。 子Window,不能單獨存在,需要特定的父Window,比如一般的Dialog。層級范圍1000~1999。 系統(tǒng)Window,需要權(quán)限聲明,比如Toast。層級范圍2000~2999。一般可以選用WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,同時聲明權(quán)限。
  3. WindowManager提供的功能:addView,updateViewLayout,removeView

Window的內(nèi)部機(jī)制

Window并不實際存在,以View的形式存在。每個Window對應(yīng)著一個View和ViewRootImpl,Window和View通過ViewRootImpl建立聯(lián)系。所以在實際使用中其實我們并不能訪問到真正的Window,而只能通過WindowManager。

  1. 幾個重要的window類的關(guān)系
    這里寫圖片描述
  2. Window的添加過程
    a. WindowManagerGlobal中的addView:
    b. 檢查參數(shù)是否合法;
    c. 如果子Window還需要調(diào)節(jié)布局參數(shù);
    d. 創(chuàng)建ViewRootImpl并將View添加到列表中;
    e. 通過ViewRootImpl的setView來更新界面并完成Window的添加過程:
    requestLayout中的scheduleTraversals是View繪制的入口,最終通過WindowSession來完成Window的添加過程,注意其實這里是個IPC過程,最終會通過WindowManagerService的addWindow方法來實現(xiàn)Window的添加。

  3. Window的刪除過程
    a. WinodwManagerGlobal中的removeView;
    b. findViewLocked來查找待刪除待View的索引,再調(diào)用removeViewLocked來做進(jìn)一步刪除;
    c. removeViewLocked通過ViewRootImpl的die方法來完成刪除操作,包括同步和異步兩種方式,同步方式可能會導(dǎo)致意外的錯誤,不推薦,一般使用異步的方式,其實就是通過handler發(fā)送了一個刪除請求,將View添加到mDyingViews中;
    d. die方法本質(zhì)調(diào)用了doDie方法,真正刪除View的邏輯在該方法的dispatchDetachedFromWindow方法中,主要做了四件事:垃圾回收,通過Session的remove方法刪除Window,調(diào)用View的dispatchDetachedFromWindow方法同時會回調(diào)View的onDetachedFromWindow以及onDetachedFromWindowInternal,調(diào)用WindowManagerGlobal的doRemoveView刷新數(shù)據(jù)。

  4. Window的更新過程
    a. WindowManagerGlobal的updateViewLayout;
    b. 更新View的LayoutParams;
    c. 更新ViewImple的LayoutParams,實現(xiàn)對View的重新測量,布局,重繪;
    d. 通過WindowSession更新Window的視圖,WindowManagerService.relayoutWindow()。

Window的創(chuàng)建過程

  1. Activity
    a. Activity的attach方法中,系統(tǒng)會創(chuàng)建Activity所屬的Window并為其設(shè)置回調(diào);
    b. Window對象的創(chuàng)建通過PolicyManager的makeNewWindow方法;
    c. Window的具體實現(xiàn)是PhoneWindow類;
    d. Window創(chuàng)建好之后,通過PhoneWindow的setContentView將Activity與Window進(jìn)行關(guān)聯(lián),這個方法大致步驟:
    d.1. 如果沒有DecorView就創(chuàng)建,id是android.R.id.content;
    d.2. 將Activity設(shè)置的ContentView設(shè)置到DecorView的mContentParent中;
    d.3. 回調(diào)Activity的onContentChanged方法通知Activity視圖已經(jīng)發(fā)生改變;
    d.4. Activity onResume的時候會調(diào)用Activity的makeVisible方法真正完成DecorView的添加和顯示。
  2. Dialog
    a. 通過PolicyManager的makeNewWindow方法創(chuàng)建Window;
    b. 初始化DecorView,和Activity類似;
    c. Dialog的show方法中,通過WindowManager將DecorView添加到Window中;
    d. Dialog關(guān)閉時,會通過WindowManager來移除DecorView,方法為removeViewImmediate(mDecor);
    e. 想要創(chuàng)建一個使用application context的Dialog可按照本章2-2的方法設(shè)置,dialog.getWindow.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR),記得在manifest中設(shè)置權(quán)限。
  3. Toast
    a. Toast內(nèi)部有兩類IPC:Toast訪問NotificationManagerService;NotificationManagerService(下文簡稱NMS)訪問Toast的TN接口;
    b. Toast屬于系統(tǒng)Window,內(nèi)部視圖mNextView一種為系統(tǒng)默認(rèn)樣式,另一種通過setView方法來指定一個自定義View。
    c. TN是一個Binder類,NMS處理Toast的顯示隱藏請求時會跨進(jìn)程回調(diào)TN中的方法,所以TN運行在Binder線程池中,所以需要handler切換到當(dāng)前發(fā)送Toast請求的線程中,也就是說沒有Looper的線程是無法彈出Toast的。
    d. Toast的show方法調(diào)用了NMS的enqueueToast方法,該方法先將Toast請求封裝成ToastRecord并丟入mToastQueue隊列中(非系統(tǒng)應(yīng)用最多塞50個)。
    e. NMS通過showNextToastLocked方法來顯示當(dāng)前View,Toast顯示由ToastRecord的callback方法中的show方法完成,callback其實就是TN對象的遠(yuǎn)程Binder,所以最終調(diào)用的是TN中的方法,并運行在發(fā)起Toast請求應(yīng)用的Binder線程池中。
    f. 顯示以后,NMS通過scheduleTimeoutLocked方法發(fā)送延時消息,延時后NMS通過cancelToastLocked方法來隱藏Toast并從隊列中移除,隱藏依然通過ToastRecord的callback中的hide方法實現(xiàn)。
    g. callback回調(diào)TN的show和hide方法后,會通過handler發(fā)送兩個Runnable,里面的handleShow和handleHide方法是真正完成顯示和隱藏Toast的地方。handleShow方法中將Toast的視圖添加到Window中,handleHide方法將Toast視圖從Window中移除。

四大組件的工作過程

四大組件概述:

  1. Activity的主要作用是展示一個界面并和用戶交互,它扮演的是一種前臺界面的角色。
  2. Service是一種計算型組件,用于在后臺執(zhí)行一系列計算任務(wù),但因為其本身還是運行在主線程中的,因此耗時的后臺計算仍然需要在單獨的線程中去完成。
  3. BroadcastReceiver是一種消息型組件,用于在不同的組件乃至不同的應(yīng)用之間傳遞消息。廣播注冊有兩種方式,動態(tài)注冊通過Context.registerReceiver()來實現(xiàn),必須要應(yīng)用啟動才能注冊;靜態(tài)注冊則在AndroidManifest文件中進(jìn)行,應(yīng)用安裝時會被系統(tǒng)解析,不需要啟動應(yīng)用就可接收廣播。
  4. ContentProvider是一種共享型組件,用于向其他組件乃至其他應(yīng)用共享數(shù)據(jù)。
startActivity.png
startService.png
bindService.png

Android 的消息機(jī)制

三大件

Hanlder,MessageQueue,Looper。

  1. MessageQueue內(nèi)部的數(shù)據(jù)結(jié)構(gòu)并非隊列,而是單鏈表,它只是用來存儲數(shù)據(jù)。
  2. Looper是真正的數(shù)據(jù)處理者,線程默認(rèn)沒有Looper,使用Handler必須為線程創(chuàng)建Looper,UI線程也就是ActivityThread創(chuàng)建時會初始化Looper,所以主線程中默認(rèn)可以直接使用Handler。
    UI線程檢查當(dāng)前線程的操作在ViewRootImpl的checkThread方法中,我們常見的不能在子線程中訪問view的異常就是在這里拋出的。
    不允許子線程訪問主線程的原因是UI控件不是線程安全的,而加鎖又會導(dǎo)致UI的操作過于復(fù)雜。
  3. Handler的工作過程,圖(page374),單概括一下就是:假設(shè)創(chuàng)建Handler的線程是A,耗時操作的線程是B,B線程中拿到handler實例發(fā)送消息或者post一個Runnable,實際是調(diào)用了MessageQueue的enqueueMessage方法,進(jìn)入了消息隊列,線程A中的Looper發(fā)現(xiàn)了這個消息,就會處理這個消息,也就是消息中的Runnable或者h(yuǎn)andler的handleMessage方法會被調(diào)用,這樣handler中的業(yè)務(wù)邏輯就被切換到線程A中去了。
  4. ThreadLocal看上去只new了一份,但在每個不同的線程中卻可以擁有不同數(shù)據(jù)副本的神奇類。其本質(zhì)是ThreadLocal中的Values類維護(hù)了一個Object[],而每個Thread類中有一個ThreadLocal.Values成員,當(dāng)調(diào)用ThreadLocal的set方法時,其實是根據(jù)一定規(guī)則把這個線程中對應(yīng)的ThreadLocal值塞進(jìn)了Values的Object[]數(shù)組中的某個index里。這個index總是為ThreadLocal的reference字段所標(biāo)識的對象的下一個位置。

三大件原理

  1. MessageQueue的工作原理:主要方法為enqueueMessage和next。
    a. enqueueMessag主要就是一個單鏈表的插入操作,
    b. next方法是一個無限循環(huán),如果消息隊列中沒有消息,next方法就阻塞,有新消息到來時,next方法會返回這條消息并將其從單鏈表中刪除。
    2。 Looper的工作原理:
    a. prepare方法,為當(dāng)前沒有Looper的線程創(chuàng)建Looper。
    b. prepareMainLooper和getMainLooper方法用于創(chuàng)建和獲取ActivityThread的Looper。
    c. quit和quitSafely方法,前者立即退出,后者只是設(shè)定一個標(biāo)記,當(dāng)消息隊列中的所有消息處理完畢后會才安全退出。子線程中創(chuàng)建的Looper建議不需要的時候都要手動終止。 d. loop方法,死循環(huán),阻塞獲取msg并丟給msg.target.dispatchMessage方法去處理,這里的target就是handler。
  2. Handler的工作原理:
    a. 無論sendMessage還是post最終都是調(diào)用的sendMessageAtTime方法。
    b. 發(fā)送消息其實就是把一條消息通過MessageQueue的enqueueMessage方法加入消息隊列,Looper收到消息就會調(diào)用handler的dispatchMessage方法。它的處理過程參考書page388的流程圖,一看就懂~
    c. 這里我補充一個東西,當(dāng)我們直接Handler h = new Handler()時,本質(zhì)調(diào)用的是Handler(Callback callback, Boolean async)構(gòu)造方法,這個方法里會調(diào)用Looper.myLooper()方法,這個方法其實就是返回的ThreadLocal里保存的當(dāng)前線程的Looper,這也就解釋了為什么我們在主線程中這樣new沒有問題,子線程中如果不先Looper.prepare會拋出異常的原因,前面多次說了,因為ActivityThread會在初始化的時候創(chuàng)建自己的Looper。
    主線程的消息循環(huán):


    這里寫圖片描述

Android 的線程和線程池

Bitmap 的加載和 Cache

綜合技術(shù)

JNI 和 NDK 編程

Android 性能優(yōu)化

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內(nèi)容