zoukankan      html  css  js  c++  java
  • Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面

      Android应用的开发过程中需要把繁重的任务(IO,网络连接等)放到其他线程中异步执行,达到不阻塞UI的效果。

      下面将由浅入深介绍Android进行异步处理的实现方法和系统底层的实现原理。

    本文介绍Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面:

                                            即如何使用Thread+Handler的方式从非UI线程发送界面更新消息到UI线程。

    概述:每个Android应用程序都运行在一个dalvik虚拟机进程中,进程开始的时候会启动一个主线程(MainThread),主线程负责处理和ui相关的事件,因此主线程通常又叫UI线程。而由于Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作。如果在非UI线程直接对UI进行了操作,则会报错:

    CalledFromWrongThreadException:only the original thread that created a view hierarchy can touch its views。

    Android为我们提供了消息循环的机制,我们可以利用这个机制来实现线程间的通信。那么,我们就可以在非UI线程发送消息到UI线程,最终让Ui线程来进行ui的操作。

    对于运算量较大的操作和IO操作,我们需要新开线程来处理这些繁重的工作,以免阻塞ui线程。

    例子:下面我们以获取CSDN logo的例子,演示如何使用Thread+Handler的方式实现在非UI线程发送消息通知UI线程更新界面。

    ThradHandlerActivity.java:

    [java] view plaincopy
     
    1. public class ThreadHandlerActivity extends Activity {  
    2.     /** Called when the activity is first created. */  
    3.       
    4.     private static final int MSG_SUCCESS = 0;//获取图片成功的标识  
    5.     private static final int MSG_FAILURE = 1;//获取图片失败的标识  
    6.       
    7.     private ImageView mImageView;  
    8.     private Button mButton;  
    9.       
    10.     private Thread mThread;  
    11.       
    12.     private Handler mHandler = new Handler() {  
    13.         public void handleMessage (Message msg) {//此方法在ui线程运行  
    14.             switch(msg.what) {  
    15.             case MSG_SUCCESS:  
    16.                 mImageView.setImageBitmap((Bitmap) msg.obj);//imageview显示从网络获取到的logo  
    17.                 Toast.makeText(getApplication(), getApplication().getString(R.string.get_pic_success), Toast.LENGTH_LONG).show();  
    18.                 break;  
    19.   
    20.             case MSG_FAILURE:  
    21.                 Toast.makeText(getApplication(), getApplication().getString(R.string.get_pic_failure), Toast.LENGTH_LONG).show();  
    22.                 break;  
    23.             }  
    24.         }  
    25.     };  
    26.       
    27.     @Override  
    28.     public void onCreate(Bundle savedInstanceState) {  
    29.         super.onCreate(savedInstanceState);  
    30.         setContentView(R.layout.main);  
    31.         mImageView= (ImageView) findViewById(R.id.imageView);//显示图片的ImageView  
    32.         mButton = (Button) findViewById(R.id.button);  
    33.         mButton.setOnClickListener(new OnClickListener() {  
    34.               
    35.             @Override  
    36.             public void onClick(View v) {  
    37.                 if(mThread == null) {  
    38.                     mThread = new Thread(runnable);  
    39.                     mThread.start();//线程启动  
    40.                 }  
    41.                 else {  
    42.                     Toast.makeText(getApplication(), getApplication().getString(R.string.thread_started), Toast.LENGTH_LONG).show();  
    43.                 }  
    44.             }  
    45.         });  
    46.     }  
    47.       
    48.     Runnable runnable = new Runnable() {  
    49.           
    50.         @Override  
    51.         public void run() {//run()在新的线程中运行  
    52.             HttpClient hc = new DefaultHttpClient();  
    53.             HttpGet hg = new HttpGet("http://csdnimg.cn/www/images/csdnindex_logo.gif");//获取csdn的logo  
    54.             final Bitmap bm;  
    55.             try {  
    56.                 HttpResponse hr = hc.execute(hg);  
    57.                 bm = BitmapFactory.decodeStream(hr.getEntity().getContent());  
    58.             } catch (Exception e) {  
    59.                 mHandler.obtainMessage(MSG_FAILURE).sendToTarget();//获取图片失败  
    60.                 return;  
    61.             }  
    62.             mHandler.obtainMessage(MSG_SUCCESS,bm).sendToTarget();//获取图片成功,向ui线程发送MSG_SUCCESS标识和bitmap对象  
    63.   
    64. //          mImageView.setImageBitmap(bm); //出错!不能在非ui线程操作ui元素  
    65.   
    66. //          mImageView.post(new Runnable() {//另外一种更简洁的发送消息给ui线程的方法。  
    67. //                
    68. //              @Override  
    69. //              public void run() {//run()方法会在ui线程执行  
    70. //                  mImageView.setImageBitmap(bm);  
    71. //              }  
    72. //          });  
    73.         }  
    74.     };  
    75.       
    76. }  

    main.xml布局文件:

    [html] view plaincopy
     
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     android:orientation="vertical" android:layout_width="fill_parent"  
    4.     android:layout_height="fill_parent">  
    5.     <Button android:id="@+id/button" android:text="@string/button_name" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>  
    6.     <ImageView android:id="@+id/imageView" android:layout_height="wrap_content"  
    7.         android:layout_width="wrap_content" />  
    8. </LinearLayout>  

    strings.xml

    [html] view plaincopy
     
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     android:orientation="vertical" android:layout_width="fill_parent"  
    4.     android:layout_height="fill_parent">  
    5.     <Button android:id="@+id/button" android:text="@string/button_name" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>  
    6.     <ImageView android:id="@+id/imageView" android:layout_height="wrap_content"  
    7.         android:layout_width="wrap_content" />  
    8. </LinearLayout>  

    Manifest.xml:

    [html] view plaincopy
     
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    3.       package="com.zhuozhuo"  
    4.       android:versionCode="1"  
    5.       android:versionName="1.0">  
    6.     <uses-sdk android:minSdkVersion="9" />  
    7.     <uses-permission android:name="android.permission.INTERNET"></uses-permission><!--不要忘记设置网络访问权限-->  
    8.   
    9.     <application android:icon="@drawable/icon" android:label="@string/app_name">  
    10.         <activity android:name=".ThreadHandlerActivity"  
    11.                   android:label="@string/app_name">  
    12.             <intent-filter>  
    13.                 <action android:name="android.intent.action.MAIN" />  
    14.                 <category android:name="android.intent.category.LAUNCHER" />  
    15.             </intent-filter>  
    16.         </activity>  
    17.   
    18.     </application>  
    19. </manifest>  


    运行结果:



    为了不阻塞ui线程,我们使用mThread从网络获取了CSDN的LOGO

    ,并用bitmap对象存储了这个Logo的像素信息。

    此时,如果在这个线程的run()方法中调用

    [java] view plaincopy
     
    1. mImageView.setImageBitmap(bm)  

    会出现:CalledFromWrongThreadException:only the original thread that created a view hierarchy can touch its views。原因是run()方法是在新开的线程中执行的,我们上面提到不能直接在非ui线程中操作ui元素。

    非UI线程发送消息到UI线程分为两个步骤

    一、发送消息到UI线程的消息队列

    通过使用Handler的

    [java] view plaincopy
     
    1. Message obtainMessage(int what,Object object)  

    构造一个Message对象,这个对象存储了是否成功获取图片的标识what和bitmap对象,然后通过message.sendToTarget()方法把这条message放到消息队列中去。

    二、处理发送到UI线程的消息

    在ui线程中,我们覆盖了handler的 

    [java] view plaincopy
     
    1. public void handleMessage (Message msg)   

    这个方法是处理分发给ui线程的消息,判断msg.what的值可以知道mThread是否成功获取图片,如果图片成功获取,那么可以通过msg.obj获取到这个对象。

    最后,我们通过

    [java] view plaincopy
     
    1. mImageView.setImageBitmap((Bitmap) msg.obj);  

    设置ImageView的bitmap对象,完成UI的更新。

    补充:

    事实上,我们还可以调用

    View的post方法来更新ui

    [java] view plaincopy
     
    1. mImageView.post(new Runnable() {//另外一种更简洁的发送消息给ui线程的方法。  
    2.                   
    3.                 @Override  
    4.                 public void run() {//run()方法会在ui线程执行  
    5.                     mImageView.setImageBitmap(bm);  
    6.                 }  
    7.             });  

    这种方法会把Runnable对象发送到消息队列,ui线程接收到消息后会执行这个runnable对象。

    总结:从例子中我们可以看到handler既有发送消息和处理消息的作用,会误以为handler实现了消息循环和消息分发,其实Android为了让我们的代码看起来更加简洁,与UI线程的交互只需要使用在UI线程创建的handler对象就可以了。

    http://blog.csdn.net/mylzc/article/details/6736988

  • 相关阅读:
    hdu 6702 ^&^ 位运算
    hdu 6709 Fishing Master 贪心
    hdu 6704 K-th occurrence 二分 ST表 后缀数组 主席树
    hdu 1423 Greatest Common Increasing Subsequence 最长公共上升子序列 LCIS
    hdu 5909 Tree Cutting FWT
    luogu P1588 丢失的牛 宽搜
    luogu P1003 铺地毯
    luogu P1104 生日
    luogu P1094 纪念品分组
    luogu P1093 奖学金
  • 原文地址:https://www.cnblogs.com/kings-boke/p/4249212.html
Copyright © 2011-2022 走看看