您的位置:首页 > 移动开发 > Android开发

android Handler及消息处理机制的简单介绍

2014-11-25 14:20 417 查看
  学习android线程时,直接在UI线程中使用子线程来更新TextView显示的内容,会有如下错误:android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

  大致意思就是:只有创建这个控件的线程才能去更新该控件的内容。

  android中,如果要操作UI,都必须在UI线程,即主线程中去做,我们不能直接在UI线程中去创建子线程来实现,即不能通过这种方式来实现:

new Thread( new Runnable() {
public void run() {
textView.setText("update");
}
}).start();


要想实现更新TextView显示的内容,要利用消息机制:Handler;

消息机制的三个方面,也可以是四个方面:

(1)Looper:一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列);

(2)Handler:使用Handler发送消息对象到消息队列,或者接受Looper从消息队列里取出来的消息;

(3)MessageQueue:用来存放线程放入的消息;

(4)线程:UI Thread通常就是main Thread,而Android启动程序时会替它建立一个MessageQueue;

1.Handler创建消息:

  每一个消息都需要被指定的Handler处理,通过Handler创建消息便可完成此功能。

  Android消息机制中引入了消息池,Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。

  使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用,消息池提高了消息对象的复用,减少系统垃圾回收的次数。

  消息的创建流程如图所示:



2.Handler发送消息

  UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应,使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper,这点可以查看android的源代码;

  之后其他Handler初始化的时候直接获取第一个Handler创建的Looper,而Looper初始化的时候会创建一个消息队列MessageQueue。

  至此,主线程、消息循环、消息队列之间的关系是1:1:1;

  Handler、Looper、MessageQueue的初始化流程如图所示



  而Hander持有对UI主线程消息队列MessageQueue和消息循环Looper的引用,子线程可以通过Handler将消息发送到UI线程的消息队列MessageQueue中;

3.Handler处理消息

  UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出;

  首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。

  子线程通过Handler、Looper与UI主线程通信的流程如图所示:



下面我们用一个例子来实现更新UI Thread中TextView的内容;

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.xiaozhang.handler1.MainActivity" >

<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/hello_world" />

<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/textView"
android:text="button"
/>

</RelativeLayout>


MainActivity.java

package com.xiaozhang.handler1;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

private Button button;
private TextView textView;
private Handler handler;

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

button = (Button) findViewById(R.id.button);
textView = (TextView) findViewById(R.id.textView);
button.setOnClickListener(new ButtonListener());
handler = new MyHandler();
}

class MyHandler extends Handler {

@Override
public void handleMessage(Message msg) {
textView.setText((String) msg.obj);
}
}

class ButtonListener implements OnClickListener {

@Override
public void onClick(View v) {
Thread t = new MyThread();
t.start();
}
}

class MyThread extends Thread {
@Override
public void run() {
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

String s = "the message from child thread!";
Message msg = handler.obtainMessage();

msg.obj = s;
handler.sendMessage(msg);
}
}
}


注:MainActivity中的内部类一般通过匿名内部类来实现;

例如:

package com.xiaozhang.handler1;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

private Button button;
private TextView textView;
private Handler handler;

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

button = (Button) findViewById(R.id.button);
textView = (TextView) findViewById(R.id.textView);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {
@Override
public void run() {
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String s = "the message from child thread!";
Message msg = handler.obtainMessage();

msg.obj = s;
handler.sendMessage(msg);
}
}.start();
}
});
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
textView.setText((String) msg.obj);
}
};
}
}


我只是随便写了下,应该还可以再进行改进;

显示效果为:



然后点击button,等待2秒后,再次显示为:



文章参考及整理自:

  http://blog.csdn.net/itachi85/article/details/8035333

  Mars视频;

推荐:
http://www.jb51.net/article/33514.htm
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: