进程和线程

进程和线程

标签(空格分隔): Android API指导


此文档介绍Android应用的进程和线程是如何工作的。

进程

默认的,同一个应用中的所有组件都是运行在同一个进程。
所有组件在manifest文件中可以通过android:process属性来指定运行的进程。

进程生命周期

Android系统会给每个进程分级,如果系统需要回收内存时,就先销毁优先级低的进程,各层级如下(序号越高,重要性越低,越容易被销毁):

  1. 前台进程
    用户正在使用的,有如下场景:
  • 与用户交互的Activity界面(onResume已经执行)
  • 绑定到与用户正在交互界面的Service
  • 在前台运行的Service(用startForeground启动)
  • 正在执行生命周期回调函数的Service(onCreate、onStart、onDestroy)
  • 正在执行onReceiver的BroadcastReceiver

一般来说,某个时间点上只有少数的前台进程在运行。他们只有在内存低到无法继续运行的时候才有可能会被销毁。
3. 可视进程
进程不包含前台组件,但仍是用户可见的,有如下场景:

  • Activity不在前台,但是仍然是可见的(onPause已经执行),例如A弹出前台对话框B,B不能完全覆盖A时。
  • 绑定到可见或者前台Activity的service
  1. 服务进程
    由startService启动的服务,并且不在1和2场景中。
  2. 后台进程
    承载不可见的Activity(onStop已经执行)
  3. 空进程
    不承载任何组件的进程

线程

Android UI是单线程模型,应用中所有组件的实例都在同一个UI线程中执行,当UI线程阻塞时,会无法分发用户的点击事件导致ANR。
另外,UI线程是非线程安全的,所有与用户界面相关的交互必须都放在UI线程中,而不是用其他工作线程,因此,对于Android线程模型来说有两条简单的规则:

  1. 不要阻塞UI线程
  2. 不要在UI线程之外的其他线程中访问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线程中使用了界面元素mImageView。
为了避免这个问题,Android提供了如下方法:

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

例如,可以这么用:

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来处理,也可以扩展AsyncTask类

使用AsyncTask

AsyncTask允许执行UI元素的异步操作,它会在工作线程中执行阻塞UI的操作,然后将结果发送给UI线程,而不需要你自己处理线程。
使用方法:继承AsyncTask,实现在后台线程池执行的doInBackground,实现onPostExecute来更新UI。
例子:

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

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    /** 系统在工作线程中执行这个动作,其中入参是由AsyncTask.execute()提供 */
    protected Bitmap doInBackground(String... urls) {
        return loadImageFromNetwork(urls[0]);
    }
    
    /** 系统调用这个刷新UI界面,其中result是doInBackground()的返回值 */
    protected void onPostExecute(Bitmap result) {
        mImageView.setImageBitmap(result);
    }
}
  1. AsyncTask用之前,建议看一下SDK文档
  2. 运行时的配置变化(比如横竖屏)会销毁你的工作线程,可以查看Shelves例子代码。

线程安全方法

某些情况下,你写的方法可能被多个线程调用,那就需要这个方法是线程安全的。
像Service的onBind,ContentProvider的query、insert、delete、update、getType都是类似需要线程安全的函数。

进程间通讯

IPC,RPC,详见Service章节

原文地址:https://www.cnblogs.com/konger/p/3978246.html