当前位置: 首页 > 科技观察

Android多线程的四种方式

时间:2023-03-13 04:19:46 科技观察

当我们启动一个App时,Android系统会启动一个LinuxProcess,其中包含一个Thread,称为UIThread或MainThread。通常应用程序的所有组件都在此进程中运行。当然,你可以修改Manifest.xml中四大组件的代码块()中的android:process属性指定它运行在不同的进程中。组件启动时,如果进程已经存在,则直接通过该进程启动组件,运行在该进程的UIThread中。UIThread中运行着很多重要的逻辑,比如系统事件处理、用户输入事件处理、UI绘制、Service、Alarm等,如下图所示:UIThread中包含的逻辑和我们写的代码是穿插在这些逻辑当中,比如检测并响应用户触摸事件,处理用户输入,绘制自定义View等。如果我们插入的代码比较耗时,比如网络请求或者数据库读取,就会阻塞其他的执行UI线程上的逻辑,导致界面冻结。如果冻结时间超过5秒,系统会报ANR错误。因此,如果我们要进行耗时操作,就需要另起一个线程来执行。新线程执行完耗时逻辑后,往往需要将结果反馈给界面进行UI更新。Android的UI工具包不是线程安全的,不能在非UI线程上执行UI更新。所有界面更新都必须在UI线程上执行。Android提供了四种常见的多线程操作方式,分别是:1.Handler+Thread2。异步任务3。线程池执行器4。IntentService下面分发介绍四种方式。Handler+ThreadAndroid主线程包含一个消息队列(MessageQueue),可以存储一系列的Message或Runnable对象。通过Handler,您可以将Message或Runnable对象发送到此消息队列并处理这些对象。每次创建一个新的Handle对象,它都会绑定到创建它的线程(也就是UI线程)和线程的消息队列中。从那时起,处理程序将开始将Message或Runnable对象传递给消息队列,并在它们出队时执行它们。Handler线程示意图Handler可以将一个Message对象或Runnable对象推入消息队列,然后在UI线程中获取Message或执行Runnable对象。Handler有两种方式推入消息队列,Post和sendMessage:Post方法:Post允许将一个Runnable对象入队到消息队列中。它的方法是:post(Runnable)/postAtTime(Runnable,long)/postDelayed(Runnable,long)对于Handler的Post方法,它会传递一个Runnable对象给消息队列。在这个Runnable对象中,重写run()方法。一般需要在UI线程上执行的操作都写在这个run()方法中。handlerpostusagesendMessage:sendMessage允许将包含消息数据的消息对象推送到消息队列中。它的方法是:sendEmptyMessage(int)/sendMessage(Message)/sendMessageAtTime(Message,long)/sendMessageDelayed(Message,long)Handler如果使用sendMessage将消息放入消息队列,需要传入一个Message对象,并在Handler,需要重写handleMessage()方法,获取工作线程传递过来的消息。此方法在UI线程上运行。Message是final类,不能继承。Handler定义了handlersendMessage用法的优缺点1.Handler的用法简单明了,可以将多个异步任务更新UI的代码放在一起,清晰明了。2、处理单个异步任务的代码稍微适用一些。UIAsyncTaskAsyncTask是android提供的轻量级异步类。可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后将执行结果反馈给UI主线程。AsyncTask通过一个阻塞队列BlockingQuery存储要执行的任务,并使用静态线程池THREAD_POOL_EXECUTOR提供一定数量的线程,默认为128。在Android3.0之前,默认使用并行任务执行器。3.0之后默认改为串行任务执行器。任务的串行执行由静态串行任务执行器SERIAL_EXECUTOR控制,循环取出任务交给THREAD_POOL_EXECUTOR中的线程。执行,执行一个,然后执行下一个。使用示例:classDownloadTaskextendsAsyncTask{//AsyncTask//后面的尖括号是参数(例子中的线程休息时间),进度(publishProgress使用的),以及返回值类型@OverrideprotectedvoidonPreExecute(){//第一种执行方式super.onPreExecute();}@OverrideprotectedStringdoInBackground(Integer...params){//第二种执行方式,在onPreExecute()之后执行for(inti=0;i<=100;i++){publishProgress(i);try{Thread.sleep(params[0]);}catch(InterruptedExceptione){e.printStackTrace();}}返回“执行完成”;}@OverrideprotectedvoidonProgressUpdate(整数...progress){//这个函数是在doInBackground调用publishProgress的时候触发的,虽然调用的时候只有一个参数//但是这里是一个数组,所以使用progesss[0]取值//第N个参数使用progress[n]获取值tv.setText(progress[0]+"%");super.onProgressUpdate(progress);}@OverrideprotectedvoidonPostExecute(Stringresult){//在doInBackground返回时触发,换句话说,TriggeredafterdoInBackgroundisexecuted//这里的result是上面doInBackground执行后的返回值,所以这里是“执行”了setTitle(result);super.onPostExecute(result);}}优缺点1.处理单个异步任务简单,可以获取异步任务的进度2.可以通过cancel方法取消ExecutedAsyncTask3.处理多个异步任务的代码似乎更适用范围1.处理单个异步任务ThreadPoolExecutorThreadPoolExecutor提供了一组线程池,可以管理多个线程并行执行。一方面,它减少了每个并行任务独立创建线程的需要;另一方面,它可以管理多个并发线程的公共资源,从而提高多线程的效率。所以ThreadPoolExecutor更适合执行一组任务。Executors使用工厂模式封装了ThreadPoolExecutor,使用起来更方便。ThreadPoolExecutorExecutors提供了四种创建ExecutorService的方法。它们的使用场景如下:1.Executors.newCachedThreadPool()创建一个定长线程池,每提交一个任务就创建一个线程,直到达到池的最大长度。2.Executors.newFixedThreadPool()创建一个可缓存的线程池。如果当前线程池长度超过处理需要,可以灵活回收空闲线程。当需要增加时,可以灵活的增加新的线程,对池的长度没有任何限制3.Executors.newScheduledThreadPool()创建一个固定长度的线程池,支持定时和周期性的任务执行,类似于Timer4。Executors.newSingleThreadExecutor()创建一个单线程执行器,只创建一个唯一的工作线程来执行任务适用范围1.批处理任务IntentServiceIntentService继承自Service,是一个封装好的轻量级Service,用于接收和处理异步请求通过Intent交付。客户端通过调用startService(Intent)启动一个IntentService,使用一个工作线程依次处理顺序请求,处理完成后自动结束Service。Feature1.一个可以处理异步任务的简单Service原文链接:http://www.jianshu.com/p/2b634a7c49ec