一、Application的用途
1、Application是什么?
Application和Activity,Service一样,是Android框架的一个系统组件,当Android程序启动时系统会创建一Application 对象,用来存储系统的一些信息。
通常我们是不需要指定一个Application的,这时系统会自动帮我们创建,如果需要创建自己的Application,也很简单。
创建一个类AppApplication继承Application并在AndroidManifest的application标签中进行注册(只需要给application 标签增加个name属性把自己的Application的名字写入即可)。
Android系统会为每个程序运行时创建一个Application类的对象且仅创建一个
(打开微信安卓系统会为微信创建一个Application对象,再打开微博安卓系统又会为微博创建一个Application对象),
所以Application可以说是单例 (singleton)模式的一个类.
且Application对象的生命周期是整个程序中最长的,它的生命周期就等于这个程序的生命周期。因为它是全局的单例 的,所以在不同的Activity,Service中获得的Application对象都是同一个对象。
所以可以通过Application来进行一些,数据传递,数据共享,数据缓存等操作。
2、通过Application传递数据
假如有一个Activity A, 跳转到 Activity B ,并需要传递一些数据,通常的作法是Intent.putExtra()让Intent携带,或者有 一个Bundle把信息加入Bundle让Intent推荐Bundle对象,实现传递。但这样作有一个问题在于,Intent和Bundle所能携 带的数据类型都是一些基本的数据类型,如果想实现复杂的数据传递就比较麻烦了,通常需要实现Serializable或者
Parcellable接口。这其实是Android的一种IPC数据传递的方法。如果我们的两个Activity在同一个进程当中为什么还要
这么麻烦呢,只要把需要传递的对象的引用传递过去就可以了。
基本思路是这样的。在Application中创建一个HashMap ,以字符串为索引,Object为value这样我们的HashMap就可 以存储任何类型的对象了。
在Activity A中把需要传递的对象放入这个HashMap,然后通过Intent或者其它途经再把这索引的字符串传递给Activity B ,Activity B 就可以根据这个字符串在HashMap中取出这个对象了。只要再向下转个型 ,就实现对象的传递。
3、Application数据共享
多个组件之间数据共享。举例:两个Activity之间数据共享
Application 对同一个应用程序是唯一的,所以可以使用Application进行数据共享。
4、Application数据缓存
我一般会习惯在Application中建立两个HashMap一个用于数据的传递,一个用于缓存一些数据。
比如有一个Activity需要从网站获取一些数据,获取完之后我们就可以把这个数据cache到Application当中,
当页面设置到其它Activity再回来的时候,就可以直接使用缓存好的数据了。但如果需要cache一些大量的数据,
最好是cache一些 (软引用)SoftReference ,并把这些数据cache到本地rom上或者sd卡上。
如果在application中的缓存不存在,从本地缓存查找,如果本地缓存的数据也不存在再从网络上获取。
5、易导致的错误
使用Application如果保存了一些不该保存的对象很容易导致内存泄漏。
如果在Application的onCreate中执行比较耗时的操作,将直接影响程序的启动时间。
一些清理工作不能依靠onTerminate完成,因为android会尽量让你的程序一直运行,所以很有可能onTerminate()方法 不会被调用。
二、Application的生命周期
1、onCreate() 程序创建的时候执行
2、onTerminate() 程序终止的时候执行
在模拟环境下执行。当终止应用程序对象时调用,不保证一定被调用,
当程序是被内核终止以便为其他应用程序释放资源,那么将不会提醒,
并且不调用应用程序Application对象的onTerminate方法而直接终止进程。
3、onLowMemory() 低内存的时候执行
好的应用程序一般会在这个方法里面释放一些不必要的资源来应付当后台程序已经终止,
前台应用程序内存还不够时的情况。
4、onConfigurationChanged(Configuration newConfig) 配置改变时触发这个方法。
5、onTrimMemory(int level)程序在进行内存清理时执行
备注:application 被杀死的情况分析:
为了决定在内存较低的时候杀掉哪个进程, Android会根据运行在这些进程内的组件及他们的状态把进程划分成一个”重 要程度层次”.
其重要的程度按以下规则排序:
1、前端进程可以是一个持有运行在屏幕最前端并与用户交互的Activity的进程(onResume方法被调用时),
也可以是持有一个正在运行的IntentReceiver(也就是说他正在执行自己的onReceiveIntent方法)的进程.
在系统中, 只会有少数这样的进程, 并且除非内存已经低到不够这些进程运行, 否则系统不会主动杀掉这些进程.
这时, 设备通常已经达到了需要内存整理的状态, 所以杀掉这些进程是为了不让用户界面停止响应.
2、可视进程是持有一个被用户可见, 但没有显示在最前端 (onPause方法被调用时) 的Activity的进程.
举例来说, 这种进程通常出现在一个前端Activity以一个对话框出现并保持前一个Activity可见时.
这种进程被系统认为是极其重要的, 并且通常不会被杀掉, 除非为了保持所有前端进程正常运行不得不杀掉这些 可见进程.
3、服务进程是持有一个Service的进程, 该Service是由startService()方法启动的, 尽管这些进程用户不能直接看到,
但是通常他们做的工作用户是十分关注的(例如, 在后台播放mp3或是在后台下载上传文件), 所以, 除非为了保 持所有的前端进程和可视进程正常运行外,系统是不会杀掉服务进程的.
4、后台进程是持有一个不再被用户可见的Activity(onStop()方法被调用时)的进程. 这些进程不会直接影响用户体验. 加入这些进程已经完整的,正确的完成了自己的生命周期(访问Activity查看更多细节), 系统会在为前三种进程释 放内存时随时杀掉这些后台进程. 通常会有很多的后台进程在运行, 所以这些进程被存放在一个LRU列表中, 以 保证在低内存的时候, 最近 一个被用户看到的进程会被最后杀掉.
5、空进程是没有持有任何活动应用组件的进程. 保留这种进程的唯一理由是为了提供一种缓存机制, 缩短他的应用下 次运行时的启动时间. 就其本身而言, 系统杀掉这些进程的目的是为了在这些空进程和底层的核心缓存之间平衡 整个系统的资源.
当需要给一个进程分类的时候, 系统会在该进程中处于活动状态的所有组件里掉选一个重要等级最高作为分类依据.
查看Activity, Service,和IntentReceiver的文档, 了解每个组件在进程整个生命周期中的贡献.
每一个classes的文档详细描述他们在各自应用的生命周期中所起得作用.
三:实例
怎么知道用户什么时候离开你的应用?
为什么需要知道当前的状态?
假设我们的应用会收到一条通知。如果当时应用处于前台运行,肯定会展示出应用内相应的通知界面。
相反,如果应用处于后台运行,那在通知栏进行展示就比较合适。
例如其他情况,你想知道应用从前台运行到切换至后台的会话长短,又或者当用户需要处理其他事务离开时,你需要把你应用 的缓存清空。
应用切换至后台
从API 14(Android 4.0 ICS),我们可以调用Application.onTrimMemory(int level),这个方法包含了一个等级叫TRIM_MEMORY_UI_HIDDEN,用于记录应用即将进入后台运行。
下面是一个自定义Applicaiton的使用
public class MyApplication extends Application
{
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (level == TRIM_MEMORY_UI_HIDDEN) {
isBackground = true;
notifyBackground();
}
}
}
这样就能知道应用切换至后台运行啦。
手机熄屏
Application.onTrimMemory(int level)在手机熄屏时不回调怎么办?用Intent.ACTION_SCREEN_OFF
注册BroadcastReceiver
public class MyApplication extends Application {
// ...
@Override
public void onCreate() {
super.onCreate();
// ...
IntentFilter screenOffFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
isBackground = true;
notifyBackground();
}
}, screenOffFilter);
}
}
应用切换至前台
没有任何flag或者trim level来判断应用切换至前台,覆写Activity.onResume()是最好的方法。在基类Activity中复写它是一 个选择,但无须如此。
一个更简洁的做法是,利用Application.registerActivityLifeStyleCallbacks(),如名字描述一样,可以覆写每一个生命周期 函数。
在这个例子中,在不侵入式改动每个Activity的代码的前提下,在Activity.onResume()中执行了代码。
public class MyApplication extends Application {
// ...
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
// ...
@Override
public void onActivityResumed(Activity activity) {
if (isBackground) {
isBackground = false;
notifyForeground();
}
}
// ...
});
}
// ...
}
下面是一个应用前后台切换的完整例子。
public class MyApplication extends Application {
// Starts as true in order to be notified on first launch
private boolean isBackground = true;
@Override
public void onCreate() {
super.onCreate();
listenForForeground();
listenForScreenTurningOff();
}
//应用切换至前台
private void listenForForeground() {
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
//...
@Override
public void onActivityResumed(Activity activity) {
if (isBackground) {
isBackground = false;
notifyForeground();
}
}
//...
});
}
//手机息屏
private void listenForScreenTurningOff() {
IntentFilter screenStateFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
isBackground = true;
notifyBackground();
}
}, screenStateFilter);
}
//应用切换至后台
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (level == TRIM_MEMORY_UI_HIDDEN) {
isBackground = true;
notifyBackground();
}
}
private void notifyForeground() {
// This is where you can notify listeners, handle session tracking, etc
}
private void notifyBackground() {
// This is where you can notify listeners, handle session tracking, etc
}
public boolean isBackground() {
return isBackground;
}
}
总结:
1)API 14及以上
2)用Application.onTrimLevel(int level)和TRIM_MEMORY_UI_HIDDEN判断应用是否切换至后台运行。
3)通过INTENT.ACTION_SCREEN_OFF注册广播接受器监听屏幕熄灭
4)注册Activity.registerLifeStyleCallback监听应用切换至前台运行
四:onLowMemory和OnTrimMemory
1、OnLowMemory
OnLowMemory是Android提供的API,在系统内存不足,所有后台程序(优先级为background的进程,不是指后台运行 的进程)都被杀死时,
系统会调用OnLowMemory。系统提供的回调有:Application/Activity/Fragment/Service/ContentProvider
除了上述系统提供的API,还可以自己实现ComponentCallbacks,通过API注册,这样也能得到OnLowMemory回调。 例如:
public static class MyCallback implements ComponentCallbacks {
@Override
public void onConfigurationChanged(Configuration arg) {
}
@Override
public void onLowMemory() {
//do release operation
}
}
然后,通过Context.registerComponentCallbacks ()在合适的时候注册回调就可以了。
通过这种自定义的方法,可以在很多地方注册回调,而不需要局限于系统提供的组件。
2、OnTrimMemory
OnTrimMemory回调是Android 4.0之后提供的API,系统会根据不同的内存状态来回调。
这个API是提供给开发者的,它的主要作用是提示开发者在系统内存不足的时候,通过处理部分资源来释放内存,
从而避免被Android系统杀死。这样应用在下一次启动的时候,速度就会比较快。
系统提供的回调有:Application/Activity/Fragment/Service/ContentProvider
OnTrimMemory的参数是一个int数值,代表不同的内存状态:
以下4个是4.0增加的
TRIM_MEMORY_COMPLETE(80):内存不足,并且该进程在后台进程列表最后一个,马上就要被清理
表示手机目前内存已经很低了,并且我们的程序处于LRU缓存列表的最边缘位 置,系统会最优先考虑杀掉我们的应用程序,在这个时候应当尽可能地把一切 可以释放的东西都进行释放。
TRIM_MEMORY_MODERATE(60):内存不足,并且该进程在后台进程列表的中部。
表示手机目前内存已经很低了,并且我们的程序处于LRU缓存列表的中间位 置,如果手机内存还得不到进一步释放的话,那么我们的程序就有被系统杀掉 的风险了。
TRIM_MEMORY_BACKGROUND(40):内存不足,并且该进程是后台进程。
表示手机目前内存已经很低了,系统准备开始根据LRU缓存来清理进程。
这个时候我们的程序在LRU缓存列表的最近位置,是不太可能被清理掉的,
但这时去释放掉一些比较容易恢复的资源能够让手机的内存变得比较充足,
从而让我们的程序更长时间地保留在缓存当中,这样当用户返回我们的程序
时会感觉非常顺畅,而不是经历了一次重新启动的过程。
TRIM_MEMORY_UI_HIDDEN(20):内存不足,并且该进程的UI已经不可见了。
表示应用程序的所有UI界面被隐藏了,即用户点击了Home键或者Back键导 致应用的UI界面不可见.这时候应该释放一些资源.
以下3个是4.1增加的
TRIM_MEMORY_RUNNING_CRITICAL(15):内存不足(后台进程不足3个),并且该进程优先级比较高,需要清 理内存表示应用程序仍然正常运行,但是系统已经根据LRU缓存规 则杀掉了大部分缓存的进程了。这个时候我们应当尽可能地去释放 任何不必要的资源,不然的话系统可能会继续杀掉所有缓存中的进 程,并且开始杀掉一些本来应当保持运行的进程,比如说后台运行 的服务。
TRIM_MEMORY_RUNNING_LOW(10):内存不足(后台进程不足5个),并且该进程优先级比较高,需要清理内 存表示应用程序正常运行,并且不会被杀掉。但是目前手机的内存已经 非常低了,我们应该去释放掉一些不必要的资源以提升系统的性能,同 时这也会直接影响到我们应用程序的性能。
TRIM_MEMORY_RUNNING_MODERATE(5):内存不足(后台进程超过5个),并且该进程优先级比较高,需要 清理内存 表示应用程序正常运行,并且不会被杀掉。但是目 前手机的内存已经有点低了,系统可能会开始根据LRU缓存规 则来杀死进程了。
将参数值进行分类:
1)下面3个等级是当我们的应用程序真正运行时的回调:
TRIM_MEMORY_RUNNING_MODERATE(5) (后台进程超过5个)
TRIM_MEMORY_RUNNING_LOW(10) (后台进程不足5个)
TRIM_MEMORY_RUNNING_CRITICAL(15) (后台进程不足3个)
2)当应用程序是缓存的,则会收到以下几种类型的回调:
TRIM_MEMORY_BACKGROUND(40) (处于LRU缓存列表的最近位置)
TRIM_MEMORY_MODERATE(60) (处于LRU缓存列表的中间位置)
TRIM_MEMORY_COMPLETE(80) (处于LRU缓存列表的最边缘位置)
系统也提供了一个ComponentCallbacks2,任何实现了ComponentCallbacks2接口的类都可以重写实现这个回调方 法。
通过Context.registerComponentCallbacks()注册后,就会被系统回调到。
3、OnLowMemory和OnTrimMemory的比较
1)OnLowMemory被回调时,已经没有后台进程了(已经全部被杀死了);而onTrimMemory被回调时,还有后台进 程。
2)OnLowMemory是在最后一个后台进程被杀时调用,一般情况是low memory killer 杀进程后触发;
而OnTrimMemory的触发更频繁,每次计算进程优先级时,只要满足条件,都会触发。
3)通过一键清理后,OnLowMemory不会被触发,而OnTrimMemory会被触发一次。
4)在引入OnTrimMemory之前都是使用OnLowMemory回调,需要知道的是,OnLowMemory大概和 OnTrimMemory中的TRIM_MEMORY_COMPLETE级别相同,如果你想兼容api<14的机器,那么可以用 OnLowMemory来实现,否则你可以
忽略OnLowMemory,直接使用OnTrimMemory即可.
4、OnTrimMemory和onStop的关系?
onTrimMemory()方法中的TRIM_MEMORY_UI_HIDDEN回调只有当我们程序中的所有UI组件全部不可见的时候才会触 发,这和onStop()方法还是有很大区别的,因为onStop()方法只是当一个Activity完全不可见的时候就会调用,
比如说用户打开了我们程序中的另一个Activity。 因此,我们可以在onStop()方法中去释放一些Activity相关的资源,
比如说取消网络连接或者注销广播接收器等,但是像UI相关的资源应该一直要等到 onTrimMemory(TRIM_MEMORY_UI_HIDDEN)这个回调之后才去释放,这样可以保证如果用户只是从我们程序的一个 Activity回到了另外一个Activity,界面相关的资源都不需要重新加载,从而提升响应速度。
需要注意的是,onTrimMemory的TRIM_MEMORY_UI_HIDDEN 等级是在onStop方法之前调用的.
5.为什么要调用OnTrimMemory?
尽管系统在内存不足的时候杀进程的顺序是按照LRU Cache中从低到高来的,但是它同时也会考虑杀掉那些占用内存较 高的应用来让系统更快地获得更多的内存。
所以如果你的应用占用内存较小,就可以增加不被杀掉的几率,从而快速地恢复(如果不被杀掉,启动的时候就是热启 动,否则就是冷启动,其速度差在2~3倍)。
所以说在几个不同的OnTrimMemory回调中释放自己的UI资源,可以有效地提高用户体验。
6、有哪些典型的使用场景?
1)常驻内存的应用
一些常驻内存的应用,比如Launcher、安全中心、电话等,在用户使用过要退出的时候,
需要调用OnTrimMemory来及时释放用户使用的时候所产生的多余的内存资源:比如动态生成的View、
图片缓存、Fragment等。
2)有后台Service运行的应用
这些应用不是常驻内存的,意味着可以被任务管理器杀掉,但是在某些场景下用户不会去杀。
这类应用包括:音乐、下载等。用户退出UI界面后,音乐还在继续播放,下载程序还在运行。
这时候音乐应该释放部分UI资源和Cache。