Application.ActivityLifecycleCallbacks監聽App進入前後臺狀態最佳實踐

筆者最近在一款Android端App產品開發中,有這樣一個需求:監聽當前App的狀態:處於前臺,亦或是後臺?

產品需求:當用戶點擊當前播放視頻進入後臺,視頻暫停,再次進入前臺,首先獲取服務器播放進度,然後更新本地視頻播放進度,以此保證App端視頻的同步。

筆者在一番技術選型之後,決定使用 Application.ActivityLifecycleCallbacks 來處理,至於其原理和簡述,讀者可直接參考此文:https://blog.csdn.net/crazymo_/article/details/78875686,筆者就不再贅述,本文主要是記錄代碼,方便他人所需。

第一步:創建核心封裝監聽接口類ForegroundCallbacks,代碼如下:

package com.qunhong.mobilevideo;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;


public class ForegroundCallbacks implements Application.ActivityLifecycleCallbacks {
    public static final long CHECK_DELAY = 500;
    public static final String TAG = ForegroundCallbacks.class.getName();
    public interface Listener {
        public void onBecameForeground();
        public void onBecameBackground();
    }
    private static ForegroundCallbacks instance;
    private boolean foreground = false, paused = true;
    private Handler handler = new Handler();
    private List<Listener> listeners = new CopyOnWriteArrayList<Listener>();
    private Runnable check;
    public static ForegroundCallbacks init(Application application){
        if (instance == null) {
            instance = new ForegroundCallbacks();
            application.registerActivityLifecycleCallbacks(instance);
        }
        return instance;
    }
    public static ForegroundCallbacks get(Application application){
        if (instance == null) {
            init(application);
        }
        return instance;
    }
    public static ForegroundCallbacks get(Context ctx){
        if (instance == null) {
            Context appCtx = ctx.getApplicationContext();
            if (appCtx instanceof Application) {
                init((Application)appCtx);
            }
            throw new IllegalStateException(
                    "Foreground is not initialised and " +
                            "cannot obtain the Application object");
        }
        return instance;
    }
    public static ForegroundCallbacks get(){
        if (instance == null) {
            throw new IllegalStateException(
                    "Foreground is not initialised - invoke " +
                            "at least once with parameterised init/get");
        }
        return instance;
    }
    public boolean isForeground(){
        return foreground;
    }
    public boolean isBackground(){
        return !foreground;
    }
    public void addListener(Listener listener){
        listeners.add(listener);
    }
    public void removeListener(Listener listener){
        listeners.remove(listener);
    }
    @Override
    public void onActivityResumed(Activity activity) {
        paused = false;
        boolean wasBackground = !foreground;
        foreground = true;
        if (check != null)
            handler.removeCallbacks(check);
        if (wasBackground){
            Log.d ("TAG","went foreground");

            for (Listener l : listeners) {
                try {
                    l.onBecameForeground();


                } catch (Exception exc) {
                    Log.d ("TAG","Listener threw exception!:"+exc.toString());
                }
            }
        } else {
            Log.d ("TAG","still foreground");
        }
    }
    @Override
    public void onActivityPaused(Activity activity) {
        paused = true;
        if (check != null)
            handler.removeCallbacks(check);
        handler.postDelayed(check = new Runnable(){
            @Override
            public void run() {
                if (foreground && paused) {
                    foreground = false;
                    Log.d ("TAG","went background");
                    for (Listener l : listeners) {
                        try {
                            l.onBecameBackground();
                        } catch (Exception exc) {
                            Log.d ("TAG","Listener threw exception!:"+exc.toString());
                        }
                    }
                } else {
                    Log.d ("TAG","still foreground");
                }
            }
        }, CHECK_DELAY);
    }
    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
    @Override
    public void onActivityStarted(Activity activity) {}
    @Override
    public void onActivityStopped(Activity activity) {}
    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
    @Override
    public void onActivityDestroyed(Activity activity) {}
}

第二步:創建BaseApplication,監聽App生命週期必須在此類中,同時切記在配置文件中標明此類,代碼如下:

  • 注意需要這裏初始化;
  • 注意配置文件中name;
package com.qunhong.mobilevideo;

import android.app.Application;
import android.util.Log;

public class BaseApplication extends Application {


    @Override
    public void onCreate() {
        super.onCreate();

        ForegroundCallbacks.init(this);

        ForegroundCallbacks.get().addListener(new ForegroundCallbacks.Listener() {
            @Override
            public void onBecameForeground() {
                Log.d("TAG", "當前程序切換到前臺");
            }

            @Override
            public void onBecameBackground() {
                Log.d("TAG", "當前程序切換到後臺");
            }
        });
    }

}
 <application
        android:name=".BaseApplication"
        android:allowBackup="true"
        android:icon="@drawable/icon"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:networkSecurityConfig="@xml/network_config"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity"
            android:theme="@style/AppTheme.StartingWindowTheme"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>


    </application>

第三步:在Activity中使用,核心代碼如下:

public class MainActivity extends Activity implements ForegroundCallbacks.Listener{

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


        setContentView(R.layout.activity_main);


        // 註冊監聽
        ForegroundCallbacks.get(this).addListener(this);

    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 移除監聽
        ForegroundCallbacks.get(this).removeListener(this);
    }


    @Override
    public void onBecameForeground() {
        // 切換爲前臺
        Log.d("TAG","App切換到前臺了");
//        Toast.makeText(getBaseContext(),"App切換到前臺了",Toast.LENGTH_LONG).show();
    }

    @Override
    public void onBecameBackground() {
        //切換爲後臺
        Log.d("TAG","App切換到後臺了");
//        Toast.makeText(getBaseContext(),"App切換到後臺了",Toast.LENGTH_LONG).show();
    }
}

至此,代碼部分就全部完成,不管是全局的BaseApplication監聽,還是某個具體Activity中監聽,基本都能滿足日常開發需求。

參考:http://steveliles.github.io/is_my_android_app_currently_foreground_or_background.html

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