做移动应用开发,尤其是iOS和安卓,首先要有下面的认知:
在安卓里,除了用Java本身的Runnable和Thread之外,比较推荐的做法是使用下面的Handler和AsyncTask。
其实Handler本身是绑定一个消息循环Looper,主要就是发送处理消息。
默认Handler绑定就是主线程的Looper,可以利用Handler的一些特性,比用下面
//下面这两个一般用来做动画 handler.postAtTime(Runnable r, long uptimeMillis); //定时执行某个Runnable,在绑定的线程 handler.postDelayed(Runnable r, long delayMillis); //延时执行某个Runnable,在绑定的线程 //下面这两个一般用来做后台操作,对应的handler也应该绑定在线程上 handler.sendMessageAtTime(Message msg, long uptimeMillis); //定时发送消息 handler.sendMessageDelayed(Message msg, long delayMillis) //延时发送消息
那么怎么绑定Handler在线程里呢?如下,使用HandlerThread:
class MyHandler extends Handler { MyHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch(msg.what) { case MSG_SOMETHING_THREAD: //do something in thread //notify main thread to refresh UI mainHandler.sendMessage(mainHandler.obtainMessage(MSG_SOMETHING_MAIN); break; } } }; HandlerThread ht = new HandlerThread("MyHandlerThread"); ht.start(); subHandler = new MyHandler(ht.getLooper()); mainHandler = new Handler(){ @Override public void handleMessage(Message msg) { switch(msg.what) { case MSG_SOMETHING_MAIN: //do something in main thread //e.g. update UI break; } } };
通过上面的代码,我们获得了两个Handler对象,mainHandler绑定在主线程,subHandler绑定在子线程。那么我们就可以用这两个对象相互发消息来通讯和做异步操作了。
//比如用户点击了某个按钮,要读取SD卡上的很多数据,那么就用下面的代码发subHandler消息 subHandler.sendMessage(subHandler.obtainMessage(MSG_SOMETHING_THREAD); //在subHandler处理完后,会调用下面的代码,给mainHandler发消息,刷新UI mainHandler.sendMessage(mainHandler.obtainMessage(MSG_SOMETHING_MAIN);
使用Handler是可以更加精确的控制,而且可以更加定制化,但是对于普通的应用来讲,只要求把一个耗时操作做成异步的,然后操作前后需要更新UI。这种典型需求,安卓提供了一个非常简单实用的类:AsyncTask。
class DownloadFilesTask extends AsyncTask{ //真正线程里执行的函数,耗时操作 protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); //通知主线程,调用onProgressUpdate,刷新进度 publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } //主线程,在doInBackground之前执行,可以设置显示进度条之类的UI操作 protected void onPreExecute(){ progress.setVisibility(View.VISIBLE); } //主线程,和doInBackground是并行的,一般更新进度 protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress); } //主线程,在doInBackground之后进行,更新UI protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } }; //执行 new DownloadFilesTask().execute(url1, url2, url3);
由上面可以看到,AsyncTask把异步变的更加清晰和简单了,但是使用AsyncTask也有需要注意的,如下: