Ⅰ android採用什麼方式管理activity實例
Android採用任務線(Task)的方式來管理Activity的實例搭模。
在開發Android應用時,經常會涉及一些消耗大量系統內存的情況,例如視頻播放、大量圖片或者程序中開啟多個Activity沒有及時關閉等,會導致程序出現錯誤。為了避免這種問題,Google提供了一套完整的機制讓開發人員控制 Android中的任務線。
Android系統中的任務線,類似於一個容器,用於管理所有的Activity實例。在存放Activity時,滿足「先進後出 (First-In/Last-Out )"的原則。
但是使用任務線有以下缺點:粗枝鏈
每開啟一次頁面都會在任務棧中添加一個岩孫Activity,而只有任務棧中的Activity全部清除出線時,任務線被銷毀,程序才會退出。這樣就造成了用戶體驗差, 需要點擊多次返回才可以把程序退出。
每開啟一次頁面都會在任務棧中添加一個Activity還會造成數據冗餘, 重復數據太多, 會導致內存溢出的問題(OOM)。為了解決任務棧產生的問題,Android為Activity設計了啟動模式。
在實際開發中,應根據特定的需求為每個Activity指定恰當的啟動模式。Activity的啟動模式有4種,分別是standard、singleTop、singleTask和singlelnstance。在AndroidManifest.xml中,通過<activity>標簽的android:launchMode屬性可以設置啟動模式。
Ⅱ 全面解析Activity: Activity的工作過程
本文將對Activity的工作過程進行分析。
主要學習以下內容:
(1)系統內部是如何啟動一個Activity的?
(2)新Activity的對象是何時創建的?
(3)Activity的各個生命周日是被系統何時回調的?
Activity啟動流程分兩種,一種是啟動正在運行的app的Activity,即啟動子Activity。如無特殊聲明默認和啟動該activity的activity處於同一進程。如果有聲明在一個新的進程中,則處於兩個進程。另一種是打開新的app,即為Launcher啟動新的Activity。後邊啟動Activity的流程是一樣的,區別是前邊判斷進程是否存在的那部分。
Activity的啟動流程整體如下:
一.Activity啟動階段
(一)涉及到的概念
進程:Android系統為每個APP分配至少一個進程
IPC:跨進程通信,Android中採用Binder機制。
(二)涉及到的類
ActivityStack:Activity在AMS的棧管理,用來記錄已經啟動的Activity的先後關系,狀態信息等。通過ActivityStack決定是否需要啟動新的進程。
ActivitySupervisor:管理 activity 任務棧
ActivityThread:ActivityThread 運行在UI線程(主線程),App的真正入口。
ApplicationThread:用來實現AMS和ActivityThread之間的交互。
ApplicationThreadProxy:ApplicationThread 在服務端的代理。AMS就是通過該代理與ActivityThread進行通信的。
IActivityManager:繼承與IInterface介面,抽象出跨進程通信需要實現的功能
AMN:運行在server端(SystemServer進程)。實現了Binder類,具體功能由子類AMS實現。
AMS:AMN的子類,負責管理四大組件和進程,包括生命周期和狀態切換。AMS因為要和ui交互,所以極其復雜,涉及window。
AMP:AMS的client端代理(app進程)。了解Binder知識可以比較容易理解server端的stub和client端的proxy。AMP和AMS通過Binder通信。
Instrumentation:儀表盤,負責調用Activity和Application生命周期。測試用到這個類比較多。
(三)涉及到的進程
(1)Launcher所在的進程
(2)AMS所在的SystemServer進程
(3)要啟動的Activity所在的app進程
如果是啟動根Activity,就涉及上述三個進程。
如果是啟動子Activity,那麼就只涉及AMS進程和app所在進程。
(四)具體流程
Launcher:Launcher通知AMS要啟動activity。
startActivitySafely->startActivity->Instrumentation.execStartActivity()(AMP.startActivity)->AMS.startActivity
AMS:PMS的resoveIntent驗證要啟動activity是否匹配。如果匹配,通過ApplicationThread發消息給Launcher所在的主線程,暫停當前Activity(即Launcher)。
暫停完,在該activity還不可見時,通知AMS,根據要啟動的Activity配置ActivityStack。然後判斷要啟動的Activity進程是否存在?
存在:發送消息LAUNCH_ACTIVITY給需要啟動的Activity主線程,執行handleLaunchActivity
不存在:通過socket向zygote請求創建進程。進程啟動後,ActivityThread.attach
判斷Application是否存在,若不存在,通過LoadApk.makeApplication創建一個。在主線程中通過thread.attach方法來關聯ApplicationThread。
在通過ActivityStackSupervisor來獲取當前需要顯示的ActivityStack。
繼續通過ApplicationThread來發送消息給主線程的Handler來啟動Activity (handleLaunchActivity)。
handleLauchActivity:調用了performLauchActivity,里邊Instrumentation生成了新的activity對象,繼續調用activity生命周期。
IPC過程:
雙方都是通過對方的代理對象來進行通信。
1.app和AMS通信:app通過本進程的AMP和AMS進行Binder通信
2.AMS和新app通信:通過ApplicationThreadProxy來通信,並不直接和ActivityThread通信
(五)參考函數流程
Activity啟動流程(從Launcher開始):
第一階段: Launcher通知AMS要啟動新的Activity(在Launcher所在的進程執行)
第二階段:AMS先校驗一下Activity的正確性,如果正確的話,會暫存一下Activity的信息。然後,AMS會通知Launcher程序pause Activity(在AMS所在進程執行)
第三階段:pause Launcher的Activity,並通知AMS已經paused(在Launcher所在進程執行)
第四階段:檢查activity所在進程是否存在,如果存在,就直接通知這個進程,在該進程中啟動Activity;不存在的話,會調用Process.start創建一個新進程(執行在AMS進程)
第五階段: 創建ActivityThread實例,執行一些初始化操作,並綁定Application。如果Application不存在,會調用LoadedApk.makeApplication創建一個新的Application對象。之後進入Loop循環。(執行在新創建的app進程)
第六階段:處理新的應用進程發出的創建進程完成的通信請求,並通知新應用程序進程啟動目標Activity組件(執行在AMS進程)
第七階段: 載入MainActivity類,調用onCreate聲明周期方法(執行在新啟動的app進程)
從另一個角度下圖來概括:
下面簡要介紹一下啟動的過程:
Step 1. 無論是通過Launcher來啟動Activity,還是通過Activity內部調用startActivity介面來啟動新的Activity,都通過Binder進程間通信進入到ActivityManagerService進程中,並且調用ActivityManagerService.startActivity介面;
Step 2. ActivityManagerService調用ActivityStack.startActivityMayWait來做准備要啟動的Activity的相關信息;
Step 3. ActivityStack通知ApplicationThread要進行Activity啟動調度了,這里的ApplicationThread代表的是調用ActivityManagerService.startActivity介面的進程,對於通過點擊應用程序圖標的情景來說,這個進程就是Launcher了,而對於通過在Activity內部調用startActivity的情景來說,這個進程就是這個Activity所在的進程了;
Step 4. ApplicationThread不執行真正的啟動操作,它通過調用ActivityManagerService.activityPaused介面進入到ActivityManagerService進程中,看看是否需要創建新的進程來啟動Activity;
Step 5. 對於通過點擊應用程序圖標來啟動Activity的情景來說,ActivityManagerService在這一步中,會調用startProcessLocked來創建一個新的進程,而對於通過在Activity內部調用startActivity來啟動新的Activity來說,這一步是不需要執行的,因為新的Activity就在原來的Activity所在的進程中進行啟動;
Step 6. ActivityManagerServic調用ApplicationThread.scheleLaunchActivity介面,通知相應的進程執行啟動Activity的操作;
Step 7. ApplicationThread把這個啟動Activity的操作轉發給ActivityThread,ActivityThread通過ClassLoader導入相應的Activity類,然後把它啟動起來。
Ⅲ activity(Android組件中最重要的四大組件之一)詳細資料大全
activity是Android組件中最基本也是最為常見用的四大組件之一。Android四大組件有Activity,Service服務,Content Provider內容提供,BroadcastReceiver廣播接收器。
Ⅳ Android中的Activity詳解--啟動模式與任務棧
目錄
activity的簡單介紹就不寫了,作為最常用的四大組件之一,肯定都很熟悉其基本用法了。
首先,是都很熟悉的一張圖,即官方介紹的Activity生命周期圖.
情景:打開某個應用的的FirstActivity調用方法如下:
由於之前已經很熟悉了,這里就簡單貼一些圖。
按下返回鍵:
重新打開並按下home鍵:
再重新打開:
在其中打開一個DialogActivity(SecondActivity)
按下返回:
修改SecondAcitvity為普通Activity,依舊是上述操作:
這里強調一下 onSaveInstanceState(Bundle outState) 方法的調用時機:
當Activity有可能被系統殺掉時調用,注意,一定是被系統殺掉,自己調用finish是不行的。
測試如下:FirstActivity啟動SecondActivity:
一個App會包含很多個Activity,多個Activity之間通過intent進行跳轉,那麼原始的Activity就是使用棧這個數據結構來保存的。
Task
A task is a collection of activities that users interact with when performing a certain job. The activities are arranged in a stack (the back stack ), in the order in which each activity is opened.
即若干個Activity的集合的棧表示一個Task。
當App啟動時如果不存在當前App的任務棧就會自動創建一個,默認情況下一個App中的所有Activity都是放在一個Task中的,但是如果指定了特殊的啟動模式,那麼就會出現同一個App的Activity出現在不同的任務棧中的情況,即會有任務棧中包含來自於不同App的Activity。
標准模式,在不指定啟動模式的情況下都是以此種方式啟動的。每次啟動都會創建一個新的Activity實例,覆蓋在原有的Activity上,原有的Activity入棧。
測試如下:在FirstActivity中啟動FirstActivity:
當只有一個FirstActivity時堆棧情況:
此種模式下,Activity在啟動時會進行判斷,如果當前的App的棧頂的Activity即正在活動的Activity就是將要啟動的Activity,那麼就不會創建新的實例,直接使用棧頂的實例。
測試,設置FirstActivity為此啟動模式,多次點擊FirstActivity中的啟動FirstActivity的按鈕查看堆棧情況:
(其實點擊按鈕沒有啟動新Activity的動畫就可以看出並沒有啟動新Activity)
大意就是:
對於使用singleTop啟動或Intent.FLAG_ACTIVITY_SINGLE_TOP啟動的Activity,當該Activity被重復啟動(注意一定是re-launched,第一次啟動時不會調用)時就會調用此方法。
且調用此方法之前會先暫停Activity也就是先調用onPause方法。
而且,即使是在新的調用產生後此方法被調用,但是通過getIntent方法獲取到的依舊是以前的Intent,可以通過setIntent方法設置新的Intent。
方法參數就是新傳遞的Intent.
1.如果是同一個App中啟動某個設置了此模式的Activity的話,如果棧中已經存在該Activity的實例,那麼就會將該Activity上面的Activity清空,並將此實例放在棧頂。
測試:SecondActivity啟動模式設為singleTask,啟動三個Activity:
這個模式就很好記,以此模式啟動的Activity會存放在一個單獨的任務棧中,且只會有一個實例。
測試:SecondActivity啟動模式設為singleInstance
結果:
顯然,啟動了兩次ThirdActivity任務棧中就有兩個實例,而SecondActivity在另外一個任務棧中,且只有一個。
在使用Intent啟動一個Activity時可以設置啟動該Activity的啟動模式:
這個屬性有很多,大致列出幾個:
每個啟動的Activity都在一個新的任務棧中
singleTop
singleTask
用此種方式啟動的Activity,在它啟動了其他Activity後,會自動finish.
官方文檔介紹如下:
這樣看來的話,通俗易懂的講,就是給每一個任務棧起個名,給每個Activity也起個名,在Activity以singleTask模式啟動時,就檢查有沒有跟此Activity的名相同的任務棧,有的話就將其加入其中。沒有的話就按照這個Activity的名創建一個任務棧。
測試:在App1中設置SecondActivity的taskAffinity為「gsq.test」,App2中的ActivityX的taskAffinity也設為「gsq.test」
任務棧信息如下:
結果很顯然了。
測試:在上述基礎上,在ActivityX中進行跳轉到ActivityY,ActivityY不指定啟動模式和taskAffinity。結果如下:
這樣就沒問題了,ActivityY在一個新的任務棧中,名稱為包名。
這時從ActivityY跳轉到SecondActivity,那應該是gsq.test任務棧只有SecondActivity,ActivityX已經沒有了。因為其啟動模式是singleTask,在啟動它時發現已經有一個實例存在,就把它所在的任務棧上面的Activity都清空了並將其置於棧頂。
還有一點需要提一下,在上面,FirstActivity是App1的lunch Activity,但是由於SecondActivity並沒有指定MAIN和LAUNCHER過濾器,故在FirstActivity跳轉到SecondActivity時,按下home鍵,再點開App1,回到的是FirstActivity。
大致就先寫這么多吧,好像有點長,廢話有點多,估計也有錯別字,不要太在意~~~
Ⅳ Android Activity生命周期解析
Activity 用戶可以做單一的、集中的事情。幾乎所有的Activity都與用戶進行交互,所以Activity類負責創建一個窗口,你可以通過調用setContentView(View)把你的UI布局放置在Activity的窗口中。作為四大組件之一,使用頻率非常高。深入了解Activity,對於我們高質量開發是很有幫助的,下面我們就來看看Activity的生命周期。
Android系統中是通過Activity棧的方式來管理Activity的,而Activity自身則是通過生命周期的方法來管理的自己的創建與銷毀。那麼我們就來看看Activity生命周期是怎樣運作的。
周期即活動從開始到結束所經歷的各種狀態。生命周期即活動從開始到結束所經歷的各個狀態。從一個狀態到另一個狀態的轉變,從無到有再到無,這樣一個過程中所經歷的狀態就叫做生命周期。
Acitivity本質上有四種狀態:
在上面的四中常有的狀態之間,還有著其他的生命周期來作為不同狀態之間的過度,用於在不同的狀態之間進行轉換。
我們先來看看下面這張經典的生命周期流程圖:
1)啟動Activity
onCreate() —> onStart() —> onResume()
2)按Home鍵回到桌面 / 鎖屏
onPause() —> onStop()
3)從桌面回到Activity / 解鎖
onRestart() —> onStart() —> onResume()
4)跳轉新Activity
A: onPause() —> onStop()
B: onCreate() —> onStart() —> onResume()
A —> B: onPause()_A —> onCreate()_B —> onStart()_B —> onResume()_B —> onStop()_A
5)返回上一個Activity
B: onPause() —> onStop() —> onDestroy()
A: onRestart() —> onStart() —> onResume()
B —> A: onPause()_B —> onRestart()_A —> onStart()_A —> onResume()_A —> onStop()_B —> onDestroy()_B
6)退出Activity
onPause() —> onStop() —> onDestroy()
Activity官方文檔,開啟傳送門
至此Activity的整個生命周期都介紹完了,現在我們再看之前的生命周期流程圖,是不是清晰許多。搞清楚Activity活動原理,這樣理解起來就會容易許多,工作中也能如魚得水。
Ⅵ Android Activity啟動模式與狀態保存及恢復詳解
Activity是 Android組件 中最基本也是最為常見用的四大組件(Activity,Service服務,Content Provider內容提供者,BroadcastReceiver廣播接收器)之一 。
Activity是一個應用程序 組件 ,提供一個 屏幕 ,用戶可以用來交互為了完成某項任務。
Activity中所有操作都與用戶密切相關,是一個負責與 用戶交互 的組件,可以通過setContentView(View)來 顯示指定控制項 。
在一個android應用中,一個Activity通常就是一個單獨的屏幕,它上面可以顯示一些控制項也可以監聽並處理用戶的事件做出響應。Activity之間通過Intent進行通信。
關於Activity啟動流程請參考之前的文章 Android activity啟動流程分析
activity有四種啟動模式,分別為standard,singleTop,singleTask,singleInstance。如果要使用這四種啟動模式,必須在manifest文件中<activity>標簽中的launchMode屬性中配置。
標準的默認啟動模式,這種模式下activity可以被多次實例化,即在一個task中可以存在多個activity,每一個activity會處理一個intent對象,(在A中再次啟動A,會存在後面的A在前面的A上面,當前task會存在兩個activity的實例對象)
如果一個singleTop模式啟動的activity實例已經存在於棧頂,那麼再次啟動這個activity的時候,不會重新創建實例,而是重用位於棧頂的那個實例,並且會調用實例的onNewIntent()方法將Intent對象傳遞到這個實例中,如果實例不位於棧頂,會創建新的實例。
啟動模式設置為singleTask,framework在啟動該activity時只會把它標示為可在一個新任務中啟動,至於是否在一個新任務中啟動,還要受其他條件的限制,即taskAffinity屬性。
taskAffinity :默認情況下,一個應用中的所有activity具有相同的taskAffinity,即應用程序的包名。我們可以通過設置不同的taskAffinity屬性給應用中的activity分組,也可以把不同的應用中的activity的taskAffinity設置成相同的值,當兩個不同應用中的activity設置成相同的taskAffinity時,則兩個activity會屬於同一個TaskRecord。
在啟動一個singleTask的Activity實例時,如果系統中已經存在這樣一個實例,就會將這個實例調度到任務棧的棧頂,並清除它當前所在任務中位於它上面的所有的activity;如果這個已存在的任務中不存在一個要啟動的Activity的實例,則在這個任務的頂端啟動一個實例;若這個任務不存在,則會啟動一個新的任務,在這個新的任務中啟動這個singleTask模式的Activity的一個實例。
以singleInstance模式啟動的Activity具有全局唯一性,即整個系統中只會存在一個這樣的實例,如果在啟動這樣的Activiyt時,已經存在了一個實例,那麼會把它所在的任務調度到前台,重用這個實例。
以singleInstance模式啟動的Activity具有獨占性,即它會獨自佔用一個任務,被他開啟的任何activity都會運行在其他任務中(官方文檔上的描述為,singleInstance模式的Activity不允許其他Activity和它共存在一個任務中)。
被singleInstance模式的Activity開啟的其他activity,能夠開啟一個新任務,但不一定開啟新的任務,也可能在已有的一個任務中開啟,受條件的限制,這個條件是:當前系統中是不是已經有了一個activity B的taskAffinity屬性指定的任務。
涉及到Activity啟動,就不得不說一下Activity的管理,Activity是以什麼方式及被什麼類來進行管理的,涉及的類主要如下:
歷史棧中的一個條目,代表一個activity。ActivityRecord中的成員變數task表示其所在的TaskRecord,ActivityRecord與TaskRecord建立了聯系。
內部維護一個 ArrayList<ActivityRecord> 用來保存ActivityRecord,TaskRecord中的mStack表示其所在的ActivityStack,TaskRecord與ActivityStack建立了聯系。
內部維護了一個 ArrayList<TaskRecord> ,用來管理TaskRecord,ActivityStack中持有ActivityStackSupervisor對象,由ActivityStackSupervisor創建。
負責所有ActivityStack的管理。內部管理了mHomeStack、mFocusedStack和mLastFocusedStack三個Activity棧。其中,mHomeStack管理的是Launcher相關的Activity棧;mFocusedStack管理的是當前顯示在前台Activity的Activity棧;mLastFocusedStack管理的是上一次顯示在前台Activity的Activity棧。
ActivityThread 運行在UI線程(主線程),App的真正入口。
用來實現AMS和ActivityThread之間的交互。
負責調用Activity和Application生命周期。
當一個Activity未被主動關閉,即「被動關閉」時,可能需要系統給用戶提供保持一些狀態的入口。
前面說的入口就是:Activity提供了onSaveInstanceState()方法,該方法是Activity在關閉前保存狀態的核心方法。
前面提到「被動關閉」,如果是主動關閉那麼就不會調用,比如:按back鍵、調用finish()等,那麼"被動關閉"的場景有哪些呢?下面給列舉一下:
肯定在調用onStop()前被調用,但不保證在onPause()前 / 後,一般是在onPause()後調用。
當需要保持狀態時,在onSaveInstanceState()內執行以下邏輯:
當需要恢復時,在onCreate()內部執行以下邏輯:
布局每個View默認實現:onSaveInstanceState(),即UI的任何改變都會自動的存儲和在activity重新創建的時候自動的恢復(只有在為該UI提供了唯一ID後才起作用);
若需復寫該方法從而存儲額外的狀態信息時,應先調用父類的onSaveInstanceState()(因為默認的onSaveInstanceState()幫助UI存儲它的狀態);
只使用該方法記錄Activity的瞬間狀態(UI的狀態),而不是去存儲持久化數據,因為onSaveInstanceState()調用時機不確定性;可使用 onPause()[一定會執行]存儲持久化數據;
Activity提供了onRestoreInstanceState()方法,該方法是Activity在重新創建後恢復之前保存狀態的核心方法。
若被動關閉了Activity,即調用了onSaveInstanceState(),那麼下次啟動時會調用onRestoreInstanceState()。
onCreate()--->onStart()--->onRestoreInstanceState()--->onResume()
注意: onSaveInstanceState()、onRestoreInstanceState()不一定 成對被調用
如:當正在顯示Activity A時,用戶按下HOME鍵回到主界面,然後用戶緊接著又返回到Activity A,此時Activity A一般不會因為內存的原因被系統銷毀,故Activity A的onRestoreInstanceState()不會被執行;
針對以上情況,onSaveInstanceState保持的參數可以選擇在onCreate()內部進行解析使用,因為onSaveInstanceState的bundle參數會傳遞到onCreate方法中,可選擇在onCreate()中做數據還原。
至此:Activity的啟動模式及Activity的狀態保持及恢復介紹完畢。
Ⅶ Activity的基礎知識(下)
上篇總結了Activity的一些知識,現在繼續對Activity的知識進行梳理,包括Activity直接傳遞數據,Activity的生命周期,Activity的啟動模式等.
1.intent傳遞數據:
使用startActivity方法,intent的putExtra()方法,以鍵值對的形式傳遞數據,該方法有很多重載方法,可以根據傳遞數據的不同類型選擇合適的方法.除了有putExtra()方法外,還有putExtras()方法,傳遞的參數是Bundle.
如果傳遞的是對象,這個對象要實現序列化,也就是實現Parcelable或者Serializable介面.
如果希望被啟動的頁面返回數據,需要使用startActivityForResult()方法,這個方法中需要設置訪問號,用來區分不同的訪問者.並且在啟動頁重寫onActivityResult方法用來接收返回的數據,
2.兩種情況下的Activity的生命周期.
正常情況下的生命周州春或期,正常情況是指用戶的正常操作下的Activity的生命周期.後面會分析異常情況下的生命周期.
onCreate: Activity第一次創建時候的回調,主要是在這個方法進行初始化工作,比如初始化控制項和事件綁定工作.
onStart:Activity從不可見狀態變成可見狀態.
onResume:Activity變成前台,可以和用戶交互.
onPause:Activity可見但是不能和用戶交互.
onStop:Activity從可見變得不可見,成為後台Activity.
onDestroy:Activity銷毀時調用.
onRestart:Activity從後台冊伍變成前台Activity.
在啟動Activity和兩個Activity之間跳轉時,可以知道Activity的生命周期變化過程,有兩個說明:(1)在兩個Activity跳轉時,第一個Activty的onPause,onStop方法和第二個Activity的生命周期方法調用時機.
第一個Activity先執行onPause方法,第二個Activity才能創建.這也就意味著在onPause方法中不能執行太耗時的操作,否則會影響第二個Activity的創建.在源碼(ActivityStack)中有這樣的注釋:
(2)onStart和onResume,onPause和onStop這兩對方法的實質不同處:onStart和onStop這兩個方法是從Activity的可見性來區分的,onResume和onPause是從Activity是否處於前台,是否可以和用戶交互來區分的,注意在onPause調用時Activity還是可見的,調用時機比如彈出dialog時,下面的Activity是可見的.這個時候調用的是onPause方法.
異常情況下Activity的生命周期:異常情況下是指資源相關配置發生變化或後台Activity被系統回收時Activity的生命周森茄期.後台Activity被系統回收的情況比較難復現,在資源相關配置發生變化時和後台Activity被回收時的生命周期執行過程是一樣的,比較容易復現的就是橫豎屏切換時的生命周期執行過程.在 AndroidManifest的Activity組件下配置android:screenOrientation標簽,當設置可以橫豎方向隨著方向感應器來調節時,在切換時會出現先銷毀Activity再創建的過程.
過程:
在這種情況下有可能會有數據的丟失,系統提供用來保存數據和還原數據的方法:onSaveInstanceState和onRestoreInstanceState.用方法參數Bundle可以保存和還原數據.
可以根據需要設置android:screenOrientation標簽,設定activity的方向,如果activity的方向是需要橫豎屏切換,但是不容許銷毀Activity,可以設置如下標簽,當這些情況(常用的)發生變化是不會重新走Activity的生命周期方法,只會調用onConfigurationChanged,可以根據情況在這個方法里更新操作.
切換時的log輸出
3.Activity的四種啟動模式
標准:是Activity的默認啟動模式,對於AndroidManifest的Activity節點下的android:launchMode="standard"標簽.
特定:每次啟動都會重新創建新的Activity.
singleTop:對應的AndroidManifes的Activity節點下的android:launchMode="singleTop"標簽
特點:當此模式的Activity處於棧頂時,不會重新創建新的Activity,會調用onNewIntent方法,如果更新Activity的intent,需要調用 setIntent()方法,具體的生命周期過程
singleTask:在activity棧中已經有需要再啟動的activity時,會先清除位於需要啟動activity之上的activity,例如:啟動順序mainActivity-activityA -activityB-activityA,其中activityA是singleTask的啟動模式:
singleInstance:在一個棧中單獨存在的activity.
關於activity棧:是指用來管理activity一種"先進先出"的隊列結構,查看activity對應棧的方法:Activity的getTaskId()方法,同一個棧的id值是相同的.adb shell mpsys activity在終端查看棧結構,比如還是上面的activity啟動順序,不同是ActivityA這是設置成singleInstance,這是的棧結構:有兩個TaskRecord,其中ActivityB和MainActivity位於同一個棧中.
4.Activity開發中使用技巧:
<1>定義一個父Activity,在創建新的Activity時繼承這個activity即可,將一些activity的公共設置可以設置在父activity中,比如獲取每個Activity的名字,設置activity的窗體屬性,同一管理activity的生命周期等,
<2>在啟動的activity中定義靜態方法,啟動條件會顯而易見:
<3>管理activity類,用來一鍵退出app.在父類Activity的創建和銷毀時用來添加和移除Activity,在需要一鍵退出的地方調用靜態finishAllActivity方法.
Ⅷ Android之Activity全面解析,有些知識點容易忘記
Activity作為安卓四大組件之一,是最重要也是用得最多的組件,涉及的知識點非常多,有些知識點平時開發很少用到,但在某些場景下需要特別注意,本文詳細整理了Activity涉及的知識點,供開發參考。
針對Activity可以提出很多問題,如:
Activity 的生命周期?
Activity 之間的通信方式?
Activity 各種情況下的生命周期?
橫豎屏切換時 Activity 的生命周期?
前台切換到後台,然後再回到前台時 Activity 的生命周期?
彈出 Dialog 的時候按 Home 鍵時 Activity 的生命周期?
兩個Activity之間跳轉時的生命周期?
下拉狀態欄時 Activity 的生命周期?
Activity 與 Fragment 之間生命周期比較?
Activity 的四種 LaunchMode(啟動模式)的區別?
Activity 狀態保存與恢復?
Activity的轉場動畫有哪些實現方式?
Activity的生命周期中怎麼獲取控制項寬高?
onNewIntent的執行時機?
如何連續退出多個Activity?
如何把Acitivty設置成Dialog樣式 ,android:theme="@android:style/Theme.Dialog"
關於橫豎屏切換的生命周期,對應不同的手機,由於廠商定製的原因,會有不同的效果,如設置了configChanges="orientation」在有些手機會執行各個生命周期,但有些手機卻不會執行。
網上常見的結論如下:
但實際的測試如下:
可以看出,不同廠商的手機切屏生命周期會有差異。
從API 13以上,當設備在橫豎切屏時,「屏幕尺寸」也會發生變化,因此為了杜絕切屏導致頁面銷毀重建,需要加上screenSize,使用設置4,即 android:configChanges="orientation|keyboardHidden|screenSize" .
Activity的四種狀態如下:
在activity處於paused或者stoped狀態下,如果系統內存緊張,可能會被銷毀,當重回該activity時會重建,正常返回和被回收後返回的生命周期如下:
如果是回收後返回,onCreate的參數savedInstanceState不為空。
有哪些場景會觸發onNewIntent回調呢?跟啟動模式有關,首先該Activity實例已經存在,再次啟動才可能觸發。一種情況是啟動模式是singleTask或者singleInstance,無論該activity在棧中哪個位置,都會觸發onNewIntent回調,並且把上面其他acitivity移除,另一種情況是啟動模式是singleTop或者以FLAG_ACTIVITY_SINGLE_TOP啟動,並且該activity實例在棧頂,會觸發onNewIntent,如果不在棧頂是重新創建的,不會觸發。
在實際業務開發中,往往碰到需要連續退出多個activity實例,下面整理了幾種常見方法:
● 發送特定廣播
1、在需要處理連續退出的activity注冊該特定廣播;
2、發起退出的activity發送該特定廣播;
3、接收到該廣播的activity 調用finish結束頁面。
● 遞歸退出
1、用startActivityForResult啟動新的activity;
2、前一個頁面finish時,觸發onActvityResult回調,再根據requestCode和resultCode處理是否finish,達到遞歸退出的效果。
● FLAG_ACTIVITY_CLEAR_TOP
通過intent.setFlag(Intent.FLAG_ACTIVITY_CLEAR_TOP)啟動新activity,如果棧中已經有該實例,則會把該activity之上的所有activity關閉,達到singleTop啟動模式的效果。
● 自定義activity棧
1、自定義activity列表,新打開activity則加入棧中,關閉則移除棧;
2、需要退出多個activity時,則循環從棧中移除activity實例,並調用finish。
在討論Activity啟動模式經常提到任務棧,那到底什麼是任務棧?
任務是一個Activity的集合,它使用棧的方式來管理其中的Activity,這個棧又被稱為返回棧(back stack),棧中Activity的順序就是按照它們被打開的順序依次存放的。返回棧是一個典型的後進先出(last in, first out)的數據結構。下圖通過時間線的方式非常清晰地向我們展示了多個Activity在返回棧當中的狀態變化:
taskAffinity 任務相關性,可以用於指定一個Activity更加願意依附於哪一個任務,在默認情況下,同一個應用程序中的所有Activity都具有相同的affinity, 名字為應用的包名。當然了,我們可以為每個 Activity 都單獨指定 taskAffinity 屬性(不與包名相同)。taskAffinity 屬性主要和 singleTask 啟動模式和 allowTaskReparenting 屬性配對使用,在其他情況下沒有意義。
taskAffinity 有下面兩種應用場景:
分為顯示啟動和隱式啟動。
(1)顯示啟動
直接指定待調整的Activity類名。
(2)隱式啟動
Intent 能夠匹配目標組件的 IntentFilter 中所設置的過濾信息,如果不匹配將無法啟動目標 Activity。IntentFilter 的過濾信息有 action、category、data。
IntentFilter 需要注意的地方有以下:
● 一個 Activity 中可以有多個 intent-filter
● 一個 intent-filter 同時可以有多個 action、category、data
● 一個 Intent 只要能匹配任何一組 intent-filter 即可啟動對應 Activity
● 新建的 Activity 必須加上以下這句,代表能夠接收隱式調用
<category android:name="android.intent.category.DEFAULT" />
只要匹配一個action即可跳轉,注意的是action要區分大小寫。
規則:如果intent中有category,則所有的都能匹配到intent-filter中的category,intent中的category數量可用少於intent-filter中的。另外,單獨設置category是無法匹配activity的,因為category屬性是一個執行Action的附加信息。
intent不添加category會匹配默認的,即 「android:intent.category.DEFAULT」
如果上面例子,如果去掉intent.setAction("action_name"),則會拋出異常:
規則:類似action,但data有復雜的結構,只要匹配一個data並且與data中所有屬性都一致就能匹配到Activity,只要有1個屬性不匹配,都無法找到activity。
data的結構:
data 主要是由 URI 和 mimeType 組成的。
URI 可配置很多信息,的結構如下:
與url類似,例如:
mineType:指資源類型包括文本、圖片、音視頻等等,例如:text/plain、 image/jpeg、video/* 等
下面看下data匹配的例子:
只匹配scheme
只匹配scheme也是能匹配到activity的。
匹配scheme、host、port
將上面的data改為
匹配mineType
如果有mineType,則不能僅設置setData或setMineType了,因為setData會把mineType置為null,而setMineType會把data置為null,導致永遠無法匹配到activity,要使用setDataAndType。
使用scheme的默認值contentfile
注意該方法需要在startAtivity方法或者是finish方法調用之後立即執行,不能延遲,但可以在子線程執行。
而在windowAnimationStyle中存在四種動畫:
activityOpenEnterAnimation // 打開新的Activity並進入新的Activity展示的動畫
activityOpenExitAnimation // 打開新的Activity並銷毀之前的Activity展示的動畫
activityCloseEnterAnimation //關閉當前Activity進入上一個Activity展示的動畫
activityCloseExitAnimation // 關閉當前Activity時展示的動畫
overridePendingTransition的方式比較生硬,方法也比較老舊了,不適用於MD風格,google提供了新的轉場動畫ActivityOptions,並提供了兼容包ActivityOptionsCompat。
我們知道在onCreate和onResume裡面直接獲取到控制項寬高為0,那有什麼辦法獲取到控制項的實際寬高?只要有onWindowFocusChanged、view.post、ViewTreeObserver三種方式獲取。
當用戶點擊桌面圖標啟動APP時,背後的流程如下:
我們看到的手機桌面是Launch程序的界面,點擊應用圖標會觸發點擊事件,調用startActivity(intent),然後通過Binder IPC機制,與ActivityManagerService(AMS)通訊,AMS執行一系列操作,最終啟動目前應用,大概流程如下:
通過PackageManager的resolveIntent()收集跳轉intent對象的指向信息,然後通過grantUriPermissionLocked()方法來驗證用戶是否有足夠的許可權去調用該intent對象指向的Activity。如果有許可權,則在新的task中啟動目標activity,如果發現沒有進程,則先創建進程。
如果進程不存在,AMS會調用startProcessLocked創建新的進程,在該方法中,會通過socket的通訊方式通知zygote進程孵化新的進程並返回pid,在新的進程中會初始化ActivityThread,並依次調用Looper.prepareLoop()和Looper.loop()來開啟消息循環。
創建好進程後下一步要將Application和進程綁定起來,AMS會調用上一節創建的ActivityThread對象的bindAppliction方法完成綁定工作,該方法會發送一條BIND_APPLICATION的消息,最終會調用handleBindApplication方法處理消息,並調用makeApplication方法處理消息,載入APP的classes到內存中。
通過前面的步驟,系統已經擁有了該Application的進程,後續的啟動則是從已存在其他進程中啟動Acitivity,即調用realStartAcitvityLocked,該方法會調用Application的主線程對象ActivityThread的sheleLaunchActivity方法,在方法中會發送LAUNCH_ACTIVITY到消息隊列,最終通過handleLaunchActivity處理消息,完成Acitivty的啟動。
Activity
Activity 的 36 大難點,你會幾個?「建議收藏」
[譯]Android Application啟動流程分析