Android技能樹 — Activity小結

前言

最近年末了,打算把本身的Android知識都整理一下。android

Android技能書系列:git

Android基礎知識github

Android技能樹 — 動畫小結面試

Android技能樹 — View小結算法

Android技能樹 — Activity小結數組

Android技能樹 — View事件體系小結bash

Android技能樹 — Android存儲路徑及IO操做小結數據結構

Android技能樹 — 多進程相關小結app

Android技能樹 — Drawable小結ide

Android技能樹 — Fragment整體小結

數據結構基礎知識

Android技能樹 — 數組,鏈表,散列表基礎小結

Android技能樹 — 樹基礎知識小結(一)

算法基礎知識

Android技能樹 — 排序算法基礎小結

此次是講Activity的相關內容。仍是老樣子,先上腦圖,而後具體一塊塊詳細說明。

Activity腦圖連接

Activity

Activity生命週期

我估計若是面試面試官問你Activity的生命週期報一遍看,你內心必定暗罵mmp。由於這種通常菜鳥都知道有哪些經常使用的,雖然可能有些不經常使用的,不清楚也是很正常,並且面試官也就是想知道幾個經常使用的而已。

詳細的生命週期能夠看我這篇文章:超詳細的生命週期圖-你能回答全嗎

正常生命週期

通常來講咱們講的生命週期就是下面幾個:

其中主要提下(onStart - onStop),(onResume - onPause)這二對。

問題1:

咱們通常app裏面啓動一個新的Activity後,onStart ——> onResume,都會執行,那何時會執行onStart,何時接着執行onResume呢?而咱們啓動另一個新的Activity,或者按了Home鍵回到了桌面,這時候會執行onPause ——> onStop,那何時會執行onPause呢?何時會執行onStop呢?
咱們能夠這麼記:Activity啓動後,咱們會看到界面,而後能夠點擊界面上的按鈕,這時候是否是分紅了二大塊:

  1. 看獲得咱們寫Activity的界面。
  2. 而後能夠操做咱們的界面。

因此(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,而後咱們能夠取出咱們剛存的東西。

咱們先來看何時會出現異常生命週期:

  1. 資源相關的系統配置改變(最多見的就是旋轉屏幕)
  2. 內存不足時候,會殺死優先級低的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,我也沒在onSaveInstanceStateonRestoreInstanceState裏面作特殊處理,可是手機屏幕轉過來後,個人EditText仍是能顯示123456。咱們具體來分析下這個現象。

豎屏

橫屏

咱們知道在重寫onSaveInstanceStateonRestoreInstanceState的時候,默認代碼是這樣的:

@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的onSaveInstanceStateonRestoreInstanceState代碼(好比ListView會自動恢復滾動位置等)。

Activity的優先級

內存不足時候殺死優先級低的Activity,這時候的數據存儲和恢復過程和咱們上面講的也是同樣的。

那Activity的具體的優先級怎麼樣的呢:

  1. 前臺Activity - 正在和用戶交互的Activity,優先級最高。
  2. 可見但不能操做的Activity - 好比咱們上面說的彈出彈框等狀況。
  3. 後臺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方法,就會自動跳到撥號界面。

但是咱們並無設置Intent指向了具體的某個XXX名字的Activity。可是仍是打開了撥號界面的這個Activity,由於咱們是隱式調用,而且設置了規則。只要規則匹配上,就會調用。

不過若是咱們設置的規則有多個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);    
複製代碼

因此actiondata必定是匹配規則,同時其實還有另一個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比較相似,因此也不具體些例子了。你們看腦圖便可。

結語

圖片表明個人心。。。有啥寫錯的,歡迎吐槽留言。。哈哈。

相關文章
相關標籤/搜索