Android 异步更新UI-线程池-Future-Handler实例分析

 前言: 我们在开发Android过程中,在处理耗时任务和UI交互的过程中,都会将耗时任务放到子线程处理并刷新. 下面我提出的两个问题,相信大多数开发者都会碰到:

1. 数据经常需要读取更新,并且比较耗时,需要分步刷新UI.

2. UI界面切换后,如何停止掉子线程里面正在读取的数据而不会将旧数据刷新到新UI界面上.

 目前网上大部分教程主要只是简单的Handler.postDelayed(), Thread + Handler, Async等方式, 只适用于简单的一次性刷新. 或许有人会说我可以采用不断地new Thread的方式来创建子线程刷新,然后传message回去更新UI,但是这样的不断地new会有性能消耗大和数据同步的问题.

 关于以上这两个问题的解决, 我在这里想要介绍的是使用线程池+Future+handler的配合使用.

 为了更好理解异步更新UI的原理,这里先介绍下Thread + Handler + Looper + Message模型, 如下图1所示:

  图1 Thread + Handler + Looper + Message模型

  图1清楚给我们展示出了消息队列在Looper里面的处理方式,这里有两个重要的要点: (1)子线程也可以通过Looper管理Message, 但是需要加Looper.prepare() 和Looper.loop()才能实现消息循环; (2)UI主线程无需实现prepare()和loop()因为主线程中已经默认实现了.

  现在开始介绍线程池+Future+handler的一些基本概念和使用demo实例.

线程池

  是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源.如果在程序中反复创建和销毁线程,将会对程序的反应速度造成严重影响,有时甚至会Crash掉程序.这里我们使用简单的ExecutorService类.

Future

  Future模式可以这样来描述:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。就相当于下了一张订货单,一段时间后可以拿着提订单来提货,这期间可以干别的任何事情。其中Future 接口就是订货单,真正处理订单的是Executor类,它根据Future接口的要求来生产产品。

Handler

  连接子线程和主线程的桥梁,可以通过sendmessage或者post的方式跟主线程通信.

说了这么多,如果还有对基本概念不太熟悉的童鞋可以先移步到最后的参考文章里看下再回来看本文章,此处直接上代码干货.

方法一:利用sendMessage实现

public class MyActivity extends Activity {

    private final String TAG = DemoExecutorService;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        initFindView();
        setListener();
    }

    private TextView mTitle;
    private Button mBtn;
    private void initFindView() {
        mTitle = (TextView) findViewById(R.id.title);
        mBtn = (Button) findViewById(R.id.btn);
    }

    private void setListener() {
        mBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                TestCallBack testCallBack = new TestCallBack();
                testCallBack.loadToHandler();
            }
        });
    }

    private class TestCallBack {
        public TestCallBack() {
            Log.d(TAG, #####TestCallBack===Constructor);
        }

        public void loadToHandler() {
            Handler myHandler = new Handler(getMainLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    Log.d(TAG, #######receive the msg?? what =  + msg.what);
                    int num = msg.what;
                    switch(num){
                        case 1:
                            mTitle.setText(######Yeah, we receive the first msg 1);
                            break;
                        case 2:
                            mTitle.setText(######Yeah, we receive the second msg 2);
                            break;
                        default:
                            break;
                    }
                }
            };
            testExecutorHandler(myHandler);
        }
    }

    private final ExecutorService mExecutor  = Executors.newSingleThreadExecutor();
    Future<!--?--> mTask;
    boolean mSendMsg;

    public void testExecutorHandler(final Handler handler) {
        Log.d(TAG, ########testExecutorHandler, mTask =  + mTask);
        if(mTask != null) {
            // 通过取消mTask,来实现之前排队但未运行的submit的task的目的,通过标志位不让其发msg给UI主线程更新.
            mTask.cancel(false);
            Log.d(TAG, ########mTask.isCannel? ===  + mTask.isCancelled());
            mSendMsg = false;
        }
        Runnable r = new Runnable() {
            @Override
            public void run() {
                mSendMsg = true;
                try {
                    Log.d(TAG, ###step 1####start to sleep 6s.);
                    Thread.sleep(6000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Message msg;
                Log.d(TAG, #######1111 mSendMsg ===  + mSendMsg);
                if(mSendMsg) {
                    msg = handler.obtainMessage();
                    msg.what = 1;
                    handler.sendMessage(msg);
                } else {
                    return ;
                }
                Log.d(TAG, ####step 2####start to sleep 4s.);
                try {
                    Thread.sleep(4000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 若没有重新obtainMessage的话,就会出现以下错误,因为已经被回收, 所以报错. 需要重新 obtainMessage().
//                E/AndroidRuntime( 1606): java.lang.IllegalStateException: The specified message queue synchronization  barrier token has not been posted or has already been removed.
                Log.d(TAG, #######22222 mSendMsg ===  + mSendMsg);
                if(mSendMsg) {
                    msg = handler.obtainMessage();
                    msg.what = 2;
                    handler.sendMessage(msg);
                } else {
                    return ;
                }
            }
        };
//        mExecutor.submit(r);      // 若只是这样子就不会进入Future任务里面,那样每一个submit提交的都会被依次执行.
        mTask = mExecutor.submit(r);
    }

}

结果和打印如下图2和3所示:

图4用多次点击来模拟UI不停调用刷新的情况,后台的执行任务会只是保留当前task和最后一次提交的task,中间的task都被Futurecancel掉了.而且当前旧的task也会受到标志位的控制,不会将更新内容sendMessage出来,从而不会影响最后一次UI的刷新.

方法二:利用runnable实现更新:

由于部分方法跟上面一样,所以要看完整代码可以在下面下载,以下只是核心代码.

public void loadToRunnable() {
           Runnable runable = new Runnable(){
               @Override
               public void run() {
                   Log.d(TAG, #########Ok..1111 let update callback1...);
                   mTitle.setText(####Yeah, Refresh in runnable, callback1);
               }
           };

           Runnable runable2 = new Runnable() {
               @Override
               public void run() {
                   Log.d(TAG, ######Ok, let update callback2, ...);
                   mTitle.setText(####Callback2 update success!!!);
               }
           };

           testExecutorRunnable(runable, runable2, getMainLooper());
       }
   }
public void testExecutorRunnable(final Runnable callback,
                             final Runnable callback2, final Looper looper) {
        Log.d(TAG, #####testExecutor##mTask..#### =  + mTask);
        if(mTask != null) {www.2cto.com
            mTask.cancel(false);        // true 表示强制取消,会发生异常; false表示等待任务结束后取消.
            mSendMsg = false;
        }

        Runnable r = new Runnable(){
            @Override
            public void run() {
                mSendMsg = true;
                try {
                    Log.d(TAG, #####Step 1####Async run after submit...###sleep 6s);
                    Thread.sleep(6000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(callback != null && mSendMsg){
//                    Handler handler = new Handler();      // Not use it, should use the Looper.
                    Handler handler = new Handler(looper);
                    handler.post(callback);
                }

                try {
                    Log.d(TAG, #####Step 2####Async run after submit...###sleep 4s);
                    Thread.sleep(4000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(callback2 != null && mSendMsg) {
                    Handler handler = new Handler(looper);
                    handler.post(callback2);
                }
            }
        };

        mTask = mExecutor.submit(r);
    }
时间: 2024-11-08 15:07:11

Android 异步更新UI-线程池-Future-Handler实例分析的相关文章

android异步更新UI界面的方法

在android平台下,进行多线程编程时,经常需要在主线程之外的一个单独的线程中进行某些处理,然后更新用户界面显示.但是,在主线线程之外的线程中直接更新页面显示的问题是:系统会报这个异常,android.view.viewroot$calledfromwrongthreadexception: only the original thread that created a view hierarchy can touch its views. (只有原始创建这个视图层次(view hierach

android异步更新UI的几种方法

前言 ?我们知道在android开发中不能在非ui线程中更新ui,但是,有的时候我们需要在代码中执行一些诸如访问网络.查询数据库等耗时操作,为了不阻塞ui线程,我们时常会开启一个新的线程(工作线程)来执行这些耗时操作,然后我们可能需要将查询到的数据渲染到ui组件上,那么这个时候我们就需要考虑异步更新ui的问题了. android中有下列几种异步更新ui的解决办法: Activity.runOnUiThread(Runnable) View.post(Runnable) long) View.po

Android异步更新UI的方式之使用Handler的post(Runnabel r)方法

由于性能要求,android要求只能在UI线程中更新UI,要想在其他线程中更新UI,给大家介绍一种方式:使用Handler的post(Runnabel r)方法 下面用这种方式更新一个TextView: package com.example.runonuithreadtest;  import android.app.Activity;  import android.os.Bundle;  import android.os.Handler;  import android.widget.T

Android异步更新UI的方式之使用Handler消息传递机制

由于性能要求,android要求只能在UI线程中更新UI,要想在其他线程中更新UI,给大家介绍一种方式:使用Handler消息传递机制. 下面用这种方式更新一个TextView: package com.example.runonuithreadtest; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.widget.TextView; public

Android异步更新UI的方式之使用AsyncTask异步任务

由于性能要求,android要求只能在UI线程中更新UI,要想在其他线程中更新UI,给大家介绍一种方式:使用AsyncTask异步任务. 下面用这种方式更新一个TextView: 注:更新UI的操作只能在onPostExecute(String result)方法中. package com.example.runonuithreadtest;  import android.app.Activity;  import android.os.AsyncTask;  import android.

Android异步更新UI的方式之使用runOnUiThread(action)方法

由于性能要求,android要求只能在UI线程中更新UI,要想在其他线程中更新UI,给大家介绍一种方式:使用runOnUiThread(action)方法 下面用这种方式更新一个TextView: 1.  package com.example.runonuithreadtest; 2.  import android.app.Activity; 3.  import android.os.Bundle; 4.  import android.widget.TextView; 5.  publi

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

Android应用的开发过程中需要把繁重的任务(IO,网络连接等)放到其他线程中异步执行,达到不阻塞UI的效果. 下面将由浅入深介绍Android进行异步处理的实现方法和系统底层的实现原理. 本文介绍Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面: 即如何使用Thread+Handler的方式从非UI线程发送界面更新消息到UI线程. 概述:每个Android应用程序都运行在一个dalvik虚拟机进程中,进程开始的时候会启动一个主线程(MainThread),

Android异步处理二:使用AsyncTask异步更新UI界面

Android异步处理二:使用AsyncTask异步更新UI界面 - lzc的专栏 - 博客频道 - CSDN.NET 在<Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面>中,我们使用Thread+Handler的方式实现了异步更新UI界面,这一篇中,我们介绍一种更为简洁的实现方式:使用AsyncTask异步更新UI界面. 概述: AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程与UI线程交互的辅助类.AsyncTask的

Android异步处理系列文章四篇之二 使用AsyncTask异步更新UI界面

Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面Android异步处理二:使用AsyncTask异步更新UI界面Android异步处理三:Handler+Looper+MessageQueue深入详解Android异步处理四:AsyncTask的实现原理 Android异步处理二:使用AsyncTask异步更新UI界面 概述: AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程与UI线程交互的辅助类.AsyncTask的内部实现