您的位置:首页 > 移动开发 > Android开发

【Android 官方文档】翻译Android官方文档 Processes and Threads(五)

2016-07-31 13:58 441 查看
注:转载请注明出处:

http://blog.csdn.net/u011669081/article/details/52077956

前言

这是Android官方文档翻译的第五篇了,一路下来很不容易,尤其是那些又臭又长的英文句子太难理解,很费功夫,不过坚持下来,却有不一样的感受,不仅仅是简单的翻译,更有意义的是自己将知识点重新梳理,自己概括了出来。比较重要的英文官方文档我也会继续翻译下去,在这里继续分享出现~谢谢大家的支持~

当一个应用程序组件首次启动,并且此时应用程序也没有其他组件在运行,那么Android系统就会给应用创建一个含有单线程的linux进程。默认情况下,来自相同应用中的所有组件都会运行在同一个进程和线程(也称为 “main”主线程) 。如果组件启动时,已经有了应用的进程(应用的其他组件已经在运行),那么组件就会在已经存在的进程和线程中启动组件。另外,可以指定组件运行在其他进程中,那么也可以为任何进程创建额外的线程。

这里我们讨论进程和线程,如何在Android中运用。

进程

在默认情况下,一个应用程序中的所有组件是运行在同一个进程中的,一般情况下应用不会改变这样的现状。但是,如果需要指定某个组件运行到某个特定进程,这里就需要用上manifest文件了。

manifest文件中的元素 、 、 和 都可以去定义 android:procress ,可以指定组件运行的进程。设置属性可以实现每个组件在不同的进程中运行,或者说某几个组件一同运行在一个进程中。设置属性也可以让不同的应用中的组件运行在同一个进程中,从而实现多个程序共享一个Linux 用户ID,得到一样的权限。

另外,元素也支持android:process属性,用于指定所有组件的默认进程。

如果手机内存不足,可又有其它为用户提供更紧急服务的进程需要更多内存,那么Android可能会决定关闭一个进程。在此进程中运行着的应用程序组件也会因此被销毁。当需要再次工作时,会为这些组件重新创建一个进程。

那么怎样决定去关闭哪个进程呢,Android系统会判断这些进程对使用者的重要程度。例如,一个可见的Activity与不可见的相比,应当选择关闭不可见的进程。这也就是说,某个进程是否被关闭,这要取决这个进程中的组件所处的状态。

进程的生命周期

Android系统会尽量保持应用的进程不被杀死,不过当新建进程或者需要运行重要进程时,Android 可能需要清理点一些进程,来保证其他进程的正常运行。为了判断保留或者关闭某个进程,会参照进程中的组件以及这些组件的状态来决定,Android系统把进程按照重要程序划分。重要程度低的进行会先被关闭,然后接着是下一个,这是释放系统资源所需的办法。

按照重要性划分,共有5个,如下列举:

前台进程:

用户当前操作的进程,如下列举:

正在与用户交互的Activity

被正在与用户交互的activity绑定的服务

其中运行着“前台”服务——服务以[startForeground()方式被调用。

正在执行生命周期回调方法(onCreate()、onStart()或onDestroy())的服务

正在执行onReceive()方法的BroadcastReceiver

通常来说,关闭前台进程在任何时候都是少数的,只有在内存严重不足,才会被终止。一般情况下,Android设备到了影响用户界面响应,才会终止前台进程。

可见进程

不包含前台组件、不过任然会影响用户在屏幕上所见内容的进程。满足以下任一条件时,进程被认为是可见的:

运行着不在前台的Activity,但是这个Activity任然是可见的(onPause()调用)。

运行着被可见(或前台)activity绑定的服务。

服务进程

进程中运行着由startService()方法启动的服务。虽然服务进程不会直接和用户所见内容有关联,但是他们通常会去执行一些用户关心的操作(例如如在后台播放音乐,或者是从网络下载数据),所以,除非内存不足以维持所有前台、可见进程同时运行,否则不会清理服务进程,系统会保持服务进程的运行。

后台进程

运行着目前用户不可见activity(Activity对象的onStop()方法已被调用)的进程。一般情况下,这些进程对用户体验没有直接的影响,系统可能在任意时间终止它们,为了回收内存供前台进程、可见进程及服务进程使用。通常会有很多后台进程在运行,所以它们会被保存在一个LRU(最近最少使用算法)列表中,为的是确保最近被用户使用的activity最后一个被终止。如果一个Activity实现了生命周期方法,并保存了当前的状态,则终止此类进程不会对用户体验产生可见的影响。因为在用户返回时,Activity会恢复所有可见的状态。

空进程

它不包含然后应用程序组件的进程。通常保留这些进程的目的是为了缓存,清理内存时,系统通常会清理掉空进程。

系统根据进程中目前活跃组件的重要程度,Android会给进程评估一个尽可能高的级别。

所以说,一个进程的级别可能会由于某个进程的依赖而被提高——为其它进程提供服务的进程级别永远不会低于使用此服务的进程。比如:如果A进程中的content provider为进程B中的客户端提供服务,或进程A中的服务被进程B中的组件所调用,则A进程至少被视为与进程B同样重要。

因为运行服务的进程级别是高于后台activity进程的,所以,如果activity需要启动一个长时间运行的操作,则为其启动一个服务会比简单地创建一个工作线程更好些——尤其是该操作时间比activity的生存期还要长的情况下。比如,一个activity要把图片上传至Web网站,就应该创建一个服务来执行之,即使用户离开了此activity,上传还是会在后台继续运行。不论activity发生什么情况,使用服务可以保证操作至少拥有“服务进程”的优先级。同理,广播接收器broadcast receiver也是使用服务来处理耗时任务的,而不是简单地把它放入线程中。

线程

根据上面的描述,想要确保界面的响应能力,这个关键在于不可以阻塞UI线程。假如操作不能很快完成,那么就应该让它在一个单独的线程中运行。

例如:下面是响应点击的代码,其中包含下载图片:

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线程里修改了ImageView,可能会导致不确定,不可预见的后果,而且找这种问题也是非常耗时的。

为了解决上述问题,Android提供了几种方法,从其他线程中访问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();
}


以上代码现在是线程安全的,网络操作在单独的线程里完成。

但是随着操作越来越复杂,代码也会越来越复杂,那么就可以考虑在工作线程中使用Handler来处理UI线程分发来的消息。不过,最好的办法是使用异步任务类AsyncTask,它简化了工作线程与UI线程的交互操作。

使用异步任务

异步任务允许异步的方式对用户界面进行操作,它先阻塞工作线程,再在UI线程中呈现结果,整个过程中不需要对线程和Handler进行操作。

要使用异步任务,要先继承AsyncTask并且去实现 doinBackground方法,对象将运行在一个后台线程池中,所以就能安全地改变UI了,在UI线程中调用 execute()来执行任务。

如下:

public void onClick(View v) {
new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
/** The system calls this to perform work in a worker thread and
* delivers it the parameters given to AsyncTask.execute() */
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}

/** The system calls this to perform work in the UI thread and delivers
* the result from doInBackground() */
protected void onPostExecute(Bitmap result) {
mImageView.setImageBitmap(result);
}
}


这样线程UI是安全的,代码也够精简。

线程安全的方法

在某些情况下,多个线程需要调用同一个方法,所以被调用的方法必须要是线程安全的。

能被远程调用的方法—bound服务中的方法,这是必须线程安全的。如果对IBinder所实现方法的调用发起于IBinder所在进程的内部,那么这个方法是执行在调用者的线程中的。但是,如果调用发起于其他进程,那么这个方法将运行于线程池中选出的某个线程中(而不是运行于进程的UI线程中),该线程池由系统维护且位于IBinder所在的进程中。例如,即使一个服务的onBind()方法是从服务所在进程的UI线程中调用的,onBind()所返回对象中的方法(比如,执行了RPC方法的一个子类)仍会从线程池中的线程被调用。因为一个服务可以有不止一个客户端,所以同时可以有多个线程池与同一个IBinder方法相关联。因此IBinder方法必须实现为线程安全的。

类似地,内容提供者(content provider)也能接收来自其它进程的数据请求。尽管ContentResolver类、ContentProvider类隐藏了进程间通讯管理的细节,ContentProvider中响应请求的方法——query()、insert()、delete()、update()和getType()方法——是从ContentProvider所在进程的线程池中调用的,而不是进程的UI线程。因为这些方法可能会从很多线程同时调用,它们也必须实现为线程安全的。

进程间通讯

Android利用远程过程调用(remote procedure call,RPC)提供了一种进程间通信(IPC)机制,使用这种机制,被activity或者其他应用程序的组件调用的方法将(在其他进程中)被远程执行,而所有的结果将被返回给调用者。这就要求把方法调用及其数据分解到操作系统可以理解的程度,并将其从本地的进程和地址空间传输至远程的进程和地址空间,然后在远程进程中重新组装并执行这个调用。执行后的返回值将被反向传输回来。Android提供了执行IPC事务所需的全部代码,因此只要把注意力放在定义和实现RPC编程接口上即可。

要执行IPC,应用程序必须用bindService()绑定到服务上。

以上为 Progress and Thread 翻译~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 文档