Android.Application 约定不等于承诺〃 2022-06-04 07:42 212阅读 0赞 ##### **先来整体看一下Application是用来干什么的** ##### 一.先对它的整体概念解释: **在android源码中对他的描述是** \* 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. **SDK中的描述**:Application类是为了那些需要保存全局变量设计的基本类,你可以在AndroidManifest.xml的标签中进行自己的实现,这样的结果是:当你的application或者包被建立的时候将引起那个类被建立。 **总结**:就是说application是用来保存全局变量的,并且是在package创建的时候就跟着存在了。所以当我们需要创建全局变量的时候,不需 要再像j2se那样需要创建public权限的static变量,而直接在application中去实现。只需要调用Context的getApplicationContext或者Activity的getApplication方法来获得一个application对象,再做出相应的处理。 例如Launcher模块中;它自己就写了个application,在AndroidManifest.xml中将它进行了设置 **在Android程序中,通过继承Application类,可以实现三种功能** 1. 管理应用程序状态。 2. 在应用程序的组件之间传递对象。 3. 管理多个组件都要使用的资源 Application类会最先初始化,一个Android程序只能有一个注册的Application类 #### **Application** #### **1.Application是什么** Application和Activity,Service一样,是android框架的一个系统组件,当android程序启动时系统会创建一个 application对象,用来存储系统的一些信息。通常我们是不需要指定一个Application的,这时系统会自动帮我们创建,如果需要创建自己 的Application,也很简单创建一个类继承 Application并在manifest的application标签中进行注册(只需要给Application标签增加个name属性把自己的 Application的名字定入即可)。 android系统会为每个程序运行时创建一个Application类的对象且仅创建一个,所以Application可以说是单例 (singleton)模式的一个类.且application对象的生命周期是整个程序中最长的,它的生命周期就等于这个程序的生命周期。因为它是全局 的单例的,所以在不同的Activity,Service中获得的对象都是同一个对象。所以通过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数据缓存** 我一般会习惯在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的引用。 通常情况下,当用户转动手机的时候,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,然后在Application的生命周期中做一些操作** Application生命周期 public class App extends Application { @Override public void onCreate() { // 程序创建的时候执行 Log.d(TAG, "onCreate"); super.onCreate(); } @Override public void onTerminate() { // 程序终止的时候执行 Log.d(TAG, "onTerminate"); super.onTerminate(); } @Override public void onLowMemory() { // 低内存的时候执行 Log.d(TAG, "onLowMemory"); super.onLowMemory(); } @Override public void onTrimMemory(int level) { // 程序在内存清理的时候执行 Log.d(TAG, "onTrimMemory"); super.onTrimMemory(level); } @Override public void onConfigurationChanged(Configuration newConfig) { Log.d(TAG, "onConfigurationChanged"); super.onConfigurationChanged(newConfig); } } 打开Activity 08-16 15:16:27.142: D/CARLOZ - Application(28202): onCreate 08-16 15:16:27.172: D/CARLOZ - MainActivity3(28202): onCreate 08-16 15:16:27.232: D/CARLOZ - MainActivity3(28202): onStart 08-16 15:16:27.232: D/CARLOZ - MainActivity3(28202): onResume HOME键退出应用程序 08-16 15:16:55.372: D/CARLOZ - MainActivity3(28202): onPause 08-16 15:16:55.942: D/CARLOZ - MainActivity3(28202): onStop 08-16 15:16:55.952: D/CARLOZ - Application(28202): onTrimMemory 回收内存 重新打开应用程序 08-16 15:17:20.962: D/CARLOZ - MainActivity3(28202): onRestart 08-16 15:17:20.962: D/CARLOZ - MainActivity3(28202): onStart 08-16 15:17:20.962: D/CARLOZ - MainActivity3(28202): onResume 长按MENU键,打开Recent TASK 08-16 15:17:28.972: D/CARLOZ - MainActivity3(28202): onPause 08-16 15:17:28.992: D/CARLOZ - MainActivity3(28202): onStop 08-16 15:17:29.022: D/CARLOZ - Application(28202): onTrimMemory 彻底关闭应用程序 08-16 15:17:31.542: D/CARLOZ - MainActivity3(28202): onDestroy **备注:application 被杀死的情况分析** 为了决定在内存较低的时候杀掉哪个进程, Android会根据运行在这些进程内的组件及他们的状态把进程划分成一个”重要程度层次”. **其重要的程度按以下规则排序** **1:前端进程**可以是一个持有运行在屏幕最前端并与用户交互的Activity的进程(onResume方法被调用时),也可以是持有一个正在运行的IntentReceiver(也就是说他正在执行自己的onReceiveIntent方法)的进程. 在系统中, 只会有少数这样的进程, 并且除非内存已经低到不够这些进程运行, 否则系统不会主动杀掉这些进程. 这时, 设备通常已经达到了需要内存整理的状态, 所以杀掉这些进程是为了不让用户界面停止响应. **2:可视进程**是持有一个被用户可见, 但没有显示在最前端 (onPause方法被调用时) 的Activity的进程. 举例来说, 这种进程通常出现在一个前端Activity以一个对话框出现并保持前一个Activity可见时. 这种进程被系统认为是极其重要的, 并且通常不会被杀掉, 除非为了保持所有前端进程正常运行不得不杀掉这些可见进程. **3:服务进程**是持有一个Service的进程, 该Service是由startService()方法启动的, 尽管这些进程用户不能直接看到, 但是通常他们做的工作用户是十分关注的(例如, 在后台播放mp3或是在后台下载 上传文件), 所以, 除非为了保持所有的前端进程和可视进程正常运行外, 系统是不会杀掉服务进程的. **4:后台进程**是持有一个不再被用户可见的Activity(onStop()方法被调用时)的进程. 这些进程不会直接影响用户体验. 加入这些进程已经完整的,正确的完成了自己的生命周期(访问Activity查看更多细节), 系统会在为前三种进程释放内存时随时杀掉这些后台进程. 通常会有很多的后台进程在运行, 所以这些进程被存放在一个LRU列表中, 以保证在低内存的时候, 最近一个被用户看到的进程会被最后杀掉. **5:空进程**是没有持有任何活动应用组件的进程. 保留这种进程的唯一理由是为了提供一种缓存机制, 缩短他的应用下次运行时的启动时间. 就其本身而言, 系统杀掉这些进程的目的是为了在这些空进程和底层的核心缓存之间平衡整个系统的资源. www.2cto.com 当需要给一个进程分类的时候, 系统会在该进程中处于活动状态的所有组件里掉选一个重要等级最高作为分类依据. 查看Activity, Service,和IntentReceiver的文档, 了解每个组件在进程整个生命周期中的贡献. 每一个classes的文档详细描述他们在各自应用的生命周期中所起得作用. #### **1. Application的使用** #### 多个组件之间数据共享 举例:两个Activity之间数据共享 Application 对同一个应用程序是唯一的,所以可以使用Application进行数据共享 ![这里写图片描述][SouthEast] **定义两个Activity: MainActivity.java 和 MainActivity2.java, 代码是一样的** TextView tv; EditText et; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.activity_main_activity2); setContentView(R.layout.activity_main2); tv = (TextView) findViewById(R.id.tv); et = (EditText) findViewById(R.id.et); tv.setText("共享数据:" + getApp().getTextData()); findViewById(R.id.btnTextData).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { getApp().setTextData(et.getText().toString()); tv.setText("共享数据:" + et.getText().toString()); } }); } public App getApp() { return ((App)getApplicationContext()); } **定义一个类App,集成Application** public class App extends Application { private String textData = "default"; public void setTextData(String textData) { this.textData = textData; } public String getTextData() { return textData; } } **修改AndroidManifest.xml文件, 使用自定义的Application,两个Activity都是入口:** <application android:name="com.carloz.learnapplication.App" android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.carloz.learnapplication.MainActivity2" android:label="@string/title_activity_main_activity2" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> **结果,在一个Activity修改App的数据,在另一个Activity都能获取到** ![这里写图片描述][SouthEast 1] #### **Application Task Activities** #### **什么是Android Application** 简单来说,一个apk文件就是一个Application。 任何一个Android Application基本上是由一些Activities组成,当用户与应用程序交互时其所包含的部分Activities具有紧密的逻辑关系,或者各自独立处理不同的响应。 这些Activities捆绑在一起成为了一个处理特定需求的Application, 并且以“。apk”作为后缀名存在于文件系统中。 Android平台默认下的应用程序 例如:Email、Calendar、Browser、Maps、Text Message、Contacts、Camera和Dialer等都是一个个独立的Apps。 安装 Application的过程也可以简单理解为将其所包裹的Activities导入到当前的系统中,如果系统中已经存在了相同的Activities, 那么将会自动将其关联,而不会重复安装相同的Activities,避免资源的浪费。 Application卸载的过程也会检查当前所关联的 Activities是否有被其它Application标签所关联,如果仅仅是提供当前的Application使用,那么将会彻底被移除,相反则不做 任何操作。 就像我们已经知道的,Application基本上是由四个模块组成:Activity、Service、Content Provider 和 Broadcast Receiver,其中Activity是实现应用的主体。 **什么是 Activity Stack?** 操作应用程序时,有时需要调用多个Activities来完成需求,例如:发送邮件程序,首先是进入邮件主界面,然后启动一个新的Activity用于填写新邮件内容,同时可以调出联系人列表用于插入收件人信息等等。在这个操作过程中 Android平台有一个专门用于管理Activities堆栈的机制,其可以方便的线性记录Activities实例,当完成某个操作时,可以通过导航功能返回之前的Activity(通过按操作台的“Back”按钮)。 每次启动新的Activity都将被添加到Activity Stack。用户可以方便的返回上一个Activity直到Home Screen,到达Home Screen后,将无法再继续查看堆栈记录(俗话说:到头了)。如果当前Task被中止(Interrupting the task),返回到系统主界面后启动了其它操作,当希望返回到前一个Task继续执行时,只需要再次通过主界面的Application launcher或者快捷方式启动这个Task的Root Activity便可返回其中止时的状态继续执行。 相对于Views、Windows、Menus和Dialogs而言,Activity是唯一可被记录在History stack中的数据,所以当你所设计的应用程序需要用户由A界面进入到次一级界面B,当完成操作后需要再次返回A,那么必须考虑将A看作为 Activity,否则将无法从历史堆栈中返回。 **什么是Task** 当我们需要一个Activity可以启动另一个Activity,可能另外一个Activity是定义在不同应用程序中的Activity。 例如,假设你想在你的应用中让用户显示一些地方的街景。而这里已经有一个Activity可以做到这一点,因此,你的Activity所需要做的只是在Intent对象中添加必要的信息,并传递给startActivity()。地图浏览将会显示你的地图。当用户按下BACK键,你的Activity会再次出现在屏幕上。 对于用户来说,看起来好像是地图浏览与你的Activity一样,属于相同的应用程序,即便是它定义在其它的应用程序里,并运行在那个应用程序的进程里。 Android通过将这两个Activity保存在同一个Task里来体现这一用户体验。简单来说,一个Task就是用户体验上的一个“应用”。 它将相关的Activity组合在一起,以stack的方式管理(就是前面提到的Activity Stack),这就是Task。 在Android平台上可以将task简单的理解为幽多个Activity共同协作完成某项应用,而不管Activity具体属于哪个Application, 通过下图可以更清晰的理解Application、task、Activity三者之间的关系: ![这里写图片描述][SouthEast 2] **Task 有啥用?** 我们用过Android的手机就会知道有下面的场景: 假设我们首先在用IReader在看书,从选书到具体书的阅读界面,这是有好几个Activity。我们每一个点击的Activity都被放在阅读这个Task对应的Activity Stack中了,这可以放我们通过回退键返回每一个前面的Activity。 我们在阅读到一半时,想看看Sina微博,按Home键离开了IReader。 在Sina微博界面也是有多个Activity,我们一步到阅读界面。这时候我们每一个点击的Activity都被放在Sina微博这个Task对应的Activity Stack中了,这可以放我们通过回退键返回每一个前面的Activity。 我们这时候再回到IReader读书界面,原先的状态还是保留的。 显然每一个Task有自己的 Activity Stack。 Task就是这样为了方便人们使用手机而设置的,就像前面提到的场景Task可以跨Application。 下面这个图从另外一个角度描述了Application Task Activities的关系 ![这里写图片描述][SouthEast 3] Task通过Application launcher、Home screen的快捷方式或者 由 “Recent Tasks”(长时间按住Home键)最近使用过的Task记录中启动。 **当从一个Activity中启动另外一个Activity时,Back键将作用于返回前一个Activity,与此同时 新开启的Activity将被添加到Activity Stack中。** #### **Application 配置全局Context** #### 第一步、写一个全局的单例模式的MyApplication继承自Application 覆盖onCreate ,在这个方法里面实例化Application 第二步、配置全局的Context <application android:name=”com.appstore.service.MyApplication” ></application> 第三步、使用,使用的时候用的时候根据类的名称访问Context #### **Android程序的入口点** #### Android使用Google Dalvik VM,相对于传统Java VM而言有着很大的不同,在Sun的Java体系中入口点和标准c语言一样是main(),而每个Android程序都包含着一个Application实例,一个Application实例中有多个Activity、Service、ContentProvider或Broadcast Receiver。因为大部分的应用都包含Activity所以,说很多网友认为是Activity的onCreate,但是你没有发现你的工程中有多个Activity吗? 你可能没有见过没有Activity的Android应用吧。 其实在**android.app.Application这个包的onCreate才是真正的Android入口点**,只不过大多数开发者无需重写该类,他的继承关系如下图: java.lang.Object –android.content.Context —-android.content.ContextWrapper —–android.app.Application android.app.Application类包含了4个公开的方法 void onConfigurationChanged(Configuration newConfig) **void onCreate() //这里才是真正的入口点。** void onLowMemory() void onTerminate() 说明: **onCreate(); 这个函数是当我们的应用开始之时就被调用了,比应用中的其他对象创建的早**,这个实现尽可能的快一点,因为这个时间直接影响到我们第一个activity/service/receiver。**如果你要重写这个方法必须调用super.onCreate().** **onTerminate(): 这个函数是模拟一个过程环境,在真机中永远也不会被调用。** 所以希望大家,**记住真正的Android入口点是application的main**,你可以看下androidmanifest.xml的包含关系就清楚了,并不是每个应用都必须有Activity的。 **推荐阅读:** [\[译\]玩转Android Application的生命周期(不,不许覆盖那个Home键)][Android Application_Home] [SouthEast]: /images/20220604/4613b37a74da4811ac1431d1360377da.png [SouthEast 1]: /images/20220604/3a0131becf4c4b7f89889470c932fe74.png [SouthEast 2]: /images/20220604/75daf7903db54949bed8f2ef89d2ff21.png [SouthEast 3]: /images/20220604/57cfa4f195a9431983b50c46af0a47a8.png [Android Application_Home]: http://www.jianshu.com/p/11654f7f0c4a
还没有评论,来说两句吧...