Android的UI操作不是线程安全的(出于提高性能考虑,避免实现多线程同步等机制所引入的延时),若多个线程同时对UI元素进行操作,可能导致线程安全问题。因此,Android中做了严格的规定:只有UI主线程才能对UI进行设置与操作。
网站建设哪家好,找创新互联!专注于网页设计、网站建设、微信开发、小程序制作、集团企业网站建设等服务项目。为回馈新老客户创新互联还提供了竞秀免费建站欢迎大家使用!
在实际编程中,为了避免UI界面长时间得不到响应而导致的ANR(Application Not Responding)异常,通常将网络访问、复杂运算等一些耗时的操作被放在子线程中执行。这就需要子线程在运行完毕后将结果返回到主线程并通过UI进行显示。在Android中,是通过Handler+Loop+MessageQueue实现线程间通信的。
先看两个实例:
实例1:模拟通过网络下载数据并返回UI显示。
操作过程为:1.UI线程获得用户请求。2.启动子线程完成网络数据下载(网络下载过程通过强制子线程休眠若干秒来模拟)。3.子线程将下载的数据返回UI线程并显示。
主要代码如下:
public class MainActivity extends ActionBarActivity { private Button mButton; private TextView mTextView; private Handler mHandler; private Thread mNetAccessThread; private ProgressDialog mProgressDialog; private int mDownloadCount = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_main); mButton = (Button) findViewById(R.id.btReqNet); mTextView = (TextView) findViewById(R.id.tvDownload); //设置按钮的点击事件监听器 mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showProgressDialog("","正在下载..."); //启动子线程进行网络访问模拟 mNetAccessThread = new ChildTread(); mNetAccessThread.start(); } }); //继承Handler类并覆盖其handleMessage方法 mHandler = new Handler(){ //覆盖Handler类的handleMessage方法 //接收子线程传递的数据并在UI显示 @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: mTextView.setText((String) msg.obj); dismissProgressDialog(); break; //可以添加其他情况,如网络传输错误 //case... default: break; } } }; } class ChildTread extends Thread { @Override public void run() { //休眠6秒,模拟网络访问延迟 try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } //将结果通过消息返回主线程 Message msg = new Message(); msg.what = 1; mDownloadCount ++; msg.obj = new String("第"+mDownloadCount+"次从网上下载的数据"); mHandler.sendMessage(msg); } }; /** * 开启progressDialog. * * @param title 对话框标题. * @param content 对话框正文. */ protected void showProgressDialog(String title,String content) { mProgressDialog = new ProgressDialog(this); if(title != null) mProgressDialog.setTitle(title); if(content != null) mProgressDialog.setMessage(content); mProgressDialog.show(); } /** * 关闭progressDialog. * */ protected void dismissProgressDialog() { if(mProgressDialog != null) { mProgressDialog.dismiss(); } } }
程序运行效果:
点击下载按钮,UI线程通过handler.sendMessage()向子线程发送消息,子线程收到消息后启动数据下载(通过休眠线程模拟)。
下载完毕,子线程将下载数据返回主线程并显示。
实例2:模拟子线程向主线程发送消息。
操作过程为:1.UI线程获得用户输入的消息内容。2.通过Handler将消息发送给子线程。3.子线程获得消息并通过Toast将内容打印。
主要代码如下:
public class MainActivity extends ActionBarActivity { private EditText mEditText; private Button mButton; private Handler mHandler; private Thread mChildTread; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_main); mEditText = (EditText) findViewById(R.id.etEditText); mButton = (Button) findViewById(R.id.btButton); // 设置按钮的点击事件监听器 mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Message msg = new Message(); msg.what = 1; msg.obj = mEditText.getText(); //将消息发送到子线程 mHandler.sendMessage(msg); mEditText.setText(""); } }); //启动子线程进行msg接收 mChildTread = new ChildTread(); mChildTread.start(); } /** * 子线程为内部类,可以直接访问其外部类的mHandler变量 * */ class ChildTread extends Thread { @Override public void run() { //以下三步是handler looper机制工作的固定模式 Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here switch (msg.what) { case 1: //子线程无权操作UI,只能通过Toast.makeText将收到的消息显示 String st = msg.obj.toString(); if (st == null || st.equals("")) st = "收到的消息内容为空"; else st = "收到来自主线程的消息:" + st; Toast.makeText(MainActivity.this, st, 6000).show(); break; //可以添加其他情况,如传输错误 //case... default: break; } } }; Looper.loop(); } }; } 程序运行效果:
小结:Android通过Handler+Looper+MessageQueue机制实现线程间的通信,本文通过两个简单的实例分别基于该机制实现了UI线程到子线程和子线程到UI线程的消息传递。下一篇博文将会对Handler Looper机制的原理进行深入研究。