主线程和子线程
主线程是指进程所拥有的线程,在java中默认情况下一个进程只有一个线程,这个线程就是主线程。主线程主要处理界面交互相关的逻辑,因为用户随时会和界面发生交互,因此主线程在任何时候都必须有较高的响应速度,否则就会产生一种界面卡顿的感觉。为了保持较高的响应速度,就要求主线程不能执行耗时的任务,这个时候子线程就派上用场了。子线程也叫工作线程,除了主线程之外的线程都是子线程。
Android 沿用了java、的线程模型,其中的线程也分为主线程和子线程,其中的主线程也叫UI线程。主线程的作用是运行四大组件以及处理他们和用户的交互,而子线程的作用则是执行耗时任务,比如网络请求、I/O操作等。从Android 3.0开始系统要求网络访问必须在子线程中进行,否则网络访问将会失败并抛出NetworkOnMainThreadExceptioin这个异常,这样做是为了避免由于被耗时操作所阻塞从而出现ANR现象。
AsynaTask
AsynaTask 是一种轻量级的异步人物类,他可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程中更新UI。从实现上来说,AnyncTask封装了Thread和Handler,通过AsyncTask可以更加方便的执行后台任务以及在主线程中访问UI,但是AsyncTask并不适合进行特别耗时的任务,对于特别耗时的任务来说,建议使用线程池。
AsynaTask 是一个抽象的泛型类,他提供了Params、Progress和Result这三个泛型参数,其中Params表示参数的类型,Projress表示后台任务的执行进度的类型,而Result则表示后台任务的返回结果的类型,如果AsyncTask确实不需要传递具体的参数,那么这三个泛型参数可以用Void来代替。AsyncTask这个类的申明如下所示。
public abstract class AsyncTask<Params,Progress,Result>
1)onPreExecute(),在主线程中执行,在异步任务执行之前,此方法会被调用,一般可以用于做一些准备工作。
2)doInBackground(Params...params),在线程池中执行,此方法用于执行异步任务,params参数表示异步任务的输入参数。在此方法中可以通过publishProgress方法来更新任务条的进度,publishProgress方法会调用onProgressUpdate方法。另外此方法需要返回计算结果给onPostExecute方法。
3)onProgressUpdate(Progress... values),在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用。
4)onPostExecute(Result result),在主线程中执行,在异步任务执行之后,此方法会被调用,其中result参数是后台任务的返回值,即doInBackground的返回值。
上面这几个方法,onPreExecute先执行,接着是doInBackground,最后才是onPosttExecute。除了上述四个方法以外,AsyncTask还提供了onCancelled()方法,他同样在主线程中执行,当异步任务呗取消时,onCancelled()方法会被调用,这个时候onPostExecute则不会被调用。下面提供一个典型的实例,如下所示。
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { // totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } protected void onProgressUpdate(Integer... progress) { // setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { // showDialog("Downloaded " + result + " bytes"); } }
new DownloadFilesTask().execute(url1,url2,url3);
AsyncTask在具体的使用过程中也是有一些限制的,,主要有如下几点:
1)AsyncTask的类必须在主线程中加载,这就意味着第一次访问AsyncTask必须发生在主线程
2)AsyncTask的对象必须在主线程中创建。
3)execute方法必须在UI线程调用。
4)不要在程序中直接调用onPreExecute(),onPostExecute,doInBackground和onProgressUpdate方法。
5)一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常。
HandlerThread
HandlerThread继承了Thread,他是一种可以使用Handler的thread,他的实现也很简单,就是在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环,这样在实际的使用中就允许在HandlerThread中创建Handler了。HandlerThread的run方法如下所示。
public void run(){ mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = getMainLooper().myLooper(); notifyAll(); } Process.setThreadPriority(mPrioroty); onLooperPrepared(); Looper.loop(); mTid = -1; }
从HandlerThread的实现来看,它个普通的Thread有显著的不同之处。普通的Thread主要用于在run方法中执行一个耗时任务,而HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式来通知HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式来通知HandlerThread执行一个具体的任务。HandlerThread是一个很有用的类,他在Android 中的一个具体的使用场景是IntentService,IntentSersive将在后面介绍。由于HandlerThread的run方法是一个无线循环,因此当明确不需要再使用HandlerThread时,可以通过他的quit或者quitSafely方法来终止线程的执行,这是一个良好的编程习惯。
IntentService
IntentService是一种特殊的Service,它继承了Service并且它是一个抽象类,因此必须创建它的子类才能使用IntentService。IntentService可用于执行后台耗时的任务,当任务执行后他会自动停止,同时由于IntentService是服务的原因,这导致他的优先级比单纯的线程高很多,所以IntentService比较适合执行一些高级优先的后台任务,因为它优先级高不容易被系统杀死。在实现上,IntentService封装了HandlerThread和Handler,这一点可以从它的onCreate方法中看出来,如下所示。
public void onCreate(){ super.onCreate(); HandlerThread thread = new HandlerThread("IntentService["+mName+"]"); thread.start(); mServiceLooper = thread.getLooper(); mServiceLooper = new ServiceHandler(mServiceLooper); }
public void onStart(Intent intent,int startId){ Message message = mServiceHandler.obtainMessage(); message.arg1 = startId; message.obj =intent; mServiceHandler.sendMessage(msg); }
可以看出,IntentService仅仅是通过mServiceHandler发送了一个消息,这个消息会在HandlerThread中被处理。mServiceHandler收到消息后,会将传递给onHabdleIntebt方法去处理。注意这个Intent对象的内容和外界的starrtService(intent)中的intent的内容是完全一致的,通过这个Intent对象的内容和外界的startService(intent) 中的intent的内容是完全一致的,通过这个Intent对象即可解析出外界启动IntentService是所传递的参数,通过这些参数就可以区分具体的后台任务,这样在onHanleIntent方法中就可以对不同的后台任务来做处理了。当onHandleIntent 方法执行结束后,IntentService会通过stopSelf(int startId) 方法来尝试停止服务。这里之所以采用stopSelf(int startId)而不是stopSelf()来停止服务,那是因为stopSelf()会立刻停止服务,而这个时候可能还有其他消息未处理,stopSelf(int startId)则会等待所有的消息都处理完毕后才终止服务。一般来说,stopSelf(int startId)在尝试停止服务之前会判断最近启动服务的次数是否和startId相等,如果相等就立刻停止服务,不相等则不停止服务,这个策略可以从AMS的stopServiceToken方法的实现中找到依据。ServiceHandler的实现如下所示。
private final class ServiceHandler extends Handler{ public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { onHandleIntent((Intent)msg.abj); super.handleMessage(msg); } }
下面通过一个示例来进一步说明IntentService的工作方式,首先派生一个IntentService的子类,比如LocalIntentService,它的实现如下所示。
public class LocalIntentService extends IntentService { private static final String TAG = "LocalIntentService"; public LocalIntentService() { super(TAG); } @Override protected void onHandleIntent(Intent intent) { String action = intent.getStringExtra("task_action"); Log.d(TAG, "receive task :" + action); SystemClock.sleep(3000); if ("com.ryg.action.TASK1".equals(action)) { Log.d(TAG, "handle task: " + action); } } @Override public void onDestroy() { Log.d(TAG, "service destroyed."); super.onDestroy(); }
Intent service = new Intent(this,LocalIntentService.class); service.putExtra("task_action", "com.ryg.action.TASK1"); startService(service); service.putExtra("task_action", "com.ryg.action.TASK2"); startService(service); service.putExtra("task_action", "com.ryg.action.TASK3"); startService(service);