Learn && Live
虚度年华浮萍于世,勤学善思至死不渝
前言
Hey,欢迎阅读Connor学Android系列,这个系列记录了我的Android原理知识学习、复盘过程,欢迎各位大佬阅读斧正!原创不易,转载请注明出处:http://t.csdn.cn/BK6K4,话不多说我们马上开始!
1.更新UI的三种方式
在工作线程中更新UI(通过某些特定方法)
(1)使用 View 的 postInvalidate() 方法刷新页面
(2)使用 View 的 post() 或 postDelayed() 方法更新UI
(3)使用 Activity 的 runOnUIThread() 方法更新UI
TextView tv = (TextView) findViewById(R.id.hello_text);
// 1
tv.postInvalidate();
// 2
tv.post(new Runnable() {
@Override
public void run() {
tv.setText("Hi,Fresh By Post");
}
});
tv.postDelayed(new Runnable() {
@Override
public void run() {
tv.setText("Hi,Fresh By postDelayed :");
}
},1000);
// 3
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText("OH,I am Fine!!!");
}
});
使用 Handler
(1)Handler是专门用来在线程之间传递消息的工具类,主要用于UI界面的更新、消息的传递与处理
(2)Handler发送消息的两种方法:
-
send方法:用于发送一个包含数据的Message对象,并在handleMessage(Message message)方法中处理
-
post系列的方法:用于发送一个Runnable对象,并在MessageQueue收到消息时执行
使用 AsyncTask
(1)AsyncTask是Android给我们提供的一种轻量级的异步任务类
(2)使用步骤:
- 自定义一个继承 AsyncTask 类的子类
- 重写 onPreExecute()、onPostExecute()、onProgressUpdate()、onCancelled() 方法
- 在使用时 new 一个该类的对象,调用 execute() 方法完成更新
2.AsyncTask 重要方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H6U8AMF8-1662515145951)(F:\yhy925\dailylife\Work\2.Android\2.《Android进阶之光》\《Android进阶之光》知识点总结\3.AsncTask\1.方法.png)]
onPreExecute():在主线程执行。一般在任务执行前做准备工作,比如对 UI 做一些标记
doInBackground(Params… params):在线程池中执行。在 onPreExecute() 执行后执行,用于执行较为耗时的操作。在执行过程中可以调用 publishProgress(Progress… values) 来更新进度信息
onProgressUpdate(Process… values):在主线程中执行。当调用 publishProgress() 时,此方法会将进度更新到UI组件上
onPostExecute(Result result):在主线程中执行。当后台任务执行完成后,这个方法会被执行。doInBackground 方法得到的结果就是返回的 result 的值。此方法一般做任务执行后的收尾工作,比如更新 UI 和数据
3.AsyncTask 源码分析
public abstract class AsyncTask<Params, Progress, Result> { }
AsyncTask 是一个抽象的泛型类,它有三个泛型参数,如果不需要某个参数,可以将其设置为 void
(1)Params:参数类型
(2)Progress:后台任务执行进度的类型
(3)Result:返回结果的类型
3.1 Android 3.0 之前的 AsyncTask
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 1;
……
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
(1)线程池使用的是 ThreadPoolExecutor ,核心线程数是5个,最大线程数是128,非核心线程空闲等待新任务的最长时间为1s
(2)采用的阻塞队列是 LinkedBlockingQueue,容量为10
(3)综合(1)(2),AsyncTask 最多同时容纳139个任务,超出后就会执行饱和策略,默认抛出 RejectedExecutionException
3.2 Android 7.0 版本的 AsyncTask
重要属性
(1)WorkerRunnable 实现了 Callable 接口,并实现了 call 方法,在 call 方法中调用了 doInBackground 方法来处理任务并得到结果,并最终调用 postResult 将结果投递出去
(2)FutureTask 是一个可管理的异步任务,实现了 Runnable 和 Future 接口,可以包装 Runnable 和 Callable,并提供给 Executor 执行,也可以调用线程直接执行(FutureTask.run())
(3)在构造函数中,将 WorkerRunnable 作为参数传给了 FutureTask
执行过程
(1)调用 AsyncTask 的 execute() 方法
(2)execute() 方法内会做下面三件事
- 首先调用 onPreExecute() 方法
- 将 AsyncTask 的参数传给 WorkerRunnable,再在构造函数中将 WorkerRunnable 作为参数传递到 FutureTask 中
- 将 FutureTask 作为参数,调用 exec 的 execute() 方法
(3)这个 exec 是一个 SerialExecutor,完成对 FutureTask 列表进行串行执行,它的 execute() 方法内会完成下面几件事
- 维护一个 ArrayDeque 的任务队列,将之前的 FutureTask 加到队列中
- 处理这个 FutureTask,因为 FutureTask 是一个逐层封装的异步任务,所以接下来会依次完成
- 调用 FutureTask 的 run() 方法
- 由构造函数可知,run() 方法内部调用 WorkRunnable 的 call() 方法
- 同样由构造函数可知,call() 方法内部会首先调用 doInBackground()
- 执行完 doInBackground() 内部的操作后,会调用 postResult() 方法将执行结果(Callable)
- 在 postResult() 方法内会创建一个标志为 MESSAGE_POST_RESULLT 的 Message,再通过 getHandler() 方法获取一个 InternalHandler 发送这个 Message
- InternalHandler 中接收到标志为 MESSAGE_POST_RESULT 的 Message 后会调用 AsyncTask 的 finish() 方法
- finish() 方法中会判断这个 AsyncTask 任务是否被取消,若被取消则执行 onCancelled() 方法,否则执行 onPostExecute() 方法
- 通过这个 onPostExecute() 方法,我们才最终完成了这个任务的执行过程,并得到了异步任务执行后的结果
串行任务处理
SerialExecutor 实际上是一个 ThreadPoolExecutor,只不过对其做了处理,只能完成对 FutureTask 的串行处理,那是如何实现的呢?
(1)首先回到 exec 的 execute() 方法中,其中不仅维护了一个任务队列,还有一个指向队首的 FutureTask 的 Runnable 引用 mActive
(2)当处理队列中的第一个任务或 FutureTask 的 run() 方法执行完毕时,会调用synchronized修饰的 scheduleNext() 方法
(3)scheduleNext() 方法中会取出队首元素并赋值给 mActive,并由 ThreadPoolExecutor 调用 execute() 方法执行任务并返回结果
并行任务处理
那么有没有可能借助 AsyncTask 实现对任务的并行处理吗,答案是肯定的
Android 3.0及以上版本将 SerialExecutor 作为默认的线程,并没有完全删除原有的 ThreadPoolExecutor,因此可以通过如下代码实现
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
也可以通过该方法传入其他类型的线程池,甚至是自定义的线程池
// 其他类型
task.executeOnExecutor(Executors.newCachedThreadPool(), "");
// 自定义类型
Execuror exec = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
task.executeOnExecutor(exec, "");
onProgressUpdate
综合上面的叙述并没有发现这个方法的调用和执行过程,那么它是在什么情况下被调用的呢?
(1)首先明确可以在重写的 doInBackground() 方法中调用 publishProgress() 方法
(2)这个方法内部与 postResult() 方法类似,创建一个标志为 MESSAGE_POST_PROGRESS 的 Message 并通过 InternalHandler 发送
(3)InternalHandler 中接收到标志为 MESSAGE_POST_PROGRESS 的 Message 后就会调用 AsyncTask 的 onProgressUpdate() 方法
4.AsyncTask 使用要求
- AsyncTask 类必须在UI线程中被加载(Android 4.1之后自动执行)
- AsyncTask 实例对象,必须在UI线程中创建
- 必须在 UI 线程中调用 execute() 来运行任务
- 必须创建 AsyncTask 的子类,以它的子类形式来使用
- 必须在子类中至少实现 doInBackground() 回调方法,通常我们还会实现 onPostExecute() 方法
- 不要手动调用 onPreExecute(), onPostExecute(Result), doInBackground(Params…), onProgressUpdate(Progress…) 方法
- AsyncTask 后台任务,只能执行一次,否则会抛出异常
- 整个进程中的 AsyncTask 线程池及任务队列都是同一个(自定义的除外),大量的调用 AsyncTask 执行后台任务会造成任务队列整体执行时间的变长,导致任务执行的时间不可控
- AsyncTask 的默认线程优先级是 Process.THREAD_PRIORITY_BACKGROUND(后台线程级别),优先级较低,分配的 CPU 资源会较少,不适合执行优先级较高的任务
5.AsyncTask 的问题
生命周期问题与内存泄露
AsyncTask会一直执行,直到doInBackground()方法执行完毕。如果AsyncTask被声明为Activity的非静态内部类,那么AsyncTask会持有对Activity的引用,如果在销毁activity的时候没有取消正在运行的AsyncTask,这会导致Activity没法被回收,从而导致内存泄露
结果丢失
屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效
处理不当容易导致任务阻塞
所以如果一个AsyncTask的doInBackGround方法中存在着一个无限循环没有正确处理,导致这个AsyncTask的doInBackGround方法一直运行,那么这会导致同一个进程中其他的AsyncTask的任务无法执行