最近年末了,打算把本身的Android知識都整理一下。android
Android技能書系列:git
Android基礎知識github
Android技能樹 — Android存儲路徑及IO操做小結數據結構
數據結構基礎知識
算法基礎知識
此次是講Activity的相關內容。仍是老樣子,先上腦圖,而後具體一塊塊詳細說明。
我估計若是面試面試官問你Activity的生命週期報一遍看,你內心必定暗罵mmp。由於這種通常菜鳥都知道有哪些經常使用的,雖然可能有些不經常使用的,不清楚也是很正常,並且面試官也就是想知道幾個經常使用的而已。
詳細的生命週期能夠看我這篇文章:超詳細的生命週期圖-你能回答全嗎
通常來講咱們講的生命週期就是下面幾個:
其中主要提下(onStart - onStop),(onResume - onPause)這二對。
問題1:
咱們通常app裏面啓動一個新的Activity後,onStart ——> onResume,都會執行,那何時會執行onStart,何時接着執行onResume呢?而咱們啓動另一個新的Activity,或者按了Home鍵回到了桌面,這時候會執行onPause ——> onStop,那何時會執行onPause呢?何時會執行onStop呢?
咱們能夠這麼記: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,裏面有一個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很容易被殺死,因此一些後臺工做更適合放到Service中去,這樣保證優先級。不會輕易被系統殺死。
固然通常咱們也只要知道幾個經常使用的切換過程便可,貼上網上別的文章常常用到的圖片:
其實關於啓動方式的,文章真的太多太多了,我推薦一下這二篇文章,方便你們弄懂。
基礎知識:
完全弄懂Activity四大啓動模式
這裏推薦一篇進階版的啓動模式的文章:
Android面試官裝逼失敗之:Activity的啓動模式
好吧,其實就是我偷懶了。不想長篇大論的寫啓動方式了。。。哈哈
Activity的啓動能夠分爲顯式調用和隱式調用二種。
這個是咱們最多見的方式了。直接寫上目標的Activity的名字,而後startActivity
或者startActivityForResult
來啓動。
通常的代碼是這樣的(好比從MainActivity啓動了TargetActivity):
Intent intent = new Intent();
intent.setClass(MainActivity.this,TargetActivity.class);
startActivity(intent);
複製代碼
你們可能會想,通常都是上面那種啓動方式來啓動的,那這種隱式調用有啥用。
好比咱們如今須要點擊按鈕,進入到撥打咱們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。
好比咱們在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>
複製代碼
和上面的category比較相似,因此也不具體些例子了。你們看腦圖便可。
圖片表明個人心。。。有啥寫錯的,歡迎吐槽留言。。哈哈。