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

第一章:Activity生命周期和啟動模式
  1. Activity關(guān)閉時會調(diào)用onPause()onStop(),如果下一個Activity是透明主題,則當(dāng)前Activity不會調(diào)onStop()。
  2. 啟動Activity的請求會由Instrumentation來處理,然后它通過Binder向AMS發(fā)請求,AMS內(nèi)部維護(hù)著一個ActivityStatck并負(fù)責(zé)Activity的狀態(tài)同步,AMS通過ApplicationThread(Binder)回調(diào)通知ActivityThread再去同步Activity狀態(tài)從而完成主線程生命周期回調(diào)。
  3. 前一個Activity的onPause()先調(diào)用后才后啟動下一個Activity,因此不要在onPause()里做太耗時的操作;后一個Activity啟動后,即onResume()后前一個Activity再繼續(xù)調(diào)onSaveInstanceState()onStop()。
  4. onSaveInstanceState()方法只有在系統(tǒng)異常終止時才會被調(diào)用,其在onStop()之前,不確定與onPause()的順序(Api11之前,在onPause之前;在Api11之后調(diào)整到了onPause之后onStop之前);onRestoreInstanceState()onStart()之后,方法中可以直接獲取到Bundle值不用判空,而onCreate()方法中的Bundle需要做判空。View層次的恢復(fù)過程為Activity->Window->DecorView逐步向上委托,再由上到下通知子View來保存元素。
  5. 一般給Manifest清單中的configChanges配置orientation|screenSize可避免Activity重啟。
  6. Activity啟動模式:
    ? standard:標(biāo)準(zhǔn)模式,始終創(chuàng)建新實(shí)例,誰啟動Activity,被啟動的Activity就和誰在同一個任務(wù)棧;非Activity類型的Context不能啟動Activity,除非指定標(biāo)記位為FLAG_ACTIVITY_NEW_TASK。
    ? singleTop:棧頂復(fù)用模式,Activity如果已在棧頂則不會被新建且會調(diào)用onNewIntent()方法,否則創(chuàng)建新實(shí)例與standard效果相同。以上兩個模式對于TaskAffinity屬性無影響,即使設(shè)置了不同的affinity,也不會啟動新的任務(wù)棧。
    ? singleTask:棧內(nèi)復(fù)用模式,這個比較特殊,會先尋找是否已有所需任務(wù)棧存在,不存在則創(chuàng)建指定任務(wù)棧并放入,存在則將棧拉到前臺,沒有實(shí)例則創(chuàng)建Activity,已有則調(diào)用onNewIntent()方法,自帶newTask和clearTop標(biāo)志行為清空頂部其他Activity(這個其實(shí)就是對比TaskAffinity是否相同,默認(rèn)同一個應(yīng)用下是包名,相同則不啟動新的任務(wù)棧);TaskAffinity參數(shù)可指定了任務(wù)棧名稱,默認(rèn)是包名,新指定不能和包名相同,一般與allowTaskReparenting屬性配對使用,allowTaskReparenting使得在應(yīng)用A中啟動應(yīng)用B中的某個Activity,這個Activity可以與應(yīng)用A處于同一任務(wù)棧,如果再去啟動應(yīng)用B,則這個Activity又被轉(zhuǎn)移到應(yīng)用B的Activity棧中。
    ? singleInstance:單實(shí)例模式,Activity獨(dú)自存在于一個任務(wù)棧,后續(xù)不會再創(chuàng)建新的(除非這個棧被銷毀了),用于多應(yīng)用共享Activity。
  7. Flags:FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS標(biāo)志位可以使被啟動的Activity不出現(xiàn)在歷史棧列表中,可以用于承接跳轉(zhuǎn)Activity使用。
  8. IntentFilter匹配規(guī)則:
    ? IntentFilter中的過濾信息有action、category、data等參數(shù)。
    ? 一個Activity中可以有多組intent-filter,一個intent只要匹配其中一組即可。
    ? action:intent中的action參數(shù)能匹配到filter中任何一個即可,區(qū)分大小寫;且清單中必須要有一個action參數(shù),否則一定失敗。
    ? category:intent中可以沒有category參數(shù)(系統(tǒng)默認(rèn)會加上一個default參數(shù)),要是有的話必須每個category都能和filter中的某一個匹配才行;清單filter中必須手動加一個DEFAUL。
    ? data:由scheme://host:port/path/pathPrefix/pathPattern組成;URI中scheme和host是必須的,scheme默認(rèn)為content或file;intent中必須含有data數(shù)據(jù)且需要和filter中某一條匹配。
    ? data和type必須一起設(shè)置intent.setDataAndType()才有效,否則會相互覆蓋。
  9. PackageManager或Intent的resolveActivity()方法和PackageManager的queryIntentActivities()方法可以返回匹配的intent,flag參數(shù)傳MATCH_DEFAULT_ONLY代表只找Activity,這樣可以事先判空避免找不到而崩潰。
  10. Activity被回收時?;謴?fù)情況:
    ? 場景:如果是手動強(qiáng)行殺死進(jìn)程,任何生命周期都不會被回調(diào)(考慮下要不要保留狀態(tài)什么的);如果是Activity棧在后臺由于內(nèi)存不足被回收,則棧順序會被保存,優(yōu)先恢復(fù)棧頂Activity(其余Activity還沒有創(chuàng)建),點(diǎn)擊back返回時,其余Activity再創(chuàng)建并顯示;如果是前臺Activity棧由于崩潰退出,恢復(fù)時只恢復(fù)到上一個Activity。
    ? 問題:?;謴?fù)中,只能創(chuàng)建并恢復(fù)棧頂Activity,這時前面的Activity還沒有創(chuàng)建,所以如果被恢復(fù)的棧頂Activity使用了前面Activity的靜態(tài)引用或者依賴前面Activity的數(shù)據(jù)則會出現(xiàn)問題。
    ? 解決:可以在onSaveInstanceState()onRestoreInstanceState()方法中做手腳來恢復(fù)數(shù)據(jù);如果簡單一些的話,可以自己在內(nèi)存維護(hù)一個標(biāo)志,在BaseActivity中判斷,如果標(biāo)志被置位則認(rèn)為是進(jìn)程重啟了,這時候重啟到LauncherActivity并殺死自己,需要將intent設(shè)置intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)。
第二章:IPC機(jī)制
  1. 線程是CPU調(diào)度的最小單元,線程是一種有限的系統(tǒng)資源;進(jìn)程是一個執(zhí)行單元,一般在PC或移動設(shè)備中指一個程序或應(yīng)用;一個進(jìn)程中可以包含多個線程,線程間數(shù)據(jù)共享,進(jìn)程間數(shù)據(jù)獨(dú)享。
  2. 通過給組件設(shè)置android:process=":remote"可開啟多進(jìn)程模式;":"代表在包名后面拼接進(jìn)程名,也可直接指定進(jìn)程全名。
  3. 進(jìn)程名以":"開頭的屬于當(dāng)前應(yīng)用的私有進(jìn)程,其他組件不能和它跑在一起,不以":"開頭的為全局進(jìn)程,通過shareUID方式可以將不同組件跑在同一進(jìn)程,但是簽名需要一致。
  4. 多進(jìn)程模式中,不同進(jìn)程組件會擁有獨(dú)立的虛擬機(jī)、Application以及內(nèi)存空間,數(shù)據(jù)不會共享,因此會出現(xiàn)單例失效、同步機(jī)制失效、SP讀寫不安全(多進(jìn)程文件讀寫存在并發(fā))、多個Application等情況。
  5. 實(shí)現(xiàn)Serializable接口(Java提供)并定義serialVersionUID(用于判斷反序列化時對象是否一致用的)可實(shí)現(xiàn)序列化,使用簡單但開銷大,需要大量IO操作,適用于持久化到設(shè)備或網(wǎng)絡(luò);Android特有實(shí)現(xiàn)Parcelable接口也可實(shí)現(xiàn)序列化,使用稍麻煩,效率高,適用于內(nèi)存序列化,傳值等;注意靜態(tài)成員不參數(shù)序列化,transient關(guān)鍵字標(biāo)記的成員變量不參與序列化。
  6. Binder是Android中的一個類,實(shí)現(xiàn)了IBinder接口,它是Android中的一種跨進(jìn)程通信方式,是鏈接各個Manager和ManagerService的橋梁。
  7. Binder可以理解為一種進(jìn)程間通信規(guī)范,因?yàn)閷?shí)現(xiàn)了IBinder接口,我們平時用的Stub繼承自Binder,且實(shí)現(xiàn)了業(yè)務(wù)接口,提供兩個接口的相互轉(zhuǎn)換方法asBinder()asInterface(),供客戶端使用;創(chuàng)建.aidl文件只是為了方便系統(tǒng)自動生成規(guī)范的.java代碼而已。
  8. 客戶端向遠(yuǎn)程發(fā)起請求后,客戶端線程將掛起,直到遠(yuǎn)程回復(fù),因此對客戶端來說遠(yuǎn)程調(diào)用可能是耗時的,要在子線程中發(fā)起;對于服務(wù)端來說,Binder方法是運(yùn)行在Binder線程池中的,因此同步調(diào)用即可。
  9. Binder提供了linkToDeath()unlinkToDeath()方法來給設(shè)置死亡代理,當(dāng)遠(yuǎn)程進(jìn)程異常時IBinder.DeathRecipient類的binderDied()方法會被回調(diào),客戶端可以收到回調(diào)并重連遠(yuǎn)程Service;Binder的方法isBinderAlive()也可以判斷Binder是否死亡。
  10. Android中的幾種IPC方式:
    ①Intent:可以使用Bundle傳輸數(shù)據(jù),它也實(shí)現(xiàn)了Parcelable接口,可傳輸基本數(shù)據(jù)類型和可序列化后的Object。
    ②文件共享:Linux沒有對文件并發(fā)讀寫做限制,因此要自己控制并發(fā)問題;SP文件也一樣,系統(tǒng)會在內(nèi)存緩存一份SP文件,在多進(jìn)程模式下,也是線程不安全的。
    ③Messenger:本質(zhì)是對AIDL的封裝,底層也是通過AIDL實(shí)現(xiàn);可通過Handler或IBinder構(gòu)建Messenger,服務(wù)端通過Handler構(gòu)建,返回msgner.getBinder(),客戶端通過IBinder構(gòu)建,使用msgner.send(msg)發(fā)數(shù)據(jù),可帶replyTo字段,傳入自己用Handler構(gòu)建的Messenger,收服務(wù)端的回調(diào);消息順序執(zhí),一次處理一個請求,不用處理并發(fā)問題,且只能攜帶msg支持的參數(shù),局限性還是比較大的。
    ④AIDL:
    ? IBinder是統(tǒng)的進(jìn)程間通信規(guī)范,我們定義的.aidl文件實(shí)際上就是為了讓系統(tǒng)生成.java文件,內(nèi)含Stub類,實(shí)現(xiàn)了IBinder和業(yè)務(wù)接口,主要用的就是它作為Binder返回給客戶端,服務(wù)端實(shí)現(xiàn)好定義的業(yè)務(wù)接口并將它在onBind()方法中返回即可。
    ? AIDL支持的數(shù)據(jù)類型有6種:基本數(shù)據(jù)類型、String和CharSequence、List、Map、Parcelable和AIDL接口本身。自定義對象也要定義.aidl文件聲明為parcelable類型,自定義的Parcelable對象就算在同一個包里也要現(xiàn)顯式引用import;接口方法中除了基本數(shù)據(jù)類型,其它類型必須標(biāo)上in、out或inout方向(服務(wù)端給客戶端推送,服務(wù)端相當(dāng)于客戶端,要用in)。
    ? AIDL接口中只支持方法,不支持靜態(tài)常量,這點(diǎn)區(qū)別于普通接口。
    ? 客戶端和服務(wù)端包含一套相同的.aidl包結(jié)構(gòu),序列化和反序列化時會用到其中相關(guān)的類,一部分邏輯在客戶端執(zhí)行,一部分邏輯在服務(wù)端執(zhí)行。
    ? Binder中的方法調(diào)用都是在Binder線程池中的,是異步的,所以客戶端收到服務(wù)端回調(diào),如果需要切到主線程的話要使用Handler。
    ? 對象本身是不能跨進(jìn)程傳輸?shù)?,涉及序列化與反序列化,因此服務(wù)端Binder中傳遞到客戶端的listener會反序列化出新的對象,注冊與解注冊要使用RemoteCallbackList,內(nèi)含map通過binder作為key保存每個listener實(shí)例,客戶端終止后可自動解除注冊且內(nèi)部實(shí)現(xiàn)了線程同步;RemoteCallbackList并不是List,遍歷它要使用beginBroadcast()finishBroadcast()方法,且必須配對使用。
    ? Binder意外死亡時需要重新連接服務(wù),一般可以給Binder設(shè)置死亡代理DeathRecipient監(jiān)聽binderDied()回調(diào)(線程池中調(diào)用的);也可以在onServiceDisconnected()中重連(UI線程中調(diào)用的)。
    ? AIDL遠(yuǎn)程調(diào)用一般要做身份驗(yàn)證,可以在onBind()中返回null,或者在onTracsact()中返回false都能拒絕服務(wù),建議自定義權(quán)限和包名雙重驗(yàn)證。
    ⑤ContentProvider:
    ? 底層也是AIDL實(shí)現(xiàn)。六個方法:onCreate、query、update、insert、delete和getType,只有onCreate在主線程其他都在子線程執(zhí)行;注冊Provider一般定義authorities(uri)、permission(權(quán)限)、process(可運(yùn)行在獨(dú)立進(jìn)程)。
    ? 除了query,update、insert和delete都會引起數(shù)據(jù)源改變,因此數(shù)據(jù)更新后可以context.getContentResolver().notifyChange(uri,null)來通知更新,客戶端可以注冊registerContentObserver()監(jiān)聽數(shù)據(jù)更新(如自動讀取短信驗(yàn)證碼功能)。
    ? provider的query、update、insert和delete四大方法都是存在多線程并發(fā)訪問問題的,要自己做好線程同步(Sqlite單連接的話可讀不可寫,算做了線程同步了)。
    ⑥Socket:
    ? 分為流套接字和數(shù)據(jù)報套接字,對應(yīng)TCP和UDP協(xié)議,TCP是面向連接的(需要三次握手),提供穩(wěn)定的雙向通信,而UDP是無連接的,提供不穩(wěn)定的單向通信(效率高但不保證數(shù)據(jù)正確傳輸)。
    ? socket不僅可以進(jìn)程間通信,只要設(shè)備之間的IP地址已知,網(wǎng)絡(luò)通訊也可以。
  11. 一般一個Service對應(yīng)一個Binder(業(yè)務(wù)接口),如果業(yè)務(wù)接口太多,創(chuàng)建太多Service就不合適了,所以一般使用Binder連接池。服務(wù)端就一個Service,創(chuàng)建好實(shí)現(xiàn)各類業(yè)務(wù)接口的實(shí)例后,統(tǒng)一一個queryBinder接口返回給客戶端,客戶端自己去取想要的業(yè)務(wù)接口;客戶端想要哪個自己去取(取出來的是IBinder類型,要自己去強(qiáng)轉(zhuǎn)一下再使用);建議Binder連接池全程單例,并加上超時重連機(jī)制。
  12. AIDL的通信流程:
    ? 由于.aidl代碼包時客戶端和服務(wù)端有相同的兩份,因此有些代碼是運(yùn)行在客戶端,有些代碼是運(yùn)行在服務(wù)端的,且兩邊可以相互充當(dāng)客戶端或服務(wù)端,可以理解為發(fā)出去請求的就是客戶端,回應(yīng)的就是服務(wù)端。
    ? Stub類是AIDL過程的關(guān)鍵,Stub類繼承自Binder,實(shí)現(xiàn)了IBinder接口和業(yè)務(wù)接口,符合遠(yuǎn)程通信的規(guī)范,且Stub內(nèi)部類Proxy在不同進(jìn)程調(diào)用時充當(dāng)Stub代理。
    ? Stub類在服務(wù)端的onBind()方法里返回給客戶端(已實(shí)現(xiàn)了業(yè)務(wù)接口),在客戶端的onServiceConnected()方法里獲取到遠(yuǎn)程服務(wù)端的Stub(可轉(zhuǎn)為業(yè)務(wù)接口)。
    ? 使用Stub.AsInterface(binder)方法時,是調(diào)用的客戶端的方法,Stub會判斷如果是同一個進(jìn)程則返回本地Stub,否則返回Stub.Proxy代理類,封裝遠(yuǎn)程傳過來的Stub(服務(wù)端的Stub),拿到統(tǒng)一的業(yè)務(wù)接口并使用。
    ? 調(diào)用業(yè)務(wù)接口進(jìn)行遠(yuǎn)程方法調(diào)用時,方法中會先從本地生成輸入輸出的pracel對象,然后會調(diào)到mRemote.transact(),系統(tǒng)底層會回調(diào)遠(yuǎn)程Stub里的onTransact()方法,遠(yuǎn)程業(yè)務(wù)接口實(shí)現(xiàn)類將輸入輸入date對象寫回parcel。
    ? 因此注意Stub和Proxy的調(diào)用過程,transact()前的都是在客戶端執(zhí)行,onTransact()后的都是在服務(wù)端執(zhí)行,兩邊的遠(yuǎn)程調(diào)用方法都是在Binder線程池中的,因此是耗時的,客戶端會掛起等待遠(yuǎn)程回復(fù),不能在客戶端直接更新UI線程。
第三章:View的事件體系
  1. 幾種常見的位置相關(guān)類:
    ? View的位置參數(shù):主要由四個頂點(diǎn)top、left、right和bottom決定(相對父容器的位置);3.0以后,增加了x、y、translationX、translationY幾個參數(shù),getLeft()是初始位置(永遠(yuǎn)不會變),getTranslationX()是滑動偏移量,getX()是最終位置,前兩個相加(會變);getX()、getY()是相對父容器的位置(相對位置),getRawX()、getRaxY()是相對手機(jī)屏幕的位置(絕對位置)。
    ? MotionEvent:封裝了一些列事件:ACTION_DOWN、ACTION_MOVE、ACTION_UP、ACTION_CANCEL等。
    ? TouchSlop:ViewConfiguration.get(context).getScaledTouchSlop()獲取,系統(tǒng)認(rèn)為的最小滑動距離,是一個常量,與設(shè)備有關(guān),一般為8dp,優(yōu)化滑動敏感度使用。
    ? VelocityTracker:速度追蹤。通過VelocityTracker.obtain()獲取、tacker.addMovement(event)添加motionEvent、tarcker.computeCurrentVelocity(1000)設(shè)置單位、tarcker.getXVelocity()獲取速度;注意速度值可能為負(fù),因?yàn)?code>速度值 = (終點(diǎn)位置 - 起點(diǎn)位置) / 時間段,也跟設(shè)置的單位時間有關(guān),結(jié)果就是XX像素/XX毫秒。
    ? GestureDetector:手勢檢測。new GestureDetector(this)獲取、detector.onTouchEvent(event)接管motionEvent、構(gòu)造方法里傳的是個監(jiān)聽接口,在那里面獲取回調(diào),一般只有監(jiān)聽雙擊等事件才用這個。
    ? Scoller:計算輔助類。我們只設(shè)置滑動距離和時間,它只幫我們封裝了計算每一段時間和要滑動的距離scoller.getCurrX(),具體滑動要自己做,在View的computeScroll()方法中使用scroller.computeScrollOffset()判斷是否滑動結(jié)束,配合scrollTo()postInvalidate()完成分段滑動。
  2. View的滑動:
    ? Scoller、ValueAnimator和Handler等都是只計算了滑動數(shù)值,是計算輔助類,而真正的滑動只能通過View自身的scrollTo()scrollBy()實(shí)現(xiàn)。
    ? scrollTo()是絕對滑動,scrollBy()內(nèi)部也是調(diào)用了scrollTo(),是相對滑動;自定義View時srollTo()之后要postInvalidate()來重繪view,如果是在外部調(diào)用的話不用重繪。

注意:scrollTo()scrollBy()只能改變View內(nèi)容的滑動,并不能改變View本身的位置;滑動數(shù)值等于View的邊緣-View內(nèi)容的邊緣,因此有正負(fù)值。理解記憶:想看更多內(nèi)容,往下滑內(nèi)容,為正;想看之前的內(nèi)容,往上滑內(nèi)容,為負(fù)。

  1. 改變布局參數(shù)layoutParams之后可以給View再次設(shè)置view.setLayoutParams(params),也可以直接view.requestLayout()(這個params必須是view.getLayoutParams()獲取的,修改的是同一個引用)。
  2. ViewHelper.getTranslationX()可以獲取view的滑動偏移量來改變view的距離,其實(shí)ViewHelper是nineoldandroids里的View兼容庫,view本身有許多get和set方法可以直接用。
  3. View事件分發(fā)機(jī)制:
    ? 三種事件類型:dispatchTouchEvent()分發(fā),事件能發(fā)給該View一定會調(diào)用,內(nèi)部會調(diào)后兩個方法、onInterceptTouchEvent()攔截,攔截一次后不會再次判斷了,直接調(diào)本層的下一個方法、onTouchEvent()處理,如果返回fasle,后面的同一個事件序列不會再發(fā)給它。
    ? 調(diào)用順序:先分發(fā)、再內(nèi)部判斷攔截、攔截則消費(fèi)、否則繼續(xù)向下分發(fā)。
    ? 幾種回調(diào)優(yōu)先級順序:(Acitivity->PhoneWindow->DecorView->ContentView) -> dispatchTouchEvent() > onInterceptTouchEvent() > onTouchListener() > onTouchEvent() > onLongClickListener() > onClickListener()
    事件分發(fā)的一些結(jié)論:
    ①一個事件序列只能由一個View來攔截且消耗,一錘子買賣,因?yàn)閂iewGroup中會保存mMotionTarget為消耗事件的子View,后面的事件序列直接發(fā)給它 。
    ②一個ViewGroup只要決定攔截,onInterceptTouchEvent()只會調(diào)一次,后面就不再次判斷了。
    ③子View如果不消耗ACTION_DOWN事件,即onTouchEvent()返回false后,以后的事件序列就不再傳給它了,后面依次往上判斷調(diào)用父View的onTouchEvent()了,都返回false就給Activity處理了。
    ④DOWN、MOVE、UP每一個事件都會從上往下傳,都可能被攔截,onTouchEvent()返回true只是改變了傳遞深度而已,因此除了Down,每一層可以通過requestDisallowInterceptTouchEvent()來改變父View的攔截限制;如果子View消耗了DOWN卻沒有消耗后續(xù)事件序列,這些事件仍會傳過來,最終這些消失的事件會傳到Activity那里。
    ⑤ViewGroup默認(rèn)不攔截事件,即onInterceptTouchEvent()返回false,View默認(rèn)消耗事件,即onTouchEvent()返回true,不過要看view是否是可點(diǎn)擊的(clickable()值),button等默認(rèn)是true,textview等默認(rèn)是false。
    ⑥enable屬性不影響onTouchEvent()的默認(rèn)返回值,只要view是可點(diǎn)擊的(clickable為true),它就默認(rèn)消耗事件。
  4. 事件分發(fā)流程:
    ①M(fèi)otionEvent從Activity發(fā)出,如果Window上的所有View都不處理,即getWindow().superDispatchTouchEvent()返回了false,最后調(diào)用Activity自己的onTouchEvent()
    ②ViewGroup的onInterceptTouchEvent()方法不是每次都調(diào)用,只有Down事件或非Down事件且有子View處理事件(就是說子View處理了Down后的Move或Up事件)會調(diào)用,也就是即使子View處理了事件,ViewGroup還是會判斷攔截Move、Up事件的,這也是我們處理滑動沖突的地方(攔截Move處理),回到上面,如果第一次onInterceptTouchEvent()返回true的話,后面的Move和Up也就不是Down事件且mMotionTarget為空,則不會向子View分發(fā)事件序列了,直接調(diào)super.dispatchTouchEvent(),里面會調(diào)ViewGroup自己的onTouchEvent()。注意Move時子元素也是可以設(shè)置disallowInterceptTouchEvent來禁止父元素就攔截Move和Up事件的。
    ③子View的onTouchListener會屏蔽掉onTouchEvent,如果返回了true,onTouchEvent()就不會被調(diào)用了,onClick點(diǎn)擊事件也就不會被調(diào)用了,這樣是為了方便外界來處理特殊的觸摸事件。
    ④子元素是否可以消耗onTouchEvent()決定于它是否可點(diǎn)擊,也就是clickable是否為true,與enable無關(guān);默認(rèn)longclickable都為false,clickable的話Button類的為true,TextView類的為false;但是任何View只要設(shè)置了onClickListener()onLongClickListener()就會把它的clickable或longclickable顯示置為true,它就可以處理onTouchEvent了。
  5. 處理滑動沖突其實(shí)Down事件控制不了,會一直傳下去,要處理的就是有子View處理Down,且父View再攔截處理后續(xù)的Move和Up事件(一般處理move最多),這時可以根據(jù)業(yè)務(wù)需求來內(nèi)部攔截或者外部攔截來處理滑動事件。
  6. 一般推薦外部攔截法,簡單易用,在ViewGroup中控制Down的時候不攔截,Move的時候選擇性攔截,Up的時候重置為不攔截,攔截了以后在自己的onTouchEvent中就可以處理事件了。
第四章:View的工作原理
  1. ViewRoot對應(yīng)于ViewRootImpl,它是連接WindowManager和DecorView的紐帶(Window-WindowManager-ViewRootImpl-View),View的三大流程均是通過ViewRoot來完成的,即測量、布局和繪制;measure()決定了View的測量寬高,layout()決定了View的最終寬高和四個頂點(diǎn)的位置,draw()將View繪制到屏幕上;View的繪制流程是從ViewRoot的performTraversals()方法開始,經(jīng)過measure、layout和draw三大流程將一個View繪制出來。
  2. performTraversals()方法會依次調(diào)用performMeasure、performLayou和performDrow,分別完成View的三大繪制流程;measure()方法中根據(jù)自身的params和父View的Spec計算出自己的寬高的Spec,然后回調(diào)onMeasure()方法,其中后計算出自己的真實(shí)size去setMeasuredDimension()設(shè)置自己的寬高,如果是ViewGroup還會去計算子View的Spec傳給子View
  3. 三大流程:
    ①measure過程決定了View的寬高,measure以后就能通過getMeasuredWidth()getMeasureHeight()獲取View的測量寬高了,基本就定于View的最終寬高。
    ②layout過程決定了View四個頂點(diǎn)的位置坐標(biāo),layout以后就能通過getTop()、getLeft()、getRight()和getBottom()獲取View的位置了,就能得到getWidth()getHeitht()的值了。
    ③draw過程決定了View最終的顯示,只有draw以后View才能呈現(xiàn)在屏幕上(主線程繪制)。
  4. MeasureSpec:
    ? 是一個32位的int值,高兩位表示SpecMode,低30位表示SpecSize,就是封裝了一個測量值的包裝類,需要用它來確定View的測量值。
    ? SpecMode有三種模式:EXACTLY、AT_MOST、UNSPECIFIED,分別對應(yīng)具體值、wrap_content、第三個不常用,涉及minWidth和background因素。
    ? 子元素的最終大小由子View的layoutParam和父View的spec共同決定(還與margin和padding有關(guān)系),這個過程只是計算spec的基本操作,得到View的spec后回調(diào)View的onMeasure()方法傳入spec,讓View自己再去計算一下寬高size去賦值。
  5. 子View的MeasureSpec的創(chuàng)建規(guī)則:
    ? 當(dāng)子View是固定值的時候,不管父spec是什么,子spec都是EXCATLY,以子size為準(zhǔn)。
    ? 當(dāng)子View是martch_parent的時候,父spec如果是EXACTLY,子spec就是EXACTLY,父spec如果是AT_MOST,子sepc也是AT_MOST,都以父size為準(zhǔn)。
    ? 當(dāng)子View是wrap_content的時候,不管父spec是什么(除UNSPECIFIED外),子spec都是AT_MOST,以父size為準(zhǔn)。
    ? 當(dāng)父spec是UNSPECIFIED的時候,子View除固定值之外也是UNSPECIFIED,且子size為0。
  6. View的工作流程:
    ①measure:
    ? measure()是final類型的,其中算出spec后會回調(diào)onMeasure(),一般我們在onMeasure()中獲取建議的寬高值,自己再計算并設(shè)置setMeasureDimesion()來設(shè)置View的最終的測量值。
    ? 直接繼承自View一般區(qū)分USPECIFIED情況,默認(rèn)size是minSize或backgroundMinSize的值,大多為0;另一種EXACTLY、和AT_MOST,都是使用子spec中的size值(默認(rèn)是parentSize),因此wrap時要自己算寬高否則與match一樣。
    ? ViewGroup是抽象類,有個measureChildren()方法,會調(diào)用child.measure(),根據(jù)自己的sepc和child的layoutParams來得出子spec,最終參數(shù)先去計算子View寬高,然后各個ViewGroup子類定義onMeasure(),用子View的size總之決定自己的size。
    ? onMeasure()可能會被執(zhí)行多次,只有最后一次基本會與最終寬高值相等,因此一個好的習(xí)慣是在onLayout()中去獲取View最終的寬高值。
    ②layout:
    ? ViewGroup在layout()確定位置后,會在onLayout()中遍歷所有子元素并調(diào)用其
    layout()方法來布局子元素,里面又會回調(diào)子元素的onLayout()方法;layout()就是父View來調(diào)用確定你自己的位置,然后會回調(diào)你的onLayout()方法告訴你你的位置參數(shù),然后你在調(diào)子View的layout()去設(shè)置子View的位置。
    ③draw:
    ? draw繪制的過程分四步:繪制背景background.draw(canvas) -> 繪制自己(onDraw()) -> 繪制children(dispatchDraw()) -> 繪制裝飾(donDrawScrollBars());
    View有個默認(rèn)的willNotDraw標(biāo)志,ViewGroup默認(rèn)開啟,View默認(rèn)關(guān)閉,為true的話如果不需要繪制任何內(nèi)容系統(tǒng)會對其進(jìn)行一些優(yōu)化,如果明確知道一個ViewGroup必須要通過onDraw來繪制內(nèi)容時,要顯示關(guān)閉這個WILL_NOT_DRAW標(biāo)志。

注意:
? 由于View的measure過程和Activty的生命周期不是同步的,因此在onCreate()、onStart()、onResume()基本均無法獲取到View的寬高。
? 四種方式獲取View的寬高:1.onWindowFocusChanged()(會多次調(diào)用) 2.view.post(runnable)(推薦) 3.ViewTreeObserver.addOnGlobalLayoutListener()(也行,多次調(diào)用) 4.view.measure()(基本只有子View具體寬高值才行)。
? getMeasuredWidth()和getWidth()基本是相等的,只是前者在measure后確定(測量值),后者在layout后確定(位置相減),兩個順序不一致,且前者可能會被調(diào)用多次,因此,最好使用后者來得到View的寬高值。

  1. 自定義View:
    ①分類:繼承自View重寫onDraw()、繼承自ViewGroup重寫onLayout()、繼承自現(xiàn)有View、繼承自現(xiàn)有ViewGroup。
    ②須知:繼承自View需要處理wrap_content、需要處理padding、不用在View中使用handler,直接post即可、onDetachedFromWindow()時記得停止線程或動畫,否則可能內(nèi)存泄露、處理好滑動沖突等。
    ③流程:
    ? 自定義View在onMeasure()中記得getPadding()處理下padding、根據(jù)業(yè)務(wù)處理下wrap_content的情況;onMeasure()中父View計算好了一套默認(rèn)的寬高specMode和specSize,子View根據(jù)業(yè)務(wù)需要重新計算寬高值并setMeasuredDimension()賦值寬高。
    ? 自定義ViewGroup時在onMeasure()中也是父View計算好了一套specMode和specSize,先調(diào)用measureChildren()計算一下子View大小,完了再根據(jù)子View的getMeasuredWidth()算出來最終的總值,再給自己setMeasuredDimension()賦值寬高。
    ? 在onLayout()中根據(jù)left、top、right和bottom(父View給自己的定位),以及子View的個數(shù)等信息,遍歷子View調(diào)用childView.layout(l,t,r,b)來布局子View。
  2. 對于View這一塊要掌握View的三大流程(measure、layout、draw),一些常見方法回調(diào)(構(gòu)造、onAttach、onVisibilityChanged、onDetach)等,以及嵌套中滑動沖突的處理。
第五章:理解RemoteViews
  1. RemoteViews表示一個View結(jié)構(gòu),它可以在其他進(jìn)程中顯示,提供了一組基礎(chǔ)的操作用于跨進(jìn)程更新界面,多用于通知欄或桌面小部件,一般在系統(tǒng)的SystemService進(jìn)程。
  2. new RemoteView(packageName,layoutResId)可以創(chuàng)建remoteView,remoteView.setTextViewText(R.id.tv,"xxx")用于設(shè)置View值,再給notification.contentView = remoteView即可;用于桌面小部件時,本質(zhì)其實(shí)就是一個廣播(BroadcastReceiver)。
  3. PendingIntent概述:
    ? 延遲intent,支持三種意圖:getActivity()、getService()、getBroadcast()。
    ? 兩個pendingIntent相同的條件是:內(nèi)部的intent相同且requestCode也相同,intent相同指componentName和intent-filter相同,extras不參與匹配過程。
    ? requestCode參數(shù)基本填0,flag有四種類型:FLAG_ONE_SHOT(少用)、FLAG_NO_CREATE(基本不用)、FLAG_CANCEL_CURRENT(類似第一個 不常用)、FLAG_UPDATE_CURRENT(常用)。FLAG_ONE_SHOT和FLAG_CANCEL_CURRENT都是只能打開一個PI(前者打任一個其余打不開,后者只能打開最新的,前面的被cancel掉),F(xiàn)LAG_NO_CREATE直接返回null,基本不用,F(xiàn)LAG_UPDATE_CURRENT每個都能打開,只是結(jié)果都一樣。
    ? notification中id相同的話會相互覆蓋前一個,不會出現(xiàn)pendingIntent沖突現(xiàn)象,如果id不同,可以顯示多條通知,這時要注意pendingIntent如果相同的話,注意flag的設(shè)置問題(id影響顯示幾條,flag影響PI相等時的點(diǎn)擊反應(yīng))。
  4. RemoteViews對象可以序列化,通過Intent進(jìn)行傳輸,傳輸?shù)狡渌麘?yīng)用后資源文件id最好通過協(xié)商好的格式getResources().getIdentifier(id,type,packageName)來獲取。
第六章:Android的Drawable
  1. Drawable是一種可以在canvas上繪制的抽象概念,比自定義View成本低,可以做一些特殊背景和效果,作為View的背景時會被拉伸,寬高沒有什么實(shí)際用處。
  2. 9中常用Drawable分類:
    ①單層:
    ? BitmapDrawable:src、antialias、dither、filter、gravity、mipMap、tileMode等。
    ? NinePatchDrawable:src、dither等。
    ? ShapeDrawable:shape、corners、gradient、padding、size、solid、stroke等。有rectangle、oval、line和ring四種類型,畫虛線做背景時imageView需要開啟軟件加速才有效。
    ②組合:
    ? LayerDrawable:layer-list。使用item、shape層級顯示。
    ? StateListDrawable:selector。使用item、shape選擇顯示,默認(rèn)的狀態(tài)放到最后,可以匹配任何狀態(tài),類似switch語句。
    ? LevelListDrawable:level-list。使用item、shape多層切換顯示,maxLevel取值0~10000。
    ③不常用:
    ? TransitionDrawable:transition。過渡效果。
    ? InsetDrawable:inset。給內(nèi)置drawable添加內(nèi)邊距,layerdrawable或padding也能實(shí)現(xiàn)。
    ? ScaleDrawable:scale。級別越大,內(nèi)部drawable越大(正比);比例越大,內(nèi)部drawable越小(反比)。
    ? ClipDrawable:clip。級別0~10000,級別越大,裁剪的越大,剩的越少。
第七章:Android動畫深入分析
  1. Android動畫類型6種:幀動畫、補(bǔ)間動畫、布局動畫、屬性動畫、矢量動畫和切換動畫。
  2. 每種動畫的特點(diǎn):
    ? 幀動畫:animation-list標(biāo)簽;oneshot屬性;item添加動畫幀;作為View的背景,獲取后轉(zhuǎn)為AnimationDrawable,自己開啟和結(jié)束;注意不要用太多大圖以免OOM。
    ? 補(bǔ)間動畫:平移、縮放、旋轉(zhuǎn)、透明度、set五種類型;interpolator、shareInterpolator、duration、repeatCount、repeatMode、fillAfter等屬性,repeatCount默認(rèn)是0,-1表示無線循環(huán),repeatMode取值restart和reverse;AnimationUtils.loadAnimation()獲取動畫,通過view來開啟和關(guān)閉,注意detachFromWindow是要關(guān)閉動畫否則可能內(nèi)存泄露。
    ? 布局動畫:layoutAnimation標(biāo)簽;delay(0~1)、animationOrder(normal/reverse/radom)、animation等屬性;可以在布局里給View設(shè)置layoutAnimation,也可以在代碼里創(chuàng)建LayoutAnimationController來給View設(shè)置setLayoutAnimation(),默認(rèn)布局中設(shè)置layoutAnimationChanged=true可實(shí)現(xiàn)默認(rèn)動畫。
    ? 屬性動畫: 含ValueAnimator、ObjectAnimator和AnimatorSet;獨(dú)立的動畫類,自己創(chuàng)建自己開啟,可將View設(shè)為target(系統(tǒng)封好的或者自己定義的),類似于Scoller的特點(diǎn);API11之后引入的特性,可以使用nineoldandroid庫做版本兼容(3.0以下使用補(bǔ)間動畫實(shí)現(xiàn))。
    ? 矢量動畫:SVG動畫,可適配不同分辨率,實(shí)現(xiàn)完美平滑的過渡效果,5.0以上支持,實(shí)現(xiàn)方式比較復(fù)雜,建議閱讀相關(guān)博客。
    ? 切換動畫:overridePendingTransition(enter,exim),放在startActivity()finish()之后才能生效,第一個參數(shù)是下個Activity進(jìn)入的動畫,第二個參數(shù)是本Activity退出的動畫;Fragment切換動畫用到了FragmentTransaction的setCustomAnimations()方法。
  3. 屬性動畫詳解:
    ? 類似Scroller,其實(shí)就是一種計算輔助類,差值器和估值算法,原始動畫為ValueAnimator,封裝了計算過程,ObjectAnimator多了target屬性包裝一個View,封裝了一些特定屬性的get()、set()效果,也可自己實(shí)現(xiàn)動畫效果。
    ? 一般在代碼中動態(tài)創(chuàng)建,xml中創(chuàng)建的話,設(shè)置valueType、propertyName等屬性,AnimatorInflater.loadAnimator()獲取并設(shè)置setTarget()作用對象。
    ? 差值器(Interpolator)和估值器(Evaluator)一般配合使用,都是計算輔助接口,一個計算差值比例,一個計算最終的估值結(jié)果,可作用于任何地方作為計算器類,可自定義計算變化算法和估值類型和結(jié)果值。
    ? ValueAnimator的addUpdateListener()可設(shè)置更新監(jiān)聽,animator.getAnimatedFraction()animator.getAnimatedValue()可以獲取到差值和估值結(jié)果,是根據(jù)前面自己設(shè)置的計算類得到的。
    ? ObjectAnimator作用時View對應(yīng)屬性要有g(shù)et()、set()方法,否則會crash,還要定義對應(yīng)效果,否則沒效果,可以自己包裝View或者直接用ValueAnimator自己來根據(jù)計算值設(shè)置View。
  4. 使用動畫注意事項(xiàng):幀動畫注意OOM的情況;屬性動畫如果是循環(huán)的,退出時最好cancel()一下,且注意版本兼容性;補(bǔ)間動畫不用時clearAnimation()一下,否則有些view方法會無效果;使用動畫的過程中如果開啟硬件加速可以提高流暢度。
第八章:理解Window和WindowManager
  1. Window是一個抽象類,它的具體實(shí)現(xiàn)是PhoneWindow,它是一個存在但看不見的東西,外界訪問Window要通過WindowManager,Window是View的直接管理者,View要通過WindowManager附屬到Window上才能實(shí)現(xiàn)通信和顯示,Window的具體實(shí)現(xiàn)在WindowManagerService中,WindowManger與WindowManagerService的交互是IPC過程。
  2. ViewManager是個接口,有addView()、updateView()和removeView()三個方法,WindowManager繼承自ViewManager,含很多標(biāo)志位,WindowManagerImpl才是真正的實(shí)現(xiàn)類,內(nèi)部所有操作委托給WindowManagerGlobal實(shí)現(xiàn)。
  3. 有View和LayoutParams,View就能附加到Window上顯示出來,flag類型如:FLAG_NOT_FOCUSABLE(不處理焦點(diǎn)完全給下級)、FLAG_NOT_TOUCH_MODAL(處理自己范圍內(nèi)的事件 需要開啟)、FLAG_SHOW_WHEN_LOCKED(鎖屏也顯示)。
  4. Window分為三種:應(yīng)用Window(對應(yīng)Activity)、子Window(Dialog/PopupWindow等 必須有父Window)和系統(tǒng)Window(Toast等 需要權(quán)限 不需要依附token)。
  5. Window有層級,應(yīng)用Window范圍1-99,子Window范圍1000-1999,系統(tǒng)Window范圍2000-2999,層級越高顯示越在上面。
  6. 每個Window對應(yīng)著一個View和ViewRootImpl,Window和View通過ViewRootImpl建立聯(lián)系,實(shí)際使用中無法直接訪問Window,可通過WindowManger訪問;WindowManagerGlobal是真正邏輯實(shí)現(xiàn),內(nèi)部含mViews、mRoots、mParams和mDyingViews幾個列表,存儲管理的Views,通過ViewRootImpl更新界面完成Window的添加過程,ViewRootImpl中會scheduleTraversals()完成View繪制,并通過WindowManagerGlobal獲取WindowSession進(jìn)行addToDisplay(),內(nèi)部是與WindowManagerService進(jìn)行IPC交互了;removeView也是調(diào)到ViewRootImpl中,進(jìn)行View的dispatchDetachedFromWindow()以及通過WindowSession來更新IPC等;updateView過程也類似,涉及View的重繪。
  7. Window的內(nèi)部機(jī)制(涉及的類):ViewManager/WindowManager -> WindowMangerImpl -> WindowManagerGlobal -> View和ViewImpl -> WindowSession -> WindowManagerService,WindowManagerGlobal里大多是管理ViewRootImpl的邏輯,添加View到Window中的邏輯是在ViewImpl中執(zhí)行的,ViewImpl中通過WindowSession與WMS進(jìn)行通信,最后使用WMS來進(jìn)行將View添加到Window中,屬于遠(yuǎn)程IPC調(diào)用,真正的操作邏輯在WMS里。
  8. Activity中Window的創(chuàng)建過程:IPolicy -> PolicyManager -> Wiondow(PhoneWindow) -> 內(nèi)部創(chuàng)建DecorView(mContentParent) -> 等到Activity的onResume() -> makeVisible() -> ViewManager的addView()將mDecor添加到Window -> mDecor.setVisibility(),Activity最終顯示。
  9. 普通的Dialog必須使用Activity的Context才能顯示出來,因?yàn)锳ctivity的context才有windowToken;Toast可定時取消,用到了Handler,因此Toast無法在沒有Looper的線程中使用。
  10. 有了WindowManager,我們就可以隨意創(chuàng)建View和設(shè)置LayoutParams來讓這個Window顯示出來,可以設(shè)置級別和flag,用于創(chuàng)建應(yīng)用懸浮窗等。
第九章:四大組件的工作過程
  1. Android四大組件中除了BroadcastReceiver外,其余三個組件都要在Menifest中注冊,BroadcastReceiver可以在Menifest中靜態(tài)注冊也可以在代碼中動態(tài)注冊;Activity、Service和BroadcastReceiver需要借助Intent來啟動。
  2. Activity主要作用是展示一個界面并與用戶交互,扮演一個前臺界面的角色;Service是一種計算型組件用于在后臺執(zhí)行一系列計算任務(wù);BroadcastReceiver是一種消息型組件,用于在不同組件乃至不同應(yīng)用中傳遞消息;ContentProvider內(nèi)部的insert、delete、update和query方法是在Binder線程池中調(diào)用的,是異步操作,所以需要處理好線程同步問題。
  3. BroadcastReceiver可以靜態(tài)注冊和動態(tài)注冊,靜態(tài)注冊指在Menifest中注冊,此種形式應(yīng)用不需啟動便可收到廣播(5.0以后不可),適用于長期接收事件,動態(tài)注冊指在代碼中注冊,一般用的多,因?yàn)轫撁骊P(guān)閉后就取消注冊了,廣播用于實(shí)現(xiàn)低耦合的觀察者模式。
  4. Service雖然指后臺運(yùn)行,但是還是在主線程中工作的,不能直接做耗時操作,可在IntentService中可以做耗時操作,且任務(wù)完成可以自己stop自己,后臺計算不需要交互使用startService開啟,需要業(yè)務(wù)接口交互使用bindService開啟。
  5. ActivityManagerService(AMS)繼承自ActivityManagerNative,ActivityManagerNative繼承自Binder并實(shí)現(xiàn)了IActivityManager接口,因此AMS是一個Binder,是IActivityManager的具體實(shí)現(xiàn);ApplicationThread繼承自ApplicationThreadNative,ApplicationThreadNative繼承自Binder并實(shí)現(xiàn)了IApplicationThread的接口,ApplicationThread是一個實(shí)現(xiàn)了很多schedule方法的業(yè)務(wù)接口,作為Binder使用,通過Handler回調(diào)到ActivityThread中處理;Instrumentation是一個輔助類,負(fù)責(zé)分擔(dān)Activity中通過ApplicationThread與AMS交互的一些業(yè)務(wù)邏輯。
  6. Activity的啟動過程涉及到的類:Activity -> Instrumentation -> ApplicationThread -> AMS -> ActivityStarter -> ActivityStackSupervisor -> ActivityStack -> ActivityStackSupervisor -> ApplicationThread -> Handler -> ActivityThread(H);Service的啟動過程涉及到的類:Activity -> ContextImpl -> AMS -> ActiveService(AMS的輔助類) -> ApplicationThread -> Handler -> ActivityThread(H)。
  7. Activity啟動流程:
    ? Activity內(nèi)部一些邏輯執(zhí)行交給Instrumentation,Instrumentation內(nèi)部通過ApplicationThread這個Binder與AMS進(jìn)行交互,這個交互是IPC過程,ApplicationThread是ActivityThread的一個內(nèi)部類,類似Stub類,作為ActivityThread與AMS的橋接Binder對象。
    ? AMS處理了遠(yuǎn)程請求的業(yè)務(wù)之后,進(jìn)行一些遠(yuǎn)程同步、控制、存儲邏輯后,調(diào)用ApplicationThread回來,由于Binder的調(diào)用是在線程池中進(jìn)行(異步),因此在ApplicationThread內(nèi)通過一個H(handler)來發(fā)送消息給ActivityThread處理結(jié)果。
    ? ActivityThread內(nèi)處理結(jié)果時結(jié)合contextImpl、mInstrumentation等類進(jìn)行一些列邏輯處理和生命周期等回調(diào),因此onResume()、onConnection()、onReceive()等方法都是在主線程執(zhí)行的。

Activity啟動描述:
桌面Launcher也是一個APP,當(dāng)點(diǎn)擊某個應(yīng)用icon時,Launcher通知AMS啟動目標(biāo)App,AMS記錄必要信息后pauseLanucher,當(dāng)目標(biāo)應(yīng)用程序啟動時,入口方法為ActivityThread的main()方法(ActivityThread就是主線程),main()是一個靜態(tài)方法,內(nèi)部會創(chuàng)建ActivityThread實(shí)例并創(chuàng)建主線程消息隊(duì)列、綁定ApplicationThread到AMS、執(zhí)行主線程循環(huán)等,在ActivityThread的attach()方法中遠(yuǎn)程調(diào)用AMS的attachApplication()方法將ApplicationThread最為回調(diào)Binder綁定到AMS,AMS中處理一些同步工作后,會通過ApplicationThread回調(diào)各個shcedule方法到ActivityThread,最后創(chuàng)建Application、Context、初始化View等完成界面繪制并顯示。

  1. 廣播分為普通廣播、有序廣播和粘性廣播,從Android5.0(其實(shí)3.1就開始了)開始系統(tǒng)為所有廣播默認(rèn)添加了FLAG_EXCLUDE_STOPPED_PACKAGES標(biāo)志,標(biāo)記廣播是否要對已停止的應(yīng)用起作用。
  2. ContentProvider是內(nèi)容共享型組件,通過Binder向其他組件乃至其他應(yīng)用提供數(shù)據(jù);當(dāng)ContentProvider所在的進(jìn)程啟動時,ContentProvider會被創(chuàng)建并發(fā)布到AMS中,ActivityThread中會先加載ContentProvider,因此ContentProvider的onCreate()要先于Application的onCreate()執(zhí)行;ContentProvider是否是單例由
    android:multiprocess屬性值來控制,一般都是單實(shí)例存在。
  3. Service生命周期:
    ? startService():onCreate() -> onStartCommand()
    ? stopService():onDestroy()
    ? bindService():onCreate() -> onBind()
    ? unbindService():onUnbind() -> onDestroy()
    startService()bindService()或先bindService()startService(),只關(guān)閉其中一步不會onDestroy(),需要stopService()unbindService()都調(diào)用才會onDestory(),順序不分先后,但是如果沒有bind的話直接調(diào)用unbindService()會拋異常;bindService()生命周期隨調(diào)用者,Activity退出后就unbind了,startService()不隨調(diào)用了,即使程序雙擊退出Service仍存在,必須手動stopService()或去應(yīng)用管理里關(guān)掉Service;如果強(qiáng)殺進(jìn)程,Service屬于意外死亡,默認(rèn)情況下是粘性Service會自己重新onCreate() -> onStartCommand(),但是沒啥用,進(jìn)程殺死后內(nèi)存數(shù)據(jù)都沒了,僅僅是重啟了Service;在onStartCommand()中return標(biāo)志位"START_NOT_STICKY"殺進(jìn)程后不會重啟Service。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容