Android 多线程实现方式

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/Kennethdroid/article/details/90179697

一个优秀的程序员,必须具备两种能力:学习能力,时间管理能力

该原创文章首发于微信公众号“字节流动”

Android 多线程实现方式

通常来说,一个应用至少有一个进程,而一个进程至少有一个线程。

线程是 CPU 调度的基本单位,进程是系统资源分配的基本单位。

进程独享内存资源,一个进程可以看作一个 JVM ,一个进程崩溃后,在保护模式下一般不会对其它进程产生影响。
同一个进程中的线程共享内存资源,一个线程死掉就导致整个进程死掉。

Android 提供了四种常用的多线程实现方式:

  • AsyncTask
  • 异步消息机制
  • IntentService
  • ThreadPoolExcutor

AsyncTask

我们的老朋友 AsyncTask 类,它是封装好的线程池,操作 UI 线程极其方便。

瞅一眼,AsyncTask 的三个泛型参数:

public abstract class AsyncTask<Params, Progress, Result>

  • params传入参数类型,即 doInBackground() 方法中的参数类型;
  • Progress ,异步任务执行过程中返回的任务执行进度类型,即 publishProgress() 和onProgressUpdate() 方法中传入的参数类型;
  • Result ,异步任务执行完返回的结果类型,即 doInBackground() 方法中返回值的类型。

四个回调方法:

  • onPreExecute(),在主线程执行,做一些准备工作。
  • doInBackground(),在线程池中执行,该方法是抽象方法,在此方法中可以调用 publishProgress() 更新任务进度。
  • onProgressUpdate(),在主线程中执行,在 publishProgress() 调用之后被回调,展示任务进度。
  • onPostExecute(),在主线程中执行,异步任务结束后,回调此方法,处理返回结果。

注意

  • 当 AsyncTask 任务被取消时,回调 onCanceled(obj) ,此时 onPostExecute(),不会被调用,AsyncTask 中的 cancel() 方法并不是真正去取消任务,只是设置这个任务为取消状态,需要在 doInBackground() 中通过 isCancelled() 判断终止任务。
  • AsyncTask 必须在主线程中创建实例,execute() 方法也必须在主线程中调用。
  • 每个 AsyncTask 实例只能执行一次 execute() ,多次执行会报错,如需执行多次,则需创建多个实例。
  • Android 3.0 之后, AsyncTask 对象默认执行多任务是串行执行,即 mAsyncTask.execute() ,并发执行的话需要使用 executeOnExecutor() 。
  • AsyncTask 用的是线程池机制和异步消息机制(基于 ThreadPoolExecutor 和 Handler )。Android 2.3 以前,AsyncTask 线程池容量是 128 ,全局线程池只有 5 个工作线程,如果运用 AsyncTask 对象来执行多个并发异步任务,那么同一时间最多只能有 5 个线程同时运行,其他线程将被阻塞。Android 3.0 之后 Google 又进行了调整,新增接口 executeOnExecutor() ,允许自定义线程池(那么核心线程数以及线程容量也可自定义),并提供了 SERIAL_EXECUTOR 和 THREAD_POOL_EXECUTOR 预定义线程池。后来 Google 又做了一些调整(任何事物都不完美),将线程池的容量与 CPU 的核心数联系起来,如目前 SDK 25 版本中,预定义的核心线程数量最少有 2 个,最多 4 个,线程池容量范围 5 ~ 9 。改动如下:
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE_SECONDS = 30;

异步消息机制

异步消息机制的三大主角: Handler ,Message 和 Looper 。
Looper 负责创建 MessageQueue 消息对列,然后进入一个无限 for 循环中,不断地从消息队列中取消息,如果消息队列为空,当前线程阻塞,Handler 负责向消息队列中发送消息。

Looper

Looper 有两个重要的方法: prepare() 和 loop()。

  • prepare() , Looper 与当前线程绑定,一个线程只能有一个 Looper 实例和一个 MessageQueue 实例。
public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(true)); 保证 Looper 对象在当前线程唯一
}

// Looper 的构造方法
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mRun = true;
        mThread = Thread.currentThread();
}
  • loop ,进入一个无限 for 循环体中,不断地从消息队列中取消息,然后交给消息的 target 属性的 dispatchMessage 方法去处理。
public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();


        // 无限循环体,有没有想过在 UI 线程里,有这样一个死循环,为什么界面没卡死??
        // 答案最后揭晓。
        for (;;) { 
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            msg.target.dispatchMessage(msg);

            msg.recycle();
        }
}

Handler

Handler 负责向消息队列中发送消息。
在 Activity 中我们直接可以 new Handler ,那是因为在 Activity 的启动代码中,已经在当前 UI 线程中调用了 Looper.prepare() 和 Looper.loop() 方法。

在子线程中 new Handler 必须要在当前线程(子线程)中创建好 Looper 对象和消息队列,代码如下

    //在子线程中

    Looper.prepare();

    handler = new Handler() {

	    public void handleMessage(Message msg) {
		    //处理消息
	    };
    };

    Looper.loop();   

之后,你拿着这个 Handler 对象就可以在其他线程中,往这个子线程的消息队列中发消息了。

HandlerThread

HandlerThread 可以看作在子线程中创建一个异步消息处理机制的简化版,HandlerThread 对象自动帮我们在工作线程里创建 Looper 对象和消息队列。

使用方法:

mHandlerThread = new HandlerThread("MyHandlerThread");
mHandlerThread.start();

mHandler = new Handler(mHandlerThread.getLooper()){
   
    @Override
    public void handleMessage(Message msg) {
        //处理消息
    }
};

之后你就可以使用 Handler 对象往工作线程中的消息队列中发消息了。

看一下源码片段:

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    protected void onLooperPrepared() {
    }

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

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
}

注意:handler 在 UI 线程中初始化的,looper 在一个子线程中执行,我们必须等 mLooper 创建完成之后,才能调用 getLooper ,源码中是通过 wait 和 notify 解决两个线程的同步问题。

IntentService

IntentService 可以看成是 Service 和 HandlerThread 的合体。它继承自 Service ,并可以处理异步请求,其内部有一个 WorkerThread 来处理异步任务,当任务执行完毕后,IntentService 自动停止。

如果多次启动 IntentService 呢? 看到 HandlerThread ,你就应该想到多次启动 IntentService ,就是将多个异步任务放到任务队列里面,然后在 onHandlerIntent 回调方法中串行执行,执行完毕后自动结束。

下面对源码进行简单的解析,IntentService 源码:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            //onHandleIntent 方法在工作线程中执行,执行完调用 stopSelf() 结束服务。
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public IntentService(String name) {
        super();
        mName = name;
    }

    /**
     * enabled == true 时,如果任务没有执行完,当前进程就死掉了,那么系统就会令当前进程重启。
     * 任务会被重新执行。
     */
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }
    @Override
    public void onCreate() {
        super.onCreate();

        // 上面已经讲过,HandlerThread 对象 start 之后,会在工作线程里创建消息队列 和 Looper 对象。
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
			
        mServiceLooper = thread.getLooper();
        // 获得 Looper 对象初始化 Handler 对象。
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        // IntentService 每次启动都会往工作线程消息队列中添加消息,不会创建新的线程。
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    // 官方建议 IntentService onStartCommand 方法不应该被重写,注意该方法会调用 onStart 。
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
    @Override
    public void onDestroy() {  
        //服务停止会清除消息队列中的消息,除了当前执行的任务外,后续的任务不会被执行。
        mServiceLooper.quit();
    }
    /**
     * 不建议通过 bind 启动 IntentService ,如果通过 bind 启动 IntentService ,那么 onHandlerIntent 方法不会被回调。Activity 与 IntentService 之间的通信一般采用广播的方式。
     */
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }
    /**
     * 子类必须要实现,执行具体的异步任务逻辑,由 IntentService 自动回调。
     */
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

IntentService 源码很容易理解,你也可以就自己的应用场景封装自己的 IntentService 。

场景

  • 正常情况下,启动 IntentService ,任务完成,服务停止;
  • 异步任务完成前,停止 IntentService ,服务停止,但任务还会执行完成,完成后,工作线程结束;
  • 多次启动 IntentService ,任务会被一次串行执行,执行结束后,服务停止;
  • 多次启动 IntentService ,在所有任务执行结束之前,停止 IntentService ,服务停止,除了当前执行的任务外,后续的任务不会被执行;

ThreadPoolExcutor

图片来自 Jakob Jenkov 博客

ThreadPool

用来管理一组工作线程,任务队列( BlockingQueue )中持有的任务等待着被线程池中的空闲线程执行。

常用构造方法:

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
    int corePoolSize,
    int maximumPoolSize,
    long keepAliveTime,
    TimeUnit unit,
    BlockingQueue<Runnable> workQueue
);

  1. corePoolSize 核心线程池容量,即线程池中所维持线程的最低数量。corePoolSize 初始值为 0 ,当有新任务加入到任务队列中,新的线程将被创建,这个时候即使线程池中存在空闲线程,只要当前线程数小于 corePoolSize ,那么新的线程依然被创建。
  2. maximumPoolSize 线程池中所维持线程的最大数量。
  3. keepAliveTime 空闲线程在没有新任务到来时的存活时间。
  4. unit 参数 keepAliveTime 的时间单位。
  5. workQueue 任务队列,必须是 BlockingQueue 。

简单使用

创建 ThreadFactory ,当然也可以自定义。
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };
创建 ThreadPoolExecutor 。
// 根据 CPU 核心数确定线程池容量。
public static final int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors(); 

mThreadPoolExecutor = new ThreadPoolExecutor(
        NUMBER_OF_CORES * 2, 
        NUMBER_OF_CORES * 2 + 1,
        60L,
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<Runnable>(),
        backgroundPriorityThreadFactory
);
执行
mThreadPoolExecutor.execute(new Runnable() { 
    
    @Override  
    public void run() {  
         //do something  
    } 

});

Future future = mThreadPoolExecutor.submit(new Runnable() { 
    
    @Override  
    public void run() {  
         //do something  
    } 

});

//任务可取消
future.cancel(true);

Future<Integer> futureInt = mThreadPoolExecutor.submit(new Callable<Integer>() {
    @override
    public Integer call() throws Exception {
        return 0;
    }

});

//获取执行结果
futureInt.get();

FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>(){
    @override
    public Integer call() throws Exception {
        return 0;
    }    

});

mThreadPoolExecutor.submit(task);
task.get();

联系与交流

微信公众号
我的公众号
个人微信
我的微信

猜你喜欢

转载自blog.csdn.net/Kennethdroid/article/details/90179697