Android應用被後臺殺死後如何重新走閃屏邏輯

Android應用運行在後臺的時候,經常被系統的LowMemoryKiller殺掉,當用戶再次點擊icon或者從最近的任務列表啓動的時候,進程會被重建,並且恢復被殺之前的現場。什麼意思呢?假如APP在被殺之前的Activity堆棧是這樣的,A<B<C,C位於最上層

後臺殺死與恢復的堆棧.jpg

APP被後臺殺死後,APP端進程被銷燬了,也就不存在什麼Activity了,也就沒有什麼Activity堆棧,不過AMS的卻是被保留了下來:

後臺殺死與恢復的堆棧-殺後.jpg

當用戶再次啓動APP時候會怎麼樣呢?這個時候,首先看到其實C,而不是棧底部的A,也就是說往往被殺死後,恢復看到的第一個界面是用戶最後見到的那個界面。

後臺殺死與恢復的堆棧-恢復.jpg

而用戶點擊返回,看到的就是上一個界面B,其次是A

後臺殺死與恢復的堆棧-恢復b.jpg

之所以這樣是因爲APP端Activity的創建其實都是由AMS管理的,AMS嚴格維護這APP端Activity對應的ActivityRecord棧,可以看做當前APP的場景,不過,APP端Activity的銷燬同AMS端ActivityRecord的銷燬並不一定是同步的,最明顯的就是後臺殺死這種場景。Android爲了能夠讓用戶無感知後臺殺死,就做了這種恢復邏輯,不過,在開發中,這種邏輯帶了的問題確實多種多樣,甚至有些產品就不希望走恢復流程,本文就說說如何避免走恢復流程。結合常見的開發場景,這裏分爲兩種,一種是針對推送喚起APP,一種是針對從最近任務列表喚起APP(或者icon)。

從最近的任務列表喚起,不走恢復流程

首先,APP端必須知道當前Activity的啓動是不是在走恢復流程,Activity有一個onCreate方法,在ActivityThread新建Activity之後,會回調該函數,如果是從後臺殺死恢復來的,回調onCreate的時候會傳遞一個非空的Bundle savedInstanceState給當前Activity,只要判斷這個非空就能知道是否是恢復流程。

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
   }

知道恢復流程之後,如何處理呢?其實很簡單,直接吊起閃屏頁就可以了,不過這裏有一點要注意的是,在啓動閃屏頁面的時候,必須要設置其IntentFlag:Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK,這樣做的理由是爲了清理之前的場景,不然之前的ActivityRecord棧仍然保留在ActivityManagerService中,具體實現如下,放在BaseActivity中就可以:

Intent intent = new Intent(this, SplashActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);

如果不設置會怎麼樣呢?舉個例子,最常見的就是閃屏之後跳轉主界面,主界面經常有router邏輯,並且其啓動模式一般都是singleTask,處理一些推送,所以其onCreate跟onNewIntent都有相應的處理,如果不設置,在閃屏結束後,在startActivity啓動主界面的時候,其實是先走恢復邏輯,然後走singleTask的onNewIntent邏輯,也就是說,onNewIntent跟onCreate是會同時調用的,也可能就會引發重複處理的邏輯,因此最好清理乾淨。

從推送喚起被殺APP時,如何走閃屏邏輯

對於推送消息的處理,其路由器一般放在MainActivity,並且在onCreate跟onNewIntent都有添加,如果APP存活的情況,可以直接跳轉目標頁面,如果APP被殺,這個時候,希望先跳轉主界面,再跳轉目標頁面,在效果上來看就是,用戶先看到目標頁面,點擊返回的時候再看到主界面,如果加上閃屏,希望達到的效果是先看到閃屏、點擊返回看到目標頁,再點擊返回看到主頁面。如果簡單劃分一下推送場景,可以看做一下三種

  • 進程存活,Activity存活
  • 進程存活,但是沒有Activity存活
  • 進程不存在(無論是否被殺)

其實後面兩種完全可以看做一種,這個時候,都是要先start MainActivity,然後讓MainActivity在其OnCreate中通過startActivityForResult啓動SplashActivity,SplashActivity返回後,在start TargetActivity。下面的討論都是針對後面兩種,需要做的有兩件事

  • 一是:檢測出後面兩種場景,並且在喚起主界面的時候需要添加Intent.FLAG_ACTIVITY_CLEAR_TASK清理之前的現場
  • 二是:在MainActivity的路由系統中,針對這兩種場景要,先跳轉閃屏,閃屏回來後,再跳轉推送頁

如何判斷呢,後面兩種場景其實只需要判斷是否有Activity存活即可,也就是查查APP的topActivity是否爲null,注意不要去向AMS查詢,而是在本地進程中查詢,可以通過反射查詢ActivityThread的mActivities,也可以根據自己維護的Activity堆棧來判斷,判斷沒有存活Activity的前提下,就跳轉主頁面去路由

Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.setDate(跳轉的Uri scheme)
startActivity(intent);

在MainActivity的路由中,需要準確區分是否是推送跳轉進來的,如果不是推送跳轉進來,就不需要什麼特殊處理,如果是推送跳轉進來一定會攜帶跳轉scheme數據,根據是否攜帶數據做區分即可,看一下MainActivity的代碼:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Uri uri= getIntent().getData();
    <!--只有在intent被設置了跳轉數據的時候纔去跳轉,一般是推送就來,如果冷啓動,是沒有數據的-->
    if(uri!=null){
        SplashActivity.startActivityForResult(this,JUMP_TO_TARGET)
    }
}
<!--Intent.FLAG_ACTIVITY_CLEAR_TASK保證了onNewIntent被調用的時候,進程一定是正常活着的-->
@Override
protected void onNewIntent(Intent intent) {
    Uri uri= intent.getData();
    intent.setData(null);
    router(uri);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(requestCode==JUMP_TO_TARGET && requestCode == RESULT_OK){
        router(getIntent().getData());
        getIntent().setData(null);
    }
}

private void router(Uri uri) {

}

通過上面兩部分的處理,基本能夠滿足APP“死亡”的情況下,先跳轉閃屏的需求。


轉載鏈接:https://www.jianshu.com/p/4fc10026c1f8

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章