首页 > 代码库 > 错误:Only the original thread that created a view hierarchy can touch its views——Handler的深入解析
错误:Only the original thread that created a view hierarchy can touch its views——Handler的深入解析
这个错误很常见,基本上写线程操作都遇到过这个错误。根本原因是view控件的线程安全问题,通俗点讲就是所有的更新UI操作都需要在主线程(也就是UI线程中完成),而不能在新开的子线程中操作。
基本思路:既然子线程需要更新UI,但子线程自身又不能完成任务,所以只能通过建立一个通信机制,当子线程需要更新UI时,发消息通知主线程并将更新UI的任务post给主线程,让主线程来完成分内的UI更新操作。这个机制是什么呢?就是Handler。Handler 从属于谁?当然是主线程。每个线程都有自己的handler,来处理自己的消息队列,只不过平时写单线程操作,系统会缺省调用一个handler,对开发者透明。当多线程操作需要线程间通信时,handler才会被程序猿们显示调用。
下面这两个例子是更新UI时主线程和子线程通信的例子,因为控件不是线程安全的,所以子线程中涉及到的更新UI操作全都写入runnable对象、通过主线程的handler来post给UI。
第一个例子,从网上找的,总结的比较到位。谢谢原作者的辛勤总结,转载地址标注于下。
原文转自 http://blog.csdn.net/djx123456/article/details/6325983
今天写了一个更新UI的小例子,没想到出了log打印了这样一个错误:Only the original thread that created a view hierarchy can touch its views。goolgle了一下找到了原因。
原来android中相关的view和控件不是线程安全的,我们必须单独做处理。这里借此引出Handler的使用。
Handler的官方描述:
A Handler allows you to send and process Message
and Runnable objects associated with a thread‘s MessageQueue
. Each Handler instance is associated with a single thread and that thread‘s message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue
.Handler的使用场合:
1、 to schedule messages and runnables to be executed as some point in the future;
安排messages和runnables在将来的某个时间点执行。
2、 to enqueue an action to be performed on a different thread than your own.
将action入队以备在一个不同的线程中执行。即可以实现线程间通信。比如当你创建子线程时,你可以再你的子线程中拿到父线程中创建的Handler对象,就可以通过该对象向父线程的消息队列发送消息了。由于Android要求在UI线程中更新界面,因此,可以通过该方法在其它线程中更新界面。
通过Handler更新UI实例:
步骤:
1、创建Handler对象(此处创建于主线程中便于更新UI)。
2、构建Runnable对象,在Runnable中更新界面。
3、在子线程的run方法中向UI线程post,runnable对象来更新UI。
详细代码如下:
- package djx.android;
- import djx.downLoad.DownFiles;
- import android.app.Activity;
- 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;
- public class downLoadPractice extends Activity {
- private Button button_submit=null;
- private TextView textView=null;
- private String content=null;
- private Handler handler=null;
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- //创建属于主线程的handler
- handler=new Handler();
- button_submit=(Button)findViewById(R.id.button_submit);
- textView=(TextView)findViewById(R.id.textView);
- button_submit.setOnClickListener(new submitOnClieckListener());
- }
- //为按钮添加监听器
- class submitOnClieckListener implements OnClickListener{
- @Override
- public void onClick(View v) {
- //本地机器部署为服务器,从本地下载a.txt文件内容在textView上显示
- final DownFiles df=new DownFiles("http://192.168.75.1:8080/downLoadServer/a.txt");
- textView.setText("正在加载......");
- new Thread(){
- public void run(){
- content=df.downLoadFiles();
- handler.post(runnableUi);
- }
- }.start();
- }
- }
- // 构建Runnable对象,在runnable中更新界面
- Runnable runnableUi=new Runnable(){
- @Override
- public void run() {
- //更新界面
- textView.setText("the Content is:"+content);
- }
- };
- }
第二个例子,这两天在实验室写的人防工程的应用程序,包括动态新闻、巡查信息等是需要更新UI的,以下把动态新闻更新UI的代码贴出来。
/** * 动态新闻 * * @author GloryZSG */ public class NewsDetail extends Activity { private TextView bar; private TextView noticename; private TextView noticeauthor; private TextView noticetime; private TextView noticeinfo; private TextView newsImageText; private ImageView newsImage; private String imgUrl; private Bitmap bm; private Handler handler; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.news_details); handler = new Handler(); HashMap<String, Object> map = new HashMap<String, Object>(); try { Bundle bundle = getIntent().getExtras(); Serializable data = http://www.mamicode.com/bundle.getSerializable("taskinfo");>