Android异步任务总结(handler、Asyntask、handlerThread、intentService)

[本文链接:https://blog.csdn.net/kaitao0/article/details/81095620,转载请注明出处]

前言:

查阅网上众多文章,又对源代码进行了解析,大致对Handler,Asyntask,handlerThread,intentService的异步原理有了一定了解,至此做一次总结,我觉得多个异步机制一同掌握会对其理解更加深刻。

一、Handler

首先是最常用的handler有两个使用方式

1.post(runnable) 延时操作

用法:

   new Handler().post(new Runnable() {
            @Override
            public void run() {
                textView.setText("This is post");//更新UI
            }
        });

post内部代码实现:可以看出内部也是调用sendMessage方法。

  public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

2.sendMessage(message) 从子线程到主线程消息传递

  • UI 线程  主线程 ActivityThread
  • Message  Handler 发送和处理的消息,由 MessageQueue 管理。
  • MessageQueue  消息队列,用来存放通过 Handler 发送的消息,按照先进先出执行,内部使用的是单链表的结构。
  • Handler  负责发送消息和处理消息。
  • Looper 负责消息循环,循环取出 MessageQueue 里面的 Message,并交给相应的 Handler 进行处理。

看一下Handler构造源源码:

由此可见handler构造中创建了Lopper对象,而又由Lopper对象创建了MessageQueue,也就是说当Handler被new出来后就被绑定在由线程中,同时创建了Lopper,MessageQueue与之协作工作,开启Looper.myLooper()后才能从MessageQueue中获取消息。

 

  public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

在往下看Looper是从哪里获取到的呢?
以下代码看出Looper.myLooper()是从threadLocal.get()中获取的,也就是说Looper.prepare()创建looper时,looper被保存到ThreadLocal中,创建Handler()时就可以在threadLocal中获取looper了。

ThreadLocal 就是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据,这样就保证了一个线程对应了一个 Looper,从源码中也可以看出一个线程也只能有一个 Looper,否则就会抛出异常。

  /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
在看下面这段英文,如果获取的Lopper对象为空因为是没有通过Looper.prepare()创建Looper
"Can't create handler inside thread that has not called Looper.prepare()");

??那looper什么时候被创建的呢?----当然是UI类被创建时一同创建的。

总结

应用启动时会启动 UI 线程也就是主线程 ActivityThread,在 ActivityThread 的 main 方法中会调用 Looper.prepareMainLooper( ) 和 Looper.loop ( ) 启动 Looper 。
Looper 启动时会创建一个 MessageQueue 实例,并且只有一个实例,然后不断从 MessageQueue 中获取消息,无则阻塞等待消息,有则调用 msg.target.dispatchMessage(msg) 处理消息。
我们在使用 Handler 时 需要先创建 Handler 实例,Handler 在创建时会获取当前线程关联的 Looper 实例 ,和 Looper 中的消息队列 MessageQueue。然后在发送消息时会自动给 Message 设置 target 为 Handler 本身,并把消息放入 MessageQueue 中,由 Looper 处理。Handler 在创建时会重写的
handleMessage 方法中处理消息。
如果要在子线程中使用 Handler 就需要新建 Looper 实例,传给 Handler 即可。

二、AsynTask

什么是AsynTask?

轻量级的内部类,在类中可以实现异步操作,可以在UI线程与非UI线程进行切换,它本质上就是一个封装了线程池和handler的异步框架,AsyncTask泛型中三个参数,1传入参数类型,2进度,3返回参数类型,内部五个方法

AsyncTask的机制原理

线程池中的工作线程执行doInBackground(params)方法执行异步任务

当任务状态改变之后,工作线程会向UI线程发送消息,AsyncTask内部的InternalHandler响应这些消息,并调用相关的回调函数

使用:

 private class ATask extends AsyncTask<String, Integer, String> {
        //onPreExecute方法用于在执行后台任务前做一些UI操作
        @Override
        protected void onPreExecute() {
            textView.setText("loading...");
        }

        //doInBackground方法内部执行后台任务,不可在此方法内修改UI
        @Override
        protected String doInBackground(String... params) {
            return new String("返回数据");
        }
    }

    //onProgressUpdate方法用于更新进度信息
    @Override
    protected void onProgressUpdate(Integer... progresses) {
        progressBar.setProgress(progresses[0]);
        textView.setText("loading..." + progresses[0] + "%");
    }

    //onPostExecute方法用于在执行完后台任务后更新UI,显示结果
    @Override
    protected void onPostExecute(String result) {
        textView.setText(result);
    }

    //onCancelled方法用于在取消执行中的任务时更改UI
    @Override
    protected void onCancelled() {
        textView.setText("cancelled");
        progressBar.setProgress(0);
    }
}
}

 源码解析:

  • AsyncTask构造中,查看postResult(result)方法,result参数是doInBackground方法执行后返回参数,
  • 最终postResult创建Handler,Message.....发送异步消息,
  public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };
  private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

 

    private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

总结:

AsyncTask实际就是对handler的二次封装。

 

三、handlerThread

handerThrad产生的背景

当开启一个子线程做耗时操作是非常消耗资源的,线程执行完就会被销毁,如果在一段时间内又要做一个耗时任务,这时候又重新创建线程做耗时操作,多次创建和销毁线程是很消耗系统资源的,通过阻塞等待保持性能最优。

一般启动一个线程:

new Thread(){...}.start();

handler

Handler handler = new Handler(){...};

HandlerThread的用法

//String代表线程名,priority代表优先级
HandlerThread thread = new HandlerThread("MyHandlerThread");
thread.start();//创建HandlerThread时要把它启动了,即调用start()方法。

// 步骤3:创建工作线程Handler & 复写handleMessage()
// 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
Handler workHandler = new Handler( handlerThread.getLooper() ) {
            @Override
            public boolean handleMessage(Message msg) {
                ...//消息处理
                return true;
            }
        });

  // 使用工作线程Handler向工作线程的消息队列发送消息
  // 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
  //定义要发送的消息
  Message msg = Message.obtain();
  msg.what = 100; //消息的标识
  //通过Handler发送消息到其绑定的消息队列
  workHandler.sendMessage(msg);

  // 结束线程,即停止线程的消息循环
  mHandlerThread.quit();

源码解析:

HandlerThread本身继承Thread

  • 构造参数:String代表线程名,priority代表优先级。优先级范围为-20到19,默认为0,优先级越高,获得的CPU资源更多,反之则越少。-20代表优先级最高,反之19最低。
  • onLooperPrepared子类需要重写的方法,在这里做一些执行前的初始化工作
  • 调用start()执行run()后,帮我们创建了 Looepr,notifyAll()/Looper 已经创建,唤醒阻塞在获取 Looper 的线程,Looper.loop()开始循环。
  • getLooper() 获取当前线程的 Looper,如果线程启动后,Looper 还没创建,就 wait() 等待 创建 Looper 后 notify
public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    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;
    }
    
    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread 
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    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;
    }

    /**
     * Quits the handler thread's looper.
     * <p>
     * Causes the handler thread's looper to terminate without processing any
     * more messages in the message queue.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p class="note">
     * Using this method may be unsafe because some messages may not be delivered
     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
     * that all pending work is completed in an orderly manner.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     *
     * @see #quitSafely
     */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

    /**
     * Quits the handler thread's looper safely.
     * <p>
     * Causes the handler thread's looper to terminate as soon as all remaining messages
     * in the message queue that are already due to be delivered have been handled.
     * Pending delayed messages with due times in the future will not be delivered.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p>
     * If the thread has not been started or has finished (that is if
     * {@link #getLooper} returns null), then false is returned.
     * Otherwise the looper is asked to quit and true is returned.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     */
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}

总结:

HandlerThread本身就是一个线程,但与线程不同的是HandlerThread封装了Looper与Handler,避免重复多次开启线程导致内存的额外消耗。

四、IntentService

什么是IntentService?

IntentService是继承并处理异步请求的一个类,IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统的Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要手动去控制stopSelf(),另外可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且每次只会执行一个工作线程,执行一个工作线程,执行完第一个再执行第二个串行的

使用:name必传线程名称,onHandleIntent方法异步

public class MyIntentService extends IntentService {
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public MyIntentService(String name) {
        super(name);//线程名称
    }

    /**
     *实现异步任务的方法,在此方法中可以做异步操作
     * @param intent activity传来的数据在intent中
     */
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //可以多次被执行
    }
}

源码解析

首先是IntentSerivce的每次启动都执行的onCreate()

  • 方法中创建了handlerThread;证明IntentSerivce内部就是handlerThread做异部消息的传递的。
  • 方法中又创建了sericeHandler,它是继承Handler的Handler做了内部封装,构造方法参数又要传入handlerThread的looper,所以它就成了处理异步线程的执行类
  @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);
    }

再看,IntentService是怎么启动异步任务的,通过ServiceHandler.sendMessage()传递消息,最终这个方法肯定会发送到handlerThread中去处理,因为handlerThread持有looper对象,然后再调用ServiceHandler方法

handleMessage中调用onHandleIntent抽象方法给其它子类继承做些实际的操作,主动又调用了stopSelf方法停止服务

PS:stopSelf无参数会立即停止该服务,有参数会等待所有消息都处理完后才会终止任务

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
  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);
        }
    }

总结:

ItentService本质就是实现封装了handlerThread + handler开启循环的线程利用Handler发送消息构成了异步框架,IntentSerice内部就是通过发送消息的方式交给handlerThread,由handler当中的looper处理消息,是按照顺序消息对列中取出任务的, 当有多个后台任务的时候也会按照顺序来执行的 在继承ItentService的时候,可以在onHandleIntent方法里做耗时操作。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值