Android进程和线程

(译自Android SDK文档Processes and Threads, 不全)

一. 进程生命周期

 

There are five levels in the importance hierarchy. The following list presents the different types of processes in order of importance (the first process is most important and is killed last):

importance hierarchy.分为五个等级,最重要的进程最后被杀死。

1. Foreground progress前台进程

前台进程是用户完成当前任务必需的进程。满足以下条件之一的进程为前台进程:

Hosts用户正在与之进行交互的Activity

1)Hosts被用户正在与之进行交互的Activity绑定的Service

2)Hosts前台Service

3)Hosts正在执行onCreate()onStart()onDestroy()Service

4)Hosts正在执行onReceive()方法的BroadcastReceiver

通常情况下任意时刻仅有少量前台进程。前台进程仅在极端情况下才会被杀死,比如设备内存过低需要进行页面置换。

2. Visible progress可见进程

没有前台组件、但仍影响用户屏幕显示内容的进程即为可见进程。

1)Hosts Visible Activity,即不在前台、但仍然可见的Activity(已调用onPause())。比如某个原本处于前台的Activity弹出一个对话框,则这个Activity现在处于可见状态

2) HostsVisible Activity绑定的Service

可见进程非常重要,除非要保证前台进程运行,一般不会杀死可见进程

3. Service progress服务进程

通过startService()方法启动服务的 、且不属于以上两种情况的进程,归为服务进程。服务进程不直接跟用户所见产生关系,但它们通常执行一些用户非常关心的操作,比如在后台播放音乐或下载文件。

4. Background progress后台进程

Hosts对用户不可见的Activity(已调用onStop()方法)的进程为后台进程。这些进程不会对用户体验产生直接影响,所以系统可以在任何时候杀死它们以便为前台进程、可见进程、服务进程回收内存。通常同时存在许多后台进程,它们被保存在LRU(least recently used)队列中,以保证用户最近使用的Activity最后被杀死。如果一个Activity正确地实现了其生命周期方法,并且保存了当前状态,杀死该进程不会对用户体验产生明显影响。

5. 空进程

不持有任何应用组件的进程为空进程。空进程存在的意义是缓存,以加速下次组件的启动速度。

 

Android尽可能将一个进程往高级别排列。比如,某个进程hosts一个service和一个可见activity,则认为这个进程是可见进程而非服务进程。另外,一个进程的等级也可能因依赖关系而发生变化。一个进程为另一个进程提供服务,则前者等级绝对不可能比后者低。比如,A进程中的 一个content providerB进程中的一个客户提供服务,或者A进程中的一个serviceB进程中的某个组件绑定,则A进程至少与B进程有同样的优先等级。

 

由于运行service的进程级别比运行后台activity的进程级别高,发起长时间任务(long-running operation)activity最好通过服务来完成任务,而不是简单地创建一个工作线程,尤其当任务持续时间长过activity自身生命时。比如,一个activity上传图片到网站上,则该activity应当启动一个服务来完成上传操作,当用户离开这个activity时上传工作仍能继续。使用service能保证至少具有service progress优先级,不管这时activity状态如何。基于同样的理由,broadcast receiver也应当利用service而非创建线程来完成耗时操作。

 


二. 工作线程

程序启动时,系统创建一个main线程用户执行程序。Main线程非常重要,它负责分发事件到相应的UI部件,包括绘图事件(drawing events)Main线程有时也称作UI线程。

 

系统并不为每个组件实例创建单独的线程。进程中所有的组件都在UI线程中实例化,对每个组件的系统调用也都从该线程分发。结果,类似于onKeyDown()这样的系统调用都在这个进程的UI线程中运行。

 

单线程应用程序性能低下。如果所有的操作都在UI线程中进行,类似网络访问或数据库访问之类的耗时操作将会阻塞UI。当UI线程被阻塞,事件不能被分发和处理,包括drawing events。从用户角度,应用死掉了。更糟糕的是,当UI线程阻塞超过5秒钟将会提示ANR

 

此外,UI toolkit并非线程安全。所以不能在UI线程以外的其他线程操作UI组件。综上所述,归纳出以下两个简单规则:

1. 不要阻塞UI线程

2. 不要在UI线程以外的线程访问和操作UI组件

来看这段代码,它是一个click listener,用于启动一个线程下载图片并在一个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,线程)