美文网首页Android进阶多线程
多线程(四)、Android多线程使用及AsyncTask源码分

多线程(四)、Android多线程使用及AsyncTask源码分

作者: EvanZch | 来源:发表于2019-12-18 22:04 被阅读0次

    本篇是多线程系列的第四篇,如果对前三篇感兴趣的也可以去看看。

    多线程(一)、基础概念及notify()和wait()的使用

    多线程(二)、内置锁 synchronized

    多线程(三)、线程池 ThreadPoolExecutor 知识点总结

    除了前面的线程池的使用外,在Android中,我们除了通过Thread创建线程外,还可以通过 AsyncTaskIntentServiceHandleThread 来创建,线程池前面一篇已经详细介绍了,下面对其他几个方法简单的介绍。

    1.1、HandleThread

    1.1.1、源码
    public class HandlerThread extends Thread {
        int mPriority;
        int mTid = -1;
        Looper mLooper;
    
        public HandlerThread(String name) {
            super(name);
            mPriority = Process.THREAD_PRIORITY_DEFAULT;
        }
    
        public HandlerThread(String name, int priority) {
            super(name);
            mPriority = priority;
        }
    
        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;
            }
    
            synchronized (this) {
                while (isAlive() && mLooper == null) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                    }
                }
            }
            return mLooper;
        }
    
        public boolean quit() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quit();
                return true;
            }
            return false;
        }
    
        public boolean quitSafely() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quitSafely();
                return true;
            }
            return false;
        }
    
        public int getThreadId() {
            return mTid;
        }
    }
    
    

    可以看到 HandlerThread 继承的是 Thread ,其在内部自己实现了 Looper,可以单独发送和接收消息,可以实现ui线程到子线程通信和子线程到子线程的通信。

    1.1.2、使用

    1、创建HandlerThread 并启动

            // 创建 HandlerThread 实例对象,参数为自定义线程名字,作为标记
            handlerThread = new HandlerThread("HandlerThreadTest");
            // 启动 HandlerThread
            handlerThread.start();
    

    2、创建工作线程,并复写 handleMessage 方法

            // 创建工作线程,复写 handleMessage 方法
            workHandler = new Handler(handlerThread.getLooper()) {
                @Override
                public void handleMessage(@NonNull Message msg) {
                    super.handleMessage(msg);
                    boolean isMain = Looper.myLooper() == Looper.getMainLooper();
                    System.out.println("收到消息:what=" + msg.what + ",message=" + (String) msg.obj);
                    System.out.println("IsMainThread=" + isMain + ",currentThread:" + Thread.currentThread());
                }
            };
    

    3、发送消息 (这里模拟在子线程中发送消息)

        new Thread(new Runnable() {
                        @Override
                        public void run() {
                            // 使用工作线程向工作线程发送数据。
                            Message msg = Message.obtain();
                            msg.what = 1;
                            msg.obj = "测试数据";
                            workHandler.sendMessage(msg);
                            boolean isMain = Looper.myLooper() == Looper.getMainLooper();
                            System.out.println("IsMainThread=" + isMain + ",currentThread:" + Thread.currentThread());
    
                        }
                    }).start();
    

    4、释放

     // 释放
     mHandlerThread.quitSafely();
    

    运行结果:

    image

    可以看到上面的示例实现了在两个子线程之间数据传送。

    如果我们将发送消息放在ui线程,即实现了ui线程到子线程的数据通信。

    1.2、IntentService

    1.2.1、源码

    同样的,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((Intent)msg.obj);
                stopSelf(msg.arg1);
            }
        }
    
        public IntentService(String name) {
            super();
            mName = name;
        }
    
        public void setIntentRedelivery(boolean enabled) {
            mRedelivery = enabled;
        }
    
        @Override
        public void onCreate() {
            // TODO: It would be nice to have an option to hold a partial wakelock
            // during processing, and to have a static startService(Context, Intent)
            // method that would launch the service & hand off a wakelock.
    
            super.onCreate();
            HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
            thread.start();
    
            mServiceLooper = thread.getLooper();
            mServiceHandler = new ServiceHandler(mServiceLooper);
        }
    
        @Override
        public void onStart(Intent intent, int startId) {
            Message msg = mServiceHandler.obtainMessage();
            msg.arg1 = startId;
            msg.obj = intent;
            mServiceHandler.sendMessage(msg);
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            onStart(intent, startId);
            return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
        }
    
        @Override
        public void onDestroy() {
            mServiceLooper.quit();
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
        @WorkerThread
        protected abstract void onHandleIntent(Intent intent);
    }
    
    

    通过源码,可以看到 IntentService 是一个抽象类,还有一个抽象方法 onHandleIntent,继承至 Service

    1.2.2、使用

    新建一个MyIntentService继承至 IntentService

    public class MyIntentService extends IntentService {
    
    
        private static final String TAG = "MyIntentService";
    
        public MyIntentService() {
            super("IntentService");
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.i(TAG, "onCreate: ");
        }
    
        @Override
        public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    
            Log.i(TAG, "onStartCommand: ");
            return super.onStartCommand(intent, flags, startId);
        }
    
    
        @Override
        protected void onHandleIntent(@Nullable Intent intent) {
            boolean isMainThread = Looper.getMainLooper() == Looper.myLooper();
    
            Log.i(TAG, "onHandleIntent: isMainThread=" + isMainThread);
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            Log.i(TAG, "onHandleIntent: 任务结束");
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.i(TAG, "onDestroy");
        }
    }
    

    2、跟Service一样,需要在 AndroidManifest.xml 中进行注册

     <service android:name=".MyIntentService">
    

    3、启动服务

    startService(new Intent(this, MyIntentService.class));
    

    运行结果:

    image

    可以看到在MyIntentServiceonHandleIntent 默认给我们开启了一个子线程来执行耗时操作,且当任务执行结束后自动停止服务,和Service的区别是 使用Service 可以同时执行多个请求,而使用IntentService 只能同时执行一个请求。

    1.3、AsyncTask

    AsyncTask 也是我们在Android中使用较为频繁的异步通信方法,我们可以通过 AsyncTask轻松的实现子线程到ui线程的通信,保证了线程安全,减少了我们通过 Thread + Handler 这种复杂的组合方法来实现,而且AsyncTask实现原理也是通过线程池,所以也具有上面线程池的好处。

    1.3.1、使用
    public abstract class AsyncTask<Params, Progress, Result>{
        // ......
    }
    

    首先可以看到 AsyncTask是一个抽象类,所以,我们在使用的时候需要创建自己的类来实现它,还要实现里面唯一的一个抽象方法。

    protected abstract Result doInBackground(Params... params);
    

    可以看到抽象方法有三个泛型参数,分别是.

    Params : 开始异步任务执行时传入的参数类型,与 doInBackground()参数和执行时调用 excute() 参数类型一致。

    Progress : 异步任务执行过程中,返回下载进度值的类型,与 onProgressUpdate() 参数类型一致。

    Result : 异步任务执行完成后,返回的结果类型,与doInBackground()的返回值类型还有 onPostExecute() 参数类型一致

    使用 AsyncTask 时,除了必须实现的抽象方法外,一般还有几个方法比较重要,可以根据我们的需求进行重写

    • onPreExecute:主线程,执行线程任务前自动调用,通过在这个方法里面进行相应的初始化操作。
    • doInBackground:子线程,必须实现的抽象方法,方法里面为工作线程,可以进行耗时操作。
    • onProgressUpdate:主线程,任务执行时候,获取进度,通过在 doInBackground 方法中调用 publishProgress 来触发
    • onPostExecute :主线程,当任务执行结束后自动调用,可进行UI更新。

    简单示例:

    // 新建 MyAsyncTask 实现 抽象类 AsyncTask 
    // Params:Integer
    // Progress:String
    // Result:Double
    public  class MyAsyncTask extends android.os.AsyncTask<Integer, String, Double> {
            @Override
            protected void onPreExecute() {
                super.onPreExecute();
                System.out.println(TAG + "--onPreExecute");
            }
            
            // 这里的参数类型为我们自己设置的Params 参数类型
            @Override
            protected Double doInBackground(Integer... integers) {
                // 模拟耗时操作
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(TAG + "--doInBackground  length=" + integers.length);
    
                for (Integer i : integers) {
                    System.out.println(TAG + "--doInBackground  data=" + i);
                    // 在 doInBackground 调用 publishProgress 触发 onProgressUpdate
                    publishProgress(i + "—更新后的值");
                }
                // 这里模拟返回结果,参数类型为我们设置的 Double
                return 2.2;
            }
    
            // 这里的参数类型为我们自己设置的String类型 (Progress)
            @Override
            protected void onProgressUpdate(String... values) {
                super.onProgressUpdate(values);
                for (String data : values) {
                    System.out.println(TAG + "--onProgressUpdate  date=" + data);
                }
            }
    
            // 任务执行结束后自动触发,参数类型为我们自己设置的Result 参数类型
            @Override
            protected void onPostExecute(Double aDouble) {
                super.onPostExecute(aDouble);
                System.out.println(TAG + "--onPostExecute  aDouble=" + aDouble);
            }
        }
    

    调用 MyAsyncTask :

    通过 execute 方法调用,需要在主线程里面执行,传入参数需要和 doInBackground 参数一致,及 Params 设置的类型。

    只能调用一次,如果调用两次 execute 方法,会抛异常

    Caused by: java.lang.IllegalStateException: Cannot execute task: the task is already running.

     MyAsyncTask myAsyncTask = new MyAsyncTask();
     myAsyncTask.execute(100,101,102);
    

    结果:

    image
    1.3.2、源码分析

    我们深入 AsyncTask 大致看看它是怎么实现的,首先从 构造方法入手

        public AsyncTask(@Nullable Looper callbackLooper) {
            mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
                ? getMainHandler()
                : new Handler(callbackLooper);
            // 通过Callable创建一个任务
            mWorker = new WorkerRunnable<Params, Result>() {
                public Result call() throws Exception {
                    mTaskInvoked.set(true);
                    Result result = null;
                    try {
                        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                        // 从 doInBackground 中获取
                        result = doInBackground(mParams);
                        Binder.flushPendingCommands();
                    } catch (Throwable tr) {
                        mCancelled.set(true);
                        throw tr;
                    } finally {
                        // 任务执行结束,将结果 发送到
                        postResult(result);
                    }
                    // 返回结果
                    return result;
                }
            }; 
            // 将任务传给 FutureTask ,复写其 done 方法
            mFuture = new FutureTask<Result>(mWorker) {
                @Override
                protected void done() {
                    try {
                        postResultIfNotInvoked(get());
                    } catch (InterruptedException e) {
                        android.util.Log.w(LOG_TAG, e);
                    } catch (ExecutionException e) {
                        throw new RuntimeException("An error occurred while executing doInBackground()",
                                e.getCause());
                    } catch (CancellationException e) {
                        postResultIfNotInvoked(null);
                    }
                }
            };
        }
    
    
    // WorkerRunnable
    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
            Params[] mParams;
    }
    

    我们看到构造方法中,先创建了一个Callable的任务,在 多线程(一)、基础概念及notify()和wait()的使用 我们介绍了通过 Callable 来创建线程,可以在线程结束后获取结果,我们也看到在 任务里面通过 result = doInBackground(mParams) 获取到结果。任务创建后,将其放入到 FutureTask 中,这是 Callable 线程创建的标准用法。

    看完构造方法,我们再来看调用 execute 方法执行的时候,它又做了啥。

        @MainThread
        public final AsyncTask<Params, Progress, Result> execute(Params... params) {
            return executeOnExecutor(sDefaultExecutor, params);
        }
    

    这里没啥信息,直接调用了executeOnExecutor 方法,传入了 sDefaultExecutor 和我们设置的参数,再看 executeOnExecutor 方法:

    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {
            if (mStatus != Status.PENDING) {
                switch (mStatus) {
                    case RUNNING:
                        throw new IllegalStateException("Cannot execute task:"
                                + " the task is already running.");
                    case FINISHED:
                        throw new IllegalStateException("Cannot execute task:"
                                + " the task has already been executed "
                                + "(a task can be executed only once)");
                }
            }
    
            mStatus = Status.RUNNING;
            // 调用了 onPreExecute 方法
            onPreExecute();
    
            mWorker.mParams = params;
            // 执行了我们在构造方法里面创建的mFuture
            exec.execute(mFuture);
    
            return this;
        }
    

    这里面我们还是看到很有用的信息,首先调用了 onPreExecute 方法,这也是我们在执行 AsyncTask 方法的时候,调用的第一个方法,用来进行一些初始化操作,我们的任务通过 exec.execute(mFuture) 进行执行,看看我们传入的 sDefaultExecutor 是个啥。

    image

    最终创建了 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);
                }
            }
        }
    

    内部声明了一个双端队列ArrayDeque类型的mTasks(双端队列中offer方法表示从队列尾插入,poll()表示从队列头获取元素)。

    这里通过 mTasks.offer 将我们的任务不断的放入阻塞队列中,再通过 mTasks.poll() 拿出任务,最终通过 THREAD_POOL_EXECUTOR 来执行。

    再看 THREAD_POOL_EXECUTOR 又是啥?

    static {
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                    new SynchronousQueue<Runnable>(), sThreadFactory);
            threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
            THREAD_POOL_EXECUTOR = threadPoolExecutor;
     }
    

    我们前面说了,AsyncTask 内部是通过线程池来执行的,看这里应该就清楚了,任务的执行,实质是通过创建的 threadPoolExecutor线程池,来执行任务。按照前面的老规矩,看看具体参数。

    参数说明:

        private static final int CORE_POOL_SIZE = 1;
        private static final int MAXIMUM_POOL_SIZE = 20;
        private static final int KEEP_ALIVE_SECONDS = 3;
    

    int corePoolSize: 1

    int maximumPoolSize: 20

    long keepAliveTime:3

    TimeUnit unit:TimeUnit.SECONDS

    BlockingQueue<Runnable> workQueue:new SynchronousQueue<Runnable>()

    1个核心线程,非核心线程数目为20,使用 SynchronousQueue 阻塞队列。

    这里源码是查看的API 29 ,不同的版本有区别。

    看到了任务的执行,那么AsyncTask是怎么实现子线程到主线程的数据通信的呢?

    image

    postResult:

        private Result postResult(Result result) {
            @SuppressWarnings("unchecked")
            Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                    new AsyncTaskResult<Result>(this, result));
            message.sendToTarget();
            return result;
        }
    

    前面构造中,我们看到通过 doInBackground 返回的结果,通过 postResult 方法传递出去了,而在 postResult 里面就是通过Handler,来将数据从子线程传到了主线程。

        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;
                }
            }
        }
    

    我们再看看publishProgress

    protected final void publishProgress(Progress... values) {
            if (!isCancelled()) {
                getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                        new AsyncTaskResult<Progress>(this, values)).sendToTarget();
            }
        }
    

    其本质,也是通过 Handler 来实现子线程到主线程的数据通信。

    总结:所以,AsyncTask 本质是通过线程池来执行任务,并且封装了 Handler 来实现子线程到ui线程的数据传输。

    优点:AsyncTask 是一个轻量级的异步任务处理类,轻量级体现在,使用方便、代码简洁上,而且整个异步任务的过程可以通过cancel()进行控制。

    缺点:不适用于处理长时间的异步任务,一般这个异步任务的过程最好控制在几秒以内,如果是长时间的异步任务就需要考虑多线程的控制问题;当处理多个异步任务时,UI更新变得困难。

    二、总结

    多线程部分一共四篇,这篇收了个尾,后面也会继续对Android其他知识点进行总结,Android进阶系列也会一直写下去,如果文中有错误的地方,欢迎大佬们批评指点。

    相关文章

      网友评论

        本文标题:多线程(四)、Android多线程使用及AsyncTask源码分

        本文链接:https://www.haomeiwen.com/subject/irrpnctx.html