Handler的理解及android.view.ViewRootImpl$CalledFromWrongThreadException错误处理

本文探讨了在Android开发中遇到的ViewRootImpl$CalledFromWrongThreadException异常,原因是直接在非UI线程更新视图导致的。为了解决这个问题,文章详细介绍了Android的Handler消息传递机制,它允许开发者在后台线程中安全地操作UI。Handler的主要功能包括发送Message或Runnable到MessageQueue,并在适当的时间在相应线程中处理它们,以及实现子线程与主线程之间的通信。文中还提到了Looper和MessageQueue的概念,并提供了一个简单的从后台线程获取数据并更新UI的例子。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


在前面的几篇文章中,不管是AsyncTAsk方式,亦或是new一个Runable,都没有涉及UI界面的更新(),今天在进行注册反馈注册信息的时候,就发生了ViewRootImpl$CalledFromWrongThreadException异常,究其原因是因为android中的view和和控件不是线程安全的。

为此android已入了Handler消息传递机制,来实现在新建的线程中操作UI界面。

信息处理类(Handler)允许发送和处理Message或Runable对象到其所在线程的MessageQuene中。Handler主要有以下作用:

(1)将Message或Runable应用Post() 或 sendMessage() 方法发送到MessageQuene中,在发送是可以指定延迟时间,发送时间及要携带的Bundle数据,当MessageQuene循环到该Message时,调用相应的Handler对象的HandlerMessage()方法对其进行处理。

(2)在子线程与主线程进行通信,也就是在工作线程中与UI线程进行通信。

补充:在android中,一个线程对应一个Looper对象,而一个Looper对象又对应一个MessageQuene(消息队列,用于存放Message消息,MessageQuene封装在Looper中),在主线程中,系统会自动为主线程创建Looper对象,并开启消息循环,但在非主线程创建Handler对象(不加Looper.prepare()和Looper.Loop()方法来初始化一个Looper对象),会报Can't create handler inside thread that has not called Looper.prepare()错误。

从飞天开放平台获取注册数据的简单代码:

package com.example.alitest;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import ECSConnecter.SigninConnecter;
import XMLReader.SigninData;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends Activity implements Runnable {
	private Button button;
	private Handler handler;
	private TextView name;
	private TextView pwd;
	private EditText etName;
	private EditText etPwd;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		initView();
		button.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				Thread t = new Thread(MainActivity.this);		//创建新线程
		 		t.start();										//开启线程

				handler = new Handler() {						//这个handler发送的Message会被传递给主线程的MessageQueue。
					public void handleMessage(Message msg) {	//回调
						if (msg.what == 1) {
							name.setText(msg.getData().getString("result"));
							pwd.setText(msg.getData().getString("ID"));
						}
						super.handleMessage(msg);
					}

				};
			}
		}); 
	}

	@Override
	public void run() {
		while (!Thread.currentThread().isInterrupted()) {
			/*
			 * 连接注册进程
			 */
			Map<String, String> map = new HashMap<String, String>();
			map.put("name", etName.getText().toString());
			map.put("password", etPwd.getText().toString());
			SigninConnecter signconn = new SigninConnecter(map); 
			try {
				signconn.getXML();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			signconn.readXML();
			SigninData data = signconn.getData();
			Message m = handler.obtainMessage();	//获取一个Message
			Bundle bundle = new Bundle();			//获取Bundle对象	
			m.what = 1;								//设置消息标识
			bundle.putString("result", data.getResult());
			bundle.putString("ID", data.getId());	//保存数据
			m.setData(bundle);						//将Bundle对象保存到Message中
			handler.sendMessage(m);					//发送消息

		}
	}
	/*
	 * 初始化控件
	 */
	private void initView() {
		name = (TextView) findViewById(R.id.textView1);
		pwd = (TextView) findViewById(R.id.textView2);
		etName = (EditText) findViewById(R.id.etName);
		etPwd = (EditText) findViewById(R.id.etPwd);
		button = (Button) findViewById(R.id.button);
	}
}

像所有的android通信方法一样,别忘了打开internet的权限~


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值