Android多线程--HandlerThread的使用以及原理

        如果已经理解了Android的消息机制(Handler、Looper、Message、MessageQueue),再来看HandlerThread就很容易了,参考如下:

http://blog.csdn.net/kitty_landon/article/details/53410183

源码解析

        最直观的方法就是分析源码,先把源码贴出来。

/**
 * Handy class for starting a new thread that has a looper. The looper can then be 
 * used to create handler classes. Note that start() must still be called.HandlerThread可以创建拥有Looper的线程,这个Looper能够创建Handler类
 */
public class HandlerThread extends Thread {
    int mPriority;//线程优先级
    int mTid = -1;
    Looper mLooper;//当前线程持有的Looper对象

    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.可以重写,但是在Looper循环之前。
     */
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {//创建HandlerThread对象后,必须调用start()方法,调用start()方法后,表示线程启动了,也就会调用线程的run()方法;
        mTid = Process.myTid();
        Looper.prepare();//创建Looper对象,并绑定当前线程
        synchronized (this) {
            mLooper = Looper.myLooper();//获取Looper对象
            notifyAll();//唤醒等待线程
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();//循环
        mTid = -1;
    }
    
    /**
     * This method returns the Looper associated with this thread. 返回与该线程相关联的Looper对象。If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. (如果线程没有开启,或者isAlive()返回false,getLooper()返
        回null)。If this thread 
     * has been started, this method will block until the looper has been initialized. 开启了线程,这个方法将会阻塞,直到Looper初始化 
     * @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;
    }
}

        从getLooper()方法实现源码上可以看出外部在通过getLooper()方法获取Looper对象时,会先判断当前线程是否启动了,如果线程已经启动,那么将会进入同步语句并判断Looper是否为null,为null则代表Looper对象还没有被赋值,也就是还没被创建,此时当前调用线程进入等待状态,知道Looper对象被创建并通过notifyAll()方法唤醒等待线程,最后才返回Looper对象。之所以需要等待唤醒机制,是因为Looper的创建是在子线程中执行的,而调用getLooper方法则是在主线程进行的,这样我们就无法保障我们在调用getLooper方法时Looper已经被创建,到这里我们也就明白了在获取mLooper对象时会存在一个同步的问题,只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值,HandlerThread内部则通过等待唤醒机制解决了同步问题。   

        从源码可以看出当我们调用quit方法时,其内部实际上是调用Looper的quit方法而最终执行的则是MessageQueue中的removeAllMessagesLocked方法(Handler消息机制知识点),该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送)还是非延迟消息。

     当调用quitSafely方法时,其内部调用的是Looper的quitSafely方法而最终执行的是MessageQueue中的 removeAllFutureMessagesLocked方法,该方法 只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。最后需要注意的是Looper的quit方法是基于API 1,而Looper的quitSafely方法则是基于API 18的。

        从源码中我们可以看出HandlerThread有几个特点:

        (1)、HandlerThread本质上是一个线程类,它继承了Thread;

        (2)、HandlerThread有自己的内部Looper对象,可以进行loop循环;

        (3)、通过获取HandlerThread的Looper对象传递给Handler对象,可以在handleMessage()方法中执行异步任务;

        (4)、创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run()方法,创建Looper对象。

HandlerThread常规使用步骤

方案一:利用Handler(Looper looper, Callback callback)

        1、创建HandlerThread实例对象

HandlerThread mMainJobThread = new HandlerThread("qscan");

        传入参数的作用主要是标记当前线程的名字,可以是任意字符串。

        2、启动HandlerThread线程

mMainJobThread.start();

        到此,我们创建完HandlerThread并启动了线程。那么我们怎么将一个耗时的异步任务投放到HandlerThread线程中去执行呢?接下来看下面步骤:

        3、构建循环消息处理机制

  /**
     * 该callback运行于子线程
     */
    class ChildCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {
            //在子线程中进行相应的网络请求

            //通知主线程去更新UI
            mUIHandler.sendMessage(msg1);
            return false;
        }
    }

        4、构建异步Handler

Handler mMainJobHandler = new Handler(mMainJobThread.getLooper(), new ChildCallback());

        第3步和第4步是构建一个可以用于异步操作的handler,并将前面创建的HandlerThread的Looper对象以及Callback接口类作为参数传递给当前的handler,这样当前的异步handler就拥有了HandlerThread的Looper对象,由于HandlerThread本身是异步线程,因此Looper也与异步线程绑定,从而handlerMessage方法也就可以异步处理耗时任务了,这样我们的Looper+Handler+MessageQueue+Thread异步循环机制构建完成,来看看一个完整的使用案例。

方案二:利用Handler(Looper looper)

        步骤一和步骤二是相同的;

        3、构建循环消息处理机制

    private class MainJobHandler extends Handler {

        public MainJobHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            Log.e(TAG, "MainJobHandler() -> msg.what = " + msg.what);
            ......
        }
    }

        4、构建异步Handler

MainJobHandler mMainJobHandler = new MainJobHandler(mMainJobThread.getLooper());

使用案例

public class HandlerThreadActivity extends Activity {
    /**
     * 图片地址集合
     */
    private String url[]={
            "https://img-blog.csdn.net/20160903083245762",
            "https://img-blog.csdn.net/20160903083252184",
            "https://img-blog.csdn.net/20160903083257871",
            "https://img-blog.csdn.net/20160903083257871",
            "https://img-blog.csdn.net/20160903083311972",
            "https://img-blog.csdn.net/20160903083319668",
            "https://img-blog.csdn.net/20160903083326871"
    };
    private ImageView imageView;
    private Handler mUIHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            LogUtils.e("次数:"+msg.what);
            ImageModel model = (ImageModel) msg.obj;
            imageView.setImageBitmap(model.bitmap);
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread);
        imageView= (ImageView) findViewById(R.id.image);
        //创建异步HandlerThread
        HandlerThread handlerThread = new HandlerThread("downloadImage");
        //必须先开启线程
        handlerThread.start();
        //子线程Handler
        Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());
        for(int i=0;i<7;i++){
            //每个1秒去更新图片
            childHandler.sendEmptyMessageDelayed(i,1000*i);
        }
    }
    /**
     * 该callback运行于子线程
     */
    class ChildCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {
            //在子线程中进行网络请求
            Bitmap bitmap=downloadUrlBitmap(url[msg.what]);
            ImageModel imageModel=new ImageModel();
            imageModel.bitmap=bitmap;
            imageModel.url=url[msg.what];
            Message msg1 = new Message();
            msg1.what = msg.what;
            msg1.obj =imageModel;
            //通知主线程去更新UI
            mUIHandler.sendMessage(msg1);
            return false;
        }
    }
    private Bitmap downloadUrlBitmap(String urlString) {
        HttpURLConnection urlConnection = null;
        BufferedInputStream in = null;
        Bitmap bitmap=null;
        try {
            final URL url = new URL(urlString);
            urlConnection = (HttpURLConnection) url.openConnection();
            in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
            bitmap=BitmapFactory.decodeStream(in);
        } catch (final IOException e) {
            e.printStackTrace();
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            try {
                if (in != null) {
                    in.close();
                }
            } catch (final IOException e) {
                e.printStackTrace();
            }
        }
        return bitmap;
    }
}

        在这个案例中,我们创建了两个Handler,一个用于更新UI线程的mUIHandler和一个用于异步下载图片的childHandler。最终的结果是childHandler会每个隔1秒钟通过sendEmptyMessageDelayed方法去通知ChildCallback的回调函数handleMessage方法去下载图片并告诉mUIHandler去更新UI界面,以上便是HandlerThread常规使用。

        实际上在android比较典型的应用是IntentService。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值