Android 中級圣經系列之Activity

該系列文章主要介紹android里基本組件的了解和使用

圣經?該系列文章將以標簽的形式添加,在以后的工作中也會不斷完善,也希望有其他朋友補充,最終希望能讓閱讀的人通過一篇文章就能解決了解與這組件相關的大部分問題,比如說第一篇Activity,除了常見的生命周期外,還會介紹activity的創建過程,啟動模式等等

中級?如果要把android工程師分初中高三個等級的話,個人覺得應該達到以下要求


初級:能熟練使用android的view控件和activity,靈活得在前臺后臺線程進行切換,根據產品需求,完成ui層面的任務


中級:熟練使用android四大組件,初步理解背后實現的機制和原理,理解事件分發三大關鍵函數dispatchTouchEven,onInterceptTouchEvent,onTouchEvent,能夠解決大部分父子節點事件傳遞的問題,理解NestedScroll(嵌套滑動接口),熟悉view的繪制流程(onMeasure-》onLayout-》onDraw)及這三個函數在view和viewgroup之間的不同點,能熟練得自定義view。


高級:看過并理解view的源碼,對于自定義view之類的不再覺得是問題,研究理解優秀的android框架,比如Fresco(圖片加載),Volley(網絡請求),需要時可以自己開發簡化版,游刃有余地處理內存的使用和回收,處理不同線程的并發情況,能在項目中運用合適的開發模式。


希望通過這系列教程和自己的實踐,能讓大家達到中級的開發水平

Activity生命周期(地球人都知道),但你知不知道onPause和onStop的區別?為什么要存在兩個不同的回調?onDestory實際上做了什么?

當當前activity失去焦點時,會馬上觸發onPause(比如跳轉到新的activitiy,切到后臺),之后過一段時間后假如檢測改activity依然在后臺,此時才會觸發onStop。

當新打開的Activity是Dialog風格時或者是透明時;這兩種情況只會調用onPause方法,但不會調用onStop方法。

銷毀activity時如果內存緊張,系統會直接結束這個Activity,而不會觸發 onStop 方法。所以保存臨時數據的方法必須放在onPause做,而不是onStop。

onDestroy實際上并沒有馬上把activity銷毀,而是把該activity從當前的activity棧中移除,大多數 app并不需要實現這個方法,因為局部類的references會隨著activity的銷毀而銷毀,但是加入這個時候你的activity被其他地方強引用(例如有個動畫不斷在執行),這時候你會發現在即使onDestroy被調用,但是改activity依然存在系統的內存中,所以單單依靠這個函數來回收資源是不夠的。

順便說一下Fragment的生命周期

Fragment是和activity綁定的,但創建時首先會觸發onAttach,此時可以獲取到context對象,
onAttach方法:Fragment和Activity建立關聯的時候調用。
onCreateView方法:為Fragment加載布局時調用。
onActivityCreated方法:當Activity中的onCreate方法執行完后調用。
onDestroyView方法:Fragment中的布局被移除時調用。
onDetach方法:Fragment和Activity解除關聯的時候調用。

activity為什么要細化出onCreate、onStart、onResume、onPause、onStop、onDesdroy?相信很多人為這個問題困擾很久,先來看一個圖,從Activity A跳轉到Activity B的回調順序

可以發現跳轉發生時,先調用了A的onPause,之后等到B的onStart和onResume完成后才調用A的onStop,這樣設計的目的在于讓開發者可以在不同階段進行不同資源的釋放或者創建,舉個例子,先看下onResume的官方定義
void android.app.Activity.onResume()
Called after onRestoreInstanceState, onRestart, or onPause, for your activity to start interacting with the user. This is a good place to begin animations, open exclusive-access devices (such as the camera), etc.

Keep in mind that onResume is not the best indicator that your activity is visible to the user; a system window such as the keyguard may be in front. Use onWindowFocusChanged to know for certain that your activity is visible to the user (for example, to resume a game).

“open exclusive-access devices (such as the camera)",比如你的應用有打開camera的操作,假如camera的初始化你放到了onCreate里面,釋放放到了onStop或者onDestroy里面,假設A和B都需要camera的資源,那么當A跳轉到B時,B在oncreate創建camera引用,就會出現A里面的camera還沒有釋放的問題,所以google建議是在onResume()中打開獨占設備(比如相機),在onpause里把這些資源釋放出去。

所有的初始化都在onCreate()中實現?
onCreate()代表activity被創建,onStart代表該Activity變成可見狀態,Activity的onCreate()被調用時,Activity還不可見,如果要做一些動畫,既然視圖還不存在,在onCreate中來啟動動畫,明顯有問題;

其次,A Activity 切換到 B Activity,再切換到 A Activity,由于實例已經存在,所以onCreate不會再被調用,那AActivity從后臺切換至前臺時,有可能需要一些初始化,那就沒法再被調用到了;

所有的初始化都在onStart()中實現?
onStart() 被調用時,Activity可能是可見了,但還不是可交互的,onResume()的注釋中都明確地說了這不是Activity對用戶是可見的最好的指示器,onStart() 在這之前被調用,那有一些特殊的初始化相關的邏輯在這里被調用也會有問題。

這只是很簡單的例子,實際中要根據實際情況處理,總的來說可以把一些view的初始化工作放到onCreate里面,在onstart時可以開啟一些動畫事件,最后在onresume時才打開獨占設備,在onpause時把動畫停止,獨占設備的資源釋放出去,停止網絡輪詢,而不是onStop,最后看一下官方解釋:
void android.app.Activity.onStop()
Called when you are no longer visible to the user. You will next receive either onRestart, onDestroy, or nothing, depending on later user activity.
Note that this method may never be called, in low memory situations where the system does not have enough memory to keep your activity's process running after its onPause method is called.
onStop() 的注釋中明確地寫了,在內存不足而導致系統無法保留此進程的情況下,onStop() 可能都不會被執行。

Acitity啟動模式:
涉及關鍵詞“LaunchMode",”Task stack“,“intent”,由于intent我們之后還有一篇文章專門來講,所以主要看前面兩個。

LauchMode有四種,分別為standard,singleTop,singleTask,singleInstance,可以在xml里面配置好,也可以在startactivity啟動時設定,后者優先級高于前者。在這里先記錄一下官方的定義

"standard":Default. The system always creates a new instance of the activity in the target task and routes the intent to it.

"singleTop":If an instance of the activity already exists at the top of the target task, the system routes the intent to that instance through a call to itsonNewIntent()method, rather than creating a new instance of the activity.

"singleTask":The system creates the activity at the root of a new task and routes the intent to it. However, if an instance of the activity already exists, the system routes the intent to existing instance through a call to itsonNewIntent()method, rather than creating a new one.

"singleInstance":Same as "singleTask", except that the system doesn't launch any other activities into the task holding the instance. The activity is always the single and only member of its task.

這里注意一點,就是singleTop其實是并不一定保證唯一性的,比如一個視頻播放的activity,當點擊home健退到后臺后,通過點擊notification到splash頁面再回來這個activity,其實已經不是之前那一個了,已有的信息會被刪除掉。這種情況可以考慮使用singletask

接著看一下startactivity時可以設置的flag有哪些:

FLAG_ACTIVITY_BROUGHT_TO_FRONT
這個標志一般不是由程序代碼設置的,如在launchMode中設置singleTask模式時系統幫你設定。

FLAG_ACTIVITY_SINGLE_TOP
如果設置,當這個Activity位于歷史stack的頂端運行時,不再啟動一個新的。

FLAG_ACTIVITY_NEW_TASK
如果設置,這個Activity會成為歷史stack中一個新Task的開始。一個Task(從啟動它的Activity到下一個Task中的 Activity)定義了用戶可以遷移的Activity原子組。Task可以移動到前臺和后臺;在某個特定Task中的所有Activity總是保持相同的次序。

這個標志一般用于呈現“啟動”類型的行為:它們提供用戶一系列可以單獨完成的事情,與啟動它們的Activity完全無關。

使用這個標志,如果正在啟動的Activity的Task已經在運行的話,那么,新的Activity將不會啟動;代替的,當前Task會簡單的移入前臺。參考FLAG_ACTIVITY_MULTIPLE_TASK標志,可以禁用這一行為

這個標志不能用于調用方對已經啟動的Activity請求結果。

FLAG_ACTIVITY_CLEAR_TOP(top的意思是指蓋在該activity上面,比他遲創建啟動的)
如果設置,并且這個Activity已經在當前的Task中運行,因此,不再是重新啟動一個這個Activity的實例,而是在這個Activity上方的所有Activity都將關閉,然后這個Intent會作為一個新的Intent投遞到老的Activity(現在位于頂端)中。

例如,假設一個Task中包含這些Activity:A,B,C,D。如果D調用了startActivity(),并且包含一個指向Activity B的Intent,那么,C和D都將結束,然后B接收到這個Intent,因此,目前stack的狀況是:A,B。

上例中正在運行的Activity B既可以在onNewIntent()中接收到這個新的Intent,也可以把自己關閉然后重新啟動來接收這個Intent。如果它的啟動模式聲明為 “multiple”(默認值),并且你沒有在這個Intent中設置FLAG_ACTIVITY_SINGLE_TOP標志,那么它將關閉然后重新創建;對于其它的啟動模式,或者在這個Intent中設置FLAG_ACTIVITY_SINGLE_TOP標志,都將把這個Intent投遞到當前這個實例的onNewIntent()中。

這個啟動模式還可以與FLAG_ACTIVITY_NEW_TASK結合起來使用:用于啟動一個Task中的根Activity,它會把那個Task中任何運行的實例帶入前臺,然后清除它直到根Activity。這非常有用,例如,當從Notification Manager處啟動一個Activity。

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
如果設置,這將在Task的Activity stack中設置一個還原點,當Task恢復時,需要清理Activity。也就是說,下一次Task帶著 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED標記進入前臺時(典型的操作是用戶在主畫面重啟它),這個Activity和它之上的都將關閉,以至于用戶不能再返回到它們,但是可以回到之前的Activity。

這在你的程序有分割點的時候很有用。例如,一個e-mail應用程序可能有一個操作是查看一個附件,需要啟動圖片瀏覽Activity來顯示。這個 Activity應該作為e-mail應用程序Task的一部分,因為這是用戶在這個Task中觸發的操作。然而,當用戶離開這個Task,然后從主畫面選擇e-mail app,我們可能希望回到查看的會話中,但不是查看圖片附件,因為這讓人困惑。通過在啟動圖片瀏覽時設定這個標志,瀏覽及其它啟動的Activity在下次用戶返回到mail程序時都將全部清除。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
如果設置,新的Activity不會在最近啟動的Activity的列表中保存。

FLAG_ACTIVITY_FORWARD_RESULT
如果設置,并且這個Intent用于從一個存在的Activity啟動一個新的Activity,那么,這個作為答復目標的Activity將會傳到這個新的Activity中。這種方式下,新的Activity可以調用setResult(int),并且這個結果值將發送給那個作為答復目標的 Activity。

FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
這個標志一般不由應用程序代碼設置,如果這個Activity是從歷史記錄里啟動的(常按HOME鍵),那么,系統會幫你設定。

FLAG_ACTIVITY_MULTIPLE_TASK
不要使用這個標志,除非你自己實現了應用程序啟動器。與FLAG_ACTIVITY_NEW_TASK結合起來使用,可以禁用把已存的Task送入前臺的行為。當設置時,新的Task總是會啟動來處理Intent,而不管這是是否已經有一個Task可以處理相同的事情。

由于默認的系統不包含圖形Task管理功能,因此,你不應該使用這個標志,除非你提供給用戶一種方式可以返回到已經啟動的Task。

如果FLAG_ACTIVITY_NEW_TASK標志沒有設置,這個標志被忽略。

FLAG_ACTIVITY_NO_ANIMATION
如果在Intent中設置,并傳遞給Context.startActivity()的話,這個標志將阻止系統進入下一個Activity時應用 Acitivity遷移動畫。這并不意味著動畫將永不運行——如果另一個Activity在啟動顯示之前,沒有指定這個標志,那么,動畫將被應用。這個標志可以很好的用于執行一連串的操作,而動畫被看作是更高一級的事件的驅動。

FLAG_ACTIVITY_NO_HISTORY
如果設置,新的Activity將不再歷史stack中保留。用戶一離開它,這個Activity就關閉了。這也可以通過設置noHistory特性。

FLAG_ACTIVITY_NO_USER_ACTION

如果設置,作為新啟動的Activity進入前臺時,這個標志將在Activity暫停之前阻止從最前方的Activity回調的onUserLeaveHint()。

典型的,一個Activity可以依賴這個回調指明顯式的用戶動作引起的Activity移出后臺。這個回調在Activity的生命周期中標記一個合適的點,并關閉一些Notification。

如果一個Activity通過非用戶驅動的事件,如來電或鬧鐘,啟動的,這個標志也應該傳遞給Context.startActivity,保證暫停的Activity不認為用戶已經知曉其Notification。

FLAG_ACTIVITY_REORDER_TO_FRONT
如果在Intent中設置,并傳遞給Context.startActivity(),這個標志將引發已經運行的Activity移動到歷史stack的頂端。

例如,假設一個Task由四個Activity組成:A,B,C,D。如果D調用startActivity()來啟動Activity B,那么,B會移動到歷史stack的頂端,現在的次序變成A,C,D,B。如果FLAG_ACTIVITY_CLEAR_TOP標志也設置的話,那么這個標志將被忽略。

Activity是運行在『所需的任務棧』,什么是Activity所需的任務棧?這里就要提一個參數:TaskAffinity。這個參數標識了一個Activity所需的任務棧的名字,默認情況下所有的Activity所需的任務棧都是當前包名,當然我們也可以為每個Activity單獨指定TaskAffinity,注意這個屬性值必須不能和包名相同,TaskAffinity主要和singleTask配合使用,否則的話沒什么意義。當啟動一個被TaskAffinity標識了的Activity,那么該Activity就會運行在和TaskAffinity相同的任務棧中



最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容