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

Activity 的生命周期和啟動(dòng)模式

Tips

  • 新Activity是透明主題時(shí)圆恤,舊Activity不會(huì)走onStop宠纯;
  • Activity切換時(shí)伐坏,舊Activity的onPause會(huì)先執(zhí)行禀综,然后才會(huì)啟動(dòng)新的Activity掠廓;
  • Activity在異常情況下被回收時(shí)换怖,onSaveInstanceState方法會(huì)被回調(diào),回調(diào)時(shí)機(jī)是在onStop之前蟀瞧,當(dāng)Activity被重新創(chuàng)建的時(shí) 候沉颂,onRestoreInstanceState方法會(huì)被回調(diào),時(shí)序在onStart之后悦污;
  • 標(biāo)識(shí)Activity任務(wù)棧名稱的屬性:TaskAffinity铸屉,默認(rèn)為應(yīng)用包名。

Activity的LaunchMode

  1. standard 系統(tǒng)默認(rèn)切端。每次啟動(dòng)會(huì)重新創(chuàng)建新的實(shí)例彻坛,誰啟動(dòng)了這個(gè)Activity,這個(gè)Activity就在誰的棧里。
  2. singleTop 棧頂復(fù)用模式昌屉。該Activity的onNewIntent方法會(huì)被回調(diào)钙蒙,onCreate和onStart并不會(huì)被調(diào)用。
  3. singleTask 棧內(nèi)復(fù)用模式间驮。只要該Activity在一個(gè)棧中存在躬厌,都不會(huì)重新創(chuàng)建,onNewIntent會(huì)被回調(diào)竞帽。如果不存在扛施,系統(tǒng)會(huì)先尋找是否存在需要的棧,如果不存在該棧屹篓,就創(chuàng)建一個(gè)任務(wù)棧疙渣,然后把這個(gè)Activity放進(jìn)去;如果存在堆巧,就會(huì)創(chuàng)建到已經(jīng)存在的這個(gè)棧中昌阿。
  4. singleInstance。具有此種模式的Activity只能單獨(dú)存在于一個(gè)任務(wù)棧恳邀。

IntentFilter匹配規(guī)則懦冰。

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

IPC 機(jī)制

使用android:process會(huì)帶來的問題

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

Binder的工作機(jī)制

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

Android中的IPC方式

  1. Bundle

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

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

  4. AIDL
    4.1. AIDL支持的數(shù)據(jù)類型:基本數(shù)據(jù)類型;String和CharSequence颁糟;List只支持ArrayList航背,里面每個(gè)元素都必須被AIDL支持;Map只支持HashMap棱貌,里面每個(gè)元素都必須被AIDL支持(包括key和value)玖媚;Parcelable;AIDL接口本身婚脱;
    4.2. 服務(wù)端可以使用CopyOnWriteArrayList和ConcurrentHashMap來進(jìn)行自動(dòng)線程同步今魔,客戶端拿到的依然是ArrayList和HashMap勺像;
    4.3. 服務(wù)端和客戶端之間做監(jiān)聽器,服務(wù)端需要使用RemoteCallbackList错森,否則客戶端的監(jiān)聽器無法收到通知(因?yàn)榉?wù)端實(shí)質(zhì)還是一份新的序列化后的監(jiān)聽器實(shí)例咏删,并不是客戶端那份);
    dd. 客戶端調(diào)用遠(yuǎn)程服務(wù)方法時(shí)问词,因?yàn)檫h(yuǎn)程方法運(yùn)行在服務(wù)端的binder線程池中督函,同時(shí)客戶端線程會(huì)被掛起,所以如果該方法過于耗時(shí)激挪,而客戶端又是UI線程辰狡,會(huì)導(dǎo)致ANR,所以當(dāng)確認(rèn)該遠(yuǎn)程方法是耗時(shí)操作時(shí)垄分,應(yīng)避免客戶端在UI線程中調(diào)用該方法宛篇。同理,當(dāng)服務(wù)器調(diào)用客戶端的listener方法時(shí)薄湿,該方法也運(yùn)行在客戶端的binder線程池中叫倍,所以如果該方法也是耗時(shí)操作,請(qǐng)確認(rèn)運(yùn)行在服務(wù)端的非UI線程中豺瘤。另外吆倦,因?yàn)榭蛻舳说幕卣{(diào)listener運(yùn)行在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)限驗(yàn)證泛领。

  5. ContentProvider

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

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

View 的事件體系

View的定義

可以把View理解成組合模式里的葉子結(jié)點(diǎn)和有枝節(jié)點(diǎn)的關(guān)系,本質(zhì)都是Composite鸵鸥,而這里本質(zhì)ViewGroup和View都是View奠滑,組合模式最大的好處就是遍歷的時(shí)候不用關(guān)注是怎樣的結(jié)點(diǎn)丹皱,因?yàn)槌橄蠖际且粯拥摹?/p>

View的位置參數(shù)

  1. View的寬高和坐標(biāo)關(guān)系:width = right - left,height = top - bottom宋税。
  2. View在平移過程中摊崭,top和left表示的是原始左上角的位置信息,其值不會(huì)改變杰赛,發(fā)生改變的是x呢簸、y、translationX乏屯、translationY這四個(gè)參數(shù)根时,x是View左上角的坐標(biāo),translation是view移動(dòng)后相對(duì)于父容器(這里其實(shí)就是剛才說的左上角)的偏移量辰晕,所以有x = left + translationX蛤迎。y的原理相同。

MotionEvent典型事件:ACTION_DOWN含友, ACTION_MOVE, ACTION_UP替裆。

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

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

View的滑動(dòng)

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

Scoller

  1. TouchEvent的ACTION_UP事件中匙握,用戶滑動(dòng)速度很快,但是滑動(dòng)距離又不足以“翻頁”的時(shí)候陈轿,通過scroller來幫助用戶scrollBy掉滑動(dòng)一頁還需要的dx或dy圈纺。
  2. 當(dāng)用戶滑動(dòng)到最上端或最下端時(shí),我們?nèi)匀辉试S用戶繼續(xù)滑動(dòng)麦射,但是一旦松手赠堵,就把頁面彈回到最上端和最下端的位置,用IOS的用戶都知道IOS幾乎所有頁面都有這個(gè)彈性效果法褥,用戶體驗(yàn)非常好茫叭,其實(shí)我們用scroller也能輕松實(shí)現(xiàn)。
  3. 第三種場(chǎng)景和第一種類似半等,大家依然可以參考上面發(fā)的那篇仿網(wǎng)易的blog揍愁,現(xiàn)在不是翻頁,當(dāng)用戶滑動(dòng)的加速度很大的時(shí)候杀饵,我們認(rèn)為用戶需要滑動(dòng)的距離肯定是不只他手從放下到松開的那段距離的莽囤,所以這種情況下我們需要通過scroller幫助用戶去多滑一段,這個(gè)距離具體設(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)先級(jí)高于onTouchEvent。
  2. 事件傳遞順序:Activity -> Window -> View蔚叨,如果View都不處理床蜘,最終將由Activity的onTouchEvent處理。
  3. 一些結(jié)論:攔截的一定是事件序列蔑水;不消耗ACTION_DOWN邢锯,則事件序列都會(huì)由其父元素處理;只消耗ACTION_DOWN事件搀别,該事件會(huì)消失丹擎,消失的事件最終會(huì)交給Activity來處理;requestDisallowInterceptTouchEvent方法可以在子元素中干預(yù)父元素的事件分發(fā)過程领曼,除了ACTION_DOWN鸥鹉;

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

  1. Window的實(shí)現(xiàn)類為PhoneWindow蛮穿。

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

  3. View的滑動(dòng)沖突處理庶骄,普通需求基本直接復(fù)用代碼就能搞定毁渗。但是如果想要深刻理解,只有自己多寫多測(cè)多讀代碼单刁,才能很好的掌握灸异,對(duì)于自定義View來說,掌握這個(gè)專題將對(duì)你的功力有大幅的提升羔飞。

View 的工作原理

ViewRoot和DecorView

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

MeasureSpec

  1. 復(fù)習(xí)下位運(yùn)算…… a) <<左移擎鸠,>>右移缀磕,所以源碼中EXACTLY = 1 << MODE_SHIFT就相當(dāng)于010000……..0000(30個(gè)0),其他可類推劣光; b) &運(yùn)算 0011 & 1100 = 0000袜蚕,按位與;|或運(yùn)算 0011 | 1100 = 1111绢涡,按位或廷没; c)~取反運(yùn)算,~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時(shí)亡鼠,如果父容器是精確模式,則子View也為精確模式且為父容器的剩余空間大蟹蟠间涵;如果父容器是wrap_content,則子View也是wrap_content且不會(huì)超過父容器的剩余空間榜揖。
    c. 子View為wrap_content時(shí)勾哩,無論父View是精確還是wrap_content,子View的模式總是wrap_content举哟,且不會(huì)超過父容器的剩余空間思劳。

View的工作流程:

onMeasure, onLayout, onDraw

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

自定義View:

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

理解RemoteViews

Notification

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

AppWidgetProvider

  1. 本質(zhì)是一個(gè)廣播,配置步驟:定義界面xml,定義配置信息xml拓哺,定義實(shí)現(xiàn)類(繼承AppWidgetProvider)勇垛,AndroidManifest中聲明。
  2. 重要回調(diào):onEnable士鸥,第一次被添加時(shí)調(diào)用闲孤,只有一次;onUpdate础淤,添加或更新時(shí)回調(diào)崭放;onDelete哨苛,每次刪除時(shí)回調(diào)鸽凶;onDisable,最后一次刪除時(shí)回調(diào)建峭;onReceive玻侥,接收廣播的action。

PendingIntent

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

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. 簡(jiǎn)單歸納,客戶端的remoteViews通過action對(duì)象晾蜘,由binder機(jī)制來更新服務(wù)端的remoteViews邻眷,所以RemoteViews本身也實(shí)現(xiàn)了Parcelable接口(參考圖Page233)
  3. RemoteViews中真正操作View的方法apply和reapply,前者會(huì)加載布局并更新界面剔交,后者則只更新界面肆饶。

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

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

Android 的 Drawable

Android 動(dòng)畫深入分析

理解 Window 和 WindowManager

一些基礎(chǔ)知識(shí):

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

常用的WindowManager.LayoutParams的Flag和Type

  1. FLAG: FLAG_NOT_FOCUSABLE,當(dāng)前Window不獲取焦點(diǎn)洒放,也不接收各種輸入事件蛉鹿,會(huì)同時(shí)啟用FLAG_NOT_TOUCH_MODAL,事件會(huì)傳遞給下層具有焦點(diǎn)的Window往湿。 FLAG_NOT_TOUCH_MODAL妖异,當(dāng)前Window區(qū)域外的單擊事件傳遞給底層,區(qū)域內(nèi)的單擊事件自己處理领追,一般都需要開啟他膳。 FLAG_SHOW_WHEN_LOCKED,可以讓W(xué)indow顯示在鎖屏界面上绒窑。
  2. Type: 應(yīng)用Window棕孙,一般對(duì)應(yīng)一個(gè)Activity。層級(jí)范圍1~99些膨。 子Window蟀俊,不能單獨(dú)存在,需要特定的父Window订雾,比如一般的Dialog肢预。層級(jí)范圍1000~1999。 系統(tǒng)Window洼哎,需要權(quán)限聲明烫映,比如Toast。層級(jí)范圍2000~2999谱净。一般可以選用WindowManager.LayoutParams.TYPE_SYSTEM_ERROR窑邦,同時(shí)聲明權(quán)限。
  3. WindowManager提供的功能:addView壕探,updateViewLayout冈钦,removeView

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

Window并不實(shí)際存在,以View的形式存在李请。每個(gè)Window對(duì)應(yīng)著一個(gè)View和ViewRootImpl瞧筛,Window和View通過ViewRootImpl建立聯(lián)系。所以在實(shí)際使用中其實(shí)我們并不能訪問到真正的Window导盅,而只能通過WindowManager较幌。

  1. 幾個(gè)重要的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的添加過程,注意其實(shí)這里是個(gè)IPC過程岛琼,最終會(huì)通過WindowManagerService的addWindow方法來實(shí)現(xiàn)Window的添加底循。

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

  4. Window的更新過程
    a. WindowManagerGlobal的updateViewLayout魔慷;
    b. 更新View的LayoutParams;
    c. 更新ViewImple的LayoutParams著恩,實(shí)現(xiàn)對(duì)View的重新測(cè)量院尔,布局,重繪喉誊;
    d. 通過WindowSession更新Window的視圖邀摆,WindowManagerService.relayoutWindow()。

Window的創(chuàng)建過程

  1. Activity
    a. Activity的attach方法中伍茄,系統(tǒng)會(huì)創(chuàng)建Activity所屬的Window并為其設(shè)置回調(diào)栋盹;
    b. Window對(duì)象的創(chuàng)建通過PolicyManager的makeNewWindow方法;
    c. Window的具體實(shí)現(xiàn)是PhoneWindow類敷矫;
    d. Window創(chuàng)建好之后例获,通過PhoneWindow的setContentView將Activity與Window進(jìn)行關(guān)聯(lián),這個(gè)方法大致步驟:
    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的時(shí)候會(huì)調(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)閉時(shí)端壳,會(huì)通過WindowManager來移除DecorView告丢,方法為removeViewImmediate(mDecor);
    e. 想要?jiǎng)?chuàng)建一個(gè)使用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(下文簡(jiǎn)稱NMS)訪問Toast的TN接口颅湘;
    b. Toast屬于系統(tǒng)Window,內(nèi)部視圖mNextView一種為系統(tǒng)默認(rèn)樣式栗精,另一種通過setView方法來指定一個(gè)自定義View闯参。
    c. TN是一個(gè)Binder類,NMS處理Toast的顯示隱藏請(qǐng)求時(shí)會(huì)跨進(jìn)程回調(diào)TN中的方法悲立,所以TN運(yùn)行在Binder線程池中鹿寨,所以需要handler切換到當(dāng)前發(fā)送Toast請(qǐng)求的線程中,也就是說沒有Looper的線程是無法彈出Toast的薪夕。
    d. Toast的show方法調(diào)用了NMS的enqueueToast方法脚草,該方法先將Toast請(qǐng)求封裝成ToastRecord并丟入mToastQueue隊(duì)列中(非系統(tǒng)應(yīng)用最多塞50個(gè))。
    e. NMS通過showNextToastLocked方法來顯示當(dāng)前View原献,Toast顯示由ToastRecord的callback方法中的show方法完成馏慨,callback其實(shí)就是TN對(duì)象的遠(yuǎn)程Binder,所以最終調(diào)用的是TN中的方法姑隅,并運(yùn)行在發(fā)起Toast請(qǐng)求應(yīng)用的Binder線程池中写隶。
    f. 顯示以后,NMS通過scheduleTimeoutLocked方法發(fā)送延時(shí)消息讲仰,延時(shí)后NMS通過cancelToastLocked方法來隱藏Toast并從隊(duì)列中移除慕趴,隱藏依然通過ToastRecord的callback中的hide方法實(shí)現(xiàn)。
    g. callback回調(diào)TN的show和hide方法后叮盘,會(huì)通過handler發(fā)送兩個(gè)Runnable秩贰,里面的handleShow和handleHide方法是真正完成顯示和隱藏Toast的地方。handleShow方法中將Toast的視圖添加到Window中柔吼,handleHide方法將Toast視圖從Window中移除毒费。

四大組件的工作過程

四大組件概述:

  1. Activity的主要作用是展示一個(gè)界面并和用戶交互,它扮演的是一種前臺(tái)界面的角色愈魏。
  2. Service是一種計(jì)算型組件觅玻,用于在后臺(tái)執(zhí)行一系列計(jì)算任務(wù)想际,但因?yàn)槠浔旧磉€是運(yùn)行在主線程中的,因此耗時(shí)的后臺(tái)計(jì)算仍然需要在單獨(dú)的線程中去完成溪厘。
  3. BroadcastReceiver是一種消息型組件胡本,用于在不同的組件乃至不同的應(yīng)用之間傳遞消息。廣播注冊(cè)有兩種方式畸悬,動(dòng)態(tài)注冊(cè)通過Context.registerReceiver()來實(shí)現(xiàn)侧甫,必須要應(yīng)用啟動(dòng)才能注冊(cè);靜態(tài)注冊(cè)則在AndroidManifest文件中進(jìn)行蹋宦,應(yīng)用安裝時(shí)會(huì)被系統(tǒng)解析披粟,不需要啟動(dò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)并非隊(duì)列,而是單鏈表思灌,它只是用來存儲(chǔ)數(shù)據(jù)俺叭。
  2. Looper是真正的數(shù)據(jù)處理者,線程默認(rèn)沒有Looper习瑰,使用Handler必須為線程創(chuàng)建Looper绪颖,UI線程也就是ActivityThread創(chuàng)建時(shí)會(huì)初始化Looper,所以主線程中默認(rèn)可以直接使用Handler甜奄。
    UI線程檢查當(dāng)前線程的操作在ViewRootImpl的checkThread方法中,我們常見的不能在子線程中訪問view的異常就是在這里拋出的窃款。
    不允許子線程訪問主線程的原因是UI控件不是線程安全的课兄,而加鎖又會(huì)導(dǎo)致UI的操作過于復(fù)雜。
  3. Handler的工作過程晨继,圖(page374)烟阐,單概括一下就是:假設(shè)創(chuàng)建Handler的線程是A,耗時(shí)操作的線程是B紊扬,B線程中拿到handler實(shí)例發(fā)送消息或者post一個(gè)Runnable蜒茄,實(shí)際是調(diào)用了MessageQueue的enqueueMessage方法,進(jìn)入了消息隊(duì)列餐屎,線程A中的Looper發(fā)現(xiàn)了這個(gè)消息檀葛,就會(huì)處理這個(gè)消息,也就是消息中的Runnable或者h(yuǎn)andler的handleMessage方法會(huì)被調(diào)用腹缩,這樣handler中的業(yè)務(wù)邏輯就被切換到線程A中去了屿聋。
  4. ThreadLocal看上去只new了一份空扎,但在每個(gè)不同的線程中卻可以擁有不同數(shù)據(jù)副本的神奇類。其本質(zhì)是ThreadLocal中的Values類維護(hù)了一個(gè)Object[]润讥,而每個(gè)Thread類中有一個(gè)ThreadLocal.Values成員转锈,當(dāng)調(diào)用ThreadLocal的set方法時(shí),其實(shí)是根據(jù)一定規(guī)則把這個(gè)線程中對(duì)應(yīng)的ThreadLocal值塞進(jìn)了Values的Object[]數(shù)組中的某個(gè)index里楚殿。這個(gè)index總是為ThreadLocal的reference字段所標(biāo)識(shí)的對(duì)象的下一個(gè)位置撮慨。

三大件原理

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


    這里寫圖片描述

Android 的線程和線程池

Bitmap 的加載和 Cache

綜合技術(shù)

JNI 和 NDK 編程

Android 性能優(yōu)化

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末诊霹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子渣淳,更是在濱河造成了極大的恐慌脾还,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件入愧,死亡現(xiàn)場(chǎng)離奇詭異鄙漏,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)棺蛛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門怔蚌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人旁赊,你說我怎么就攤上這事桦踊。” “怎么了终畅?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵籍胯,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我离福,道長(zhǎng)杖狼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任妖爷,我火速辦了婚禮蝶涩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘絮识。我一直安慰自己绿聘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布笋除。 她就那樣靜靜地躺著斜友,像睡著了一般。 火紅的嫁衣襯著肌膚如雪垃它。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天烹看,我揣著相機(jī)與錄音国拇,去河邊找鬼。 笑死惯殊,一個(gè)胖子當(dāng)著我的面吹牛酱吝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播土思,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼务热,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼忆嗜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起崎岂,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤捆毫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后冲甘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绩卤,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年江醇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了濒憋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡陶夜,死狀恐怖凛驮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情条辟,我是刑警寧澤黔夭,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站捂贿,受9級(jí)特大地震影響纠修,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜厂僧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一扣草、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颜屠,春花似錦辰妙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至粗井,卻和暖如春尔破,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浇衬。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工懒构, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人耘擂。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓胆剧,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親醉冤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秩霍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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