Android的Handler Looper Message机制应用实例与详解(一)
Android的UI操作不是线程安全的(出于提高性能考虑,避免实现多线程同步等机制所引入的延时),若多个线程同时对UI元素进行操作,可能导致线程安全问题。因此,Android中做了严格的规定:只有UI主线程才能对UI进行设置与操作。
在实际编程中,为了避免UI界面长时间得不到响应而导致的ANR(Application Not Responding)异常,通常将网络访问、复杂运算等一些耗时的操作被放在子线程中执行。这就需要子线程在运行完毕后将结果返回到主线程并通过UI进行显示。在Android中,是通过Handler+Loop+MessageQueue实现线程间通信的。
先看两个实例:
实例1:模拟通过网络下载数据并返回UI显示。
操作过程为:1.UI线程获得用户请求。2.启动子线程完成网络数据下载(网络下载过程通过强制子线程休眠若干秒来模拟)。3.子线程将下载的数据返回UI线程并显示。
主要代码如下:
publicclassMainActivityextendsActionBarActivity{privateButtonmButton;privateTextViewmTextView;privateHandlermHandler;privateThreadmNetAccessThread;privateProgressDialogmProgressDialog;privateintmDownloadCount=0;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.fragment_main);mButton=(Button)findViewById(R.id.btReqNet);mTextView=(TextView)findViewById(R.id.tvDownload);//设置按钮的点击事件监听器mButton.setOnClickListener(newOnClickListener(){@OverridepublicvoidonClick(Viewv){showProgressDialog("","正在下载...");//启动子线程进行网络访问模拟mNetAccessThread=newChildTread();mNetAccessThread.start();}});//继承Handler类并覆盖其handleMessage方法mHandler=newHandler(){//覆盖Handler类的handleMessage方法//接收子线程传递的数据并在UI显示@OverridepublicvoidhandleMessage(Messagemsg){switch(msg.what){case1:mTextView.setText((String)msg.obj);dismissProgressDialog();break;//可以添加其他情况,如网络传输错误//case...default:break;}}};}classChildTreadextendsThread{@Overridepublicvoidrun(){//休眠6秒,模拟网络访问延迟try{Thread.sleep(6000);}catch(InterruptedExceptione){e.printStackTrace();}//将结果通过消息返回主线程Messagemsg=newMessage();msg.what=1;mDownloadCount++;msg.obj=newString("第"+mDownloadCount+"次从网上下载的数据");mHandler.sendMessage(msg);}};/***开启progressDialog.**@paramtitle对话框标题.*@paramcontent对话框正文.*/protectedvoidshowProgressDialog(Stringtitle,Stringcontent){mProgressDialog=newProgressDialog(this);if(title!=null)mProgressDialog.setTitle(title);if(content!=null)mProgressDialog.setMessage(content);mProgressDialog.show();}/***关闭progressDialog.**/protectedvoiddismissProgressDialog(){if(mProgressDialog!=null){mProgressDialog.dismiss();}}}
程序运行效果:
点击下载按钮,UI线程通过handler.sendMessage()向子线程发送消息,子线程收到消息后启动数据下载(通过休眠线程模拟)。
下载完毕,子线程将下载数据返回主线程并显示。
实例2:模拟子线程向主线程发送消息。
操作过程为:1.UI线程获得用户输入的消息内容。2.通过Handler将消息发送给子线程。3.子线程获得消息并通过Toast将内容打印。
主要代码如下:
publicclassMainActivityextendsActionBarActivity{privateEditTextmEditText;privateButtonmButton;privateHandlermHandler;privateThreadmChildTread;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.fragment_main);mEditText=(EditText)findViewById(R.id.etEditText);mButton=(Button)findViewById(R.id.btButton);//设置按钮的点击事件监听器mButton.setOnClickListener(newOnClickListener(){@OverridepublicvoidonClick(Viewv){Messagemsg=newMessage();msg.what=1;msg.obj=mEditText.getText();//将消息发送到子线程mHandler.sendMessage(msg);mEditText.setText("");}});//启动子线程进行msg接收mChildTread=newChildTread();mChildTread.start();}/***子线程为内部类,可以直接访问其外部类的mHandler变量**/classChildTreadextendsThread{@Overridepublicvoidrun(){//以下三步是handlerlooper机制工作的固定模式Looper.prepare();mHandler=newHandler(){publicvoidhandleMessage(Messagemsg){//processincomingmessageshereswitch(msg.what){case1://子线程无权操作UI,只能通过Toast.makeText将收到的消息显示Stringst=msg.obj.toString();if(st==null||st.equals(""))st="收到的消息内容为空";elsest="收到来自主线程的消息:"+st;Toast.makeText(MainActivity.this,st,6000).show();break;//可以添加其他情况,如传输错误//case...default:break;}}};Looper.loop();}};}程序运行效果:
小结:Android通过Handler+Looper+MessageQueue机制实现线程间的通信,本文通过两个简单的实例分别基于该机制实现了UI线程到子线程和子线程到UI线程的消息传递。下一篇博文将会对Handler Looper机制的原理进行深入研究。
附件:http://down.51cto.com/data/2364939声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。