这个错误很常见,基本上写线程操作都遇到过这个错误。根本原因是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。
详细代码如下:
第二个例子,这两天在实验室写的人防工程的应用程序,包括动态新闻、巡查信息等是需要更新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 = bundle.getSerializable("taskinfo"); if (data != null) { map = (HashMap<String, Object>) data; } else { return; } } catch (Exception e) { e.printStackTrace(); } // (2014.5.7第二种方法)通过服务器返回的图片url,再次向服务器请求,添加动态新闻图片 noticename = (TextView) findViewById(R.id.noticename); noticename.setText("标题:" + map.get("title").toString()); noticeauthor = (TextView) findViewById(R.id.noticeauthor); noticeauthor.setText("作者:" + map.get("reporterUser").toString()); noticetime = (TextView) findViewById(R.id.noticetime); noticetime.setText("时间:" + map.get("reportTime").toString()); noticeinfo = (TextView) findViewById(R.id.noticeinfo); noticeinfo.setText("动态新闻详情:" + map.get("detail").toString()); newsImageText = (TextView) findViewById(R.id.imgLoadingText); // 获取图片url imgUrl = map.get("activityPhoto").toString(); new Thread() { public void run() { try { URL url; url = new URL(imgUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); InputStream is = conn.getInputStream(); bm = BitmapFactory.decodeStream(is); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } handler.post(runnableUI); } }.start(); } /** * 读取图片的子线程post给主线程的runnable对象,内含各种更新UI的操作 * * @author GloryZSG */ Runnable runnableUI = new Runnable() { public void run() { // (2014.5.1第一种方法)通过服务器返回的图片url,再次向服务器请求,添加动态新闻图片 // 读取Bitmap图片 // 加载到布局文件中 newsImageText.setVisibility(View.GONE); newsImage = (ImageView) findViewById(R.id.imageView); newsImage.setImageBitmap(bm); } }; }
错误:Only the original thread that created a view hierarchy can touch its views——Handler的深入解析
原文地址:http://blog.csdn.net/zsg2063/article/details/25630705