内容提要
1. 每个Android应用程序都由一个Application对象承载,这个对象在应用程序的进程创建时被同时创建;
2. Application对象定义了清晰的生命周期,在各个阶段设定了回调方法;
3. Application对象可以放置应用程序的全局状态,通常用来共享在activity和service之间的重要但非持久化的数据。
进程与线程
进程
1. 默认情况下,同一个Android应用的所有组件运行在同一个进程中,而且在这个进程的同一个线程中,这个线程称作“main”线程;
应用的某个组件开始运行时,如果此时该应用没有任何组件正在运行,系统会为这个应用创建一个新进程;而如果此时该应用有运行中的组件,那么后运行的组件都会默认运行在已创建的进程中并使用同一个线程;
可以设定同一个应用的组件运行在不同的进程中,也可以为任何进程创建线程;
2. 当系统资源不足时,系统会在某些时刻决定结束次要的进程;重要性的等级标准决定了哪些进程更为次要而被结束,这个等级标准取决于运行在该进程中组件的状态;等级标准划分为5级:
第一级:Foreground进程
这级进程被认为是最重要的,只有系统在极端情况下最后才会选择结束的进程。进程有几种情形被认为是此种等级:
第一种. 进程中运行着正在与用户交互的Activity (该Activity的onResume方法已经执行)。
第二种. 进程中运行着某个Service,这个Service绑定在正与用户交互的Activity。
第三种. 进程中运行着某个Service,这个Service正“运行在Foreground”(Service执行了startForeground方法)。
第四种. 进程中运行着某个Service,系统正在执行它的某个生命周期的回调方法(onCreate,onStart等等)。
第五种. 进程中运行着某个正在执行onReceive方法的BroadcastReceiver。
以上情形满足任何一种,进程都会被认为是Foreground进程。
第二级:Visible进程
这级的进程是第二重要的,只有在系统在资源不足而为了保证第一级进程能正常运行的情况下才会结束。满足以下两种情形其中之一的进程被认为是此等级:
第一种. 进程中运行着能被用户看到但不能操作的Activity (Activity的onPause方法已经执行过),例如弹出框后面的界面。
第二种. 进程中运行着绑定在上述Activity的Service。
第三级:Service进程
该进程中运行着某个Service,该Service通过调用startService方法启动,并不在上两级情形中的任何一种,此时的进程被认为是Service进程。
第四级:Background进程
当进程中运行着不能被用户看见界面的Activity时(Activity的onStop方法已经调用),进程被认为是Background进程。系统中会有很多处于此状态的进程,系统会保存一个列表来标识用户使用过的Activity,并根据这个列表顺序保证运行着用户最近使用过的Activity的进程最后结束。
第五级:空进程
进程中没有运行任何组件被认为是空进程,空进程设计的目的是为了缓存,来提高组件下次启动的速度。
除了以上描述的情形来判定,Android还有一些原则:其一,将进程中组件的最高级别状态,认为是进程的级别(例如进程A中运行着一个处于与用户交互中的Activity同时还有一个一般的Service,那么此时该进程被认为是第一级);其二,如果一个进程正在为另一个进程服务,该进程的级别就高于被服务的进程。
3. Android 的进程间通信机制(IPC)可以用作在不同的进程间完成数据的传递。
线程
1. 当应用启动时,系统创建一个进程并同时创建一个线程运行在其中,这个线程称作"main"线程。这个线程负责将事件派发给适当的用户交互控件,所有运行在同一个进程的组件都在这个线程中实例化,而系统对每个组件的调用都从这个线程中派发。例如:当用户点击一个按钮,应用的"main"线程将点击事件请求传递给按钮控件,按钮控件接下来设置自己成为被点击状态,并且发出一个请求内容是将这个事件从请求队列中撤销,"main"线程接受该请求将事件从请求队列中去除并同时提醒按钮重新绘制。
2. 如果按照默认行为,让系统所有工作都在main线程中执行,当碰到需要花费一定时间去完成的工作时(例如远程下载文件),线程就会被阻塞,而此时线程便不能派发任何事件,甚至是界面绘制事件,给用户呈现的就是一个停止无法响应的界面状态,这个状态超过5秒后,系统会提示用户一个“应用程序没有响应”的对话框。另一个问题是Android UI不是线程安全的,多线程的调用会导致不可预知的行为。
因此对于UI的单线程模型要避免两中情形:第一. 阻碍main线程;第二. 在main线程以外的线程操作UI。
如何解决上述两种问题?第一个问题的解决办法是将费时的工作内容放到另一个线程去执行;而第二个问题最好的解决办法是扩展AsyncTask类。
private class RetrieveImageTask extends AsyncTask<String, Void, Bitmap> {
private ImageView imageView;
public RetrieveImageTask(ImageView imageView) {
this.imageView = imageView;
}
@Override
protected Bitmap doInBackground(String... args) {
Bitmap bitmap = app.retrieveBitmap(args[0]); //在扩展的Application子类中设置的工具方法。
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
app.getImageCache().put((Long) imageView.getTag(), bitmap);
imageView.setTag(null);
}
}
}
Application对象定义了一定的生命周期,期间有一些回调方法可以用来实现特定的逻辑:
1. onCreate方法。当应用启动时,在创建其他应用相关对象前,调用这个方法。
在此做什么:在这个方法中设置并初始化应用内部或者全局状态。
注意要点:
在这个方法中的进行工作必须尽可能的快速完成,因为在这个方法中花费的时间影响整个应用启动的时间;如果重写这个方法,确保调用了super.onCreate();
2.onLowMemory方法。当系统资源不足时应用尝试释放它们占用的不必需的资源,此时调用本方法。
在此做什么:释放缓存或者其他不必需的资源。
注意要点:
这个方法执行并没有一个确切的时间点,它大概会在所有background进程都已经结束并在系统要结束运行service和foreground UI的进程之前执行;
当这个方法返回时,系统会执行GC处理。
3.当程序结束时调用onTerminate方法,但这个方法只有在模拟环境中才会用到,生产环境下不会调用。4.当应用运行时改变设备的配置时会调用onConfigurationChanged方法。
共享全局数据
在Application的扩展中可以做以下几件事:
1. 设定应用的全局变量以及getter/setter;
2. 在onCreate方法中初始化这些变量;
3. 定义引用的工具方法;
public class StoreApp extends Application {
private ConnectivityManager cMgr;
private DailyDealsFeedParser parser;
private List<Section> sectionList;
private Map<Long, Bitmap> imageCache;
private Item currentItem;
// lifecycle
@Override
public void onCreate() {
super.onCreate();
// initializing global state
}
@Override
public void onTerminate() {
// To-Do
super.onTerminate();
}
// helper methods
//used by more than one other activity, so placed here, could be util class
public Bitmap retrieveBitmap(String urlString) {
//…
}
public boolean connectionPresent() {
//…
}
// getters/setters below
}