Android HandlerThread 使用指南

标签: HandlerThread 异步任务
8人阅读 评论(0) 收藏 举报
分类:

在 Android 开发中,需要把耗时操作放到子线程中,避免阻塞主线程,从而导致程序 ANR。实现这类异步任务的方式有:

  • Thread + Handler
  • AsyncTask
  • HandlerThread
  • IntentService

本文来讲解分析下 HandlerThread,在真正开始前,我们先了解下 Handler 的使用方式。

Handler 机制

子线程中创建 Handler
public class MainActivity extends AppCompatActivity {

    private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new Runnable() {
            @Override
            public void run() {
                handler = new Handler();
            }
        }).start();
    }

}

这里写图片描述
程序崩溃了,意思是说:在线程中没有调用 Looper.prepare() 方法,是不能创建 Handler 对象的。

原因分析:

我点进 Handler 构造函数源码看下,会发现在创建 Handler 对象时,系统会检验当前线程中是否存在 Looper 对象,如果没有,则会抛出异常。

Handler 源码:

/**
Handler.java
*/
public Handler() {
     this(null, false);
}

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();   // 获取当前线程的 Looper 对象
    if (mLooper == null) {
        throw new RuntimeException(  // 没有Looper 对象,则需要调用 Looper.prepare()创建 Looper
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

那么为什么在主线程中创建 Handler 对象,没有调用 Looper.prepare() 方法,程序没有崩溃呢?这是因为主线程在创建时,系统帮我们创建好了 Looper 对象。看下程序入口 ActivityThread.main() 源码:

/**
* ActivityThread.java
*/
public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();  // 获取主线程中的Looper对象,该方法最终会调用Looper构造函数

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
结论:在线程中创建 Handler 对象,需要存在该线程的 Looper 对象。子线程需要我们手动创建 Looper 对象,即在该线程中调用 Looper.prepare()创建,并用 Looper.loop() 方法启动轮询;主线程中系统帮我们创建好了 Looper 对象。

常规用法:

new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();   // 创建 Looper
                workerHandler = new Handler();
                Looper.loop();      // 开启轮询 
            }
        }).start();

Handler 不同线程之间通信

(1)子线程向子线程发送消息

子线程和子线程发消息,就是在一个子线程中创建 Handler ,这样回调 handleMessage()就自然会在子线程中,然后,在另一个子线程中使用该 handler 进行发送消息。

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "xinxing.tao";

    private Handler workerHandler;

    private WorkerThread workerThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        workerThread = new WorkerThread("WorkerThread");
        workerThread.start();

        // 在另一个子线程中发送消息给 WorkerThread 子线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                // sleep(1000) 确保 WorkerThread 已经初始化好了 workerHandler
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                for (int i = 0; i < 6; i++) {
                    Log.d(TAG, "sendMessage :name = " + Thread.currentThread().getName() + ", i = " + i);
                    Message message = new Message();
                    message.what = i;
                    message.obj = "WorkerThread Message";
                    workerHandler.sendMessage(message);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 停止 WorkThread,因为 WorkThread run()通过 Looper.loop() 死循环轮询,所以需要拿到他的 Looper 进行停止。
                workerHandler.getLooper().quit();
            }
        }).start();
    }

    class WorkerThread extends Thread {

        public WorkerThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            // 子线程创建时,没有创建 Looper 对象,必须手动调用 prepare() 方法创建 Looper
            Looper.prepare();
            workerHandler = new Handler() {
                // 子线程中创建 Handler, 回调自然在子线程中
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    Log.d(TAG, "handleMessage: name = " + Thread.currentThread().getName() + ", msg.what= " + msg.what + " ,msg.obj = " + msg.obj);
                }
            };
            Log.d(TAG, "run: end ??????");
            Looper.loop();  // 开启Looper轮询,直到 Looper 停止退出轮询,才能执行后面的代码
            Log.d(TAG, "run: end");
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
}

这里写图片描述

(2)主线程向子线程发消息(Thread方式)

在子线程中创建 Handler(需要创建Looper对象),然后使用该 Handler 在主线程中发送消息。

public class MainThread2WorkerThreadActivity extends AppCompatActivity {

    private static final String TAG = "debug";

    private Handler workerHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_thread2_worker_thread);
        // 子线程
        WorkerThread workerThread = new WorkerThread("WorkerThread");
        workerThread.start();
    }

    public void click(View view) {
        // send message
        Message message = new Message();
        message.what = 100;
        message.obj = "message from main to worker thread";
        workerHandler.sendMessage(message);
        Log.d(TAG, "sendMessage : " + Thread.currentThread().getName());
    }


    class WorkerThread extends Thread {

        public WorkerThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            super.run();
            // 子线程中使用 Handler,需要手动创建 Looper 对象
            Looper.prepare();
            workerHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    Log.d(TAG, "handleMessage: name = " + Thread.currentThread().getName() + ", msg.what = " + msg.what + ",msg.obj = " + msg.obj);
                }
            };
            // 开启轮询
            Looper.loop();
        }
    }
}

输出结果:

D/debug: sendMessage : main
D/debug: handleMessage: name = WorkerThread, msg.what = 100,msg.obj = message from main to worker thread

可以看到在子线程中使用 Handler 写法还是挺麻烦的,好在 Android 已经为我们提供更方便的使用方式,即 HandlerThread。

HandlerThread 解析

HandlerThread 介绍

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.

用于方便开启子线程的类,内部包含 Looper , 可用来创建 Handler 类,注意,必须调用 start() 方法。

HandlerThread 常规用法

  1. 创建 HandlerThread 对象

    HandlerThread handlerThread  = new HandlerThread("handler-thread"); // 名字随意取
  2. 启动 HandlerThread

    handlerThread.start();
  3. 创建子线程 Handler,用于处理异步任务,并与 handlerThread 进行关联。

    Handler workerHandler = new Handler(handlerThread.getLooper()){
       public void handleMessage() {
           // 执行耗时操作,运行于子线程中
       }
    }

Demo1 : 以下代码使用 HandlerThread 方式完成主线程向子线程发送消息:

public class MainThread2WorkerThreadActivity extends AppCompatActivity {

    private static final String TAG = "debug";

    private HandlerThread handlerThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_worker_thread2_main_thread);

        // 创建,开启子线程 handlerThread
        handlerThread = new HandlerThread("HandlerThread");
        handlerThread.start();

        // 创建 workerHandler对象,传入的参数是 handlerThread 中的 Looper,即在 handlerThread这个子线程中创建 handler
        Handler workerHandler = new Handler(handlerThread.getLooper()) {

            // 这个方法运行在 handlerThread 子线程中,可以执行耗时操作
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);

                Log.d(TAG, "handleMessage: name = " + Thread.currentThread().getName() + ", msg.what = " + msg.what); // handleMessage: name = HandlerThread, msg.what = 11
            }
        };

        // 主线程中使用 workerHandler 发送消息
        Log.d(TAG, "sendMessage: name = " + Thread.currentThread().getName()); // sendMessage: name = main
        Message message = new Message();
        message.what = 11;
        message.obj = "message";
        workerHandler.sendMessage(message);


    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 退出 handlerThread looper 循环
        handlerThread.quit();
    }
}

输出结果:

D/debug: sendMessage: name = main
D/debug: handleMessage: name = HandlerThread, msg.what = 11

Demo2 : 自定义 HandlerThread , 继承自 HandlerThread 类,构造 workerHandler 传入的 looper 是 HandlerThread 中的 looper 对象,所以 workerHandler 中的 handleMessage() 回调运行于子线程中。
这里写图片描述

// DownloadHandlerThread.java
public class DownloadHandlerThread extends HandlerThread implements Handler.Callback {

    public static final int WHAT_START_DOWNLOAD = 1;

    public static final int WHAT_FINISH_DOWNLOAD = 2;

    private List<String> urlList = new ArrayList<>();

    private Handler uiHandler;

    private Handler workerHandler;

    public DownloadHandlerThread() {
        super("DownloadHandlerThread");
    }


    /**
     * 构造 workerHandler 时,传入的 Looper 是 HandlerThread 中的 looper,
     * 所以 workerHandler 中的 handleMessage() 是运行于子线程中。
     */
    @Override
    protected void onLooperPrepared() {
        super.onLooperPrepared();
        //
        workerHandler = new Handler(getLooper(), this);   // 使用HandlerThread子线程中的 looper
        // 将接收到的任务消息挨个添加到消息队列中
        for (String url : urlList) {
            Message msg = workerHandler.obtainMessage();
            Bundle bundle = new Bundle();
            bundle.putString("url", url);
            msg.setData(bundle);
            workerHandler.sendMessage(msg);
        }
    }

    /**
     * 子线程中处理任务,完成后发送消息给主线程
     *
     * @param msg
     * @return
     */
    @Override
    public boolean handleMessage(Message msg) {
        if (msg == null || msg.getData() == null) {
            return false;
        }
        String url = msg.getData().getString("url");
        // 下载之前通知主线程显示下载提示
        Message startMsg = uiHandler.obtainMessage();
        startMsg.what = WHAT_START_DOWNLOAD;
        startMsg.obj = url + " = start download ";
        // 发送消息给主线程
        uiHandler.sendMessage(startMsg);

        // 开始下载
        SystemClock.sleep(2000);   // 模拟下载过程

        // 每个 URL 下载完成通知主线程更新ui
        Message finishMsg = uiHandler.obtainMessage();
        finishMsg.what = WHAT_FINISH_DOWNLOAD;
        finishMsg.obj = url + " = finish download";
        uiHandler.sendMessage(finishMsg);

        return true;
    }


    public void setUrlList(List<String> list) {
        this.urlList = list;
    }


    /**
     * 注入主线程 handler
     *
     * @param handler
     */
    public void setUiHandler(Handler handler) {
        this.uiHandler = handler;
    }

    @Override
    public boolean quitSafely() {
        uiHandler = null;
        return super.quitSafely();
    }
}
// MainActivity.java
public class MainActivity extends AppCompatActivity implements Handler.Callback {

    private String[] urls = {"http://www.baidu.com", "http://www.sina.com", "http://google.com"};

    private DownloadHandlerThread handlerThread;

    private TextView startTextView;

    private TextView finishTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startTextView = findViewById(R.id.tv_tip);
        finishTextView = findViewById(R.id.tv_finish);


        Handler uiHandler = new Handler(this);
        handlerThread = new DownloadHandlerThread();
        handlerThread.setUrlList(Arrays.asList(urls));
        handlerThread.setUiHandler(uiHandler);

    }

    public void click(View view) {
        handlerThread.start();
    }

    /**
     * 主线程回调
     *
     * @param msg
     * @return
     */
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case DownloadHandlerThread.WHAT_START_DOWNLOAD:
                startTextView.setText(startTextView.getText().toString() + "\n" + msg.obj);
                break;
            case DownloadHandlerThread.WHAT_FINISH_DOWNLOAD:
                finishTextView.setText(finishTextView.getText().toString() + "\n" + msg.obj);
                break;
        }

        return true;
    }
}

HandlerThread 源码分析

  /**
    * HandlerThread.java
    */
    public class HandlerThread extends Thread {
        int mPriority;
        int mTid = -1;
        Looper mLooper;
        private @Nullable Handler mHandler;

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

        /**
         * @return a shared {@link Handler} associated with this thread
         * @hide
         */
        @NonNull
        public Handler getThreadHandler() {
            if (mHandler == null) {
                mHandler = new Handler(getLooper());
            }
            return mHandler;
        }

        /**
         * 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 继承自 Thread ,所以本质上 HandlerThread 还是 Thread,其 run() 方法执行于子线程中。当我们使用 HandlerThread 时,通过调用它的 start() 开启异步任务时,即会调用 HandlerThread 的 run() 方法,在 run() 方法,调用 Looper.prepare() 创建该线程的 Looper 对象,然后调用了 Looper.loop() 开启消息的轮询,loop() 方法是一个无限循环,不断的从消息队列 MessageQueue 中拿到消息进行处理。由于消息队列是由链表结构构成的,所以异步任务时串行执行的。

查看评论

Hello, world!

事实上这是我第一次用 .Text……感觉它的 CSS 写得很简洁!
  • sinzy
  • sinzy
  • 2004-04-01 17:33:00
  • 783

Android 进阶15:HandlerThread 使用场景及源码解析

眼睛困得要死,但今天的计划不完成又怎么能睡呢?明日复明日,明日何其多啊! 读完本文你将了解: HandlerThread 简介 HandlerThread 源码 HandlerThread 的使用场景...
  • u011240877
  • u011240877
  • 2017-06-07 23:55:58
  • 6046

Android多线程开发之HandlerThread的使用

初次看到HandlerThread的名字,我们可能会联想到Handler和Thread这两个类,没错,它其实就是跟Handler和Thread有莫大的关系。HandlerThread继承自Thread...
  • isee361820238
  • isee361820238
  • 2016-09-23 20:49:39
  • 2203

Android中线程形态AsyncTask、HandlerThread 和 IntentService简介

上一节《Android中线程Thread的使用》,我们介绍了Java中Thread的一些基本的使用,在Android中除了Thread外,扮演线程角色的还有:底层用到线程池的AsyncTask以及底层...
  • lyz_zyx
  • lyz_zyx
  • 2017-03-08 11:53:15
  • 639

Android 多线程之HandlerThread 完全详解

转载请注明出处(万分感谢!): http://blog.csdn.net/javazejian/article/details/52334098 出自【zejian的博客】 关联文章: A...
  • javazejian
  • javazejian
  • 2016-09-04 07:59:23
  • 17260

Android HandlerThread的用法

HandlerThread 继承自Thread,内部封装了Looper。 首先Handler和HandlerThread的主要区别是:Handler与Activity在同一个线程中,HandlerT...
  • shineflowers
  • shineflowers
  • 2014-12-02 14:03:57
  • 21521

Android HandlerThread 完全解析

转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/47079737; 本文出自:【张鸿洋的博客】 1、概述话说最近股市变...
  • lmj623565791
  • lmj623565791
  • 2015-07-27 09:02:20
  • 141099

Android源码解析之(四)-->HandlerThread

HandlerThread是个什么东西?查看类的定义时有这样一段话:Handy class for starting a new thread that has a looper. The loope...
  • qq_23547831
  • qq_23547831
  • 2016-03-20 16:04:07
  • 5440

Android 并发二三事之 Handler 机制的妙用 HandlerThread

Android 并发第五篇 本篇会讲解如何利用 HandlerThread 实现异步操作。HandlerThread 本身其实就是一个 Thread ,但是其内部还利用 Handler 机制。 对于...
  • MyLero
  • MyLero
  • 2016-11-17 11:25:19
  • 2756

android HandlerThread源码解析

尊重原创,转载请注明出处:http://blog.csdn.net/a740169405/article/details/50257001HandlerThread概述: HandlerThread是...
  • a740169405
  • a740169405
  • 2015-12-11 23:16:40
  • 1658
    个人资料
    持之以恒
    等级:
    访问量: 2万+
    积分: 583
    排名: 8万+
    最新评论