🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### Android Application类的介绍 ### #### 相关参考文档 #### - [官方文档(Java版本)](https://developer.android.google.cn/reference/android/app/Application.html) - [android Application类的详细介绍](http://blog.csdn.net/pi9nc/article/details/11200969) - [Android Application类的介绍](http://blog.csdn.net/fankarl/article/details/52055147) - [Android中Application类用法](http://www.cnblogs.com/renqingping/archive/2012/10/24/Application.html) #### 类层次结构 ![Application类的类型层次结构](https://i.imgur.com/jt6rXGb.jpg) #### 类的属性及成员 ![application类的属性及成员](https://i.imgur.com/iFMxcdh.jpg) #### 类概述 - 官网原文如下 > Base class for those who need to maintain global application state. You can provide your own implementation by specifying its name in your AndroidManifest.xml's `<application>` tag, which will cause that class to be instantiated for you when the process for your application/package is created. - 对应译文 > Application类是为了那些需要保存全局变量设计的基本类,你可以在AndroidManifest.xml的<application>标签中进行自己的定义实现,这样的结果是:当你的application或者包被建立的时候,这个类就会被实例化。 - 备注 > 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子类并不是必须的,在很多情况下,单例模式在更多模块化的方式下能起到相同的效果,如果单例需要全局context,可以利用Context.getApplicationContext()拿到Context对象从而引用单例的getInstance()方法。 #### 概括 1. Application其实可以说成是单例模式的一个类,并且application对象的生命周期肯定是整个程序中最长的,因为它的生命周期就等于整个程序的生命周期。 2. 因为application全局都是单例的,所以对于程序里所有的Activity、Service来说所获得的application对象都是同一个,因此可以通过Application可以进行一些比如数据传递、数据共享、数据缓存等操作。 3. application是用来保存全局变量的,并且是在package创建的时候就跟着存在了。所以当我们需要创建全局变量的时候,不需要再像j2se那样需要创建public权限的static变量,而直接在application中去实现。只需要调用Context的getApplicationContext或者Activity的getApplication方法来获得一个application对象,再做出相应的处理。 #### 应用场景: 在Android中,可以通过继承Application类来实现应用程序级的全局变量,这种全局变量方法相对静态类更有保障,直到应用的所有Activity全部被destory掉之后才会被释放掉。 #### Android Application对象必须掌握的七点 - [理解理解Application创建过程](http://gityuan.com/2017/04/02/android-application/) - system_server进程创建application(点击[查看大图](https://i.imgur.com/Mn4hKRP.jpg)) - ![system_application](https://i.imgur.com/Mn4hKRP.jpg) - - app进程创建application(点击[查看大图](https://i.imgur.com/tRehPFk.jpg)) - ![app_application](https://i.imgur.com/tRehPFk.jpg) #### 1:Application是什么? 1. Application和Activity,Service一样,是android框架的一个系统组件,当android程序启动时系统会创建一个 application对象,用来存储系统的一些信息。通常我们是不需要指定一个Application的,这时系统会自动帮我们创建,如果需要创建自己 的Application,也很简单创建一个类继承 Application并在manifest的application标签中进行注册(只需要给Application标签增加个name属性把自己的 Application的名字定入即可)。 2. android系统会为每个程序运行时创建一个Application类的对象且仅创建一个,所以Application可以说是单例 (singleton)模式的一个类.且application对象的生命周期是整个程序中最长的,它的生命周期就等于这个程序的生命周期。因为它是全局 的单例的,所以在不同的Activity,Service中获得的对象都是同一个对象。所以通过Application来进行一些,数据传递,数据共享 等,数据缓存等操作。 3. 启动Application时,系统会创建一个PID,即进程ID,所有的Activity都会在此进程上运行。那么我们在Application创建的时候初始化全局变量,同一个应用的所有Activity都可以取到这些全局变量的值,换句话说,我们在某一个Activity中改变了这些全局变量的值,那么在同一个应用的其他Activity中值就会改变。 #### 2:通过Application传递数据 1. 假如有一个Activity A, 跳转到 Activity B ,并需要推荐一些数据,通常的作法是`Intent.putExtra()` 让Intent携带,或者有一个Bundle把信息加入Bundle让Intent推荐Bundle对象,实现传递。但这样作有一个问题在于,Intent和Bundle所能携带的数据类型都是一些基本的数据类型,如果想实现复杂的数据传递就比较麻烦了,通常需要实现 Serializable或者Parcellable接口。这其实是Android的一种IPC数据传递的方法。如果我们的两个Activity在同一个进程当中为什么还要这么麻烦呢,只要把需要传递的对象的引用传递过去就可以了。 2. 基本思路是这样的。在Application中创建一个HashMap,以字符串为索引,Object为value这样我们的HashMap就可以存储任何类型的对象了。在Activity A中把需要传递的对象放入这个HashMap,然后通过Intent或者其它途经再把这索引的字符串传递给Activity B ,Activity B就可以根据这个字符串在HashMap中取出这个对象了。只要再向下转个型,就实现了对象的传递。 #### 3:Application数据缓存 我一般会习惯在application中建立两个HashMap一个用于数据的传递,一个用于缓存一些数据。比如有一个Activity需要从网站获取一些数据,获取完之后我们就可以把这个数据cache到Application 当中,当页面设置到其它Activity再回来的时候,就可以直接使用缓存好的数据了。但如果需要cache一些大量的数据,最好是cache一些 (软引用)SoftReference,并把这些数据cache到本地rom上或者sd卡上。如果在application中的缓存不存在,从本地缓存查找,如果本地缓存的数据也不存在再从网络上获取。 #### 4:PitFalls(汉语:易犯的错误) 使用Application如果保存了一些不该保存的对象很容易导致内存泄漏。如果在Application的onCreate中执行比较耗时的操作,将直接影响的程序的启动时间。清理工作不能依靠onTerminate完成,因为android会尽量让你的程序一直运行,所以很有可能onTerminate不会被调用。 #### 5:MemoryLeak(内存泄漏) - 在Java中内存泄漏是指,某个(某些)对象已经不再被使用,而是应该被gc所回收,但有一个对象持有这个对象的引用而阻止这个对象被回收。比如我们通常会这样创建一个`View TextView tv = new TextView(this)`;这里的this通常都是Activity。所以这个TextView就持有着这个Activity的引用。下面看张图 (Google IO 2011 ppt中抄得) - 通常情况下,当用户转动手机的时候,android会重新调用OnCreate()方法生成一个新的Activity,原来的 Activity应该被GC所回收。但如果有个对象比如一个View的作用域超过了这个Activity(比如有一个static对象或者我们把这个View的引用放到了Application当中),这时候原来的Activity将不能被GC所回收,Activity本身又持有很多对象的引用,所以整个Activity的内存被泄漏了。 > 备注:经常导致内存泄漏核心原因: keeping a long-lived reference to a Context.持有一个context的对象,从而gc不能回收。 - 情况如下: 1. 一个View的作用域超出了所在的Activity的作用域,比如一个static的View或者把一个View cache到了application当中 etc **注意:内存:注意静态的数据和缓存中的数据;注意释放;** 2. 某些与View关联的Drawable的作用域超出了Activity的作用域。 3. Runnable对象:比如在一个Activity中启用了一个新线程去执行一个任务,在这期间这个Activity被系统回收了,但Runnalbe的任务还没有执行完毕并持有Activity的引用而泄漏,但这种泄漏一般来泄漏一段时间,只有Runnalbe的线程执行完闭,这个Activity又可以被正常回收了。 4. 内部类的对象作用域超出Activity的范围:比如定义了一个内存类来存储数据,又把这个内部类的对象传给了其它Activity 或者Service等。因为内部类的对象会持有当前类的引用,所以也就持有了Context的引用。解决方法是如果不需要当前的引用,把内部类写成static或者把内部类抽取出来变成一个单独的类,或者把避免内部对象作用域超出Activity的作用域。**`out Of Memery Error `**:在android中每一个程序所分到的内存大小是有限的,如果超过了这个数就会报Out Of Memory Error。 android给程序分配的内存大小与手机硬件有关,以下是一些手机的数据: G1:16M Droid:24 Nexus One:32M Xoom:48Ms 所以尽量把程序中的一些大的数据cache到本地文件。以免内存使用量超标。 记得数据传递完成之后,把存放在application的HashMap中的数据remove掉,以免发生内存的泄漏。 #### 6:生命周期 - **onCreate()** 在创建应用程序时创建 - **onTerminate()** 当终止应用程序对象时调用,不保证一定被调用,当程序是被内核终止以便为其他应用程序释放资源,那么将不会提醒,并且不调用应用程序的对象的onTerminate方法而直接终止进程 - **onLowMemory()** 当后台程序已经终止资源还匮乏时会调用这个方法。好的应用程序一般会在这个方法里面释放一些不必要的资源来应付当后台程序已经终止,前台应用程序内存还不够时的情况。 - **onConfigurationChanged()** 配置改变时触发这个方法 #### 备注 **application 被杀死的情况分析**: Android系统将尽量长时间地保持应用进程,但为了新建进程或运行更重要的进程,最终需要清除旧进程来回收内存。为了决定在内存较低的时候杀掉哪个进程, Android会根据运行在这些进程内的组件及他们的状态把进程划分成一个”重要程度层次”. 其重要的程度按以下规则排序,必要时,系统会首先消除重要性最低的进程,然后是清除重要性稍低一级的进程,依此类推,以回收系统资源。: 前台进程的重要性最高,依次递减,空进程的重要性最低 Android中对于内存的回收,主要依靠Lowmemorykiller来完成,是一种根据阈值级别触发相应力度的内存回收的机制。 可以参考文章[Android进程生命周期与ADJ](https://www.kancloud.cn/alex_wsc/androidsystem/403696) - 1:**Foreground process 前台进程** 可以是一个持有运行在屏幕最前端并与用户交互的Activity的进程(onResume方法被调用时),也可以是持有一个正在运行的IntentReceiver(也就是说他正在执行自己的onReceiveIntent方法)的进程. 在系统中, 只会有少数这样的进程, 并且除非内存已经低到不够这些进程运行, 否则系统不会主动杀掉这些进程. 这时, 设备通常已经达到了需要内存整理的状态, 所以杀掉这些进程是为了不让用户界面停止响应. 拥有下列特性的进程都是**前台进程** - 拥有用户正在交互的 Activity(已调用onResume()) - 拥有某个 Service,后者绑定到用户正在交互的 Activity - 拥有正在“前台”运行的 Service(服务已调用 startForeground()) - 拥有正执行一个生命周期回调的 Service(onCreate()、onStart() 或 onDestroy()) - 拥有正执行其 onReceive() 方法的 BroadcastReceiver - 2:**Visible process 可见进程** 是持有一个被用户可见, 但没有显示在最前端 (onPause方法被调用时) 的Activity的进程. 举例来说, 这种进程通常出现在一个前端Activity以一个对话框出现并保持前一个Activity可见时. 这种进程被系统认为是极其重要的, 并且通常不会被杀掉, 除非为了保持所有前端进程正常运行,而不得不杀掉这些可见进程 如下进程都是可见进程 - 拥有不在前台、但仍对用户可见的 Activity(已调用onPause())。 - 拥有绑定到可见(或前台)Activity 的 Service - 3:**Service process 服务进程** 是持有一个Service的进程, 该Service是由startService()方法启动的, 这一进程运行的service ,是那些已执行startService() 方法且不属于前面两个高类别的服务 尽管服务进程与用户所见内容没有直接关联,尽管这些进程用户不能直接看到, 但是通常他们做的工作,用户是十分关注的(例如,在后台播放mp3或是在后台下载上传文件),所以,除非为了保持所有的前端进程和可视进程正常运行外,系统是不会杀掉服务进程的. 如下进程 - 正在运行startService()方法启动的服务,且不属于上述两个更高类别进程的进程 - 4:**Background process 后台进程** 是持有一个不再被用户可见的Activity(onStop()方法被调用时)的进程. 后台进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用 这些进程不会直接影响用户体验. 加入这些进程已经完整的,正确的完成了自己的生命周期(访问Activity查看更多细节), 系统会在为前三种进程释放内存时随时杀掉这些后台进程. 通常会有很多的后台进程在运行, 所以这些进程被存放在一个LRU列表中, 以保证在低内存的时候, 最近一个被用户看到的进程会被最后杀掉. 如果一个Activity实现其生命周期方法正确,并保存其当前状态,杀死它的进程将不会对用户体验产生可见的效果,因为当用户导航回到该Activity,该Activity恢复它的所有可见的状态。看Activities文档信息的保存和恢复状态。 如下进程 - 对用户不可见的Activity的进程(已调用Activity的onStop()方法) - 5:**Empty process 空进程** 是没有持有任何活动应用组件的进程. 保留这种进程的唯一理由是为了提供一种缓存机制, 缩短他的应用下次运行时的启动时间. 就其本身而言, 系统杀掉这些进程的目的是为了在这些空进程和底层的核心缓存之间平衡整个系统的资源. 即:为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。 当需要给一个进程分类的时候, 系统会在该进程中处于活动状态的所有组件里,挑选一个重要等级最高作为分类依据. 查看Activity, Service,和IntentReceiver的文档, 了解每个组件在进程整个生命周期中的贡献. 每一个classes的文档详细描述他们在各自应用的生命周期中所起得作用. 如下进程 - 不含任何活动应用组件的进程 **备注**: - 一个进程可以有较高级别的Android的优先级,基于它的重要性。例如,如果一个进程开启了 一个service 和一个可见的activity,这个进程的将作为一个可见的过程,不是一个服务过程。 - 此外,一个进程的排名可能会增加,因为其他的流程都依赖于它的一个过程,所以另一个进程永远不能被排名低于这个过程中,服务。 例如,如果一个ContentProvider过程中的服务进程B中的客户端或服务过程中,如果A是必然进程B中的一个组件,进程A总是被认为至少是同样重要于进程B 因为一个进程运行服务的过程与背景活动,发起一项活动,可能会做一个长期运行的操作,操作以及启动服务 ,而不是简单地创建一个工作线程,特别是如果操作将可能高于排名经久活动。 例如,一个活动的图片上传到一个网站,应该启动一个服务执行上传,因此可以继续在后台上传,即使用户离开活动。 使用服务保证该操作将至少有“服务进程”的优先级,不管发生了什么活动。 广播接收机应采用服务,而不是简单地把在一个线程中耗时的操作,这是一样的道理。 #### Application 的Context 1. 它描述的是一个应用程序环境的信息,即上下文。 2. 该类是一个抽象(abstract class)类,Android提供了该抽象类的具体实现类(后面我们会讲到ContextIml类)。 3. 通过它我们可以获取应用程序的资源和类,也包括一些应用级别操作,例如:启动一个Activity,发送广播、接受Intent信息等。 4. 类Context的类层次结构图 ![Context的类层次结构图](https://i.imgur.com/2vzDFK1.jpg) 更深层级的结构图(部分)[大图点击这里](https://i.imgur.com/ZGHGF26.jpg) ![抽象类Context的类关系图](https://i.imgur.com/ZGHGF26.jpg) #### Application类的实现 #### - 1.继承Application类,并重写onCreate()方法 > 注:继承Application类,主要重写里面的onCreate()方法(**android.app.Application包的onCreate()才是真正的Android程序的入口点**),就是创建的时候,初始化变量的值。然后在整个应用中的各个文件中就可以对该变量进行操作了。 - 2.清单文件中配置自定义的Application - 3.在程序中使用自定义类 > 注:只需要调用Context的 getApplicationContext或者Activity的getApplication方法来获得一个Application对象,然后再得到相应的成员变量即可。它是代表我们的应用程序的类,使用它可以获得当前应用的主题和资源文件中的内容等,这个类更灵活的一个特性就是可以被我们继承,来添加我们自己的全局属性。 ### **总结来说** **Application主要有以下3点作用**: 1. 整个程序的入口 2. 为一些第三方框架做一些初始化的工作 3. 为整个应用程序的其他模块提供上下文