今天看书看到了android的线程,感觉写的挺好的,摘录总结一下。
由于Android的特性,如果在主线程中进行耗时的操作那么就会导致程序无法响应,因此耗时操作必然在子线程中去执行,除了Thread以外在,在Android中扮演线程的还有很多,比如AsyncTask和IntentService,同时HandlerThread也是一种特殊的线程。

其中AsyncTask,IntentService,HandlerThread本质仍然是传统线程。对于AsyncTask来说它的底层用到了线程池,对于IntentService和HandlerThread来说,它们的底层直接使用了线程。

AsyncTask封装了线程池和Handler,它主要为了方便开发者在子线程中更新UI。HandlerThread是一种具有消息循环的线程,在它的内部可以使用Handler.IntentService是一个服务,系统对其进行了封装使其可以更方便地执行后台任务。IntentService内部采用HandlerThread来执行任务,当任务执行完毕后IntentService是自动退出。

1.AsyncTask
AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI.它封装了Thread和Handler.但是AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时的任务建议使用线程池。

AsyncTask是一个抽象的泛型类,它提供了Params,Progress和Result这三个泛型参数,其中Params表示参数类型,Progress表示后台任务执行进度类型,Result则表示后台任务的返回结果的类型,如果AsyncTask确实不需要传递具体的参数,那么这三个泛型参数可以用Void来代替。

public abstract class AsyncTask

AsyncTask提供了4个核心方法,它们的含义如下所示:
1)onPreExecute(),在主线程中执行,在异步任务执行之前,此方法会被调用,一般可以用于做一些准备工作。

2)doInBackground(Params…params),在线程池中执行,此方法用于执行异步任务,params表示异步任务的输入参数。在java中…表示参数的数量不定,他是一种数组型参数。在此方法中可以通过PublishProgress方法来更新任务的进度,publishProgress方法会调用onProgressUpdate方法,另外此方法需要返回计算结果给onPostExecute方法。

3)onProgressUpdate(Progress…values),在主线程中执行,当给后台任务的执行进度发生改变时此方法会被调用。

4)onPostExecute(Result result),在主线程中执行,在异步任务执行后,此方法会被调用,其中result参数是后台任务的返回值,即doInBackground的返回值。

上面几个方法,OnPreExecute先执行,接着是doInBackground,最后才是onPostExecute,除了上述4个方法以外,AsyncTask还提供了onCancelled()方法,它同样在主线程中执行,当异步任务被取消时,onCancelled()方法会被调用,这个时候OnPostExecute则不会被调用。

AsyncTask在具体使用过程中也有一些限制,主要有一下几点:

1>>AsyncTask的类必须在主线程进行加载,这就意味着第一次访问AsyncTask必须发生在主线程,当然这个过程在Android4.1及以上版本中已经被系统自动完成。在Android5.0的源码中,可以查看ActivityThread的main方法,它会调用AsyncTask的init方法,这就满足了AsyncTask必须在主线程中加载的条件了。

2>>AsyncTask的对象必须在主线程中创建。

3>>execute方法必须在UI线程调用。

4>>不要在程序中直接调用onPreExecute(), onPostExecute(), doInbackground()和onProgressUpdate()方法

5>>一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常。

6>>在Android1.6之前,AsyncTask是串行执行任务的,Android1.6的时候AsyncTask开始采用线程池里处理并行任务,但是从Android3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程来串行执行任务。尽管如此,在Android3.0以及后续的版本中,我们仍然可以通过AsyncTask的executeOnExecutor方法来并行地执行任务。

2.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 = Looper.myLooper(); notifyAll();//唤醒等待线程  }  Process.setThreadPriority(mPriority);  onLooperPrepares();  Looper.loop();  mTid = -1;}

从HandlerThread实现来看,它和普通的Thread有显著的不同之处。普通Thread主要在run方法中执行一个耗时的任务,而HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式来通知HandlerThread执行一个具体的任务,它在Android中的应用场景是IntentService.由于HandlerThread是一个无限循环,因此当明确不需要使用HandlerThread时,可以通过它的quit或者quitSafety方法来终止线程的执行,这是一个良好的编程习惯。

3.IntentService
IntentService是一种特殊的Service,它继承了Service并且它是一个抽象类,因此必须创建它的子类才能才能使用IntentService。IntentService可用于执行后台耗时任务,当任务执行后他会自动停止,同时由于IntentService是服务的原因,这导致它的优先级比单纯的线程要高很多,所以IntentService比较适合执行一些高优先级的后台任务,因为它的优先级高不容易被系统杀死。实际上,IntentService封装了HandlerThread和Handler,这一点可以从它的OnCretae方法中看出来。

public void OnCreate(){super.onCreate();HandlerThread thread = new HandlerThread("IntentService["+name+"]");threadd.start();mServiceLooper = thread.getLooper();mServiceHandler = new ServiceHandler(mServiceLooper);}

当IntentService被第一次启动时,它的OnCreate()方法会被调用,OnCreate()方法会创建一个HandlerThread,然后使用它的Looper来构造一个Handler对象mServiceHandler,这样通过mServiceHandler 发送的消息最终会在HandlerThread中执行,从这个角度看IntentService也可以用于执行后台任务,每次启动IntentService,它的OnStartCommand方法就会被调用一次,IntentService在OnStartCommand中处理每个后台任务的Intent,看一下onStartCommand方法是如何处理外界Intent的,OnStartComman调用了OnStart()方法。

public void onStart(Intent intent, int startId){Message msg = mServiceHandler.obtainMessage();msg.arg1 = startId;msg.obj = intent;mServiceHandler.sendMessage(msg);}

可以看出,IntentService仅仅是通过mServiceHandler发送了一个消息,这个消息会在HandlerThread中被处理。mServiceHandler收到消息后,会将Intent对象传递给OnHandleIntent方法去处理。注意这个Intent对象的内容和外界的startService(intent)中的intent的内容是完全一致的,通过这个Intent对象即可解析出外界启动IntentService时所传递的参数,通过这些参数就可以区分具体的后台任务,这样在OnHandelIntent方法中就可以对不同的后台任务做处理了。当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.obj);  stopSelf(msg.arg1);  }}

IntentService的onHandleIntent方法是一个抽象方法,它需要我们在子类中实现,它的作用是从Intent参数中区分具体的任务并执行这些任务。如果目前只存在一个后台任务,那么onHandleIntent方法执行完这个任务后,stopSelf(int startId)就会直接停止服务,如果目前存在多个后台任务,那么当onHandleIntent方法执行完最后一个任务时,stopSelf(int startId)才会直接停止服务。另外,由于每执行一个后台任务就必须启动一次IntentService,而IntentService内部则通过消息的方式向HanderThread请求执行任务,Handler中的Looper是顺序处理消息的,这就意味着IntentService也是顺序执行后台任务的,当多个后台任务同时存在时,这些后台任务会按照外界发起的顺序执行排队。

public class LocalIntentService extends IntentService{private static final String TAG= "LocalIntentService";private LocalIntentService(){super(TAG);}@overrideprotect void onHandleIntent(Intent intent){String action = intent.getStringExtra("task_action");Log.d(TAG, "receive task:"+action);SystemClock.sleep(3000); if("com.ryg.action.TASK1".equal("action")){ Log.d(TAG, "handle task:"+action);}}@overridepublic void onDestroy(){Log.d(TAG, "Service destroy");super.onDestroy();}}

这里对LocalIntentService的实现做了简单的说明,在onHandeIntent方法中会从参数中解析出后台任务标识,即task_actio字段所代表的内容,然后根据不同的任务标识,来执行具体的后台任务,为了简单直接通过SystemClock.sleep(3000);来模拟后台耗时的任务,为了验证IntentService的停止时机,这里在OnDestroy()中打印了一句话。LocalIntentService实现完成了以后,就可以在外界请求执行后台任务了。

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);

运行结果:他们的任务时排队执行的,他们的顺序就是它们发起请求对的顺序,即TASK1,TASK2,TASK3.另外一点就是当TASK3执行完毕后,LocalIntentService才正真地停止了。从日志中可以看出LocalIntentService执行了onDestroy(),这也意味着服务正在停止。

在平时使用IntentService的时候,可以把application中一些耗时的初始化操作(例如:没有明确说明放在主线程中执行的各种第三方的sdk)放到IntentService后台中去启动,这样可以减少启动时间。

 //所有的初始化操作转移到后台,优化app启动速度 InitializeService.start(getApplicationContext());

然后在InitalizeService中重写onHandleIntent()方法,在方法中执行耗时的任务。

  @Override  protected void onHandleIntent(Intent intent) {if (intent != null) { final String action = intent.getAction(); if (“app_lanucher”.equals(action)) { performInit(); }  }  }

执行完OnHandleIntent()中的任务后,会自动执行stopSelf(msg.obj)方法。从ServiceHandler中的源码可以看出来。

    private final class ServiceHandler extends Handler {        public ServiceHandler(Looper looper) {            super(looper);        }        @Override        public void handleMessage(Message msg) {            onHandleIntent((Intent)msg.obj);            stopSelf(msg.arg1);        }    }

最后不要忘记在manifest中注册服务,否则不执行

<service android:name=".InitializeService">service>

更多相关文章

  1. android 那些事---主线程是线程不安全的
  2. Android VideoView设置静音,Android 设置VideoView静音,Android
  3. ListView去掉分割线的几种方法
  4. SDK Platform Tools component is missing! Please use the SDK
  5. Android Market google play store帐号注册方法流程 及发布应用
  6. 【自学Android】使用ModelView,DataBinding,LiveData开发计分板A
  7. Android 实现全屏显示的几种方法整理
  8. Android中TextView中内容不换行的解决方法

随机推荐

  1. Android下affinities和任务(task)
  2. Android学习之Android中Http通信:Http协议
  3. 大厂算法面试真题汇总
  4. ANDROID下面的游戏更新目录
  5. UDP协议编程(单播、多播、广播)
  6. 【Android开发学习03】短时间的音效播放
  7. 再看编译原理
  8. Android(安卓)使用NDK编译sipdroid Libra
  9. 免费的阿里云盘邀请码,我刚试了一下,速度真
  10. 2021.1.14