Android开发艺术之线程

0
回复
139
查看
[复制链接]

402

主题

403

帖子

5834

安币

手工艺人

发表于 2019-5-7 10:10:01 | 显示全部楼层 |阅读模式
如果对本篇文章感兴趣,请前往,原文地址:http://www.apkbus.com/blog-873055-80003.html

### 本篇的主要内容是Android的线程和线程池:- 概述- 线程形态- AsyncTask- HandlerThread- IntentService- 线程池一.概述1.含义:线程是CPU调度的最小单元。2.特点:线程是一种**受限**的系统资源。即线程不可无限制的产生且线程的创建和销毁都有一定的开销。>**Q**:如何避免频繁创建和销毁线程所带来的系统开销?**A**:采用**线程池**,池中会缓存一定数量的线程,进而达到效果。3.分类:- 按用途可分为两类:- **主线程**:一般一个进程只有一个主线程,主要处理**界面交互**相关的逻辑。- **子线程**:除主线程之外都是子线程,主要用于执行**耗时操作**。- 按形态可分为三类:- **AsyncTask**:底层封装了线程池和**Handler**,便于执行后台任务以及在子线程中进行UI操作。- **HandlerThread**:一种具有**消息循环**的线程,其内部可使用Handler。- **IntentService**:是一种**异步、会自动停止**的服务,内部采用HandlerThread。![Picture](https://upload-images.jianshu.io/upload_images/5494434-17c6e0eaee3499c6)二.线程形态对于主线程和子线程相信已经非常熟悉了,现在主要学习以下三种形态的线程:1.**AsyncTask**a.AsyncTask:一种轻量级的**异步**任务类。>在Android中实现异步任务机制有两种方式:(https://www.jianshu.com/p/1c79fb5296b6" target="_blank">Handler]和AsyncTask。- Handler机制存在的**问题**:代码相对臃肿;多任务同时执行时不易精确控制线程。- 引入AsyncTask的**好处**:创建异步任务更简单,直接继承它可方便实现后台异步任务的执行和进度的回调更新UI,而无需编写任务线程和Handler实例就能完成相同的任务。b.AsyncTask是抽象的泛型类,其组成成员有:- 三个泛型参数:- ```Params```:表示执行AsyncTask需要传入的参数,可用于在后台任务中使用;- ```Progress```:表示后台任务执行的进度;- ```Result```: 表示后台任务的返回结果的类型;- 若没有传递具体的参数,这三个泛型参数都可使用void。如:```//含义:在执行AsyncTask时不需要传入参数给后台任务、使用整型数据来作为进度显示单位,最后使用布尔型数据来反馈执行结果public abstract class AsyncTask```- 五个核心方法:- ```onPreExecute()```:- 运行在:主线程- 调用时刻:在异步任务执行之前被调用- 作用:可用于进行一些界面上的**初始化**操作- ```doInBackground(Params…params)```:- 运行在:子线程- 作用:可用于处理所有的**耗时任务**。若需要更新UI需调用 ```publishProgress(Progress...)```方法- 注意:任务一旦完成就通过return语句将任务的执行结果返回,若Result被指定为void,就可不返回执行结果- ```onProgressUpdate(Progress…values)```:- 运行在:主线程- 调用时刻:在后台任务中调用```publishProgress(Progress...)```之后该方法会被调用- 作用:可利用方法中携带的参数如Progress来对**UI**进行相应地更新- ```onPostExecute(Result result)```:- 运行在:主线程- 调用时刻:在异步任务执行完毕并通过return语句返回时被调用- 作用:可利用方法中返回的数据来进行一些**UI**操作- ```onCancelled()```:- 运行在:主线程- 调用时刻:当异步任务被取消时被调用- 作用:可用于做**界面取消**的更新- 注意:- 不要直接调用onPreExecute()、doInBackground()、onProgressUpdate()、onPostExecute)和onCancelled()方法- AsyncTask对象必须在**主线程**创建- 开始和结束异步任务的方法:- ```execute(Params...params)```- 必须在**主线程**中调用- 作用:表示开始一个异步任务- 注意:一个异步对象只能调用一次execute()方法- ```cancel(booleanmayInterruptIfRunning)```- 必须在**主线程**中调用- 作用:表示停止一个异步任务比如自定义一个AsyncTask,来模拟一个下载任务:```class DownloadTask extends AsyncTask {        @Override//初始化一个ProgressDialog      protected void onPreExecute() {          progressDialog.show();      }        @Override//具体的下载逻辑    protected Boolean doInBackground(Void... params) {          try {  while (true) {      int downloadPercent = doDownload();      publishProgress(downloadPercent);      if (downloadPercent >= 100) {          break;      }  }          } catch (Exception e) {  return false;          }          return true;      }        @Override//显示当前的下载进度    protected void onProgressUpdate(Integer... values) {          progressDialog.setMessage("当前下载进度:"   values[0]   "%");      }        @Override//提示任务的执行结果      protected void onPostExecute(Boolean result) {          progressDialog.dismiss();          if (result) {  Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();          } else {  Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();          }      }  }  ```任务的启动和停止只需要以下几行代码:```// 开始任务  DownloadTask mDownloadTask  = new DownloadTask();  mDownloadTask .execute();     // 停止任务  mDownloadTask .cancel(true);  ```c.工作原理- 内部有一个静态的Handler对象即**InternalHandler**:- 作用:将执行环境从线程池切换到主线程;通过它来发送任务执行的进度以及执行结束等消息。- 注意:必须在主线程中创建- 内部有两个线程池:- **SerialExecutor**:用于任务的排队,默认是**串行**的线程池- **THREAD_POOL_EXECUTOR**:用于真正执行任务。- 排队执行过程:- 把参数Params封装为FutureTask对象,相当于Runnable;- 调用```SerialExecutor.execute()```将FutureTask插入到任务队列tasks;- 若没有正在活动的AsyncTask任务,则就会执行下一个AsyncTask任务。执行完毕后会继续执行其他任务直到所有任务都完成。即默认使用**串行**方式执行任务。>**注意**:AsyncTask不适用于进行特别耗时的后台任务,而是建议用线程池。2.**HandlerThread**a.HandlerThread是一个线程类,它继承自Thread>与普通Thread的区别:具有**消息循环**的效果。原理:- 内部```HandlerThread.run()```方法中有Looper,通过```Looper.prepare()```来创建消息队列,并通过```Looper.loop()```来开启消息循环。b实现方法- 实例化一个HandlerThread对象,参数是该线程的名称;- 通过 ```HandlerThread.start()```开启线程;- 实例化一个Handler并传入HandlerThread中的looper对象,使得与HandlerThread绑定;- 利用Handler即可执行异步任务;- 当不需要HandlerThread时,通过```HandlerThread.quit()```/```quitSafely()```方法来终止线程的执行。```private HandlerThread myHandlerThread ;  private Handler handler ;  @Override  protected void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);     //实例化HandlerThread   myHandlerThread = new HandlerThread("myHandler") ;     //开启HandlerThread   myHandlerThread.start();     //将Handler对象与HandlerThread线程绑定   handler =new Handler(myHandlerThread.getLooper()){         @Override          publicvoid handleMessage(Message msg) {             super.handleMessage(msg);  // 这里接收Handler发来的消息,运行在handler_thread线程中  //TODO...          }      };        //在主线程给Handler发送消息     handler.sendEmptyMessage(1) ;     new Thread(new Runnable() {         @Override          publicvoid run() {             //在子线程给Handler发送数据             handler.sendEmptyMessage(2) ;          }      }).start();  }  @Override  protected void onDestroy() {     super.onDestroy();     //终止HandlerThread运行   myHandlerThread.quit() ;  }  ```c.用途:- 进行**串行**异步通信- 构造IntentService>在之前学习[浅析HandlerThread](https://www.jianshu.com/p/1c79fb5296b6" target="_blank">Handler机制]时知道在子线程使用Handler的方法,其实HandlerThread的出现使得这一过程变得更加简便,更多解析见(http://blog.csdn.net/ta893115871/article/details/55272187)时知道在子线程使用Handler的方法,其实HandlerThread的出现使得这一过程变得更加简便,更多解析见[浅析HandlerThread](http://blog.csdn.net/ta893115871/article/details/55272187)3.**IntentService**a.IntentService是一个继承自Service的抽象类b.优点:- 相比于线程:由于是服务,优先级比线程高,更不容易被系统杀死。因此较适合执行一些**高优先级**的后台任务。- 相比于普通Service:可**自动创建**子线程来执行任务,且任务执行完毕后**自动退出**。c.IntentService内部封装了HandlerThread和Handler,工作原理:- 在```IntentService.onCreate()```里创建一个Handle对象即**HandlerThread**,利用其内部的**Looper**会实例化一个**ServiceHandler**对象;- 任务请求的Intent会被封装到**Message**并通过ServiceHandler发送给Looper的**MessageQueue**,最终在HandlerThread中执行;- 在```ServiceHandler.handleMessage()```中会调用```IntentService.onHandleIntent()```,可在该方法中处理后台任务的逻辑。![Picture](https://upload-images.jianshu.io/upload_images/5494434-f28a930b8c32ca74)d.使用方法:- 新建类并继承IntentService,重写```onHandleIntent()```方法,该方法:- 运行在:子线程,因此可以去处理一些耗时操作。- 作用:从Intent参数中区分具体的任务并执行这些任务- 在配置文件中进行注册。- 在活动中利用Intent实现IntentService的启动:```Intent intent = new Intent(this, MyService.class);intent.putExtra("xxx",xxx);  startService(intent);//启动服务```>注意:无需手动停止服务,```onHandleIntent()```执行结束之后,IntentService会自动停止。三.**线程池**1.优点- **重用**线程池中的线程,避免线程的创建和销毁带来的性能消耗;- 有效控制线程池的**最大并发数**,避免大量的线程之间因互相抢占系统资源而导致阻塞现象;- 进行**线程管理**,提供定时/循环间隔执行等功能。- - 线程池的概念来源:Java中的**Executor**,它是一个接口。- 线程池的真正实现:**ThreadPoolExecutor**,提供一系列参数来配置线程池。```//构造参数public ThreadPoolExecutor(int corePoolSize,      int maximumPoolSize,      long keepAliveTime,      TimeUnit unit,      BlockingQueue workQueue,      ThreadFactory threadFactory,      RejectedExecutionHandler handler) {```- **corePoolSize**:核心线程数- 默认情况下,核心线程会在线程中一直存活。- 当设置ThreadPoolExecutor的**allowCoreThreadTimeOut**属性为- true:表示核心线程闲置超过超时时长,会被回收;- false:表示核心线程不会被回收,会在线程池中一直存活。- **maximumPoolSize**:最大线程数- 当活动线程数达到这个数值后,后续的任务将会被阻塞。- **keepAliveTime**:非核心线程超时时间- 超过这个时长,闲置的非核心线程就会被回收。- 当设置ThreadPoolExecutor的allowCoreThreadTimeTout属性为true时,keepAliveTime对核心线程同样有效。- **unit**:用于指定keepAliveTime参数的时间单位- 单位有:TimeUnit.MILLISECONDS、TimeUnit.SECONDS、TimeUnit.MINUTES等;- **workQueue**:任务队列- 通过线程池的```execute()```方法提交的Runnable对象会存储在这个参数中。- **threadFactory**:线程工厂,可创建新线程- 是个接口,只有一个方法```Thread newThread(Runnable r)```。- **handler**:在线程池无法执行新任务时进行调度。3.ThreadPoolExecutor的**默认工作策略**:- 若程池中的线程数量**未达到**核心线程数,则会直接启动一个核心线程执行任务。- 若线程池中的线程数量**已达到**或者超过核心线程数量,则任务会被插入到任务列表等待执行。- 若任务**无法插入**到任务列表中,往往由于任务列表已满,此时如果- 线程数量**未达到**线程池最大线程数,则会启动一个非核心线程执行任务;- 线程数量**已达到**线程池规定的最大值,则拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法来通知调用者。4.ThreadPoolExecutor线程池的分类:- **FixThreadPool**:- 含义:线程数量固定的线程池,所有线程都是**核心线程**,当线程空闲时**不会**被回收。- 特点:能**快速**响应外界请求。- **CachedThreadPool**:- 含义:线程数量不定的线程池(最大线程数为**Integer.MAX_VALUE**),只有**非核心线程**,空闲线程有超时机制,超时回收。- 特点:适合于执行大量的**耗时较少**的任务- **ScheduledThreadPool**:- 含义:核心线程数量**固定**,非核心线程数量**不定**。- 特点:**定时**任务和**固定**周期的任务。- **SingleThreadExecutor**:- 含义:只有**一个核心线程**,可确保所有的任务都在同一个线程中**按顺序**执行。- 特点:无需处理**线程同步**问题。  继续阅读全文



想在安卓巴士找到更多优质博文,可移步博客区

如果对本篇文章感兴趣,请前往,
原文地址:
http://www.apkbus.com/blog-873055-80003.html
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

领先的中文移动开发者社区
18620764416
7*24全天服务
意见反馈:1294855032@qq.com

扫一扫关注我们

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粤ICP备15117877号 )