- 对于一个Android应用程序,当启动一个应用组件,并且此时没有其他应用组件运行时,Android系统会为该应用启动一个由一个线程执行的Linux进程。默认情况下,一个应用程序的所有组件都运行在该进程的线程中,我们称该线程为“主线程”。当然我们可以通过创建线程,为该应用程序后续启动的其他组件安排在单独的进程中执行。
1进程
默认情况下,一个应用程序的所有组件都运行在一个进程中,并且大多数应用程序应该遵循这一点。然后,如果我们需要控制特定的组件属于某个进程,我们需要在manifest文件中设置。
android:process属性,通过设置该属性可以具体该组件运行的进程。这样我们就可以让一些组件共享一个进程,当然我们可以通过设置这个属性让不同应用程序的组件共享一个进程。 <application>标签也支持android:process属性,通过设置该属性来设置应用程序所有组件默认的进程。
Android可以当内存不够用,其他立即服务于用户的那些进程需要更多资源的时候来关闭一些进程。那么,在被关闭的进程中运行的组件们就会因此被销毁destroy掉。当需要他们的时候,再开启这个进程。
Android会用过衡量这些进程对用户的重要程序来决定在何时关掉何进程。例如,一个不可见的Activity和一个可见的Activity一定会选择关掉不可见。因此,决定是否终止该进程依赖于该进程中运行的组件的状态。
1.1 进程的生命周期
Android系统会尽可能的长的维持应用程序中的进程,但是最终还是会为了给其他新的更重要的进程开拓资源而销毁一些旧的进程。决定哪个线程销毁的问题,Android根据进程中运行的组件的状态,给进程分了重要性级别。系统回收资源的时候根据这个级别来依重要性的高低一次回收。
从上往下重要性降低。
Foreground Process(前台进程)
用户正在使用的进程。如果是下列情况,被认为是前台进程。
1.进程有一个用户正在与之交互的Activity(该Activity的onResume()被调用)
2.进程有一个和用户正在交互的Activity绑定的Service
3.进程有一个运行在前台的Service(Service调用的startForeground())
4.进程中有一个正在执行生命周期方法(onCreate(),onStart(),onDestroy())的Service
5.进程中有一个正在执行onReceive()方法的BroadcastReceiver
总体上来说,只有很少几个前台线程同时存在。他们在最后被杀死。
Visible Process(可见进程)
线程中没有任何的前台组件,但是他们依旧能影响用户在屏幕上所看到内容。在下列情况下被认为是可见线程。
1.进程中有一个Activity,该Activity不再前台,但是依旧对用户可见(Activity的onPause()方法被调用)。比如说前台的Activity启动了一个Dialog对话框,这回让前台的该Activity在Dialog后面,依旧可以被看到。
2.进程中有一个绑定了可见Activity的Service
可见线程也被认为是极其重要的,不到不足以维持前台进程可见进程不会被杀死。
Service Process(服务进程)
进程正在执行通过startService()启动的Service,并且不符合Foreground Process和Visible Process这两种类别。尽管服务进程不直接被用户看到,但是他们确实做些用户息息相关的事情。比如在后台播放音乐,或者从网络上下载数据,因此系统也尽量维持该中进程。
Background Process(后台进程)
随时可以杀死他们,来为了给以上三种级别进程回收资源。通常有很多后台进程运行,他们有个以“最少最近使用”列表来确保最近被看到的Activity最后被杀死,因此,注意正确重写Activity的生命周期方法来保存当前状态,这样进行进程销毁时便不会给用户带来可见的影响。
Empty Process(空进程)
进程中未包含任何active的应用组件。保持这种线程的唯一原因就是为了提高启动某些组件的速度。
另外,一个进程的优先级可能因为其他线程的依赖而提高。
因为运行着Service(Service Process)的进程优先级要比运行后台Activity的进程的高,如果初始化一个耗时操作的Activity最好开启的一个Service来做耗时操作,而不是简单的开一个worker Thread,尤其是该耗时操作执行可能比该Activity更长久,例如,上传图片到网络上应该开启一个Service来执行上传操作,这样的话即使用户离开该Activity,上传操作后台继续执行。这样无论该Activity发生了什么,Service保证了该操作有Service Process的优先级。这也是广播接受者要使用Service来接受广播而不是简单把耗时操作放在Activity里开启的线程里。
2 线程
当一个应用程序加载启动时,系统会创建一个线程来执行此应用,该线程成为“主线程”。这个线程非常重要,因为它负责调控事件给合适的UI,widget包括绘画事件。这个线程也是我们的应用程序从Android UI控件和组件交互的线程,因此main Thread 也称为UI Thread。
系统不会为每个组件实例创建单独的线程。在同一个进程中运行的所有组件都在UI 线程中初始化,也在该线程中进行分派。因此,响应系统回调的方法(比如onKeyDown())也是在UI 线程中执行。尤其是,如果我们都在UI Thread中执行所有的事情,进行耗时操作,例如获取网络连接或者查询数据库都会阻塞整个UI。当UI Thread阻塞,其他的任何时间都不会被指派,包括绘制事件。从用户的角度,应用程序就会卡掉。更糟糕的是,如果UI Thread的阻塞超过5秒,用户就会被提示“application not responsing”(ANR)对话框。
因为Android 的UI 工具包目前不是线程安全的,所有不能在Worker Thread中操作UI。必须在UI Thread中进行所有的UI 操作。有两个规则:
1.不要阻塞UI Thread
2.不能在UI Thread之外的其他线程获取操作Android Ui工具包
特别注意:在Android中,程序可以分成好几个组件,其中最重要的两个就是活动(Activity)和服务(Service)。活动是用户的GUI,而服务则运行于后台。比如说,一个IM,活动就是聊天的界面,而服务则用于网络通讯。
如果仅仅是这样的话,那么服务不过是一个没有界面的活动而已。但是实际上并非如此。为了节约资源,当一个活动不可见的时候,它是不会执行任何代码的,这时候就要靠服务了。例如在播放音乐的时候,就必须要用到服务,不然一切换到别的软件音乐就停了。
因此服务就给程序提供了后台运行的可能。更进一步的,服务是可以远程调用的,不只这个程序可以调用它,其他程序也可以调用它(前提是有相应的接口和权限)。
但是有一点要注意的是,同一个进程中的服务和活动是在同一线程中的。换句话说,后台和GUI是会相互阻塞的。因为既然是后台服务嘛,怎么会阻塞到前台界面呢,但是事实就是如此。因此要在服务中执行长时间的操作(如网络应用)时,还是要自己创建线程来操作。
Worker Thread
根据上面的描述,不阻塞UI线程非常重要。如果我们要执行耗时的操作,一定要确保分离在单独的线程中执行。
例如,以下代码是当点击按钮,用单独的线程里下载图片,然后显示到单独的ImageView中。
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork("http://example.com/image.png");
mImageView.setImageBitmap(b);
}
}).start();}
乍一看没有什么问题,因为开了单独的线程处理网络操作。然而,这段代码违反了第二个原则:不能在UI线程之外的其他线程操作UI包,这个例子在Worker Thread中修改了ImageView,这回导致一些问题。
为了解决这些问题,Android提供了许多其他的方式来从Worker Thread中获取UI线程,下列是能提供这类的帮助的方法:
- Activity.runOnUiThread(Runnable)
- View.post(Runnable)
- View.postDelayed(Runnable,long)
我们用View.post(Runnable)方法来修复上面的问题:
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
mImageView.post(new Runnable() {
public void run() {
mImageView.setImageBitmap(bitmap);
}
});
}
}).start();
}
现在的实现是线程安全的,网络操作是在单独线程中进行的并且ImageView在UI Thread 中进行操作的。
然而,随着操作的复杂度变大,这种代码会非常难维护。我们可以在Worker Thread中用Handler来处理更复杂的交互,实现Worker Thread和UI Thread进行交互。或许,最好的解决办法是通过继承AsyncTask,这个线程框架来处理。
对于Android线程的学习,还要深入学习Handler 和AsyncTask等多献策好难过通信管理框架。