不得不说的Handler

          Android为我们提供了handler类,在Android开发中是不得不说的知识点。本篇文章讨论在主线程和子线程之间使用handler的情况,因为子线程和子线程之间使用更为复杂暂不讨论。本篇文章将按以下顺序来总结handler的使用方法。

  • handler是什么?handler适合在什么情境下使用?handler的作用?
  • handler和Looper、Message、MessageQueue、thread之间的关系是什么?
  • handler的几个常用方法。
  • 使用handler传递消息的小案例。

一、handler是什么?handler适合在什么样的情境下使用?handler的作用?

handler是android framework架构的一个基础组件,它是android应开发需求而产生的,用来解决主线程中不能处理长时间操作,子线程中不能更新UI的问题。如果在主线程中处理超过大概5秒的操作,比如请求网络图片、请求网络数据、请求后台数据这种,那么界面会弹出ANR(application not responding)崩溃,如果在子线程中请求到数据开始更新也会报错崩溃,因为他规定主线程是专门处理UI更新和用户交互的问题。handler的作用有2个,第一:解决主线程和子线程的通信问题;第二:设置定时任务。

第二、handler和Looper、Message、MessageQueue、thread之间的关系是什么?

**************Message***************************

message是一种装载任意数据类型的容器,主线程和子线程之间通信的数据的载体。Message是一个类,有4个常用属性。

what:  用户定义消息代码以便收件人可以识别这是哪一个Message。每个Handler用它自己的名称空间为消息代码,所以您不需要担心你的Handler与其他handler冲突。

arg1\arg2: 存放整形数据。

object obj:  存放一个对象,可以是自定义的类,可以装载很多类型的数据。

handler提供的一些方法可以创建message对象。

public final Message obtainMessage()
public final Message obtainMessage(int what)
public final Message obtainMessage(int what, Object obj)
public final Message obtainMessage(int what, int arg1, int arg2)
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)

**************************MessageQueue*******************************

messageQueue是存放message的队列,是先进先出的结构。安卓的UI线程在创建时就在内部为我们建立好了一个looper和messageQueue队列来存储数据,但我们自己创建的子线程是要手动创建looper的。主线程只能有一个looper对象,也只能有唯一的messageQueue消息队列,但可以有多个handler,由于线程在一个时间内只能处理一项任务,所以messageQueue每行只有一个消息message。looper创建后会获得messageQueue的引用,所以looper可以找到messageQueue(android封装的代码);而handler创建时也会获取创建所在的线程的looper对象(也是封装代码);这样handler最终持有looper和messageQueue的引用,都能找到他们。

*****************************handler*******************************

hanlder在里面是负责发送消息并处理它发送的消息的任务。因为handler有messageQueue的地址,所以他能把消息插到消息队列messageQueue的队列尾部,而消息其实也持有handler的引用,因此looper把消息取出来后能找到当初发送他的handler来处理自己。handler像个在子线程和主线程间来回穿梭的秘书一类的角色。

**************************looper*******************************

looper在主线程创立后就已经创建了,并且与主线程关联(但自己创建的子线程并没有创建looper,得手动添加),looper就像个陀螺,会不断到消息队列中检查有没有消息要处理的,一旦发现有消息,立刻取出来,消息上有handler的引用,再分发给handler的handleMessage()方法来处理消息,这个消息处理完了就取出下一个。looper中文名就是回路,循环,looper.loop()方法中有个死循环,所以主线程不会结束运行。

#####handler和Looper、Message、MessageQueue、thread之间的关系######

(图片来源于网络)

从网上拿来两张图片来解释它们之间的关系。handler创建后就会拥有messageQueue的引用,handler发送消息数据到messageQueue中,messageQueue会安排它位于尾部,looper拥有messageQueue的地址,不停滴从messageQueue中拿出一个个消息,当拿出到刚刚这个消息时,消息中有handler的地址,于是找到handler,looper把消息派发给这个handler处理,handler调用自己的handleMessage()方法运行用户准备好的代码把数据拿到手并处理掉。

第三、handler的几个常用方法。

  • sendMessage(Message message),发送一个 Message 对象到 MessageQueue,并在主线程取到 Message 之后立即执行;
  • sendEmptyMessage(int what),发送一个只包含 what 的 Message 对象到 MessageQueue,并在主线程取到 Message 之后立即执行;
  • sendEmptyMessageDelayed(int what, long delayMillis),发送一个只包含 what 的 Message 对象到 MessageQueue,并在延时 delayMillis 毫秒之后在主线程执行;
  • sendEmptyMessageAtTime(int what, long uptimeMillis),发送一个只包含 what 的 Message 对象到 MessageQueue,并在特定的时间(uptimeMillis)在主线程执行;
  • sendMessageDelayed(Message message, long delayMillis),发送一个 Message 对象到 MessageQueue,并在延时 delayMillis 毫秒之后在主线程执行;
  • sendMessageAtTime(Message message, long uptimeMillis),发送一个 Message 对象到 MessageQueue,并在特定的时间(uptimeMillis)在主线程执行;
  • sendMessageAtFrontOfQueue(Message message),发送一个 Message 对象到 MessageQueue,并将 Message 对象放置在 MessageQueue 的最前端。
  •  

其中,Message 对象的获取可以通过 Handler.obtainMessage() 方法获取,也可以通过 Message.obtain() 的方式获取。事实上,这两种方式是等价的。

以下代码演示了通过使用 sendMessageDelayed(Message message, long delayMillis),从子线程向主线程发送一个 Message 的具体过程:

在主线程中实例化一个 Handler 对象,并重写 handleMessage() 方法用于处理收到的 Message:

Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Log.d("handleMessage", "收到的 Message 对象 :" + msg.what);
    }
};

在子线程中使用 handler 发送一个 Message:

Message message = Message.obtain();
message.what = 100;
handler.sendMessageDelayed(message, 1000);

在 Handler 中除了可以使用 sendMessage() 实现主线程与子线程数据交互的目的外,还可以直接使用 post() 直接在主线程执行一个 Runnable 对象。Handler 有如下 API 用于实现该功能:

  • post(Runnable runnable),发送一个 Runnable 对象到 MessageQueue,并在主线程取到 Message之后立即执行;
  • postAtTime(Runnable runnable, long uptimeMillis),发送一个 Runnable 对象到 MessageQueue,并在特定的时间(uptimeMillis)在主线程执行;
  • postAtTime(Runnable runnable, Object token, long uptimeMillis),发送一个 Runnable 对象和 Object 对象到 MessageQueue,并在特定的时间(uptimeMillis)在主线程执行;
  • postDelayed(Runnable runnable, long delayMillis),发送一个 Runnable 对象到 MessageQueue,并在延时 delayMillis 毫秒之后在主线程执行;
  • postAtFrontOfQueue(Runnable runnable),发送一个 Runnable 对象到 MessageQueue,并将 Message 对象放置在 MessageQueue 的最前端。                                                         

第四、使用handler传递消息的小案例。

我写的小例子,附上代码,在自己手机端运行没问题。

例子一:使用handler.post(runnable)实现子线程主线程切换。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    android:gravity="center">
<TextView 
    android:id="@+id/textViewId"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="线程名"
    android:textSize="18sp"
    />
<Button 
    android:id="@+id/buttonId"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="点击"
    />
</LinearLayout>
public class MainActivity extends Activity {
	private TextView tv;
	private Button button;
	private Handler handler = new Handler();
	Runnable runnable;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		button = (Button) findViewById(R.id.buttonId);
		tv = (TextView) findViewById(R.id.textViewId);
		button.setOnClickListener(new ButtonEx());
	}

	class ButtoneEx implements OnClickListener {
		@Override
		public void onClick(View v) {
			Thread t = new ThreadEx();
			t.start();//此处开始执行子线程的run()方法
		}
	}

	class ThreadEx extends Thread {
		@Override
		public void run() {
			// TODO Auto-generated method stub
			super.run();
			String currentStr = Thread.currentThread().getName();
			System.out.println("子线程名字:" + currentStr);
			runnable = new Runnable() {
				@Override
				public void run() {
					// TODO Auto-generated method stub
					String currentString = Thread.currentThread().getName();
					System.out.println("主线程名字:" + currentString);
					tv.setText(currentString);
				}
			};
			handler.post(runnable);//此处切换回主线程中执行runnaable中的run()方法
		}
	}

}

注意事项:1. runnable虽然写在了子线程的run()方法中,但是执行在handler初始化的线程当中,这里handler在主线程当中,所以handler.post后切换回到主线程当中了。

2.handler.post(runnable)方法应该写在runnable方法体后面,如果写在之前那么不会执行。

例子二:使用handler.sendMessage(Message message)在子线程和主线程之间切换并且传递信息数据。

      

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    android:gravity="center">
<TextView 
    android:id="@+id/textViewId"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="你吃饭了吗?"
    android:textSize="18sp"
    />

<TextView
    android:id="@+id/tv_respon"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="18sp"
    android:layout_marginTop="10dp"
    android:text="未回应" />

<Button 
    android:id="@+id/buttonId"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="10dp"
    android:text="点击让她回应你"
    />
<Button 
    android:id="@+id/buttonId2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="10dp"
    android:text="点击让他回应你"
    />
</LinearLayout>
public class handlerMsg extends Activity {
	private TextView tv_respon;
	private Button button, button2;
	private Handler handler;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		tv_respon = (TextView) findViewById(R.id.tv_respon);
		button = (Button) findViewById(R.id.buttonId);
		button2 = (Button) findViewById(R.id.buttonId2);
		handler = new Handler() {

			@Override
			public void handleMessage(Message msg) {
				// TODO Auto-generated method stub
				super.handleMessage(msg);
				switch (msg.what) {// what标记每个消息
				case 2:
					Person p1 = (Person) msg.obj;// 取出对象强制转换
					String str = p1.name;
					int i = msg.arg1;
					tv_respon.setText(str + "说:我在" + i + "点就吃过了。");
					break;
				case 3:
					Person p2 = (Person) msg.obj;
					String s = p2.name;
					int clock = msg.arg2;// 取出数据
					tv_respon.setText(s + "回应你:我得等到" + clock + "点才能吃。。");
					break;
				}
			}

		};
		button.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				new Thread() {
					@Override
					public void run() {
						// TODO Auto-generated method stub
						super.run();
						try {
							Thread.sleep(7000);// 模拟取数据操作
							Message msg1 = handler.obtainMessage(2);// 生成新消息
							msg1.arg1 = 11;
							Person p1 = new Person();
							p1.setName("俄罗斯小萝莉");
							p1.setSex("女");
							msg1.obj = p1;
							handler.sendMessage(msg1);// 发送消息切换到handler所在的线程执行handleMessage()
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}

				}.start();
			}
		});

		button2.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				new Thread() {
					@Override
					public void run() {
						// TODO Auto-generated method stub
						super.run();
						try {
							Thread.sleep(5000);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}// 模拟取数据操作
						Message msg2 = handler.obtainMessage(3);// 生成新消息,what改变
						msg2.arg2 = 12;
						Person p2 = new Person();
						p2.setName("搬砖工人");
						p2.setSex("男");
						msg2.obj = p2;
						handler.sendMessage(msg2);// 发送消息切换到handler所在的线程执行handleMessage()
					}

				}.start();
			}
		});
	}
}

class Person {
	public String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public String sex;

}

注意事项:handleMessage(Message msg)是在handler初始化时重写的。

   

猜你喜欢

转载自my.oschina.net/u/3630543/blog/1634312