zoukankan      html  css  js  c++  java
  • 安卓程序进入后台和前台的判断

    按照最简单的思路,一个app的所有Activity全部onStop后就算进入后台,但可能用户只是切出去看了一眼别的程序又跳了回来。从技术上来说,这种判断没有问题,但从用户活跃的角度来讲,用户并没有真正离开我们的app。还有一种情况,就是需要在程序进入前后台的时候,执行一些操作,如果用户切换的十分频繁,操作又比较耗时可能会产生一定的效率低下和浪费。因此最简单的解决办法就是添加一个延时,定义一个我们app的前后台概念,即app不可见后一段时间才当做进入后台,而一旦从后台状态显示出来就当做进入前台。

    想要判断app进入了后台,就要关注app中每一个activity的声明周期,比较笨的方法是整个项目中所有Activity都使用同一个基类,在这个基类中重写声明周期方法来进行监控。

    但还有一种简单的方式,可以监听整个app所有Activity的生命周期变化:Application.registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks)。这个方法可以注册多个callback,不是只能注册一个,因此即使有其他地方(统计SDK经常使用)使用了这个也没有影响。

    判断Activity的显示和隐藏需要使用Activity生命周期中成对出现的方法,在显示的时候设置为前台,在隐藏的时候设置为后台。

    首先考虑的是onStart/onStop,但是很快发现了问题,在一个Activity中start另一个Activity,第一个Activity的onStop方法是在第二个Activity的onStart之后执行的,会导致最后一次判定前后台发生错误。把onStart简记为左括号,onStop简记为右括号,则执行完启动另一个Activity的操作之后,执行的序列为:"(()",可以看出仍有Activity在显示状态,但最后一次执行的却是onStop,而onStop就会去设置为后台状态,所以状态是不对的。也可以通过其他手段纠正这个逻辑,但无疑比较复杂。

    可以简单地使用onResume/onPause来替代onStart/onStop,这两个方法的调用序列用括号形式表示是这样的:"()(",最后有Activity为显示状态,最后的调用也是onResume设置前台的方法。因此采用onResume/onPause来判断前后台的状态。

    定义一个负责后台判断和管理的类,这个类实现Application.ActivityLifecycleCallbacks来监听Activity声明周期变化,并维护当前状态和分发状态改变回调。

    package com.ajeyone.sample1;

    import
    android.app.Activity; import android.app.Application; import android.os.Bundle; import android.os.Handler; import android.util.Log; import java.util.ArrayList; public final class BackgroundManager implements Application.ActivityLifecycleCallbacks {
    public interface BackgroundListener { // 提供给外部使用的回调 void onBackgroundStateChanged(); // 发生变化时回调,可通过isBackground方法判断当前状态 } private static final String TAG = "BackgroundManager"; private boolean mBackground; private final long mDelay; private final ArrayList<BackgroundListener> mBackgroundListeners = new ArrayList<>(); private final Handler mHandler = new Handler(); private final Runnable mChangeToBackgroundRunnable = new Runnable() { // 延迟执行的Runnable,设置为后台有一个延迟 @Override public void run() { setBackground(true); } }; BackgroundManager(long delay) { // 包访问权限,不能在其他地方随意创建对象,当然使用singleton也可以。 mDelay = delay; // delay时间由外部决定 } public void registerBackgroundListener(BackgroundListener listener) { // 需要监听可以使用这个方法注册回调 synchronized (mBackgroundListeners) { mBackgroundListeners.add(listener); } } public void unregisterBackgroundListener(BackgroundListener listener) { // 保证不使用的时候注销,尤其是Activity使用时 synchronized (mBackgroundListeners) { mBackgroundListeners.remove(listener); } } public boolean isBackground() { return mBackground; } private void setBackground(boolean background) { Log.d(TAG, "setBackground: " + background); boolean old = mBackground; mBackground = background; if (old != background) { // 防止多余的回调,只有在发生变化时才通知 Log.d(TAG, "setBackground: state changed"); for (BackgroundListener listener : mBackgroundListeners) { listener.onBackgroundStateChanged(); } } } @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { Log.d(TAG, "onActivityCreated: " + activity); } @Override public void onActivityStarted(Activity activity) { Log.d(TAG, "onActivityStarted: " + activity); } @Override public void onActivityResumed(Activity activity) { Log.d(TAG, "onActivityResumed: " + activity); mHandler.removeCallbacks(mChangeToBackgroundRunnable); // 取消延时执行的Runnable setBackground(false); // 只要显示出来,直接设置为前台,如果没显示前就已经是前台,也不会发生回调 } @Override public void onActivityPaused(Activity activity) { Log.d(TAG, "onActivityPaused: " + activity); mHandler.postDelayed(mChangeToBackgroundRunnable, mDelay); // 隐藏后开始计时,延迟执行后台转换 } @Override public void onActivityStopped(Activity activity) { Log.d(TAG, "onActivityStopped: " + activity); } @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { Log.d(TAG, "onActivitySaveInstanceState: " + activity); } @Override public void onActivityDestroyed(Activity activity) { Log.d(TAG, "onActivityDestroyed: " + activity); } }

    使用自定义的Application类:提供获取BackgroundManager的方法和注册Callback

    package com.ajeyone.sample1;
    
    import android.app.Application;
    
    public class MyApplication extends Application {
        private static final long BACKGROUND_DELAY_MS = 30000;
    
        private static MyApplication sInstance;
        private BackgroundManager mBackgroundManager;
    
        @Override
        public void onCreate() {
            super.onCreate();
            sInstance = this;
            mBackgroundManager = new BackgroundManager(BACKGROUND_DELAY_MS);
            registerActivityLifecycleCallbacks(mBackgroundManager);
        }
    
        public static MyApplication getInstance() {
            return sInstance;
        }
    
        public BackgroundManager getBackgroundManager() {
            return mBackgroundManager;
        }
    }

    需要监听的Activity:

    package com.ajeyone.sample1;
    
    import android.content.Intent;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.view.View;
    
    public class MainActivity extends AppCompatActivity implements BackgroundManager.BackgroundListener {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            MyApplication.getInstance().getBackgroundManager().registerBackgroundListener(this);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            MyApplication.getInstance().getBackgroundManager().unregisterBackgroundListener(this); // 不要忘记注销
        }
    
        @Override
        public void onBackPressed() {
            moveTaskToBack(true);
        }
    
        private void showBackgroundState() {
            boolean background = MyApplication.getInstance().getBackgroundManager().isBackground();
            Log.d("Main", "showBackgroundState: " + background);
        }
    
        @Override
        public void onBackgroundStateChanged() {
            showBackgroundState();
        }
    
        public void onOpenDetail(View view) {
            startActivity(new Intent(this, DetailActivity.class)); // 打开另一个Activity,查看前后台效果
        }
    }

     代码在github上:samplebackground

  • 相关阅读:
    Kubernetes
    桥接模式
    原型模式
    工厂模式
    生成器模式
    Java-Sentinel限流中间件
    python模拟发送、消费kafka消息
    使用idea搭建springBoot项目
    linux 虚拟机不能启动不了系统,虚拟机更改linux初始启动5,出现无法启动现象
    vwware workstation虚机网络配置NAT
  • 原文地址:https://www.cnblogs.com/ajeyone/p/6848370.html
Copyright © 2011-2022 走看看