Android之进程和线程

如果某个应用程序组件是第一次被启动,且这时应用程序也没有其他组件在运行,则Android系统会为应用程序创建一个包含单个线程的linux进程。默认情况下,同一个应用程序的所有组件都运行在同一个进程和线程里(叫做“main”主线程)。如果组件启动时,已经存在应用程序的进程了(因为应用程序的其它组件已经在运行了),则此组件会在已有的进程和线程中启动运行。如果组件想在指定的进程里运行,可以为任何进程创建额外的线程。

一、进程

默认情况下,同一个应用程序内的所有组件都是运行在同一个进程中的,大部分应用程序也不会去改变它。如果需要指定某个特定组件所属的进程,则可以在manifest 文件设置。

manifest文件中的每种组件元素——<activity> <service> <receiver><provider>——都支持定义android:process属性,用于指定组件运行的进程。设置此属性即可实现每个组件在各自的进程中运行,或者某几个组件共享一个进程而其它组件运行于独立的进程。设置此属性也可以让不同应用程序的组件运行在同一个进程中——实现多个应用程序共享同一个Linux用户ID、赋予同样的权限。

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

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

在决定关闭哪个进程的时候,Android系统会权衡它们相对用户的重要程度。比如,相对于一个拥有可见activity的进程和一个activity已经在屏幕上看不见的进程,关闭进程时更趋向于后者。也就是说,是否终止一个进程,取决于运行在此进程中组件的状态。

二、进程的级别

若内存不足时,Android会关闭一些进程释放内存。之前已经了解到,是否关闭进程决定于进程相对于用户的重要程度,下面对进程的级别分类:

1.前台进程

用户当前操作所必须的进程。满足以下任一条件时,进程被视作处于前台:

·其中运行着正与用户交互的ActivityActivity对象的onResume()]方法已被调用)。

·其中运行着被正与用户交互的activity绑定的服务

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

·其中运行着正在执行生命周期回调方法(onCreate()onStart()onDestroy())的服务

·其中运行着正在执行onReceive()方法的BroadcastReceiver

一般而言,任何时刻前台进程都是为数不多的,只有迫不得已——当内存不足以维持它们同时运行时——才会被终止。通常,设备这时候已经到了使用虚拟内存的地步,终止一些前台进程是为了保证用户界面的及时响应。

2.可见进程

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

·其中运行着不在前台的Activity,但用户仍然可见到此activityonPause()方法被调用了)。比如以下场合就可能发生这种情况:前台activity打开了一个对话框,而之前的activity还允许显示在后面。

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

可见进程被认为是非常重要的进程,除非无法维持所有前台进程同时运行了,它们是不会被终止的。

3.服务进程

此进程运行着由startService()方法启动的服务,它不会升级为上述两级别。尽管服务进程不直接和用户所见内容关联,但他们通常在执行一些用户关心的操作(比如在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台、可见进程同时运行,系统会保持服务进程的运行。

4.后台进程

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

5.空进程

不含任何活动应用程序组件的进程。保留这种进程的唯一目的就是用作缓存,以改善下次在此进程中运行组件的启动时间。为了在进程缓存和内核缓存间平衡系统整体资源,系统经常会终止这种进程。

三、线程

应用程序启动时,系统会为它创建一个名为“main”的主线程,负责把事件分发给相应的用户界面widget——包括屏幕绘图事件。它也是应用程序与Android UI组件包(来自android.widgetandroid.view包)进行交互的线程。因此,主线程有时也被叫做UI线程。

系统并不会为每个组件的实例都创建单独的线程。运行于同一个进程中的所有组件都是在UI线程中实例化的,对每个组件的系统调用也都是由UI线程分发的。因此,对系统回调进行响应的方法(比如报告用户操作的onKeyDown()或生命周期回调方法)总是运行在UI线程中。

举个例子,当用户触摸屏幕上的按钮时,应用程序的UI线程把触摸事件分发给widgetwidget先把自己置为按下状态,再发送一个显示区域已失效(invalidate)的请求到事件队列中。UI线程从队列中取出此请求,并通知widget重绘自己。

如果应用程序在与用户交互的同时需要执行繁重的任务,单线程模式可能会导致运行性能很低下,除非你的应用程序设计得很精妙。尤其是UI线程要处理所有任务时,那些耗时很长的操作——诸如访问网络或查询数据库等——将会阻塞整个UI(线程)。一旦线程被阻塞,所有事件都不能被分发,包括屏幕绘图事件。从用户的角度看来,应用程序看上去像是挂起了。更糟糕的是,如果UI线程被阻塞超过一定时间(目前大约是5秒钟),用户就会被提示那个可恶的应用程序没有响应”(ANR)对话框。如果引起用户不满,他可能就会退出并删除这个应用程序。

此外,AndoidUI组件包并不是线程安全的。因此不允许从工作线程中操作UI——只能从UI线程中操作用户界面。于是,Andoid的单线程模式必须遵守两个规则:

1.   不要阻塞UI线程。

2.   不要在UI线程之外访问AndoidUI组件包。

更新UI操作时,使用异步任务AsyncTask 允许以异步的方式对用户界面进行操作。它先阻塞工作线程,再在UI线程中呈现结果,在此过程中不需要对线程和handler进行人工干预。要使用异步任务,必须继承AsyncTask类并实现doInBackground()回调方法,该对象将运行于一个后台线程池中。要更新UI时,须实现onPostExecute()方法来分发doInBackground()返回的结果,由于此方法运行在UI线程中,所以就能安全地更新UI了。

例如:

<span style="font-size:14px;">public void onClick(View v) { 
    new DownloadImageTask().execute("http://example.com/image.png"); 
} 
 
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { 
/** 
* 在后台做耗时操作
*/
    protected Bitmap doInBackground(String... urls) { 
        return loadImageFromNetwork(urls[0]); 
    } 
     
/** 
* 更新UI
	*/ 
    protected void onPostExecute(Bitmap result) { 
        mImageView.setImageBitmap(result); 
    } 
}
</span>




日积月累,总会进步!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值