zoukankan      html  css  js  c++  java
  • Android 异步更新UI----handler+thread

    android应用是单线程模式的。

    单线程模式需要记住两条:

    一、防止UI线程阻塞

    二、确保只在UI线程中访问Android UI工具包

    在开发Android应用时必须遵守单线程模型的原则:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。

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

    开一个线程或者在后台线程中来执行耗时的操作,如下面的例子:

    public void onClick( View v ) {  

    new Thread( new Runnable() {   

        public void run() {  

    Bitmap b = loadImageFromNetwork();   //从网络上下载图片

    mImageView.setImageBitmap( b );  //把图片设置给ImageView

    }

        }).start()

     }

    上面的代码会报错,你可能会说逻辑很正确啊,但是它违背了Android单线程模型:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行.

    例如: 如果在非UI线程直接对UI进行了操作,则会报错:

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

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

    Andriod提供了几种在其他线程中访问UI线程的方法:

    Activity.runOnUiThread( Runnable )

    View.post( Runnable )   

    View.postDelayed( Runnable, long )   

    Hanlder

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

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

    ThradHandlerActivity.java:

    [java] view plain copy
     
    1. package com.example.thread;  
    2.   
    3. import org.apache.http.HttpResponse;  
    4. import org.apache.http.client.HttpClient;  
    5. import org.apache.http.client.methods.HttpGet;  
    6. import org.apache.http.impl.client.DefaultHttpClient;  
    7.   
    8. import com.example.test.R;  
    9.   
    10. import android.annotation.SuppressLint;  
    11. import android.app.Activity;  
    12. import android.graphics.Bitmap;  
    13. import android.graphics.BitmapFactory;  
    14. import android.os.Bundle;  
    15. import android.os.Handler;  
    16. import android.os.Message;  
    17. import android.view.View;  
    18. import android.view.View.OnClickListener;  
    19. import android.widget.Button;  
    20. import android.widget.ImageView;  
    21. import android.widget.Toast;  
    22.   
    23. public class ThreadHandlerActivity extends Activity{  
    24.   
    25.     private static final int MSG_SUCCESS = 0;  
    26.     private static final int MSG_FAILURE = 1;  
    27.       
    28.     private ImageView mImageView;  
    29.     private Button mButton;  
    30.       
    31.     private Thread mThread;  
    32.       
    33.     @SuppressLint("HandlerLeak")  
    34.     private Handler mHandler = new Handler(){  
    35.         @Override  
    36.         public void handleMessage(Message msg) {  
    37.             switch (msg.what) {  
    38.             case MSG_SUCCESS:  
    39.                 mImageView.setImageBitmap((Bitmap)msg.obj);  
    40.                 Toast.makeText(getApplication(), "成功获取图片", Toast.LENGTH_LONG).show();    
    41.                 break;   
    42.   
    43.             case MSG_FAILURE:    
    44.                 Toast.makeText(getApplication(), "获取图片失败", Toast.LENGTH_LONG).show();    
    45.                 break;  
    46.             }  
    47.             super.handleMessage(msg);  
    48.         }  
    49.           
    50.     };  
    51.   
    52.     @Override  
    53.     protected void onCreate(Bundle savedInstanceState) {  
    54.         super.onCreate(savedInstanceState);  
    55.         setContentView(R.layout.thread_layout);  
    56.         mImageView= (ImageView) findViewById(R.id.logo);//显示图片的ImageView    
    57.         mButton = (Button) findViewById(R.id.click);  
    58.         mButton.setOnClickListener(new OnClickListener() {  
    59.             @Override  
    60.             public void onClick(View v) {  
    61.                 if (mThread == null) {  
    62.                     mThread = new Thread(runnable);  
    63.                     mThread.start();  
    64.                 }else {    
    65.                     Toast.makeText(getApplication(), "线程已经运行", Toast.LENGTH_LONG).show();    
    66.                 }   
    67.             }  
    68.         });  
    69.     }  
    70.       
    71.     Runnable runnable = new Runnable() {  
    72.         @Override  
    73.         public void run() {  
    74.             HttpClient hc = new DefaultHttpClient();  
    75.             HttpGet hg = new HttpGet("http://csdnimg.cn/www/images/csdnindex_logo.gif");  
    76.             final Bitmap bm;  
    77.             try {  
    78.                 HttpResponse hr = hc.execute(hg);  
    79.                 bm = BitmapFactory.decodeStream(hr.getEntity().getContent());  
    80.             } catch (Exception e) {  
    81.                 e.printStackTrace();  
    82.                 mHandler.obtainMessage(MSG_FAILURE).sendToTarget();  
    83.                 return;  
    84.             }  
    85.             mHandler.obtainMessage(MSG_SUCCESS, bm).sendToTarget();  
    86. //          mImageView.setImageBitmap(bm); //出错!不能在非ui线程操作ui元素  
    87. //           mImageView.post(new Runnable() {//另外一种更简洁的发送消息给ui线程的方法。    
    88. //             @Override    
    89. //             public void run() {//run()方法会在ui线程执行    
    90. //                 mImageView.setImageBitmap(bm);    
    91. //             }    
    92. //         });    
    93.         }  
    94.     };  
    95. }  
    96.   

    对于上面的方法,我们使用的是handler+Thread来实现更新UI,在里面也有一条注意的就是

    [java] view plain copy
     
    1. mImageView.setImageBitmap(bm); //出错!不能在非ui线程操作ui元素</span>  

    其实我们上面提到一个方法Activity.runOnUiThread( Runnable ),将这个Runnable以UI线程的方式启动

    [java] view plain copy
     
    1. /** 
    2.      * Runs the specified action on the UI thread. If the current thread is the UI 
    3.      * thread, then the action is executed immediately. If the current thread is 
    4.      * not the UI thread, the action is posted to the event queue of the UI thread. 
    5.      * 
    6.      * @param action the action to run on the UI thread 
    7.      */  
    8.     public final void runOnUiThread(Runnable action) {  
    9.         if (Thread.currentThread() != mUiThread) {  
    10.             mHandler.post(action);  
    11.         } else {  
    12.             action.run();  
    13.         }  
    14.     }


    上面Activity的runOnUiThread(Runnable)方法实现。
    利用Activity.runOnUiThread(Runnable)把更新ui的代码创建在Runnable中,然后在需要更新ui时,把这个Runnable对象传给Activity.runOnUiThread(Runnable)。 这样Runnable对像就能在ui程序中被调用。如果当前线程是UI线程,那么行动是立即执行。如果当前线程不是UI线程,操作是发布到事件队列的UI线程。
    使用示例:

    [java] view plain copy
     
    1. current_activity.this. runOnUiThread(new Runnable()   
    2.                     @Override  
    3.                    public void run() {   
    4.                           // refresh ui 的操作代码  
    5.                    }  
    6. });

    这里需要注意的是runOnUiThread是Activity中的方法,在线程中我们需要告诉系统是哪个activity调用,所以前面显示的指明了activity.

    所以我们修改一下上面的代码:

    [java] view plain copy
     
    1. package com.example.thread;  
    2.   
    3. import org.apache.http.HttpResponse;  
    4. import org.apache.http.client.HttpClient;  
    5. import org.apache.http.client.methods.HttpGet;  
    6. import org.apache.http.impl.client.DefaultHttpClient;  
    7.   
    8. import com.example.test.R;  
    9.   
    10. import android.annotation.SuppressLint;  
    11. import android.app.Activity;  
    12. import android.graphics.Bitmap;  
    13. import android.graphics.BitmapFactory;  
    14. import android.os.Bundle;  
    15. import android.os.Handler;  
    16. import android.os.Message;  
    17. import android.view.View;  
    18. import android.view.View.OnClickListener;  
    19. import android.widget.Button;  
    20. import android.widget.ImageView;  
    21. import android.widget.Toast;  
    22.   
    23. public class ThreadHandlerActivity extends Activity{  
    24.   
    25.     private static final int MSG_SUCCESS = 0;  
    26.     private static final int MSG_FAILURE = 1;  
    27.       
    28.     private ImageView mImageView;  
    29.     private Button mButton;  
    30.       
    31.     @SuppressLint("HandlerLeak")  
    32.     private Handler mHandler = new Handler(){  
    33.         @Override  
    34.         public void handleMessage(Message msg) {  
    35.             switch (msg.what) {  
    36.             case MSG_SUCCESS:  
    37.                 mImageView.setImageBitmap((Bitmap)msg.obj);  
    38.                 Toast.makeText(getApplication(), "成功获取图片", Toast.LENGTH_LONG).show();    
    39.                 break;   
    40.   
    41.             case MSG_FAILURE:    
    42.                 Toast.makeText(getApplication(), "获取图片失败", Toast.LENGTH_LONG).show();    
    43.                 break;  
    44.             }  
    45.             super.handleMessage(msg);  
    46.         }  
    47.           
    48.     };  
    49.   
    50.     @Override  
    51.     protected void onCreate(Bundle savedInstanceState) {  
    52.         super.onCreate(savedInstanceState);  
    53.         setContentView(R.layout.thread_layout);  
    54.         mImageView= (ImageView) findViewById(R.id.logo);//显示图片的ImageView    
    55.         mButton = (Button) findViewById(R.id.click);  
    56.         mButton.setOnClickListener(new OnClickListener() {  
    57.             @Override  
    58.             public void onClick(View v) {  
    59.                 ThreadHandlerActivity.this.runOnUiThread(runnable);  
    60.             }  
    61.         });  
    62.     }  
    63.       
    64.     Runnable runnable = new Runnable() {  
    65.         @Override  
    66.         public void run() {  
    67.             HttpClient hc = new DefaultHttpClient();  
    68.             HttpGet hg = new HttpGet("http://csdnimg.cn/www/images/csdnindex_logo.gif");  
    69.             final Bitmap bm;  
    70.             try {  
    71.                 HttpResponse hr = hc.execute(hg);  
    72.                 bm = BitmapFactory.decodeStream(hr.getEntity().getContent());  
    73.             } catch (Exception e) {  
    74.                 e.printStackTrace();  
    75.                 mHandler.obtainMessage(MSG_FAILURE).sendToTarget();  
    76.                 return;  
    77.             }  
    78.             mImageView.setImageBitmap(bm);  
    79.         }  
    80.     };  
    81.       
    82. }  

    也可以在线程里面直接更新UI。

    有人会说我传递一个当前的Activity到一个线程中,然后实现UI更新,那我就是调用的当前的Activity的内容,其实这个也是不对的也会提示

                       [java] view plain copy
     
      1. android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.  
  • 相关阅读:
    js操作FCKeditor方法(转)
    CommandArgument绑定多个值
    fckeditor给文件(包括图片)及文件夹增加删除功能
    linq中批量删除方法
    .net里使用 escape 和 unescape(转)
    类型转换
    MEP中创建基于面的风口族
    布尔运算符和位运算符
    循环结构之FOR语句
    坏天气
  • 原文地址:https://www.cnblogs.com/DreamRecorder/p/9132842.html
Copyright © 2011-2022 走看看