1. Handler的作用?
  2. 为什么Android设计只能UI线程更新UI?
  3. Handler相关的异常?
  4. Handler、Looper、MessageQueue之间的关系?
  5. HandlerThread分析?
  6. 主线程向子线程发消息?
  7. 在子线程中更新UI?
例一
package com.imeiren.handlertest;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);final Button button = new Button(this);button.setText("1");new Thread(new Runnable() {@Overridepublic void run() {button.setText("2");}}).start();button.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {new Thread(new Runnable() {@Overridepublic void run() {button.setText("3");}}).start();}});setContentView(button);}}
在子线程中更新UI问题。第一个线程设置内容为“2”,按钮点击之后显示内容为“3”。但是我们点击后会直接出现FC(Force Closed)。
11-10 19:31:46.186: E/AndroidRuntime(32488): FATAL EXCEPTION: Thread-5441211-10 19:31:46.186: E/AndroidRuntime(32488): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.11-10 19:31:46.186: E/AndroidRuntime(32488): at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:5945)11-10 19:31:46.186: E/AndroidRuntime(32488): at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:878)11-10 19:31:46.186: E/AndroidRuntime(32488): at android.view.ViewGroup.invalidateChild(ViewGroup.java:4264)11-10 19:31:46.186: E/AndroidRuntime(32488): at android.view.View.invalidate(View.java:10545)11-10 19:31:46.186: E/AndroidRuntime(32488): at android.view.View.invalidate(View.java:10500)11-10 19:31:46.186: E/AndroidRuntime(32488): at android.widget.TextView.checkForRelayout(TextView.java:6554)11-10 19:31:46.186: E/AndroidRuntime(32488): at android.widget.TextView.setText(TextView.java:3800)11-10 19:31:46.186: E/AndroidRuntime(32488): at android.widget.TextView.setText(TextView.java:3658)11-10 19:31:46.186: E/AndroidRuntime(32488): at android.widget.TextView.setText(TextView.java:3633)11-10 19:31:46.186: E/AndroidRuntime(32488): at com.imeiren.handlertest.MainActivity$2$1.run(MainActivity.java:60)11-10 19:31:46.186: E/AndroidRuntime(32488): at java.lang.Thread.run(Thread.java:841)
由ViewRootImpl.checkThread方法抛出的异常。据初步了解,设置为“2”的线程执行的时候,ViewRootImpl类还没有实例化,故没有执行checkThread方法,所以开始的时候在子线程中是可以更新UI的。但是在后来需要刷新View的时候,ViewRootImpl会执行checkThread方法,故抛出异常。
例二
public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(new Button(this));new Thread(new Runnable() {@Overridepublic void run() {new Handler();}}).start();}}
在子线程中创建一个Handler,这段代码会抛出异常。java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()。异常来源于下面这段源码:
    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;    }
检查成员变量mLooper为空,则抛异常。再跟进去:
    /**     * Return the Looper object associated with the current thread.  Returns     * null if the calling thread is not associated with a Looper.     */    public static Looper myLooper() {        return sThreadLocal.get();    }
 // sThreadLocal.get() will return null unless you've called prepare().    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
/** * Implements a thread-local storage, that is, a variable for which each thread * has its own value. All threads share the same {@code ThreadLocal} object, * but each sees a different value when accessing it, and changes made by one * thread do not affect the other threads. The implementation supports * {@code null} values. * * @see java.lang.Thread * @author Bob Lee */public class ThreadLocal<T> {    /* Thanks to Josh Bloch and Doug Lea for code reviews and impl advice. */    /**     * Creates a new thread-local variable.     */    public ThreadLocal() {}
使用ThreadLocal来维护一份线程本地变量。get方法返回是null。代码注释中出现了几位大神的名字 Josh Bloch和 Doug Lea.这些都是推动java技术向前发展的历史性人物! 如何让Handler的handleMessage方法在子线程中执行?我们参考Google同学写的HandlerThread来写一个简单的例子
package com.imeiren.handlertest;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class MainActivity extends Activity {class CustomThread extends Thread {protected static final String TAG = "CustomThread";Looper mLooper;@Overridepublic void run() {Looper.prepare();synchronized (this) {mLooper = Looper.myLooper();Log.d(TAG, "run() trying to notifyAll waiting object");notifyAll();}Looper.loop();}/** * 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 {Log.d(TAG, "getLooper() trying to wait");wait();Log.d(TAG, "getLooper() after wait");} catch (InterruptedException e) {e.printStackTrace();}}}return mLooper;}}protected static final String TAG = "MainActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);final CustomThread thread = new CustomThread();thread.start();final Handler handler = new Handler(thread.getLooper()) {@Overridepublic void handleMessage(Message msg) {Log.d(TAG, "handle msg in " + Thread.currentThread());}};Button button = new Button(this);button.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {handler.sendEmptyMessage(1);}});setContentView(button);}}
每一个子线程只能有一个Looper,可以有多个Handler,多个Handler引用同一个Looper,向同一个MessageQueue中发送消息,由同一个Looper来取出消息并分发到各个Handler来处理。所以要向某一个子线程发消息,那就是向它的Looper发消息。在子线程中创建Handler的前提是必须有Looper。上面的代码中其实创建的是主线程中的Handler对象,引用的是子线程中的Looper,故发送的消息是到了子线程。

更多相关文章

  1. Android(安卓)自定义Dialog,以及失去焦点之后,Dialog消失的解决
  2. Android(安卓)面试--请描述一下Activity的生命周期?
  3. [Android]调用字符串资源的几种方法
  4. android通过web service调用查询手机归属地代码
  5. Android应用推送角标设置方法
  6. Android6.0 设置默认输入法
  7. Android(安卓)- BroadcastReceiver
  8. android 悬浮球的实现,全部界面可用
  9. Android(安卓)BigNews 曾量更新

随机推荐

  1. MySQL 5.7双主同步部分表的实现过程详解
  2. centos6.5中rpm包安装mysql5.7初始化出错
  3. mysql数据库无法被其他ip访问的解决方法
  4. Mysql启动报ERROR:2002的分析与解决
  5. sql语句优化的一般步骤详解
  6. ubuntu下apt-get安装和彻底卸载mysql详解
  7. Linux下安装MySQL5.7.19问题小结
  8. Linux下Mysql5.7.19卸载方法
  9. Linux下Centos7安装Mysql5.7.19的详细教
  10. MySQL如何修改账号的IP限制条件详解