现在的位置: 首页 > 移动开发 > 正文

Android多线程Thread Runnable Handler AsyncTask等之间的关系

2019年09月01日 移动开发 ⁄ 共 5947字 ⁄ 字号 评论关闭
  • Android 的多线程实际上就是java的多线程。android的UI线程又称为主线程。

  • Thread 和 Runnable:

    Thread才是一个线程,而Runnable可以理解为一个任务。这个任务只是一个接口。具体的任务执行是在 run()方法执行。

    Thread thread = new Thread(Runnable);

    那么就是把一个Runnable任务放到线程里面。当调用thread.start() 的时候,系统新开一个线程去执行,这个runnable任务是在多线程执行的。是在新开的线程执行的。

    但是thread.run() ,这样子实际上只是在UI线程执行了Runnable 并没有实现多线程。系统也没有新开一个线程。

    如果直接 new Runnable().run();那么实际上是直接在UI线程执行了这个任务没有进行一个多线程的操作。

    总结:runnable()只是一个任务的抽象,并不是多线程。Thread.start()。才是新开一个多线程。并且在新开的线程执行Thread你们的run()方法.

  • Handler

    是android
    的多线程通信机制的产物,在Android中不允许Activity新启动的线程访问该Activity里的UI组件,这样会导致新启动的线程无法改变UI组件的属性值。但实际开发中,很多地方需要在工作线程中改变UI组件的属性值,比如下载网络图片、动画等等。

    Handler,它直接继承自Object,一个Handler允许发送和处理Message或者Runnable对象,并且会关联到主线程的MessageQueue中。每个Handler具有一个单独的线程,并且关联到一个消息队列的线程,就是说一个Handler有一个固有的消息队列。当实例化一个Handler的时候,它就承载在一个线程和消息队列的线程,这个Handler可以把Message或Runnable压入到消息队列,并且从消息队列中取出Message或Runnable,进而操作它们。

    Handler可以把一个Message对象或者Runnable对象压入到消息队列中,进而在UI线程中获取Message或者执行Runnable对象,所以Handler把压入消息队列有两大体系,Post和sendMessage.

           
    对于Handler的Post方式来说,它会传递一个Runnable对象到消息队列中,在这个Runnable对象中,重写run()方法。一般在这个run()方法中写入需要在UI线程上的操作。
    hanlder实际上是不涉及多线程的。

               post(Runnable)  让当前线程执行Runnable任务。如果是在主线程调用,那么就是在UI线程执行Runnable。实际上没有多线程执行runnable。
               postAtTime(Runnable,long)  也是让当前线程在 时间点long 执行Runnble
               postDelayed(Runnable long) 让当前线程延时 long 后执行Runnable。

               这3个方法实际上可以把handler看成是一个任务调度器,而不是一个多线程相关的。

           Handler如果使用sendMessage的方式把消息入队到消息队列中,需要传递一个Message对象,而在Handler中,需要重写handleMessage()方法,用于获取工作线程传递过来的消息,此方法运行在UI线程上。

         
    Message
    自带的有如下几个属性:

          int arg1:参数一,用于传递不复杂的数据,复杂数据使用setData()传递。

          int arg2:参数二,用于传递不复杂的数据,复杂数据使用setData()传递。

          Object obj:传递一个任意的对象。

          int what:定义的消息码,一般用于设定消息的标志。

            对于Message对象,一般并不推荐直接使用它的构造方法得到,而是建议通过使用Message.obtain()这个静态的方法或者Handler.obtainMessage()获取。Message.obtain()会从消息池中获取一个Message对象,如果消息池中是空的,才会使用构造方法实例化一个新Message,这样有利于消息资源的利用。并不需要担心消息池中的消息过多,它是有上限的,上限为10个。Handler.obtainMessage()具有多个重载方法,如果查看源码,会发现其实Handler.obtainMessage()在内部也是调用的Message.obtain()。

           sendEmptyMessage(int)
           sendMessage(Message)
           sendMessageAtTime(Message,long)
           sendMessageDelayed(Message,long) 发出消息的方法

  • handleMessage(Message msg)处理消息 这才是android 的hanlder的多线程通信用到的,看下图

    \

    可以看到3个线程共用一个消息队列Message Queue,一个消息循环器Looper(作用不断循环读取消息队列的消息)。

    另外2个线程通过hanlder的sendMessage等方法发送Message,message中可以包含数据。或者是一些action的标记值。

    主线程的hanlder通过handleMessage把包含数据等信息的消息取出来。这个过程中其他线程的数据通过message+hanlder传递到主线程实现的多线程通信。

    好的最后再说AsyncTask,可以开启一个多线程执行任务,并且多线程的数据传递也是有这个类自己完成。

    实际上AsyncTask 是由 thread + handler的封装实现。所以AsyncTask 跟Thread+handler实现没有本质的差别。

    AsyncTask的代码更少,使用比Thread+handler简单。

    主要有 onPreExecute() ; 是指在执行多线程任务之前的一个初始化执行。在UI线程调用

    doInBackground(Params... params);这个方法是AsyncTask新启动的线程实现的。在新线程调用
    onPostExecute(Result result) ;这个是doInBackground()方法执行完成后,才调用的方法。

    result 就是doInBackground()的结果,就是用Hanlder的方式传递的数据。这个方法在UI线程调用

    protected void onProgressUpdate(Progress... values);处理中间数据。doInBackGround()方法中,即在新开的线程中调用publishProgress方法,那么hanlder就把publishProgress的值传递到UI线程中。

    并且把值交给onProgressUpdate方法处理。所以onProgressUpdate是在UI线程调用的。

    看看AsyncTask 的源码。

    AsyncTask是基于线程池的实现。所以先学习一下线程池。

    线程池可以这样理解:一些线程的集合。假设我们这个线程池容量是20.

    那么一个新的任务来了,我们想去看看我们线程池的有没有线程处于休眠状态,有就唤醒然后让他去处理任务。处理完成后线程休眠而不是直接结束线程。

    没有处于休眠状态的线程,那么就意味着20个线程都在工作。系统比较忙,那么就让这个任务排队。

    如果忽然来了1000个任务--那么有20个进入线程中处理。另外的980个任务排队。这样子保证系统高效的一个运转。

    而且避免不断新建线程的开销。可以尽可能的少开启线程,并且进入系统的调度。

    如果不用线程池这个来了1000个任务开启1000个线程,然后关闭掉,下次又有任务的又新建。。这个开销明显线程池避免了。

    而且当有太多的线程后(根据硬件),系统忙于线程的调度导致实际上线程的执行效率下降。线程池可以保证一个合理的线程数量而让系统维持调度线程的低开销。

    线程池还有其他的一些种类。我这只是讲讲线程池的一个是实现的思路,简单的原理。想要详细了解还得自己升入学习。

    在android系统中这种忽然上1000的任务并发现实中虽然不太可能。。。但是还是推荐用AsyncTask的包含线程池的实现 去做多线程的任务。

    分析:AsyncTask源码

    1.publicstatic
    final Executor SERIAL_EXECUTOR =
    new SerialExecutor();

    这是有一个static的线程池对象。这个进程内共用的线程池。(不知道是不是所有android程序都共用这个线程池。。求大神现身。)

    01.publicfinal
    AsyncTask<Params, Progress, Result> execute(Params... params) {
    02.returnexecuteOnExecutor(sDefaultExecutor,
    params);
    03.}
    04.publicfinal
    AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
    05.Params... params) {
    06.if(mStatus != Status.PENDING) {
    07.switch(mStatus) {
    08.caseRUNNING:
    09.thrownew
    IllegalStateException("Cannot execute task:"
    10.+" the task is already running.");
    11.caseFINISHED:
    12.thrownew
    IllegalStateException("Cannot execute task:"
    13.+" the task has already been executed
    "
    14.+"(a task can be executed only once)");
    15.}
    16.}
    17. 
    18.//状态标记为后台线程正在运行
    19.mStatus = Status.RUNNING;
    20. 
    21.//执行 用户继承的初始化操作
    22.onPreExecute();
    23. 
    24.//运行的参数放到任务中
    25.mWorker.mParams = params;
    26.//用线程池去执行
    27.exec.execute(mFuture);
    28. 
    29.returnthis;
    30.}


    这样子线程池执行任务mWorker

    01.mWorker =new
    WorkerRunnable<Params, Result>() {
    02.publicResult call()
    throws Exception {
    03.mTaskInvoked.set(true);
    04. 
    05.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    06.//noinspection unchecked
    07.//线程池 运行这个任务。并且postResult把结果发出去
    08.returnpostResult(doInBackground(mParams));
    09.}
    10.};


    可以看到这个任务执行了doInBackground()方法。这个任务是通过线程池在多线程的环境下执行的。也就是说doInBackground()也是多线程执行的。

    1.privateResult postResult(Result result)
    {
    2.@SuppressWarnings("unchecked")
    3.Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
    4.newAsyncTaskResult<Result>(this,
    result));
    5.message.sendToTarget();
    6.returnresult;
    7.}


    通过Hanlder + message把结果result传递出去。

    01.privatestatic
    class InternalHandler extends
    Handler {
    02.@SuppressWarnings({"unchecked","RawUseOfParameterizedType"})
    03.@Override
    04.publicvoid
    handleMessage(Message msg) {
    05.AsyncTaskResult result = (AsyncTaskResult) msg.obj;
    06.switch(msg.what) {
    07.caseMESSAGE_POST_RESULT:
    08.// There is only one result
    09.result.mTask.finish(result.mData[0]);//处理结果
    10.break;
    11.caseMESSAGE_POST_PROGRESS:
    12.result.mTask.onProgressUpdate(result.mData);//处理中间数据
    13.break;
    14.}
    15.}
    16.}
    17.privatevoid
    finish(Result result) {
    18.if(isCancelled()) {
    19.onCancelled(result);
    20.}else
    {
    21.onPostExecute(result);
    22.}
    23.mStatus = Status.FINISHED;
    24.}


    这个hanlder处理接收到的消息。

    可以看到调用了finish() 并在finish中调用onPostExecute 让用户去使用结果。

    最后总结Java的多线程是Thread实现,跟runnable没有太大关系。

    android中因为需要多线程交换数据出现了handler+message+thread实现多线程数据通信。就hanlder本身而言并不是实现多线程。

    因为handler+message+thread手写比较负载,所以android提供AsyncTask去实现多线程,并且拥有多线程数据同步的能力。

    AsyncTask的本质就是handler+message+thread(线程池)的实现。因此要是简单的使用多线程,android中使用asyncTask就足够了。  

抱歉!评论已关闭.