一、概念理解
Base class for maintaining global application state. You can provide your own implementation by creating a subclass and specifying the fully-qualified name of this subclass as the "android:name" attribute in your AndroidManifest.xml's <application> tag. The Application class, or your subclass of the Application class, is instantiated before any other class when the process for your application/package is created.
Note: There is normally no need to subclass Application. In most situations, static singletons can provide the same functionality in a more modular way. If your singleton needs a global context (for example to register broadcast receivers), include Context.getApplicationContext() as a Context argument when invoking your singleton's getInstance() method.
Application是维持全局应用状态的基类,单例模式,且先于其他任意组件被创建。Application组件被设计的初衷是对整体应用级别管理负责,对其正确使用应该避免两个极端
- 过度使用——将Application当做单例工具类,耦合太多非应用级别的工具方法或业务逻辑,造成Application类臃肿
- 从不使用——因不熟悉而不敢使用或忘记使用,不使用没有错,但有时适当使用会减少代码重复,提升性能,且其自身扩展方法也会为某些特殊业务提供思路
Application实际上继承自Context,继承来的方法不必多说,也不是重点。既然有必要子类化,那它也必然会扩展自己的方法来实现其特有功能,也就是和应用管理相关方法。下面将重点介绍这些方法。
二、Application实战应用
Application的应用管理相关功能可以从其维护的三个List来分析:
private ArrayList<ComponentCallbacks> mComponentCallbacks = new ArrayList<ComponentCallbacks>();//系统配置变更、内存管理 private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks = new ArrayList<ActivityLifecycleCallbacks>();//Activity生命周期管理 private ArrayList<OnProvideAssistDataListener> mAssistCallbacks = null; //协助数据
(1)ComponentCallbacks/ComponentCallbacks2
用于系统配置变化、内存紧张时获得回调。该接口并非只有Application实现,Activity和Service等其他组件都有实现。而且还可以自定义实现接口,通过API注册。
void onConfigurationChanged(Configuration newConfig);//系统状态配置如横竖屏、语言等变化时回调 void onLowMemory();//后台进程被杀后回调 void onTrimMemory(@TrimMemoryLevel int level);//根据不同内存状态来回调
其中onLowMemory和onTrimMemory的区别:
- OnLowMemory被回调时,已经没有后台进程;而onTrimMemory被回调时,还有后台进程。
- OnLowMemory是在最后一个后台进程被杀时调用,一般情况是low memory killer 杀进程后触发;OnTrimMemory的触发更频繁,每次计算进程优先级时,只要满足条件,都会触发。
- 通过一键清理后,OnLowMemory不会被触发,而OnTrimMemory会被触发一次。
(2)ActivityLifecycleCallbacks
该接口用于对应用所有Activity的生命周期做集中回调管理,是Application独有扩展功能。注册该接口后每个Activity的生命周期发生变化后都会获得回调,可以很方便的做记录和管理。
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); }
实现原理也比较简单,以onActivityCreated为例, 在Application中有如下方法两个方法
//注册回调 public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) //分发回调 /* package */ void dispatchActivityCreated(Activity activity, Bundle savedInstanceState)
通过registerActivityLifecycleCallbacks方法注册回调,查看Activity源码,当onCreate时,再通过dispatchActivityCreated方法分发给各回调。
protected void onCreate(@Nullable Bundle savedInstanceState) { ... getApplication().dispatchActivityCreated(this, savedInstanceState); ... }
在实际应用中,通过注册registerActivityLifecycleCallbacks回调,可以对Activity的状态和数量做更精准的监控。
应用一:限制Activity实例数量
比如,我们要限制ActivityDetail页面的数量不超过MAX_ACTIVITY_DETAIL_NUM,可以在onActivityCreated回调方法中做如下处理(这是Activity启动模式无法做到的)
public static Stack<ActivityDetail> store; @Override public void onActivityCreated(Activity activity, Bundle bundle) { if (activity instanceof ActivityDetail) { if (store.size() >= MAX_ACTIVITY_DETAIL_NUM) { store.peek().finish(); //移除栈底并finish,保证不超过指定数量 } tore.add((ActivityDetail) activity); } }
应用二:控制相同内容的Activity只有一个实例
比如,要展示商品A的ActivityDetail实例已经存在,就没必要再创建一个展示相同内容的ActivityDetail。
系统级应用可以调用如下方法(需要Android.permission.STOP_APP_SWITCHES权限)
for(ActivityDetail activityDetail : store){ if(id.equalsIgnoreCase(activityDetail.getID())){ //当前商品id相同证明已有相同页面 ActivityManager am = (ActivityManager) getAppContext().getSystemService(Activity.ACTIVITY_SERVICE); am.moveTaskToFront(activityDetail.getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION); return true; } }
非系统级应用只能finish掉已经存在的页面,重新打开一个新的页面
for(ActivityDetail activityDetail : store){ if(id.equalsIgnoreCase(activityDetail.getID())){ //当前商品id相同证明已有相同页面 activityDetail.finish(); return true; } }
应用三:判断App前后台状态
每当一个Activity切到前台时都会执行onStart,切到后台时都会执行onStop,所以只需要在onActivityStarted和onActivityStopped回调中做一个简单的计数器即可
mActivityCount = 0; registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { ... @Override public void onActivityStarted(Activity activity) { Log.d(TAG,"onActivityStarted"); mActivityCount++; } @Override public void onActivityStopped(Activity activity) { Log.d(TAG,"onActivityStopped"); mActivityCount--; } ... });
当mActivityCount == 0时App为后台,否则即是前台。