四. 线程管理之Android中的多线程

栏目: Android · 发布时间: 5年前

内容简介:本文概要:在阅读本文之前,需要对Handler消息机制有所了解才能深入理解,如果对Handler了解还不够深入,可以先阅读这篇文章-Android提供的一个异步类,它封装了handler和线程池,从而简化了更新UI的问题.

本文概要:

  1. AsyncTask
  2. HandlerThread与IntentService

在阅读本文之前,需要对Handler消息机制有所了解才能深入理解,如果对Handler了解还不够深入,可以先阅读这篇文章- Android消息机制Handler

一. AsyncTask

Android提供的一个异步类,它封装了handler和线程池,从而简化了更新UI的问题.

1.1基本使用

AsyncTask是一个抽象的泛型类提供三个泛型参数分别为 Params , Progress, Result.

  • Params : 输入的参数类型
  • Progress : 执行任务的进度
  • Result : 返回结果的类型
public abstract class AsyncTask<Params, Progress, Result> 
复制代码

提供了4个核心方法:

  1. onPreExecute:主线程中执行,任务开始前.

  2. Result doInBackground(Params... params): 子线程执行,执行任务时.

    • 当我们调用publishProgress(Progress... values)更新任务进度时,会回调onProgressUpdate方法.
    • 返回计算结果Result给onPostExecute方法.
  3. onProgressUpdate(Progress... values): 主线程中执行,任务的执行进度更新时.

  4. onPostExecute(Result result):主线程中执行,任务执行完毕时.

使用示例

public class AsyncTaskActivity extends AppCompatActivity implements View.OnClickListener {
    //...省略部分代码

    class MyAsyncTask extends AsyncTask<String, Integer, Robot> {


        @Override
        protected void onPreExecute() {
            mProgressDialog = new ProgressDialog(AsyncTaskActivity.this);
            mProgressDialog.setMessage("正在加载");
            mProgressDialog.setMax(10);
            mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            mProgressDialog.show();
        }

        @Override
        protected Robot doInBackground(String... strings) {

            Robot robot = null;
            if (strings != null && strings.length > 0) {
                for (int i = 0; i < 11; i++) {
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    publishProgress(i);
                    if (isCancelled()) {
                        break;
                    } else if (i == 10) {{
                        robot = new Robot("i", strings[0]);
                    }
                }
            } else {
                throw new IllegalArgumentException("please set the params");
            }

            return robot;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            if (values != null && values.length > 0) {
                mProgressDialog.setProgress(values[0]);
            }
        }

        @Override
        protected void onPostExecute(Robot robot) {
            mProgressDialog.dismiss();
            tvName.setText(robot == null ? "参数不详" : robot.getName());
        }


        @Override
        protected void onCancelled() {
            tvName.setText("任务被取消");
        }
    }

}

复制代码

首先在onPreExecute初始化了ProgressDialog控件,接着通过doInBackground模拟执行耗时的机器人构造流程.在构造流程中调用了publishProgress去更新执行任务的进度.最后当任务执行完后在onPostExecute更新了机器人名,这时如果点击了取消任务,那么onCancelled则会调用.

关于AsyncTask的使用比较简单就到这里点到为止.我们具体来看它实现的原理.

1.2基本原理

源码版本基于Android 8.0

1. 我们首先从AsyncTask的构造开始,看具体做了哪些操作.

对应源码

public AsyncTask() {
        this((Looper) null);
}

public AsyncTask(@Nullable Looper callbackLooper) {
        //1.初始化Handler
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        //2.初始化WorkerRunnable,实际是一个实现Callable接口的类.
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                //...省略具体实现代码,后面再分析.
                return result;
            }
        };

        //3.初始化FutureTask封装了WorkerRunnable.
        //在run方法中调用了mWorker的Call方法.
        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                //...省略具体实现代码,后面再分析.
            }
        };
    }
复制代码

这里主要干了三个初始化工作.

  1. 初始化Handler,绑定主线程的Looper.
  2. 初始化WorkerRunnable,实际是一个实现Callable接口的类.
    • (如果对Callable还比较陌生,可以看前面的文章介绍.)
  3. 初始化FutureTask封装了WorkerRunnable.
    • 这里简单复习下FutureTask,它既实现了Runnable接口,又实现了Future接口.所以它既可以实现执行线程,又可以获取线程执行完后的返回值.(如果对FutureTask还比较陌生,可以看前面的文章介绍.)

2. 当我们实例化一个AsyncTask后,就会调用execute方法执行任务.接着看源码.

对应源码

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

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;//将输入参数封装到WorkRunnable
        exec.execute(mFuture);//执行任务.
        
        return this;
}
复制代码

在excute方法中调用了executeOnExecutor方法,对这个流程做个小结.

  1. 检查状态.
  2. FutureTask持有一个WorkRunnable,WorkRunnable持有传入的参数.
  3. 最后利用线程池,去执行futureTask任务,这里可以将FutureTask看成相当于Runnable的作用.

3. 线程池的处理流程.

在executeOnExecutor(sDefaultExecutor, params)方法中用到了sDefaultExecutor线程池.我们接着看线程池的实现.

对应源码

/**
     * 用于任务的排队的线程池
     */
    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            //1. 插入到任务队列
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();//调用futureTask的run方法
                    } finally {//任务执行完毕,继续执行下一个任务.
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {//无任务,执行下一个.
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {//不断从队列中取任务
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
    
     /**
     * 执行任务的线程池THREAD_POOL_EXECUTOR
     */
    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;
     private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);
    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }
    
复制代码

这里涉及到两个线程池,也是AsyncTask的核心之处.

  • SerialExecutor : 用于任务的排队.
    1. 插入到任务队列.
    2. 无任务或者任务执行完后,不断从队列中取新任务到另一线程池中执行.
  • threadPoolExecutor : 用于真正执行任务.
    • CORE_POOL_SIZE :核心线程数与CPU核有关
    • MAXIMUM_POOL_SIZE : 最大线程数与CPU核有关
    • KEEP_ALIVE_SECONDS, TimeUnit.SECONDS :超时机制为30秒
      • 超时机制也作用于核心线程.
    • sPoolWorkQueue : 阻塞队列

正因为SerialExecutor的存在,从上面可以看出3.0之后是串行执行,所以不会有并发问题(执行饱和策略).

4. 通过Call方法最后我们来看执行任务后是如何回调给主线程的.

在futureTask的run方法会回调WorkerRunnable的call方法.这里再回顾一下上一篇FutureTask的run方法源码.

对应源码

public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    //回调callable的Call方法,获取异步任务返回值.
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            //...省略部分代码
        }
    }
复制代码

这里继续回到最开始初始化WorkerRunnable时代码.

mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
    
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    /**
                      * 1.回调doInBackground方法 
                      */
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    
                    postResult(result);
                }
                return result;
            }
        };

      
     /**
      * 2.通过handler将result传递出去 
      */
     private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }
    
   
    /**
      * 3. 处理消息
      */
    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
                    //调用finish
                    result.mTask.finish(result.mData[0]);
                    break;
                //...省略部分代码
            }
        }
    }
    
    
    private void finish(Result result) {
        if (isCancelled()) {
            //回调已取消
            onCancelled(result);
        } else {
            //回调onPostExecute
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
    
复制代码

Call方法流程小结.

  1. 回调doInBackground方法.
  2. 发送消息,通过handler将result传递出去.
  3. 处理消息,回调onCancelled或onPostExecute.

最后给出一张简略版的AsyncTask的工作流程图

四. 线程管理之Android中的多线程

asyntask工作流程简略图

到这里AsyncTask的实现原理基本分析完成了,至于之前提到的3.0之前是并行的,3.0之后是串行的,如果想要实现并行可以采用如下方式.

//方式一
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,params);

//方式二
executeOnExecutor(自定义的线程池,params);
复制代码

对于3.0以下是并行的效果,了解即可.这里就不演示了.因为现在APP基本上5.0以下都不适配了,我们又何必螳臂当车,节约宝贵的时间看未来趋势的知识.

二. HandlerThread与IntentService

2.1 HandlerThread

是一个消息循环的线程,这样就可以在该线程中使用Handler了.

对应源码

@Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();//创建消息队列
        synchronized (this) {
            mLooper = Looper.myLooper();
            //Looper初始化完成,唤醒因此条件阻塞的线程
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();//开启消息循环
        mTid = -1;
    }
    
    
public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        //线程已经启动,等待looper初始化完成
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
    
复制代码

与普通线程相比,它的特点.

  • 普通线程是在run方法执行耗时任务
  • 它可以在此线程中使用handler来执行耗时任务,由于是无限循环,不再使用时,也需调用quit或者quitSafely来终止线程的执行.
  • 具体使用场景见IntentService。

对应源码

2.2 IntentService

是一个Service,封装了handlerThread与Handler.

正因为它是一个Service,所以不会容易被系统杀死.具有以下特点.

特点:

  • 相对于服务,能够在服务中执行耗时任务.
  • 相对于线程,优先级比单纯的线程高.
  • 执行完后会自动停止.

基本使用

public class MyIntentService extends IntentService {
    private static final String TAG = "MyIntentService";

    public MyIntentService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        String taskName = intent.getStringExtra("taskName");
        Log.d(TAG, "taskName: " + taskName);
        SystemClock.sleep(2500);
        if ("org.jason.taskOne".equals(taskName)){
            Log.d(TAG, "do task: "+taskName);
        }

    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy: ");
        super.onDestroy();
    }
}

private void doIntentService() {
        //连续开三个服务测试
        Intent intent = new Intent(this, MyIntentService.class);
        intent.putExtra("taskName", "org.jason.taskOne");
        startService(intent);
        intent.putExtra("taskName", "org.jason.taskTw0");
        startService(intent);
        intent.putExtra("taskName", "org.jason.taskThree");
        startService(intent);
}

//调用输出
12-27 14:34:01.338 D/MyIntentService: taskName: org.jason.taskOne
12-27 14:34:03.839 D/MyIntentService: do task: org.jason.taskOne
12-27 14:34:03.840 D/MyIntentService: taskName: org.jason.taskTw0
12-27 14:34:06.341 D/MyIntentService: taskName: org.jason.taskThree
12-27 14:34:08.841 D/MyIntentService: onDestroy: 

复制代码

从上面日志除了可以看出它的特点外,还能发现任务都是按顺序依次执行的.这与它内部的hanlder处理消息有关,因为handler的looper就是按顺序处理消息的,接着我们去看是如何实现的.

基本原理

IntentService是一个继承Service的抽象类.既然是Service我们就按照Service的生命周期来分析.

1. 首先看OnCreate方法流程

对应源码

public abstract class IntentService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        //1.初始化一个HandlerThread
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        //2.初始化一个Handler,绑定HandlerThread的Looper.这样就能使用handler,给HandlerThread线程发消息了.
        //(也就是说绑定了在哪个线程的looper,那么发送的消息就在哪个线程处理)
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
}    
复制代码

2. 接着OnStart方法流程

onStartCommand里面调用了onStart方法,这里直接看此方法.

对应源码

@Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        //1.利用handler发送消息,消息内容就是我们传入的intent以及服务id标识.
        mServiceHandler.sendMessage(msg);
    }
    
        
 private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        
        //2.处理消息,
        //注意:此handler绑定了HandlerThread的looper,所以是在子线程处理消息.
        @Override
        public void handleMessage(Message msg) {
            //a.回调onHandleIntent
            onHandleIntent((Intent)msg.obj);
            //b.停止服务
            stopSelf(msg.arg1);
        }
}    
复制代码

3.最后看onDestroy方法

@Override
    public void onDestroy() {
        //最后退出looper,这样消息队列才能退出,最终线程才会销毁.不然一直处于阻塞等待状态.
        mServiceLooper.quit();
    }
复制代码

前面讲解HandlerThread时也有提过,当不使用时,需调用quit或者quitSafely来终止线程的执行.可以看出系统源码也是有这一步,所以当我们自定义一个具有消息循环的线程一定记得退出,这是良好的编程习惯.

关于HandlerThread以及它的应用IntentService就介绍到这里了.

由于本人技术有限,如有错误的地方,麻烦大家给我提出来,本人不胜感激,大家一起学习进步.

参考链接:


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

代码整洁之道:程序员的职业素养

代码整洁之道:程序员的职业素养

罗伯特·C.马丁 (Robert C.Martin) / 余晟、章显洲 / 人民邮电出版社 / 2016-9-1 / 49.00元

1. 汇聚编程大师40余年编程生涯的心得体会 2. 阐释软件工艺中的原理、技术、工具和实践 3. 助力专业软件开发人员具备令人敬佩的职业素养 成功的程序员在以往的工作和生活中都曾经历过大大小小的不确定性,承受过永无休止的压力。他们之所以能够成功,是因为拥有一个共同点,都深切关注创建软件所需的各项实践。他们将软件开发视为一种需要精雕细琢加以修炼的技艺,他们以专业人士的标准要求自己,......一起来看看 《代码整洁之道:程序员的职业素养》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

html转js在线工具
html转js在线工具

html转js在线工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试