探究 Android 中的 ActivityLifecycleCallbacks

我一定會(huì)愛(ài)上你 - 謝春花

我一定會(huì)愛(ài)上你

ActivityLifecycleCallbacks 是用來(lái)監(jiān)聽(tīng)所有 Activity 的生命周期回調(diào)。接口定義如下:

public interface ActivityLifecycleCallbacks {
    void onActivityCreated(Activity activity, Bundle savedInstanceState);
    void onActivityStarted(Activity activity);
    void onActivityResumed(Activity activity);
    void onActivityPaused(Activity activity);
    void onActivityStopped(Activity activity);
    void onActivitySaveInstanceState(Activity activity, Bundle outState);
    void onActivityDestroyed(Activity activity);
}

Activity 的每一個(gè)生命周期都對(duì)應(yīng) ActivityLifecycleCallbacks 接口中的一個(gè)方法,比如 onActivityCreated 回調(diào)是在 Activity 的 onCreate 方法中調(diào)用 getApplication().dispatchActivityCreated(this, savedInstanceState) 完成對(duì) Activity 生命周期跟蹤監(jiān)聽(tīng)。

ActivityLifecycleCallbacks 使用

  • 要求 API 14+
package com.sunpeng.lifecycle;

import android.app.Application;

/**
 * Created by sunpeng on 17/6/2.
 */
public class MainApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        // AppLifecycleCallback 實(shí)現(xiàn) ActivityLifecycleCallbacks 接口方法
        this.registerActivityLifecycleCallbacks(new AppLifecycleCallback());
    }
}

探究在 Android 中的應(yīng)用

  • 應(yīng)用新開(kāi)進(jìn)程假重啟處理(低內(nèi)存回收、修改權(quán)限)
  • 管理 Activity 頁(yè)面棧
  • 獲取當(dāng)前 Activity 頁(yè)面
  • 判斷應(yīng)用前后臺(tái)
  • 保存恢復(fù)狀態(tài)值 savedInstanceState
  • 頁(yè)面分析統(tǒng)計(jì)埋點(diǎn)

結(jié)合 ActivityLifecycleCallbacks ,實(shí)現(xiàn)了以上的一些功能,完整源碼可以查看AppLifecycleCallback

1、應(yīng)用新開(kāi)進(jìn)程假重啟處理(低內(nèi)存回收、修改權(quán)限)

應(yīng)用在低內(nèi)存的情況下退出重新啟動(dòng),并不會(huì)執(zhí)行正常的啟動(dòng)流程,而是創(chuàng)建新的進(jìn)程,直接還原上一次的操作頁(yè)面。比如:

  • 當(dāng)前操作頁(yè)面:LoginActivity
  • 正常啟動(dòng)使用流程:SplashActivity -> MainActivity -> LoginActivity
  • 低內(nèi)存重啟流程:** 新開(kāi)進(jìn)程,直接啟動(dòng) LoginActivity **

** 低內(nèi)存重啟流程存在的問(wèn)題:**頁(yè)面棧信息丟失,頁(yè)面顯示以及返回跳轉(zhuǎn)異常;MainActivity 可能沒(méi)有執(zhí)行,部分功能不會(huì)初始化。

** 解決思路:** 通過(guò)監(jiān)聽(tīng)回調(diào)方法 onActivityCreated,判斷應(yīng)用啟動(dòng)的第一個(gè) Activity 頁(yè)面是否為 LauncherActivity,如果不是,則強(qiáng)制啟動(dòng) LauncherActivity 來(lái)執(zhí)行正常的啟動(dòng)流程。

// AppLifecycleCallback.java

private static final int APP_STATUS_UNKNOWN = -1;
private static final int APP_STATUS_LIVE = 0;
private int appStatus = APP_STATUS_UNKNOWN;

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    if (appStatus == APP_STATUS_UNKNOWN) {
        // 設(shè)置狀態(tài)標(biāo)志 APP_STATUS_LIVE
        appStatus = APP_STATUS_LIVE;
        // 判斷是否跳轉(zhuǎn)閃屏頁(yè)
        startLauncherActivity(activity);
    }
}

private static void startLauncherActivity(Activity activity) {
    try {
        Intent launchIntent = activity.getPackageManager().getLaunchIntentForPackage(activity.getPackageName());
        String launcherClassName = launchIntent.getComponent().getClassName();
        String className = activity.getComponentName().getClassName();

        if (TextUtils.isEmpty(launcherClassName) || launcherClassName.equals(className)) {
            return;
        }

        Log.e(TAG, "launcher ClassName --> " + launcherClassName);
        Log.e(TAG, "current ClassName --> " + className);

        launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        activity.startActivity(launchIntent);
        activity.finish();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

** 驗(yàn)證方式:**
1、 通過(guò) Android Studio 工具 Android Device Monitor 模擬殺死當(dāng)前應(yīng)用進(jìn)程。(Tools -> Android -> Android Device Monitor

Android Device Monitor

2、App 退到后臺(tái),修改應(yīng)用權(quán)限(6.0 以上系統(tǒng)),再次 App 回到前臺(tái),同樣會(huì)出現(xiàn)應(yīng)用新開(kāi)進(jìn)程重啟。

2、管理 Activity 頁(yè)面棧

Activity 頁(yè)面棧,最常用的實(shí)現(xiàn)就是用來(lái)完全退出應(yīng)用。ActivityLifecycleCallbacksStack 來(lái)管理所有的Activity,不僅方便集中管理存儲(chǔ) Activity 實(shí)例,也不容易造成內(nèi)存泄露。

監(jiān)聽(tīng)回調(diào)方法 onActivityCreatedonActivityDestroyed 添加刪除 Actvity 實(shí)例。

// AppLifecycleCallback.java

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    ActivityStackManager.getInstance().addActivity(activity);
}

@Override
public void onActivityDestroyed(Activity activity) {
    ActivityStackManager.getInstance().removeActivity(activity);
}
// ActivityStackManager.java

public void addActivity(Activity activity){
    if (activities == null) {
        activities = new Stack<Activity>();
    }
    if (activities.search(activity) == -1) {
        activities.push(activity);
    }
}

public void removeActivity(Activity activity){
    if (activities != null && activities.size()>0){
        activities.remove(activity);
    }
}

3、獲取當(dāng)前 Activity 頁(yè)面

React Native 開(kāi)發(fā),我們經(jīng)常需要獲取當(dāng)前的 TopActivity 實(shí)例,用來(lái)彈出安全鍵盤、RN接口通信等。關(guān)于獲取當(dāng)前Activity的一些思考詳細(xì)介紹了一些思路,結(jié)合具體實(shí)現(xiàn),推薦兩個(gè)實(shí)現(xiàn)思路:** 弱引用持有當(dāng)前 Activity 實(shí)例和 Activity 頁(yè)面棧方式。**

通過(guò)監(jiān)聽(tīng)回調(diào)方法 onActivityResumed,設(shè)置當(dāng)前 Activity 頁(yè)面

// AppLifecycleCallback.java

@Override
public void onActivityResumed(Activity activity) {
    // 弱引用持有當(dāng)前 Activity 實(shí)例
    ActivityStackManager.getInstance().setCurrentActivity(activity);
    // Activity 頁(yè)面棧方式
    ActivityStackManager.getInstance().setTopActivity(activity);
}

** 疑問(wèn):為什么 Activity 頁(yè)面棧方式還需要在 onActivityResumed 中設(shè)置當(dāng)前 Activity 頁(yè)面?
** 答疑:
當(dāng)關(guān)閉 B 頁(yè)面返回 A 頁(yè)面時(shí),首先 A 頁(yè)面的 onResume 會(huì)先執(zhí)行,然后才會(huì)調(diào)用 B 頁(yè)面的 onDestroy

再來(lái)看看 ActivityStackManager 是怎么實(shí)現(xiàn)獲取當(dāng)前 Activity 頁(yè)面

// ActivityStackManager.java

private WeakReference<Activity> sCurrentActivityWeakRef;

public Activity getCurrentActivity() {
    Activity currentActivity = null;
    if (sCurrentActivityWeakRef != null) {
        currentActivity = sCurrentActivityWeakRef.get();
    }
    return currentActivity;
}

public void setCurrentActivity(Activity activity) {
    sCurrentActivityWeakRef = new WeakReference<Activity>(activity);
}

public Activity getTopActivity(){
    if (activities != null && activities.size() > 0) {
        return activities.peek();
    }
    return null;
}

public void setTopActivity(Activity activity){
    if (activities != null && activities.size() > 0) {
        if (activities.search(activity) == -1) {
            activities.push(activity);
            return;
        }

        int location = activities.search(activity);
        if (location != 1) {
            activities.remove(activity);
            activities.push(activity);
        }
    }
}

4、判斷應(yīng)用前后臺(tái)

判斷應(yīng)用是否在后臺(tái)運(yùn)行,針對(duì)前后臺(tái)運(yùn)行會(huì)做一些處理,比如提示用戶應(yīng)用運(yùn)行在后臺(tái)、以及應(yīng)用前后臺(tái)切換回調(diào)通知等。業(yè)務(wù)場(chǎng)景非常多,對(duì)于開(kāi)發(fā)而言就是提供穩(wěn)定可靠的檢測(cè)前后臺(tái)方法,避免出現(xiàn)機(jī)型不兼容問(wèn)題。

AndroidProcess 提供 6 種方法判斷 App 位于前臺(tái)或者后臺(tái)。利用
ActivityLifecycleCallbacks 實(shí)現(xiàn)的方式簡(jiǎn)單有效兼容性好,也不要需要申請(qǐng)權(quán)限。

// AppLifecycleCallback.java

private int appCount = 0;
private boolean isForground = true;

@Override
public void onActivityStarted(Activity activity) {
    appCount++;
    if (!isForground) {
        isForground = true;
        Log.e("AppLifecycle", "app into forground ");
    }
}

@Override
public void onActivityStopped(Activity activity) {
    appCount--;
    if (!isForgroundAppValue()) {
        isForground = false;
        Log.e("AppLifecycle", "app into background ");
    }
}

private boolean isForgroundAppValue() {
    return appCount > 0;
}

5、保存恢復(fù)狀態(tài)值

Activity 異常退出經(jīng)常需要保存恢復(fù)一些數(shù)據(jù),ActivityLifecycleCallbacks 實(shí)現(xiàn)數(shù)據(jù)保存恢復(fù)也是比較簡(jiǎn)單的。

// AppLifecycleCallback.java

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    if (savedInstanceState != null && savedInstanceState.getBoolean("saveStateKey", false)) {
        Log.e(TAG, "localTime --> " + savedInstanceState.getLong("localTime"));
    }
}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
    outState.putBoolean("saveStateKey", true);
    outState.putLong("localTime", System.currentTimeMillis());
}

Demo源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容