资讯专栏INFORMATION COLUMN

HandlerThread与AsyncQueryHandler源码分析

Java_oldboy / 993人阅读

摘要:源码分析继承了,但是他提供的构造方法中却没有参数,也就是说它与其所在的当前线程的绑定。同时,创建了一个线程并开启,初始化了对象并与线程的进行绑定。类的方法如下子线程的消息发送到该方法中,然后根据消息中的操作结果执行相应的代码。

HandlerThread与AsyncQueryHandler源码分析
1. HandlerThread

HandlerThread继承自Thread,其本质还是一个Thread子类,只不过它内部封装了一个Looper,可以进行消息轮询,实现主线程向子线程发送消息,子线程根据消息类型执行相应代码,主要是耗时操作,如访问IO、查询数据库,但不建议进行网络请求,因为子线程中的消息是串行执行,访问网络较为耗时,因此HandlerThread适合处理耗时较短的任务。

1.1 HandlerThread源码分析

HandlerThread的内容并不多

package android.os;

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;
    }
    
    //初始化Looper后,在开启loop循环前进行一些准备工作
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();//给当前线程创建Looper
        synchronized (this) {
            mLooper = Looper.myLooper();//将当前线程的Looper传给mLooper变量
            notifyAll();
        }
        Process.setThreadPriority(mPriority);//设置线程优先级
        onLooperPrepared();
        Looper.loop();//开启消息循环
        mTid = -1;
    }

HandlerThread类中包含一个Looper对象mLooper,这个Looper对象在run方法中初始化,loop()开启后,不断的循环从MessageQueue中取出消息处理,当没有消息的时候会阻塞,有消息的到来的时候会唤醒。

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

getLooper方法返回当前线程创建的Looper对象,若调用该方法时Looper还未创建,则一直等待直到Looper创建完毕。

当HandlerThread使用完后,需要让线程内的Looper退出循环

    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;
        }
1.2 HandlerThread用法
HandlerThread handlerThread = new HandlerThread("subThread");
handlerThread.start();//开启子线程,同时也开启了子线程内消息循环
Looper looper = handlerThread.getLooper();//获取HandlerThread中的Looper
Handler handler = new Handler(looper){ //初始化Handler,与子线程Looper绑定
            @Override
            public void handleMessage(Message msg){
                //在子线程中执行
            }
};  
2. AsyncQueryHandler

AsyncQueryHandler是一个异步查询操作帮助类,封装了HandlerThread和ContentResolver,可以方便地将耗时操作放在子线程中执行,并将执行结果回传至主线程中处理。

2.1 AsyncQueryHandler源码分析

AsyncQueryHandler继承了Handler,但是他提供的构造方法中却没有Looper参数,也就是说它与其所在的当前线程的looper绑定
AsyncQueryHandler构造方法:

public AsyncQueryHandler(ContentResolver cr) {
        super();//在父类构造方法中绑定了looper
        mResolver = new WeakReference(cr);
        synchronized (AsyncQueryHandler.class) {
            if (sLooper == null) {
                //创建HandlerThread,用于执行耗时的操作
                HandlerThread thread = new HandlerThread("AsyncQueryWorker");
                thread.start();

                sLooper = thread.getLooper();
            }
        }
        //创建工作线程的Handler对象
        mWorkerThreadHandler = createHandler(sLooper);
    }

    protected Handler createHandler(Looper looper) {
        return new WorkerHandler(looper);
    }

AsyncQueryHandler的构造方法中传入ContentResolver参数,然后创建一个对应的弱引用对象mResolver。
同时,创建了一个HandlerThread线程并开启,初始化了mWorkerThreadHandler对象并与HandlerThread线程的looper进行绑定。mWorkerThreadHandler是一个WorkerHandler类的对象,下面看WorkerHandler类:

protected class WorkerHandler extends Handler {
        public WorkerHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            final ContentResolver resolver = mResolver.get();
            if (resolver == null) return;

            WorkerArgs args = (WorkerArgs) msg.obj;

            int token = msg.what;//可理解为消息的ID
            int event = msg.arg1;//消息类型标记

            switch (event) {
                case EVENT_ARG_QUERY:
                    Cursor cursor;
                    try {
                        cursor = resolver.query(args.uri, args.projection,
                                args.selection, args.selectionArgs,
                                args.orderBy);
                        // Calling getCount() causes the cursor window to be filled,
                        // which will make the first access on the main thread a lot faster.
                        if (cursor != null) {
                            cursor.getCount();
                        }
                    } catch (Exception e) {
                        Log.w(TAG, "Exception thrown during handling EVENT_ARG_QUERY", e);
                        cursor = null;
                    }

                    args.result = cursor;
                    break;

                case EVENT_ARG_INSERT:
                    args.result = resolver.insert(args.uri, args.values);
                    break;

                case EVENT_ARG_UPDATE:
                    args.result = resolver.update(args.uri, args.values, args.selection,
                            args.selectionArgs);
                    break;

                case EVENT_ARG_DELETE:
                    args.result = resolver.delete(args.uri, args.selection, args.selectionArgs);
                    break;
            }

            // passing the original token value back to the caller
            // on top of the event values in arg1.
            Message reply = args.handler.obtainMessage(token);
            reply.obj = args;
            reply.arg1 = msg.arg1;

            if (localLOGV) {
                Log.d(TAG, "WorkerHandler.handleMsg: msg.arg1=" + msg.arg1
                        + ", reply.what=" + reply.what);
            }

            reply.sendToTarget();
        }
    }

WorkerHandler类是AsyncQueryhandler的内部类,继承了Handler,它的构造函数中传入looper作为参数,与之绑定。由上可知,实例化的WorkerHandler对象是mWorkerThreadHandler,它与HandlerThread的looper绑定,因此其handleMessage方法会在子线程中执行,处理耗时的操作

在handleMessage方法中,首先获取到mResolver对象,然后从消息中获取到WorkerArgs对象、event和token。

event:消息类型,AsyncQueryhandler中定义了四种消息类型,分别对应增删查改的四种操作

    private static final int EVENT_ARG_QUERY = 1;
    private static final int EVENT_ARG_INSERT = 2;
    private static final int EVENT_ARG_UPDATE = 3;
    private static final int EVENT_ARG_DELETE = 4;

token: 我理解为消息的ID,比如查询操作可能有很多次,每次都是发送不同的消息,因此要标识其ID,这在AsyncQueryhandler暴露的增删查改方法中也需要作为参数传入。当要取消某个操作时,执行cancelOperation方法,传入的参数就是token

public final void cancelOperation(int token) {
        mWorkerThreadHandler.removeMessages(token);
    }

WorkerArgs:静态内部类,封装了查询操作的各种参数,定义如下

protected static final class WorkerArgs {
        public Uri uri;
        public Handler handler;
        public String[] projection;
        public String selection;
        public String[] selectionArgs;
        public String orderBy;
        public Object result;
        public Object cookie;
        public ContentValues values;
    }

然后判断消息类型,跟据消息类型,进行相应的操作,以查询为例:

case EVENT_ARG_QUERY:
                    Cursor cursor;
                    try {
                        cursor = resolver.query(args.uri, args.projection,
                                args.selection, args.selectionArgs,
                                args.orderBy);
    
                        if (cursor != null) {
                            cursor.getCount();
                        }
                    } catch (Exception e) {
                        Log.w(TAG, "Exception thrown during handling EVENT_ARG_QUERY", e);
                        cursor = null;
                    }
                    args.result = cursor;
                    break;

就是基本的查询操作,将查询的结果保存在args.result,最后:

Message reply = args.handler.obtainMessage(token);
            reply.obj = args;
            reply.arg1 = msg.arg1;

            reply.sendToTarget();

注意:这里将带有操作结果的args对象封装入一个message中,这个message的handler是args中保存的handler,然后将这个消息发送出去。

那么,WorkerHandler类中handleMessage方法的消息从何处发来的?reply这个消息又将发向哪里?

handleMessage方法中的消息是主线程执行操作时发送的,这里以查询为例,在AsyncQueryHandler类中:

public void startQuery(int token, Object cookie, Uri uri,
            String[] projection, String selection, String[] selectionArgs,
            String orderBy) {
        // Use the token as what so cancelOperations works properly
        Message msg = mWorkerThreadHandler.obtainMessage(token);//获取一个消息
        msg.arg1 = EVENT_ARG_QUERY;

        WorkerArgs args = new WorkerArgs();
        args.handler = this;//指向AsyncQueryHandler对象
        args.uri = uri;
        args.projection = projection;
        args.selection = selection;
        args.selectionArgs = selectionArgs;
        args.orderBy = orderBy;
        args.cookie = cookie;
        msg.obj = args;

        mWorkerThreadHandler.sendMessage(msg);
    }

这里开启异步查询操作,首先获取一个消息,这个消息将携带查询操作所需的各种参数,这些参数被封装在WorkerArgs中,这个消息将被发送到工作线程(子线程)中的MessageQueue,等待被执行,因此,子线程HandlerThread将会接收到这个消息,所以该消息的handler对象为mWorkerThreadHandler。

注意,在构造args对象后,args.handler = this 将args对象的handler指向了AsyncQueryHandler对象,因此,在WorkerHandler的handleMessage方法中:

Message reply = args.handler.obtainMessage(token);
            reply.obj = args;
            reply.arg1 = msg.arg1;

            reply.sendToTarget();

这个reply消息将会发送到AsyncQueryHandler对象的handleMessage方法中,也就是将查询结果发送到主线程中的handleMessage方法中。
AsyncQueryHandler类的handleMessage方法如下:

@Override
    public void handleMessage(Message msg) {
        WorkerArgs args = (WorkerArgs) msg.obj;

        if (localLOGV) {
            Log.d(TAG, "AsyncQueryHandler.handleMessage: msg.what=" + msg.what
                    + ", msg.arg1=" + msg.arg1);
        }

        int token = msg.what;
        int event = msg.arg1;

        // pass token back to caller on each callback.
        switch (event) {
            case EVENT_ARG_QUERY:
                onQueryComplete(token, args.cookie, (Cursor) args.result);
                break;

            case EVENT_ARG_INSERT:
                onInsertComplete(token, args.cookie, (Uri) args.result);
                break;

            case EVENT_ARG_UPDATE:
                onUpdateComplete(token, args.cookie, (Integer) args.result);
                break;

            case EVENT_ARG_DELETE:
                onDeleteComplete(token, args.cookie, (Integer) args.result);
                break;
        }
    }
}

子线程的消息发送到该handleMessage方法中,然后根据消息中的操作结果执行相应的代码。这样整个异步查询流程就完成了。

2.2 AsyncQueryHandler用法
QueryHandler handler = new QueryHandler(getContentResolver());//创建异步查询对象
//开启查询,传入查询参数
handler.startQuery(token, cookie, uri, projection, selection, selectionArgs, String orderBy)

//自定义AsyncQueryHandler类
private final class QueryHandler extends AsyncQueryHandler{

        public QueryHandler(ContentResolver cr) {
            super(cr);
            //do something
        }
        
        //查询完毕后将结果后回传至此方法
        @Override
        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
            //do something
        }
    }
3. 总结

AsyncQueryHandler是一个Handler,其内部还有一个Handler类WorkerHandler

AsyncQueryHandler:与创建它的线程(调用线程)的looper绑定
AsyncQueryHandler.handleMessage:处理子线程发来的消息,消息携带操作结果

WorkerHandler:与HandlerThread线程(工作线程)的looper绑定
AsyncQueryHandler.handleMessage:接收主线程发送的增删查改消息,并执行,将结果消息发送至主线程。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/13825.html

相关文章

  • IntentService源码分析

    摘要:目录介绍的作用使用场景使用步骤源码分析整体源码展示及归纳如何单独开启个新的工作线程如何将传递给服务并且依次插入到工作队列中与其他线程对比问题答疑解答问题答疑源码是如何设计的原理是什么,有什么样的特点呢内部源码为何不会阻塞线程如果启动多次,会 目录介绍 1.IntentService的作用 2.IntentService使用场景 3.IntentService使用步骤 4.Inten...

    DangoSky 评论0 收藏0
  • Android多线程之HandlerThread源码解析

    摘要:一概述先来了解一下的几个特性继续于本身就是一个线程类在内部维护了自己的对象,所以可以进行循环创建后需要先调用方法再向其下发任务,通过方法来创建对象通过传递的对象给对象,从而可以通过来向下发号是任务二使用方式再来看的使用方式创建并调用方法,使 一、概述 先来了解一下HandlerThread的几个特性 HandlerThread继续于Thread,本身就是一个线程类 HandlerTh...

    mengbo 评论0 收藏0
  • Android IntentService源码分析

    摘要:在分析之前,先要了解。对象可用于创建类。这些在上一篇文章中已经分析过。处理完成后会调用自动停止。为了把它执行的结果体现在上,需要把结果返回给。正在执行的任务无法打断。 简介 Service 是 Android 四大组件之一,用于后台运行,但由于 Service 依然运行在主线程,所以是不能直接进行耗时操作的。如果有耗时操作,还是需要放到子线程中,可以手动开启线程,也可以使用 Andro...

    1fe1se 评论0 收藏0
  • 读书笔记(1)-Android进程概述

    摘要:其中的一个解决方法就是把任务放在多个进程中执行,但是多个进程必然占用大量的资源,并且进程间的通讯速度太慢,也没有高效的异步运行机制,所以使用多线程是个很好的解决方法。 读书笔记(1)-Android进程概述 每次介绍Android都会遇见的一张图: showImg(https://segmentfault.com/img/bVZRn8?w=1384&h=2038);从上至下: Syst...

    focusj 评论0 收藏0
  • Android_Message_Handler_消息处理机制总结笔记

    摘要:比如处理用户交互的线程,它等待并执行用户的点击滑动等等操作事件,也执行由系统触发的广播等事件,称之为主线程,也叫线程。源码链接源码链接源码链接源码链接版权声明消息处理机制总结笔记由在年月日写作。 一次性线程和无限循环线程 普通线程是一次性的,执行结束后也就退出了(这种说法可能不严谨,但为了下文描述方便)。但某些情况下需要无限循环、不退出的线程。比如处理用户交互的线程,它等待并执行用户的...

    EscapedDog 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<