Android进程和线程
(译自Android SDK文档Processes and Threads, 不全)
一. 进程生命周期
Therearefivelevelsintheimportancehierarchy.Thefollowinglistpresentsthedifferenttypesofprocessesinorderofimportance(thefirstprocessismostimportantandiskilledlast):
importancehierarchy.分为五个等级,最重要的进程最后被杀死。
1.Foregroundprogress前台进程
前台进程是用户完成当前任务必需的进程。满足以下条件之一的进程为前台进程:
Hosts用户正在与之进行交互的Activity;
1)Hosts被用户正在与之进行交互的Activity绑定的Service
2)Hosts前台Service
3)Hosts正在执行onCreate(),onStart()或onDestroy()的Service
4)Hosts正在执行onReceive()方法的BroadcastReceiver
通常情况下任意时刻仅有少量前台进程。前台进程仅在极端情况下才会被杀死,比如设备内存过低需要进行页面置换。
2.Visibleprogress可见进程
没有前台组件、但仍影响用户屏幕显示内容的进程即为可见进程。
1)HostsVisibleActivity,即不在前台、但仍然可见的Activity(已调用onPause())。比如某个原本处于前台的Activity弹出一个对话框,则这个Activity现在处于可见状态
2)Hosts被VisibleActivity绑定的Service
可见进程非常重要,除非要保证前台进程运行,一般不会杀死可见进程
3.Serviceprogress服务进程
通过startService()方法启动服务的、且不属于以上两种情况的进程,归为服务进程。服务进程不直接跟用户所见产生关系,但它们通常执行一些用户非常关心的操作,比如在后台播放音乐或下载文件。
4.Backgroundprogress后台进程
Hosts对用户不可见的Activity(已调用onStop()方法)的进程为后台进程。这些进程不会对用户体验产生直接影响,所以系统可以在任何时候杀死它们以便为前台进程、可见进程、服务进程回收内存。通常同时存在许多后台进程,它们被保存在LRU(leastrecentlyused)队列中,以保证用户最近使用的Activity最后被杀死。如果一个Activity正确地实现了其生命周期方法,并且保存了当前状态,杀死该进程不会对用户体验产生明显影响。
5.空进程
不持有任何应用组件的进程为空进程。空进程存在的意义是缓存,以加速下次组件的启动速度。
Android尽可能将一个进程往高级别排列。比如,某个进程hosts一个service和一个可见activity,则认为这个进程是可见进程而非服务进程。另外,一个进程的等级也可能因依赖关系而发生变化。一个进程为另一个进程提供服务,则前者等级绝对不可能比后者低。比如,A进程中的一个contentprovider为B进程中的一个客户提供服务,或者A进程中的一个service被B进程中的某个组件绑定,则A进程至少与B进程有同样的优先等级。
由于运行service的进程级别比运行后台activity的进程级别高,发起长时间任务(long-runningoperation)的activity最好通过服务来完成任务,而不是简单地创建一个工作线程,尤其当任务持续时间长过activity自身生命时。比如,一个activity上传图片到网站上,则该activity应当启动一个服务来完成上传操作,当用户离开这个activity时上传工作仍能继续。使用service能保证至少具有serviceprogress优先级,不管这时activity状态如何。基于同样的理由,broadcastreceiver也应当利用service而非创建线程来完成耗时操作。
二. 工作线程
程序启动时,系统创建一个main线程用户执行程序。Main线程非常重要,它负责分发事件到相应的UI部件,包括绘图事件(drawingevents)。Main线程有时也称作UI线程。
系统并不为每个组件实例创建单独的线程。进程中所有的组件都在UI线程中实例化,对每个组件的系统调用也都从该线程分发。结果,类似于onKeyDown()这样的系统调用都在这个进程的UI线程中运行。
单线程应用程序性能低下。如果所有的操作都在UI线程中进行,类似网络访问或数据库访问之类的耗时操作将会阻塞UI。当UI线程被阻塞,事件不能被分发和处理,包括drawingevents。从用户角度,应用死掉了。更糟糕的是,当UI线程阻塞超过5秒钟将会提示ANR。
此外,UItoolkit并非线程安全。所以不能在UI线程以外的其他线程操作UI组件。综上所述,归纳出以下两个简单规则:
1.不要阻塞UI线程
2.不要在UI线程以外的线程访问和操作UI组件
来看这段代码,它是一个clicklistener,用于启动一个线程下载图片并在一个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();}
一开始,代码看似一切正常。但仔细就会发现,它违反了以上第2条规则:不要从UI线程以外的其他线程访问和操作UI。以上代码会导致未定义和不可预料的程序行为,这种行为难以跟踪和分析。
为了解决这个问题,Android提供了几种方法以便能够从其他线程访问UI线程。方法如下:
1.Activity.runOnUiThread(Runnable)
2.View.post(Runnable)
3.View.post(Runnable,long)
所以,可以用第1种方法将代码改成下面这样
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();}
现在的实现是线程安全的了:网络操作在一个单独的线程中完全,而且UI操作也是在UI线程中完成。但问题又来了,当操作变得复杂时,类似的代码会越趋复杂并且难以维护。为了能够在工作线程中完成更加复杂的操作,可以考虑在工作线程中使用Handler来处理来自UI线程的消息。有可能最好的解决方案是继承AsyncTask类,它可以简化需要与UI交互的工作线程。
AsyncTask允许你在UI上执行异步操作。它在工作线程中完成可能产生阻塞的操作,然后在UI线程中发布执行结果,不用自己编码操作线程或Handler。使用AsyncTask时,必须继承AsyncTask并且实现doInBackground()回调方法,这个方法在一个后台线程池中运行。为了能够更新UI,还必须实现onPostExecute(),该方法在UI线程中运行、并且能接收和处理doInBackground()的执行结果。这样,就能安全地更新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); }}
更多相关文章
- android操作xml
- android的文件、目录操作
- Nodejs 或 js 判断手机操作系统 Android or IOS
- android文件操作的实例
- Android手机操作系统中实现图片浏览
- 图解 Android Handler 线程消息机制