android的进程和线程(Processes and Threads)

当一个应用程序组件启动,应用程序没有任何其他组件上运行,Android系统的启动与执行一个新的Linux进程。默认情况下,同一个应用程序的所有组件在同一个进程和线程(称为“主”线程)上运行。如果一个应用程序组件启动和已经存在用于该应用程序的处理(因为从应用程序的另一个组件存在),则该组件被启动的过程中,并使用执行相同的线程。你可以在你的应用中安排另外的进程去运行,亦可以为任何进程创建创造额外线程。


本文讨论了如何进程和线程工作在一个Android应用程序。

进程

默认情况下,同一应用程序的所有组件运行在相同的进程,大多数应用程序不应该改变这种情况。然而,如果你发现你需要控制哪些过程属于某一组件,你可以在manifest文件中。

为每种类型的组件清单条目元素——<activity>,<service>、<receiver>,和<provider> 支持android:process进程属性,可以指定一个组件应该运行的过程。您可以设置该属性,这样每个组件运行在它自己的进程或这样一些组件共享一个过程而其他的没有。设置此属性还可以让不同的应用程序的组件运行在相同的进程中——实现多个应用程序共享相同的Linux用户ID和赋予同样的证书。

<application>元素也支持android:process属性,设置适用于所有组件的默认值。

Android可能决定关闭一个过程在某种程度上当内存低时,系统资源被其他进程请求时,更直接的服务于用户。应用程序组件运行的过程,也会因此被销毁。为这些组件重新开始一个过程的时候再为他们工作。

在决定哪个进程时,Android系统向用户衡量他们的相对重要性。例如,相比一个托管可见的活动过程,它更容易关闭没有显示在屏幕上的托管应用。因此,决定是否终止一个进程取决于组件在进程中运行的状态。终止进程的判定规则用于下面讨论。


进程的生命周期

android系统想要尽可能地维持一个进程。但是旧的进程需要让位于新的或者是更为重要的进程。为了确定哪些进程被保留哪些进程被杀死,基于正在运行的进程和其他进程的状态,系统将每一个进程都划分一个“重要性层次”。系统依次会杀死最不重要的进程。

有五个级别中的重要层次。下面的列表列出了不同类型的进程按重要性顺序(第一个过程是最重要的,是最后一个被杀):

1.前台进程 
这需要什么样的用户当前正在执行的方法。进程被认为是在如果任何下列条件为真前景: 
它承载了用户感兴趣的。
它承载绑定到用户与之交互的活动服务。 
它承载的“在前台”运行,该服务已被称为startForeground()服务。 
它承载的执行它的生命周期回调的一个服务。 
它承载的执行其的OnReceive()方法,一个BroadcastReceiver。 
通常,只有少数前台进程在任何给定时间存在。他们被杀害只能作为最后的手段,如果记忆是如此之低,他们不可能都继续运行。一般来说,在这一点上,该设备已经达到了内存分页状态,所以杀死某些前台进程需要保持用户界面的响应。 
2.可见进程 
阿那没有任何前台组件,但是仍然过程可以影响用户看到的画面是什么。一个进程被认为是可见的,如果任一下列条件为真: 
它承载的活动,是不是在前台,但仍然是对用户可见(它的onPause()方法被调用)。这可能会发生,例如,如果在前台活动开始一个对话,这使得先前的活性,可以看出它的后面。 
它承载绑定到一个可见的(或前景)活动服务。 
一个可视进程被认为是极其重要的,并且不会被杀死,除非这样做需要保持所有前台进程运行。 
3.服务流程 
运行已开始与的StartService()方法,并且不属于任何两个以上职类服务的过程。尽管服务进程不会直接连接到用户看到的任何东西,他们一般都做的事情,用户关心的(如播放的背景音乐或下载网络上的数据),因此系统保持它们运行,除非没有足够的记忆体留住他们以及所有前台和可视进程。 
4.后台进程 
一个进程举行一个活动,是当前不可见的用户(该活动的的onStop()方法被调用)。这些进程对用户体验没有直接影响,并且系统可以在任何时候杀了他们回收内存的前景,可见光或服务的过程。通常有很多正在运行的后台进程,因此他们保持在一个LRU(最近最少使用)清单,以确保与是最近看到的用户活动的过程中是最后被杀害。如果活动正确实现它的生命周期方法,并保存其当前状态,杀死它的进程不会对用户体验的一个明显的效果,因为当用户返回到活动中,活动恢复其所有的可见状态。请参阅有关保存和恢复状态信息的活动文档。 
5.空进程 
一个不举行任何活动的应用程序组件的过程。保持这种过程还活着的唯一原因是高速缓存的目的,以提高启动时间,下一次一个组件需要在运行它。系统经常杀死,以处理高速缓存和底层内核缓存之间平衡整个系统资源这些进程。 


Android排名最高水平的过程,基于目前活跃组件的重要性。例如,如果一个过程主机一个服务和一个可见的活动,这个过程是列为一个可见的过程,而不是一个服务进程。

此外,一个进程的级别可能会增加,因为其他进程依赖于这是另一个进程提供服务的过程永远无法排名低于此服务的进程。例如,如果一个内容提供者在处理服务客户机进程B,或如果一个服务进程a是绑定到组件的过程,过程总是认为至少进程B一样重要。

因为Service排名高于与背景活动进程,活动,发起一个长时间运行的操作最好是开始一个Service。例如,一个活动的将照片上传到一个网站应该启动一个服务来执行上传,这样在后台上传可以继续而使用户离开了活动。使用服务保证操作至少都会有“service进程”优先,不管发生了什么活动。这是同样的原因,广播接收器应该雇佣Service而不是简单地把耗时的操作在一个线程。


线程

当应用程序启动时,系统为应用程序创建一个执行线程,称为“主线程。“这个线程是非常重要的,因为它负责把事件分发给相应的用户界面小部件,包括屏幕绘图事件。这也是您的应用程序与组件交互的线程从Android UI toolkit(从android.widget组件和android.view包)。因此,主线程有时也称为UI线程。
系统不为每个组件的实例创建一个单独的线程。所有组件运行在同一进程中实例化在UI线程,每个组件和系统调用是派遣的线程。因此,应对系统回调的方法(如onKeyDown()报告用户操作或生命周期回调方法)总是在UI线程的运行过程。
例如,当用户触摸屏幕上的按钮时,应用程序的UI线程分派触摸事件的小部件,进而设置按下和帖子一个无效请求到事件队列。UI线程dequeues请求和通知小部件应该改变自己。
当你的应用程序在响应用户交互执行密集的工作,这个单线程模型能产生正常表现不佳,除非您实现您的应用程序。具体地说,如果一切发生在UI线程,耗时很长的操作,如访问网络或数据库查询将会阻塞整个UI。当线程被阻塞,没有事件可以派遣,包括屏幕绘图事件。从用户的角度来看,应用程序挂起。更糟糕的是,如果UI线程被阻塞超过几秒钟目前(大约5秒钟)用户提出了臭名昭著的“应用程序没有响应”(ANR)对话框。用户可能会决定退出你的应用程序和卸载它,如果他们不开心。
此外,Andoid UI工具包不是线程安全的。所以,你不能控制你的UI允许从一个工人操作的用户界面UI线程。因此,仅仅有两个规则,Android的单线程模型:
1.不阻塞UI线程
2.不从外部访问Android UI toolkit UI线程

工作线程

由于上述单线程模型,应用程序的响应能力是至关重要的UI,你不能阻塞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();
}
起初,这似乎工作好,因为它创建了一个新线程来处理网络操作。然而,它违反了单线程模型的第二条规则:不要从外部访问Android UI toolkit的UI thread-this样本修改ImageView工作线程,而不是UI线程。这可能导致未定义的和意想不到的行为,可以是困难和耗时的追踪。
为了解决这个问题,Android提供了几种方法来从其他线程访问UI线程。这里是一个列表的方法,可以帮助:

1.Activity.runOnUiThread(Runnable)
2.View.post(Runnable)
3.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线程操纵。

然而,随着操作的复杂性,这种代码也会变得很复杂,很难维护。工作线程来处理更复杂的交互,您可能会考虑使用一个处理程序在工作线程从UI线程处理消息交付。也许最好的解决方案,是扩展AsyncTask类,它简化了工作线程的执行任务,需要与用户界面交互。

使用的AsyncTask

AsyncTask允许您执行异步工作在你的用户界面。它执行阻塞操作在一个工作线程,然后在UI线程上公布结果,不需要你自己处理线程和/或处理程序。

要使用它,你必须继承的AsyncTask和实施doInBackground()回调方法,它在后台运行一个线程池。要更新你的UI,你应该实现onPostExecute(),它从doInBackground()和运行在UI线程中传递的结果,这样你就可以安全地更新你的UI。然后,您可以通过调用execute()从UI线程中运行任务。

例如,您可以使用AsyncTask的这种方式实现前面的例子:

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线程上完成的部分做了一部分。
你应该读为充分了解如何使用这个类的AsyncTask的参考,但这里是它是如何工作的简要概述:

1.您可以指定的类型参数

2.doInBackground()方法执行自动工作线程

3.onPostExecute onPreExecute(),()和onProgressUpdate()都是在UI线程上调用

4.doInBackground()返回的值发送到onPostExecute()

5.你可以随时调用publishProgress() 在doInBackground()来执行onProgressUpdate()在UI线程上

6.你可以在任何时候取消任务,从任何线程


线程安全的方法

在某些情况下,这些方法实现可能从多个线程调用,因此必须是线程安全的。
主要是这样的方法,可以被称为remotely-such绑定服务。当在一个方法的调用实现的一个内部起源于同一进程的内部运行,该方法在调用者的线程中执行。然而,当调用源自另一个进程,一个线程中执行的方法从一个线程池,选择系统保持在同一进程中theIBinder(这不是UI线程的执行过程)。例如,而服务的onBind()方法会从UI线程调用服务的过程中,方法中实现onBind()返回的对象(例如,一个子类实现RPC方法)将从线程池中。因为一个服务可以有多个客户端,可以多个线程池与IBindermethod同时相同。因此,内部方法必须实现线程安全的。
同样,一个内容提供者可以接收来自其他进程的数据请求。尽管theContentResolver和ContentProvider类隐藏进程间通信是如何管理的细节,ContentProvider方法,应对那些requests-the()方法查询,插入、删除、更新——从一个线程池内容提供者的过程,不是UI线程的过程。因为这些方法可能被称为从任意数量的线程同时,他们也必须实现线程安全的。


进程间通信

Android提供了进程间通信(IPC)机制使用远程过程调用(rpc),一个方法被调用的一个活动或其他应用程序组件,但执行远程(在另一个进程),与任何结果返回给调用者。这需要分解方法调用和操作系统的数据水平可以理解,从当地的进程和地址空间传输到远程进程和地址空间,然后再组装和执行调用。然后返回值相反的方向传播。Android提供了所有的代码来执行这些IPC事务,这样你就可以专注于定义和实现RPC编程接口。

执行IPC,应用程序必须绑定到一个服务,使用bindService()。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值