在详解之前,贴出我们的基本代码。
layout_main.xml
布局文件<?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" >
<TextView android:id="@+id/textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/app_name" />
</LinearLayout>
MainActivity
public class MainActivity extends Activity {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.layout_main);
initView();
initEvent();
}
private void initView() {
textView = (TextView) findViewById(R.id.textview);
new Thread() {
public void run() {
try {
Thread.sleep(1000);
textView.setText("更新");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
}
private void initEvent() {
}
}
05-29 22:43:10.524: E/AndroidRuntime(20222): FATAL EXCEPTION: Thread-13219
05-29 22:43:10.524: E/AndroidRuntime(20222): Process: com.scp.handler, PID: 20222
05-29 22:43:10.524: E/AndroidRuntime(20222): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
05-29 22:43:10.524: E/AndroidRuntime(20222): at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6386)
05-29 22:43:10.524: E/AndroidRuntime(20222): at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:874)
05-29 22:43:10.524: E/AndroidRuntime(20222): at android.view.View.requestLayout(View.java:16500)
05-29 22:43:10.524: E/AndroidRuntime(20222): at android.view.View.requestLayout(View.java:16500)
05-29 22:43:10.524: E/AndroidRuntime(20222): at android.view.View.requestLayout(View.java:16500)
05-29 22:43:10.524: E/AndroidRuntime(20222): at android.view.View.requestLayout(View.java:16500)
05-29 22:43:10.524: E/AndroidRuntime(20222): at android.view.View.requestLayout(View.java:16500)
05-29 22:43:10.524: E/AndroidRuntime(20222): at android.widget.TextView.checkForRelayout(TextView.java:6714)
05-29 22:43:10.524: E/AndroidRuntime(20222): at android.widget.TextView.setText(TextView.java:3893)
05-29 22:43:10.524: E/AndroidRuntime(20222): at android.widget.TextView.setText(TextView.java:3744)
05-29 22:43:10.524: E/AndroidRuntime(20222): at android.widget.TextView.setText(TextView.java:3719)
05-29 22:43:10.524: E/AndroidRuntime(20222): at com.scp.handler.MainActivity$1.run(MainActivity.java:25)
产生这个异常的根本原因是Android中不允许在子线程中直接更新UI,那么我们要更新UI可以怎么做呢?接下来,我们通过handler这种机制来更新UI。
handler.post
用法public class MainActivity extends Activity {
private TextView textView;
private Handler handler = new Handler();//Handler
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.layout_main);
initView();
initEvent();
}
private void initView() {
textView = (TextView) findViewById(R.id.textview);
new Thread() {
public void run() {
try {
Thread.sleep(1000);
handler.post(new Runnable() {
@Override
public void run() {
textView.setText("更新");
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
}
private void initEvent() {
}
}
我们这里呢使用了handler一个比较简单点的更新UI的方法。运行我们的程序,没有报错。那么为什么它能执行成功呢,这是因为我们这个线程是执行在UI线程里的,所以它是可以直接更新UI的。
handler.postDelayed
用法 <?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" >
<TextView android:id="@+id/textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/app_name" />
<ImageView android:id="@+id/imageview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_launcher" />
</LinearLayout>
修改我们的MainActivity
,各位自己准备三张图片,我们做一个图片轮播的程序。
public class MainActivity extends Activity {
private TextView textView;
private ImageView imageView;
private int[] imgIds = new int[] { R.drawable.img1, R.drawable.img2,
R.drawable.img3 };
private int index;// 图片索引
private MyRunnable myRunnable = new MyRunnable();
private Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.layout_main);
initView();
initEvent();
}
private void initView() {
textView = (TextView) findViewById(R.id.textview);
imageView = (ImageView) findViewById(R.id.imageview);
handler.postDelayed(myRunnable, 1000);
}
class MyRunnable implements Runnable {
@Override
public void run() {
index++;
index = index % 3;
imageView.setImageResource(imgIds[index]);
/** * r:Runnable对象 * * delayMillis:每隔一段时间执行一次,单位是毫秒 */
handler.postDelayed(myRunnable, 1000);
}
}
private void initEvent() {
}
}
上面程序运行正常。
setText
//省略上面代码
public void run() {
index++;
index = index % 3;
imageView.setImageResource(imgIds[index]);
textView.setText(index);//这是我们新加的
/** * r:Runnable对象 * * delayMillis:每隔一段时间执行一次,单位是毫秒 */
handler.postDelayed(myRunnable, 1000);
}
运行我们的程序,发现会出错。这是因为我们不能直接这样频繁setText
05-29 23:26:14.774: E/AndroidRuntime(24525): FATAL EXCEPTION: main
05-29 23:26:14.774: E/AndroidRuntime(24525): Process: com.scp.handler, PID: 24525
05-29 23:26:14.774: E/AndroidRuntime(24525): android.content.res.Resources$NotFoundException: String resource ID #0x1
05-29 23:26:14.774: E/AndroidRuntime(24525): at android.content.res.Resources.getText(Resources.java:289)
05-29 23:26:14.774: E/AndroidRuntime(24525): at android.widget.TextView.setText(TextView.java:3968)
05-29 23:26:14.774: E/AndroidRuntime(24525): at com.scp.handler.MainActivity$MyRunnable.run(MainActivity.java:41)
05-29 23:26:14.774: E/AndroidRuntime(24525): at android.os.Handler.handleCallback(Handler.java:733)
05-29 23:26:14.774: E/AndroidRuntime(24525): at android.os.Handler.dispatchMessage(Handler.java:95)
05-29 23:26:14.774: E/AndroidRuntime(24525): at android.os.Looper.loop(Looper.java:136)
05-29 23:26:14.774: E/AndroidRuntime(24525): at android.app.ActivityThread.main(ActivityThread.java:5336)
05-29 23:26:14.774: E/AndroidRuntime(24525): at java.lang.reflect.Method.invokeNative(Native Method)
05-29 23:26:14.774: E/AndroidRuntime(24525): at java.lang.reflect.Method.invoke(Method.java:515)
05-29 23:26:14.774: E/AndroidRuntime(24525): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:871)
05-29 23:26:14.774: E/AndroidRuntime(24525): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687)
05-29 23:26:14.774: E/AndroidRuntime(24525): at dalvik.system.NativeStart.main(Native Method)
handler.sendMessage
用法 //修改部分代码
private void initView() {
textView = (TextView) findViewById(R.id.textview);
imageView = (ImageView) findViewById(R.id.imageview);
new Thread() {
public void run() {
try {
Thread.sleep(2000);
Message message = new Message();
message.what = 01;
message.obj = "更新UI";
handler.sendMessage(message);// 发送消息
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
}
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 在子线程中更新UI文本信息
switch (msg.what) {
case 01:
String update = (String) msg.obj;
textView.setText(update);
break;
default:
break;
}
}
};
上面这段代码中,我们可以看到
Message message = new Message();
message.what = 01;
message.obj = "更新UI";
handler.sendMessage(message);// 发送消息
其中
message.what = 01;
是handleMessage
区分我们发送的是哪一个消息。作为一个执行ID来用。
message.obj = "更新UI";
则是存储数据用的,它不仅仅可以发送一段文本,还可以发送一个对象,大家可以自行试试发送一个对象,这里我们就不演示了,使用方法都相同。
handler.sendMessage(message);
发送一个消息。
public void handleMessage(Message msg) {
// 在子线程中更新UI文本信息
switch (msg.what) {
case 01:
String update = (String) msg.obj;
textView.setText(update);
break;
default:
break;
}
}
获取message
对象
Message message = new Message();
来得到一个message
对象;Message message = handler.obtainMessage();
来得到一个message
对象。他们大体上是一样的。发送message
消息
handler.sendMessage(message);
来发送一个消息;message.sendToTarget();
我们需要注意的是,当我们使用这种方式来发送一个消息时,我们message
对象必须是通过Message message = handler.obtainMessage();
来得到的,否则会出现异常。05-30 00:14:26.974: E/AndroidRuntime(28528): FATAL EXCEPTION: Thread-13840
05-30 00:14:26.974: E/AndroidRuntime(28528): Process: com.scp.handler, PID: 28528
05-30 00:14:26.974: E/AndroidRuntime(28528): java.lang.NullPointerException
05-30 00:14:26.974: E/AndroidRuntime(28528): at android.os.Message.sendToTarget(Message.java:360)
05-30 00:14:26.974: E/AndroidRuntime(28528): at com.scp.handler.MainActivity$2.run(MainActivity.java:34)
message.setTarget(handler)
用法 handler
来处理我们发送的消息。我们需要绑定一个handler
来处理这个消息。Message message = handler.obtainMessage();
就可以? Message message = handler.obtainMessage();
来得到message
对象,之后才能使用message.sendToTarget();
来发送消息。大家想一想,我们这个时候是通过handler
来得到的一个消息,换言之这个message
绑定了handler
,所以它可以。message
还有一个方法message.setTarget(handler);
这个方法是设置一个handler来处理我们发送的消息。当我们在使用Message message = new Message();
来得到一个message
对象;时,我们就可以使用这样一个方法来解决消息无人处理的问题。 public void sendToTarget(){
target.sendMessage(this);//target也就是handler
}
实质上还是通过handler
在发送消息,只不过方式不同而已。
y1笑而过的CSDN博客
y1笑而过的博客园
y1笑而过的新浪博客
y1笑而过的安卓巴士博客
y1笑而过的51CTO技术博客