前言: 咱们在开发Android过程当中,在处理耗时任务和UI交互的过程当中,都会将耗时任务放到子线程处理并刷新. 下面我提出的两个问题,相信大多数开发者都会碰到:java
1. 数据常常须要读取更新,而且比较耗时,须要分步刷新UI.异步
2. UI界面切换后,如何中止掉子线程里面正在读取的数据而不会将旧数据刷新到新UI界面上.ide
目前网上大部分教程主要只是简单的Handler.postDelayed(), Thread + Handler, Async等方式, 只适用于简单的一次性刷新. 或许有人会说我能够采用不断地new Thread的方式来建立子线程刷新,而后传message回去更新UI,可是这样的不断地new会有性能消耗大和数据同步的问题.oop
关于以上这两个问题的解决, 我在这里想要介绍的是使用线程池+Future+handler的配合使用.post
为了更好理解异步更新UI的原理,这里先介绍下Thread + Handler + Looper + Message模型, 以下图1所示:性能
图1 Thread + Handler + Looper + Message模型spa
图1清楚给咱们展现出了消息队列在Looper里面的处理方式,这里有两个重要的要点: (1)子线程也能够经过Looper管理Message, 可是须要加Looper.prepare() 和Looper.loop()才能实现消息循环; (2)UI主线程无需实现prepare()和loop()由于主线程中已经默认实现了.线程
如今开始介绍线程池+Future+handler的一些基本概念和使用demo实例.code
线程池对象
是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样能够避免反复建立线程对象所带来的性能开销,节省了系统的资源.若是在程序中反复建立和销毁线程,将会对程序的反应速度形成严重影响,有时甚至会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); }