• android UI线程安全问题


      在Android的子线程去更新UI的内容,会导致不确定的异常。

    因为Android有个模式是,单一线程模型:Android UI工具箱(toolkit)不是一个线程安全的,并且它总是被放在主线程上操作。

    public void onClick(View v) {
      new Thread(new Runnable() {
        public void run() {
          Bitmap b = loadImageFromNetwork();
          mImageView.setImageBitmap(b);
        }
      }).start();
    }

    首先,从上面代码看上去好像非常完美,因为它不会阻塞UI线程,不幸的是,它违背了单一线程模型:Android UI工具箱(toolkit)不是一个线程安全的,并且它总是被放在主线程上操作。

    这个ImageView被一个工作线程操作,这导致非常不可思议的问题。跟踪和修复这样一个bug很难并且也耗时。
    Android提供了几种从其他线程访问主线程的方式:
    1. Activity.runOnUiThread(Runnable)
    2. View.post(Runnable)
    3. View.postDelayed(Runnable, long)
    4. Handler

    以上任何一种方式都能修正我们的代码:

    public void onClick(View v) {
      new Thread(new Runnable() {
        public void run() {
          final Bitmap b = loadImageFromNetwork();
          mImageView.post(new Runnable() {
            public void run() {
              mImageView.setImageBitmap(b);
            }
          });
        }
      }).start();
    }

    当然我们还有更简单的方法,使用AsyncTask

    不幸的是,这些类和方法导致我们的代码变得复杂和可读性差。当你实现复杂的操作来频繁的更新界面,使用这种方式变得更加糟糕。为了解决这个问题,Android1.5提供了一个公共类叫做AsyncTask,它简化了任务线程和主线程之间的通信。
     
            在Android1.0和1.1也可使用AsyncTask只不过它的名字为UserTask。
            AsyncTask的目的就是帮助你管理线程。我们之前的例子很容易被改写如下形式:
    public void onClick(View v) {
      new DownloadImageTask().execute("http://example.com/image.png");
    }
    
    private class DownloadImageTask extends AsyncTask<string, integer, Bitmap> {
         protected Bitmap doInBackground(String... urls) {
             return loadImageFromNetwork(urls[0]);
         }
    
         protected void onPostExecute(Bitmap result) {
             mImageView.setImageBitmap(result);
         }
     }
    AsyncTask通过它的子类才能使用。要记住,一个AsyncTask实例必须在主线程创建并且只能被执行一次。完全理解和使用这个类,你可以阅读AsyncTask文档。这里快速的说一下AsyncTask是怎么工作的:
    1>可以通过泛型指定它的类型:参数,进度值,任务的结果值。
    2>doInBackGround()方法自动在工作线程中只想能够。
    3>onPreExecute(),onPostExecute(),onProgressUpdate()方法都在UI线程中执行。
    4>doInBackground()方法返回的值被当作参数传递给onPostExecute()方法。
    5>你能够在doInBackground()方法里任何时候调用publishProgress()方法在UI线程中去执行onProgressUpdate()方法。
     
    下面写一个重载方法比较多得类:
     1 package vic.wong.main;
     2 import android.os.AsyncTask;
     3 import android.widget.ProgressBar;
     4 import android.widget.TextView;
     5 
     6 /**
     7  * 生成该类的对象,并调用execute方法之后
     8  * 首先执行的是onProExecute方法
     9  * 其次执行doInBackgroup方法
    10  *
    11  */
    12 public class ProgressBarAsyncTask extends AsyncTask<Integer, Integer, String> {
    13     private TextView textView;
    14     private ProgressBar progressBar;
    15     public ProgressBarAsyncTask(TextView textView, ProgressBar progressBar) {
    16         super();
    17         this.textView = textView;
    18         this.progressBar = progressBar;
    19     }
    20     /**
    21      * 这里的Integer参数对应AsyncTask中的第一个参数 
    22      * 这里的String返回值对应AsyncTask的第三个参数
    23      * 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改
    24      * 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作
    25      */
    26     @Override
    27     protected String doInBackground(Integer... params) {
    28         NetOperator netOperator = new NetOperator();
    29         int i = 0;
    30         for (i = 10; i <= 100; i+=10) {
    31             netOperator.operator();
    32             publishProgress(i);
    33         }
    34         return i + params[0].intValue() + "";
    35     }
    36 
    37     /**
    38      * 这里的String参数对应AsyncTask中的第三个参数(也就是接收doInBackground的返回值)
    39      * 在doInBackground方法执行结束之后在运行,并且运行在UI线程当中 可以对UI空间进行设置
    40      */
    41     @Override
    42     protected void onPostExecute(String result) {
    43         textView.setText("异步操作执行结束" + result);
    44     }
    45     //该方法运行在UI线程当中,并且运行在UI线程当中 可以对UI空间进行设置
    46     @Override
    47     protected void onPreExecute() {
    48         textView.setText("开始执行异步线程");
    49     }
    50     /**
    51      * 这里的Intege参数对应AsyncTask中的第二个参数
    52      * 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行
    53      * onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操作
    54      */
    55     @Override
    56     protected void onProgressUpdate(Integer... values) {
    57         int vlaue = values[0];
    58         progressBar.setProgress(vlaue);
    59     }
    60 }
  • 相关阅读:
    SQL Server中事务transaction如果没写在try catch中,就算中间语句报错还是会提交
    EF Core中Key属性相同的实体只能被跟踪(track)一次
    Android--数据持久化之内部存储、Sdcard存储
    SQLite的介绍 操作Sqlite 具体实例
    Android提供了5种方式存储数据:
    Eclipse集成Tomcat的配置步骤实例
    如何实现上下左右键盘控制焦点使之落在相邻文本框或下拉框中-Web开发/JavaScript
    android布局
    android布局 及 布局属性
    Android开发之SD卡上文件操作
  • 原文地址:https://www.cnblogs.com/chuanwei-zhang/p/4015353.html
Copyright © 2020-2023  润新知