Android Handler 分析

  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 {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		final Button button = new Button(this);
		button.setText("1");

		new Thread(new Runnable() {

			@Override
			public void run() {
				button.setText("2");
			}
		}).start();

		button.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				new Thread(new Runnable() {

					@Override
					public 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-54412
11-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 {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(new Button(this));
		new Thread(new Runnable() {

			@Override
			public 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;

		@Override
		public 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";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		final CustomThread thread = new CustomThread();
		thread.start();
		final Handler handler = new Handler(thread.getLooper()) {
			@Override
			public void handleMessage(Message msg) {
				Log.d(TAG, "handle msg in " + Thread.currentThread());
			}
		};

		Button button = new Button(this);
		button.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				handler.sendEmptyMessage(1);
			}
		});
		setContentView(button);
	}
}
每一个子线程只能有一个Looper,可以有多个Handler,多个Handler引用同一个Looper,向同一个MessageQueue中发送消息,由同一个Looper来取出消息并分发到各个Handler来处理。所以要向某一个子线程发消息,那就是向它的Looper发消息。在子线程中创建Handler的前提是必须有Looper。上面的代码中其实创建的是主线程中的Handler对象,引用的是子线程中的Looper,故发送的消息是到了子线程。

你可能感兴趣的:(Android Handler 分析)