Android 学习笔记 - 进程、线程与线程池

Author Avatar
ChihoPang 3月 19, 2018
  • 在其它设备中阅读本文章

参考资料

《Android 开发艺术探索》第 11 章
进程和线程 | Android Developers
问答Android开发中何时使用多进程? · Issue #7 · android-cn/android-discuss · GitHub

进程

进程的概念

进程是一个可并发执行的、具有独立功能的关于某个数据集合的一次执行过程,也是操作系统进行资源分配和保护的基本单位。

进程的属性

  • 结构性:程序块 + 数据块 + 进程控制块
  • 共享性
  • 动态性
  • 独立性
  • 制约性
  • 并发性

使用多进程的原因

  • 解决应用内存的问题,分担主进程的压力。
  • 独立于主进程,确保某些任务的执行和完成。

开启多进程模式

在 AndroidManifest 中,为 < application > 、四大组件添加 android:process 属性,即可开启多进程。
应用的默认进程名为包名,android:process 属性值则为新开启的进程名。
可通过指定 android:process=“:xxx” ,将新开启的进程设置为当前应用的私有进程,此时其他应用的组件不可和它运行在同一进程当中。

进程优先级

如果内存不足,而其他为用户提供更紧急服务的进程又需要内存时,Android 可能会决定在某一时刻关闭某一进程。在被终止进程中运行的应用组件也会随之销毁。 当这些组件需要再次运行时,系统将为它们重启进程。
其中,进程分五个级别的重要性,来决定优先回收哪个进程,下列重要性由高到低排序:

  1. 前台进程
    满足以下任一条件:
    • 包含正在交互的 Activity(已执行 onResume())
    • 包含某个 Service,后者已绑定到正在交互的 Activity
    • 包含前台 Service(已执行 startForeground())
    • 包含正在执行生命周期的 Service
    • 包含正在执行 onReceive() 的 BroadcastReceiver
  2. 可见进程
    满足以下任一条件:
    • 包含不在前台,但仍可见的 Activity(已执行 onPause())
    • 包含某个 Service,后者已绑定到可见或前台的 Activity
  3. 服务进程
    包含已经运行并通过 startService() 启动,但不属于上述类别的服务。尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。
  4. 后台进程
    包含目前对用户不可见的 Activity 的进程(已调 onStop() )。
  5. 空进程
    不含任何活动应用组件的进程。

由于服务进程优先级高于后台进程,所以,当执行重要操作(如网络上传、广播接收)时,不要简单的创建线程执行,而是考虑使用服务来执行。

线程

线程的概念

处理器调度和分派的基本单位。不同线程是不同的执行流。

线程的控制操作

  • 创建
  • 阻塞
  • 恢复
  • 结束

主线程

Android UI 工具包组件(来自 android.widget 和 android.view 软件包的组件)不可在主线程以外访问,故称主线程为 UI 线程。
主线程不可阻塞,否则发生 ANR 异常。

工作线程

主线程之外的线程。
若需要从工作线程访问 UI 线程,可通过以下方式:

  • activity.runOnUiThread(Runnable)
  • view.post(Runnable)
  • view.postDelayed(Runnable, long)
  • Handler
  • AsyncTask

启动工作线程的方式

直接使用 Thread 类

new Thread().start();

HandlerThread

原理

handlerThread.run() 方法创建消息队列,开发过程中提供 new Handler(thread.getLooper) ,通过 handler.handleMessage() 执行操作。
因其 run() 方法为一个无限循环

@Override public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}
使用场景

IntentService。
自定义 Handler,将每一个 Intent 都传递给 onHandleIntent() 方法,且在处理完后执行停止服务的操作。

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

onCreate() 方法创建 Handler 和 HandlerThread。

    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

onStart() 方法将 intent 组装进消息中发送。

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

线程池

具体实现 ThreadPoolExecutor
  • corePoolSize:核心线程数,核心线程会在线程池中一直存活
  • maximumPoolSize:线程池所能容纳的最大线程数,超过后后续任务会被阻塞
  • keepAliveTime:线程超时时间,线程闲置超过时长则回收。默认只对非核心线程作用,allowCoreThreadTimeOut=true 时对核心线程池也生效。
  • workQueue:线程池的任务队列
  • threadFactory:用于构建新线程的工厂类。
FixedThreadPool

线程数量固定的线程池。
因核心线程无超时机制,响应速度快。

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
CachedThread

只有非核心线程,线程数可任意大,超时时间为 60 s。
适合执行大量的耗时较少的任务,闲置时暂用系统资源极少。

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
ScheduleThreadPool

固定核心线程数,非核心线程数不限制,且非核心线程闲置后立即回收。
主要用于执行定时任务和具有固定周期的重复任务。

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
          10L, MILLISECONDS,
          new DelayedWorkQueue());
}
SingleThreadExecutor

只有一个核心线程,统一所有任务到一个线程。

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

AsyncTask 异步任务助手类

抽象泛型类

AsyncTask 是一个抽象泛型类,其类声明如下:

public abstract class AsyncTask<Params,Progress,Result>
重写方法
  • onPreExecute():主线程执行,执行任务前的准备工作。
  • doInBackground(params):线程池中执行,执行异步方法。可在此调用 publishProgress() 方法更新进度。
  • onProgressUpdate(values):主线程执行,更新进度。
  • onPostExecute(result):主线程执行,异步任务执行后的回调。
原理

双线程池 + InternalHandler。

线程池 1:SERIAL_EXECUTOR

用于排队的线程池。保证任务串行执行。

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
线程池 2:THREAD_POOL_EXECUTOR

负责具体任务的执行。线程池的核心线程数为 Math.max(2, Math.min(CPU_COUNT - 1, 4)),线程总数为 CPU_COUNT * 2 + 1,闲置超时时间为 60 s。

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
InternalHandler

因 InternalHandler 负责将执行环境切换至主线程,故 sHandler 必须在主线程创建,所以 AsyncTask 类必须在主线程中加载(sHandler 为静态成员,会在类加载时初始化)。

    private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }