編輯:關於Android編程
學習章節:
第一章 Activity的生命周期和啟動模式
學習內容:
1正常情況下Activity的生命周期分析
先上一張經典圖片鎮樓:
測試正常情況Activity生命周期的代碼:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.e("tag", "生命周期:onCreate"); } @Override protected void onStart() { super.onStart(); Log.e("tag", "生命周期:onStart"); } @Override protected void onResume() { super.onResume(); Log.e("tag", "生命周期:onResume"); } @Override protected void onRestart() { super.onRestart(); Log.e("tag", "生命周期:onRestart"); } @Override protected void onPause() { super.onPause(); Log.e("tag", "生命周期:onPause"); } @Override protected void onStop() { super.onStop(); Log.e("tag", "生命周期:onStop"); } @Override protected void onDestroy() { super.onDestroy(); Log.e("tag", "生命周期:onDestroy"); } }
測試結果:
第一次啟動Activity,生命周期回調如下:
這時候將應用切換到後台或打開一個新的Activity,生命周期回調如下:
然後再次打開這個應用,生命周期回調如下:
最後按返回鍵退出這個應用,生命周期回調如下:
假如應用已經被切換到後台,這時直接結束所有進程,生命周期回調如下:
從整個生命周期來說,onCreate和onDestroy是配對的,分別標識著Activity的創建和銷毀,並且只可能有一次被調用。從Activity是否可見來說,onStart和onStop是配對的,隨著用戶的操作或者設備屏幕的點亮和熄滅,這兩個方法可能被調用多次;從Activity是否在前台來說,onResume和onPause是配對的,隨著用戶的操作或者設備屏幕的點亮和熄滅,這兩個方法可能被調用多次。
假設當前Activity為A,如果這時用戶打開一個新ActivityB,那麼B的onResume和A的onPause哪個先執行???
測試代碼:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;"> private void initView() { btn = (Button) findViewById(R.id.btn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startActivity(new Intent(MainActivity.this, SecondActivity.class)); } }); }
測試結果:
可以看到,新啟動一個Activity的時候,舊Activity的onPause會先執行,然後才會啟動新的Activity。通過分析這個問題,我們知道不能在onPause中做重量級的操作,因為必須onPause執行完成以後新Activity才能Resume。
2異常情況下Activity的生命周期分析
2.1資源相關的系統配置發生改變導致Activity的生命周期的改變
最常見的一種情況就是屏幕旋轉導致的Activity的生命周期的改變,網上關於Android橫豎屏切換生命周期的資料一大把,照著這些資料高高興興的敲著代碼,尼瑪卻發現坑爹呀!!!
代碼與結果才是最有說服力,開始舉栗子:
Activity的配置文件不設置任何參數,從豎屏切換到橫屏:
調用了一次完整的生命周期
Activity的配置文件不設置任何參數,從橫屏切換到豎屏:
調用了一次完整的生命周期,並沒有調用兩次!!!
Activity的配置文件設置:android:configChanges=”orientation”,從豎屏切換到橫屏或者從橫屏切換到豎屏:
調用了一次完整的生命周期,也就是說,設置這個參數沒有任何作用!!!
Activity的配置文件:android:configChanges=”orientation"keyboardHidden,從豎屏切換到橫屏或者從橫屏切換到豎屏:
調用了一次完整的生命周期,也就是說,額外設置這個參數沒有任何作用,這不坑爹嘛!說好的不調用生命周期呢???
後來才知道,當android:targetSdkVersion<=12,不會調用完整生命周期;當android:targetSdkVersion>12,會調用完整生命周期。我的 targetSdkVersion是23,嗦嘎寺內。
那麼我該怎樣設置才能在targetSdkVersion大於12的時候,橫豎屏切換不調用生命周期呢:
android:configChanges="orientation|keyboardHidden|screenSize"
這樣就夠了,看看打印結果,沒有打印與生命周期相關的log,只是調用了系統的onConfigurationChanged方法,這時候我們可以自己做一些處理,完美。
當然,你要是這樣設置
android:screenOrientation="portrait"
這就另當別論,只允許豎屏,管你怎麼切換都沒用,就不談生命周期的調用了。
OK,我們已經知道這種系統配置發生變化的情況會調用Activity的完整生命周期。這種Activity被異常中止的情況下,系統會調用onSaveInstanceState來保存當前Activity的狀態,正常情況下系統不會回調這個方法。 繼續舉例子:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.e("tag", "A生命周期:onCreate"); initView(); if (savedInstanceState != null) { Log.e("tag", "savedInstanceState保存的值為:" + savedInstanceState.get("data")); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.e("tag", "onSaveInstanceState"); outState.putString("data", "我是需要保存的值"); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); Log.e("tag", "savedInstanceState保存的值為:" + savedInstanceState.get("data")); }
還是這段代碼,當我第一次正常啟動時,沒有什麼不同:
當我切換到橫屏時,高潮來了:
果然和預期的一樣,這種異常情況調用了onSaveInstanceState方法,比如我們需要在這種異常情況下保存一些必要的參數,像播放視頻時橫豎屏切換,我一定得保存當前的播放進度等信息。那麼我們可以把這些參數以鍵值對的形式保存在onSaveInstanceState方法中。怎樣恢復這些數據呢,恢復數據的位置有兩種:onRestoreInstanceState和onCreate,區別如下:
onRestoreInstanceState一旦被調用,其參數Bundle savedInstanceState一定是有值的,我們不用額外判斷是否為空
onCreate如果正常啟動的話,其參數Bundle savedInstanceState為空,我們需要額外判斷是否為空
2.2資源內存不足導致優先級的Activity被殺死
Activity按照優先級從高到低,可以分為以下三種:
(1)前台Activity:正在和用戶交互的Activity,優先級最高;
(2)可見但非前台Activity:比如Activity中彈出了一個對話框,導致Activity可見但是位於後台無法和用戶直接交互;
(3)後台Activity:已經被暫停的Activity,比如執行了onStop,優先級最低
系統會按照上述優先級去殺死目標Activity所在的進程,並通過onSaveInstanceState與onRestoreInstanceState方法來存儲和恢復數據。一些後台工作不適合脫離四大組件而獨自運行在後台中,這樣很容易被殺死。比較好的做法是將後台工作放入Service中從而保證進程有一定的優先級,這樣不會輕易地被系統殺死。
3Fragment的生命周期分析:
先上經典圖片:
測試情況,初始化第一個Fragment:
然後將當前Fragment進行remove操作,並加入回退棧:
返回到第一個Fragment:
退出當前Fragment:
測試結果一目了然。更好的理解與掌握Activity與Fragment的生命周期對以後開發有很大作用!!!
4 Activity的啟動模式
任務棧:任務棧Task:一種以棧的形式來存放Activity實例的容器,原則是“後進先出”,主要有兩個操作:壓棧和出棧。其中存放的Activity是不支持重新排序的,只能根據壓棧和出棧操作更改Activity的順序。是為實現一個功能而負責管理所有用到的Activity實例的棧。默認情況下,所有Activity所需的任務棧名字為應用的包名。
啟動一個Application的時候,系統會為它默認創建一個對應的Task,用來放置根Activity。默認情況下,啟動Activity會放在同一個Task中,新啟動的Activity會被壓入啟動它的那個Activity的棧中,並且顯示新啟動的Activity。當用戶按下回退鍵時,這個Activity就會被彈出棧;當按下Home鍵回到桌面,再啟動另一個應用,這時候之前那個Task就被移到後台,成為後台任務棧,而剛啟動的那個Task就被調到前台,成為前台任務棧,Android系統顯示的就是前台任務棧中的Top實例Activity。
啟動模式: 通過設置啟動模式來修改系統的默認行為
standard:標准模式,可以不用寫配置,這也是系統的默認模式。每次啟動一個Activity都會創建一個新的實例,不管這個實例是否已經存在。
應用場景:絕大多數Activity。
singleTop:棧頂復用模式,在這中模式下,如果新Activity已經位於任務棧的棧頂,那麼此Activity不會被重新創建,同時它的onNextIntent方法會被回調,通過此方法的參數我們可以取出當前請求的信息。需要注意的是,這個Activity的onCreate和onStart不會被系統調用,因為它並沒有發生改變。
應用場景:假設目前棧內的情況為ABC,其中ABC為三個Activity,A位於棧底,C位於 棧頂。這個時候再次啟動C,如果C的啟動模式為singleTop,那麼棧內的情況依然為ABC;如果C的啟動模式為standard,那麼由於C被創建,導致棧內的情況就變為ABCC。
singleTask:棧內復用模式, activity只會在任務棧裡面存在一個實例。如果要激活的activity,在任務棧裡面已經存在,就不會創建新的activity,而是復用這個已經存在的activity,調用 onNewIntent() 方法,並且清空這個activity任務棧上面所有的activity。
應用場景:大多數App的主頁。對於大部分應用,當我們在主界面點擊回退按鈕的時候都是退出應用,那麼當我們第一次進入主界面之後,主界面位於棧底,以後不管我們打開了多少個Activity,只要我們再次回到主界面,都應該使用將主界面Activity上所有的Activity移除的方式來讓主界面Activity處於棧頂,而不是往棧頂新加一個主界面Activity的實例,通過這種方式能夠保證退出應用時所有的Activity都能報銷毀。
測試例子:首先從主界面A跳轉到第二個界面B,再從第二個界面B跳轉到第三個界面C,最後從第三個界面C跳轉到主界面A。
main.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startActivity(new Intent(MainActivity.this, SecondActivity.class)); } }); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ second.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startActivity(new Intent(getApplicationContext(), ThirdActivity.class)); } }); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ third.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startActivity(new Intent(getApplicationContext(), MainActivity.class)); } });
設置主界面A的啟動模式為,其他兩個是默認啟動模式
android:launchMode="singleTask"
我們監聽一下最後從第三個界面C跳轉到主界面A發生了什麼:
第二個界面B首先執行了onDestroy,然後第三個界面C也執行了onDestroy,也就是說兩個Activity的實例都被銷毀了,是不是這樣呢?我們這時候在主界面按返回鍵,看看發生了什麼:
果然,主界面A實例銷毀,直接退出了應用,與預期效果一致。
singleInstance:單一實例模式,整個手機操作系統裡面只有一個實例存在。不同的應用去打開這個activity 共享公用的同一個activity。他會運行在自己單獨,獨立的任務棧裡面,並且任務棧裡面只有他一個實例存在。
應用場景:呼叫來電界面。這種模式的使用情況比較罕見,在Launcher中可能使用。或者你確定你需要使Activity只有一個實例。建議謹慎使用。
1.3 Activity的Flags
系統提供了兩種方式來設置一個Activity的啟動模式,除了在AndroidManifest文件中設置以外,還可以通過Intent的Flag來設置一個Activity的啟動模式,下面我們在簡單介紹下一些Flag。
FLAG_ACTIVITY_NEW_TASK
使用一個新的Task來啟動一個Activity,但啟動的每個Activity都講在一個新的Task中。該Flag通常使用在從Service中啟動Activity的場景,由於Service中並不存在Activity棧,所以使用該Flag來創建一個新的Activity棧,並創建新的Activity實例。
FLAG_ACTIVITY_SINGLE_TOP
使用singletop模式啟動一個Activity,與指定android:launchMode=“singleTop”效果相同。
FLAG_ACTIVITY_CLEAR_TOP
使用SingleTask模式來啟動一個Activity,與指定android:launchMode=“singleTask”效果相同。
FLAG_ACTIVITY_NO_HISTORY
Activity使用這種模式啟動Activity,當該Activity啟動其他Activity後,該Activity就消失了,不會保留在Activity棧中。
測試例子:
還是上面一樣的場景,這次我們不在配置文件設置,將從第三個界面C跳轉到主界面A的代碼設置如下:
third = (Button) findViewById(R.id.third_btn); third.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(); intent.setClass(ThirdActivity.this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); } });
看看發生了什麼:
然後按下返回鍵:
與預期效果一致,完美。
5 IntentFilter的匹配原則
只有一個Intent同時匹配action類別,category類別,date類別才算完全匹配,只有完全匹配才能成功啟動目標Activity。一個Activity中可以有多個intent-filter,一個Intent只要能匹配任何一組intent-filter即可成功啟動對應的Activity。
action的匹配規則:
action的匹配要求Intent中的action存在且必須和過濾規則中的其中一個action相同,action區分大小寫,大小寫不同字符串相同的action會匹配失敗。
category的匹配規則:
Intent如果含有category,那麼所有的category都必須和過濾規則中的其中一個category相同,如果沒有category依舊可以匹配成功。
data的匹配規則:
與action類似,如果過濾規則中定義了data,那麼Intent中必須也要定義可匹配的data。
在android中,經常要用到幫助、about、關於作者等的提示頁面。類似這樣的頁面:這樣的頁面,我們可以通過AlertDialog對話框來設計。設計一個AboutDia
一、前言快過年了,先提前祝賀大家新年快樂,這篇文章也是今年最後一篇了。今天我們繼續來看逆向的相關知識,前篇文章中我們介紹了如何解析Android中編譯之後的Android
寫這篇博客的目的就是教大家利用AndroidSDK自帶的support lib來實現APP日間/夜間模式的切換,最近看到好多帖子在做關於這個日夜間模式切換的開源項目,其實
什麼是SplashSplash也就是應用程序啟動之前先啟動一個畫面,上面簡單的介紹應用程序的廠商,廠商的LOGO,名稱和版本等信息,多為一張圖片,顯示幾秒鐘後會自動消息,