MainActivity如下:
package cc.testui1;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.app.Activity;
/**
* Demo描述:
* 在子线程中更改UI的方式一
*
* 在子线程中利用主线程的Handler的post()方法
* 更改UI这个在子线程中sendMessage()原理和形式都很类似.
*
* 详细分析:
* 在Handler调用其post()方法时,方法的调用顺序如下:
* Handler的post()方法--->Handler的sendMessageDelayed()方法--->
* Handler的sendMessageAtTime()方法.
* 至此又回到了前面熟悉的sendMessageAtTime()
*
* 详细的代码如下:
* public final boolean post(Runnable r){
* return sendMessageDelayed(getPostMessage(r), 0);
* }
*
*
* private final Message getPostMessage(Runnable r) {
* Message m = Message.obtain();
* m.callback = r;
* return m;
* }
*
*
* public final boolean sendMessageDelayed(Message msg, long delayMillis){
* if (delayMillis < 0) {
* delayMillis = 0;
* }
* return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
* }
*
*
* 在这个详细的过程中可以看到在post(Runnable r)中利用该方法的输入参数r调用了getPostMessage(r)
* 将r封装成了一个Message.
* 重点是getPostMessage(r)方法中的 m.callback = r
* 将r赋值给了Message的callback!!!!!
*
* 回顾一下出队时调用的dispatchMessage()方法
* 下面接着看Handler的dispatchMessage(Message msg)方法:
* public void dispatchMessage(Message msg) {
* //1 message的callback
* if (msg.callback != null) {
* handleCallback(msg);
* } else {
* //2 handler的callback
* if (mCallback != null) {
* if (mCallback.handleMessage(msg)) {
* return;
* }
* }
* //3 Handler的handleMessage()
* handleMessage(msg);
* }
* }
*
*
* 在1处就判断出该Message的callback不为null,于是调用 handleCallback(msg);
* 详细代码如下:
* private static void handleCallback(Message message) {
* message.callback.run();
* }
* 该方法比较简单:就是直接调用post(Runnable r)的输入参数(Runnable对象)的run()方法.
*
* 小结:
* 1 在子线程中利用post(Runnable r)更新UI,原理和sendMessage()类似.
* 2 注意:
* 在下面的示例中mHandler是主线程的Handler.
* 所以“在子线程中利用post(Runnable r)更新UI”这个说法不是特别准确.
* 确切地说还是在子线程中发送了消息到主线程的消息队列从而更新了UI.
* 3 调用post(Runnable r)不会开启一个新的线程,UI的更新是在主线程中完成的!!!!!!!!!!!!!!
* 在下面的示例中可在两个地方输出线程ID发现两个值是一样的.
* 所以在post方法中勿做耗时操作!!!!!!!!!!!!!!!!!!!!!!!
* 4 其实在第一次看到“message.callback.run().....调用post(Runnable r)不会开启一个新的线程,UI更新是在主线程中完成的....”
* 时,脑子里发热有点晕:既然都调用run()方法了,还不会开启线程???
* 哎,这是平常看多了线程Thread和它里面的run()方法带来的误解.
* 当然Thread调用start()是会开启一个线程的,这个毋庸置疑.
* 但是如下:
* Runnable runnable=new Runnable() {
* @Override
* public void run() {
* System.out.println("Runnable中线程ID:"+Thread.currentThread().getId());
* }
* };
* runnable.run();
* 它会在一个新线程中运行么?当然不会,直观地说就是它还没有和Thread任何关系.
* 这么写的话此处的runnable只是一个Runnable接口实现类的对象而已,且该对象调用了方法run().
* 此时就和一个普通的Object类对象object调用method没啥区别了.
*
*
*
* 参考资料:
* 1 http://www.jb51.net/article/37465.htm
* 2 http://www.eoeandroid.com/blog-660972-48541.html
* 3 https://software.intel.com/zh-cn/blogs/2010/09/15/start-run
* Thank you very much
*
*/
public class MainActivity extends Activity {
private TextView mTextView;
private Handler mHandler;
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
init();
}
private void init(){
mHandler=new Handler();
mTextView=(TextView) findViewById(R.id.textView);
mButton=(Button) findViewById(R.id.button);
mButton.setOnClickListener(new OnClickListenerImpl());
System.out.println("UI线程ID="+Thread.currentThread().getId());
}
private class OnClickListenerImpl implements OnClickListener{
@Override
public void onClick(View v) {
new Thread(){
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
System.out.println("post(Runnable r)里的run()方法中线程ID="+Thread.currentThread().getId());
mTextView.setText("My number is 007");
}
});
};
}.start();
}
}
}
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"
>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="test"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dip"
/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
android:layout_centerHorizontal="true"
android:layout_marginTop="120dip"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试在子线程中更新UI"
android:layout_centerInParent="true"
/>
</RelativeLayout>