前言
最近年底了,打算把自己的Android知識都整理一下。
Android技能書系列:
Android基礎知識
Android技能樹 — Android儲存路徑及IO操作小結
資料結構基礎知識
演算法基礎知識
這次是講Activity的相關內容。還是老樣子,先上腦圖,然後具體一塊塊詳細說明。
Activity生命週期
我估計如果面試面試官問你Activity的生命週期報一遍看,你心裡一定暗罵mmp。因為這種一般菜鳥都知道有哪些常用的,雖然可能有些不常用的,不清楚也是很正常,而且面試官也就是想知道幾個常用的而已。
詳細的生命週期可以看我這篇文章:超詳細的生命週期圖-你能回答全嗎
正常生命週期
一般來說我們講的生命週期就是下面幾個:
其中主要提下(onStart - onStop),(onResume - onPause)這二對。
問題1:
我們一般app裡面啟動一個新的Activity後,onStart ——> onResume,都會執行,那什麼時候會執行onStart,什麼時候接著執行onResume呢?而我們啟動另外一個新的Activity,或者按了Home鍵回到了桌面,這時候會執行onPause ——> onStop,那什麼時候會執行onPause呢?什麼時候會執行onStop呢?
我們可以這麼記:Activity啟動後,我們會看到介面,然後可以點選介面上的按鈕,這時候是不是分成了二大塊:
- 看得到我們寫Activity的介面。
- 然後可以操作我們的介面。
所以(onStart - onStop)和介面的可見與否有關, (onResume - onPause)和介面是否可以操作有關。
舉個簡單例子,比如我們在Activity上有個按鈕A,,這時候啟動一個彈框或者啟動一個新的透明的Activity,這個按鈕A肯定是不能按的,但是我們可以看到A這個按鈕,這時候onStop不會執行,但會執行onPause ,因為我們已經不能點選這個按鈕了,但是我們能看到這個按鈕。
問題2:
如果我們從A 這個Activity ,跳到了 B 這個Activity,那二個Activity的(onStart - onStop)和(onResume - onPause)又分別如何執行。
大家如果要實驗,只要在相應的生命週期處打上Log即可。結果是這樣的:
A(onPause) -> B(onCreate)->B(onStart) —> B(onResume) -> A(onStop)
所以,如果你在從一個Activity跳轉到另外一個Activity之前,要做一些操作的話,最好是放在onStop中,因為如果放在onPause中的話,會影響新的Activity啟動速度。
異常生命週期
我們看腦圖就知道,在異常生命週期中,我們會額外執行二個方法:onSaveInstanceState(Bundle outState)
和onRestoreInstanceState(Bundle savedInstanceState)
,看字面意思就知道了。在Activity銷燬的時候先通過onSaveInstanceState
的Bundle引數裡面,儲存一些內容,然後在重建時候呼叫onRestoreInstanceState
方法傳遞剛才那個Bundle,然後我們可以取出我們剛存的東西。
我們先來看什麼時候會出現異常生命週期:
- 資源相關的系統配置改變(最常見的就是旋轉螢幕)
- 記憶體不足時候,會殺死優先順序低的Activity
我們來分別檢視:
旋轉螢幕
最簡單的還是用例子來說明,我們寫了一個Activity,裡面有一個EditText:
public class SaveActivity extends AppCompatActivity {
TextView a;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_save);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("data", "儲存的資料");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.v("dyp", savedInstanceState.getString("data"));
}
}
複製程式碼
我們旋轉手機螢幕,在onSaveInstanceState中,在bundle裡面存了個字串,然後Activity重建後會在onRestoreInstanceState中的bundle中可以拿到我們存的字串。
列印的內容:
V/dyp: 儲存的資料
複製程式碼
有人會問了。我發現一個現象,就是比如我們的Activity裡面有個EditText
,這時候我在裡面輸入了123456
,我也沒在onSaveInstanceState
和onRestoreInstanceState
裡面做特殊處理,但是手機螢幕轉過來後,我的EditText
還是能顯示123456
。我們具體來分析下這個現象。
我們知道在重寫onSaveInstanceState
和onRestoreInstanceState
的時候,預設程式碼是這樣的:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
複製程式碼
既然我們沒有做額外處理,那說明關鍵點就在super.onSaveInstanceState(outState);
和super.onRestoreInstanceState(savedInstanceState);
這二句話了。
簡單來說:super.onSaveInstanceState(outState);
它會幫我們儲存Activity的相關檢視,然後分別呼叫每個View的onSaveInstanceState
方法,比如EditText
在自己的類中的這個方法就是儲存了輸入的內容。然後在super.onRestoreInstanceState(savedInstanceState);
方法中會再呼叫每個View的onRestoreInstanceState
方法,我們的EditText
就是會呼叫自己的這個方法,然後再把儲存好的內容再賦值進去。所以我們如果想知道某一個具體的View系統能自動幫我們恢復哪些資料,我們可以檢視這個View的onSaveInstanceState
和onRestoreInstanceState
程式碼(比如ListView會自動恢復滾動位置等)。
Activity的優先順序
記憶體不足時候殺死優先順序低的Activity,這時候的資料儲存和恢復過程和我們上面講的也是一樣的。
那Activity的具體的優先順序怎麼樣的呢:
- 前臺Activity - 正在和使用者互動的Activity,優先順序最高。
- 可見但不能操作的Activity - 比如我們上面說的彈出彈框等情況。
- 後臺Activity - 比如執行了onStop的activity。
我們可以看到後臺Activity很容易被殺死,所以一些後臺工作更適合放到Service中去,這樣保證優先順序。不會輕易被系統殺死。
生命週期切換過程
當然一般我們也只要知道幾個常用的切換過程即可,貼上網上別的文章經常用到的圖片:
Activity啟動方式
其實關於啟動方式的,文章真的太多太多了,我推薦一下這二篇文章,方便大家弄懂。
基礎知識:
徹底弄懂Activity四大啟動模式
這裡推薦一篇進階版的啟動模式的文章:
Android面試官裝逼失敗之:Activity的啟動模式
好吧,其實就是我偷懶了。不想長篇大論的寫啟動方式了。。。哈哈
啟動Activity
Activity的啟動可以分為顯式呼叫和隱式呼叫二種。
顯式呼叫啟動Activity
這個是我們最常見的方式了。直接寫上目標的Activity的名字,然後startActivity
或者startActivityForResult
來啟動。
一般的程式碼是這樣的(比如從MainActivity啟動了TargetActivity):
Intent intent = new Intent();
intent.setClass(MainActivity.this,TargetActivity.class);
startActivity(intent);
複製程式碼
隱式呼叫啟動Activity
大家可能會想,一般都是上面那種啟動方式來啟動的,那這種隱式呼叫有啥用。
比如我們現在需要點選按鈕,進入到撥打我們APP客服電話。我們總不可能讓使用者每次都背下來號碼,然後手動開啟電話那裡去按。
我們當前Activity上有一個按鈕,並且設定按鈕的點選事件為:
Button btn = findViewById(R.id.button);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri data = Uri.parse("tel:10086");
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(data);
startActivity(intent);
}
});
複製程式碼
這時候,你點選了這個按鈕,我們呼叫startActivity
方法,就會自動跳到撥號介面。
不過如果我們設定的規則有多個Activity都匹配,則會出現選擇框,讓你進行選擇。
比如說我們在app中開啟一個網址,
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri data=Uri.parse("http://www.baidu.com");
intent.setData(data);
startActivity(intent);
複製程式碼
這時候一般都會跳出這麼一個介面:
那具體的匹配規則是怎麼樣呢,我們可以看到我們上面有這二行程式碼:
intent.setAction(Intent.ACTION_VIEW);
intent.setData(data);
複製程式碼
所以action
和data
一定是匹配規則,同時其實還有另外一個category
。
好,我們回頭來看,我們如何設定一個Activity的規則,然後讓其他Activity通過隱式呼叫來啟動自己,就是在AndroidManifest.xml
中進行設定<intent-filter>
標籤,我們還記不記得我們設定的一個APP的啟動Activity,是不是也用的隱式呼叫。
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
複製程式碼
PS:在這裡我們看到了category了。
所以我們只需要在AndroidManifest.xml
中新增<intent-filter>
標籤,然後加入相應的action
,category
,data
等過濾條件,只要符合了,就會啟動相應的Activity。
其中具體的匹配規則,上面的腦圖也已經寫出來了:
注意點
避免隱式呼叫時候找不到Activity產生的報錯
採用相關方法,提前判斷是否有相匹配的Activity。
隱式呼叫中category的注意點
比如我們在AndroidManifest.xml中這麼寫的:
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="dyp"/>
</intent-filter>
</activity>
複製程式碼
我們只設定了action的過濾條件,然後我們在其他activity中想啟動這個MainActivity。寫了如下程式碼:
Intent intent = new Intent();
intent.setAction("dyp");
startActivity(intent);
複製程式碼
你會發現,會報異常:
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=dyp }
複製程式碼
會提示找不到,為什麼會這樣,因為我們在呼叫startActivity或者startActivityForResult的時候會預設幫我們的Intent加上一個category,也就是intent.addcategory("android.intent.category.DEFAULT");
所以如果你的activity在AndroidManifest.xml
中的<intent-filter>
沒有新增這個category
,就會無法成功匹配。
所以我們這裡要改成這樣:
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="dyp"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
複製程式碼
隱式呼叫中data的注意點
和上面的category比較類似,所以也不具體些例子了。大家看腦圖即可。
結語
圖片代表我的心。。。有啥寫錯的,歡迎吐槽留言。。哈哈。