注意:本篇文章是本人阅读相关文章所写下的总结,方便以后查阅,所有内容非原创,侵权删。
本篇文章内容来自于:
Android开发艺术探索
Android第一行代码
Android高级进阶
Android中Handler的使用
Android异步处理技术
目录
- 异步处理技术有哪些?
- Thread(基础类)
--2.1 创建线程(2种方法)
--2.2 线程分类(主线程+Binder线程+后台线程) - HandlerThread
- AsyncQueryHandler(待补)
- IntentService(待补)
- Executor Framework 线程池
- AsyncTask
1.异步处理技术有哪些?
2. Thread(基础类)
线程是Java语言的一种概念,是实际执行任务的基本单元。
Thread是Android中异步处理技术的基础。
2.1 创建线程(2种方法)
方法一:继承Thread类并重写run方法
public class Mythread extends Thread {
@Override
public void run() {
//实现具体的逻辑,如文件读写,网络请求等
}
public void startThread(){
Mythread mythread = new Mythread();
mythread.start();//使用start启动线程
}
}
方法二:实现Runnable接口并实现run方法
public class MyRunnable implements Runnable {
@Override
public void run() {
//实现具体的逻辑,如文件读写,网络请求等
}
public void startThread(){
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();//同样利用start启动线程
}
}
2.2 线程分类
Android应用中各种类型的线程本质上都基于Linux系统的pthreads。
在应用层可以分为三种类型的线程:
1.主线程/UI线程
主线程随着应用启动而启动。
主线程用来运行Android组件,同时刷新屏幕上的UI元素。
非主线程更新UI组件,会抛出CallFromWrongThreadException异常。
为什么只有主线程才能操作UI?
因为Android的UI工具不是线程安全的,只能让他在同一个线程中操作UI来保证线程安全。
为什么主线程会出现阻塞?
主线程中创建的Handler会顺序执行接收到的消息,包括从其他线程发送的消息。
因此如果消息队列中前面的消息没有很快执行完,那么它可能会阻塞队列中的其他消息的及时处理。
2.Binder线程
Binder线程用于不同进程之间线程的通信。
每个进程都维护了一个线程池,用来处理其他进程中线程发送的消息。
其他进程有哪些?
其他进程包括系统服务、Intents、ContentProviders和Service等
大部分情况下,应用不需要关心Binder进程,因为系统会优先将请求转换为使用主线程。
一个典型的需要使用Binder进程场景是:
应用提供一个给其他进程通过AIDL接口绑定的Service。
3.后台线程
在应用中显式创建的线程都是后台线程。
2.3 Thread和Handler、Looper配合使用
(1) 主线程Thread中可直接使用Handler
public class MainActivity extends BaseActivity {
//在执行new Handler()的时候,默认情况下Handler会绑定当前代码执行的线程
//handler在主线程中创建,所以自动绑定主线程
private Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyThread myThread = new MyThread();
myThread.start();
}
class MyThread extends Thread {
@Override
public void run() {
//...
//向另外一个线程发送消息
//运行Runnable代码的线程与Handler所绑定的线程是一致的
Runnable runnable = new Runnable() {
@Override
public void run() {
//进行操作
}
};
handler.post(runnable);
}
}
}
(2) 子线程使用Handler必须先创建Looper
new Thread("thread1") {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
}
}.start();
3. HandlerThread
HandlerThread是一个集成了Looper和MessageQueue的线程。
当启动HandlerThread时,会同时生成Looper和MessageQueue,然后等待消息进行处理。
使用HandlerThread的好处是开发者不需要自己去创建和维护Looper。
HandlerThread中只有一个消息队列,队列中的消息是顺序执行的,因此是线程安全的。队列中的人物可能会被前面没有执行完的任务阻塞。
3.1 使用HandlerThread
用法和普通线程一样。
HandlerThread handlerThread = new HandlerThread("handlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//处理 在handlerThread中
}
};
3.2 HandlerThread的进一步用法(开始接受消息前进行初始化)
可以重写HandlerThread的onLooperPrepared函数。
比如可以在这个函数中创建于HandlerThread关联的Handler实例,这同时也可以对外隐藏我们的Handler实例,提供公共方法来让外界来调用。
public class MyHandlerThread extends HandlerThread {
private Handler handler;
public MyHandlerThread(String name) {
super(name);
}
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
handler = new Handler(getLooper()){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
break;
}
}
};
}
public void publishedMethod1(){
handler.sendEmptyMessage(1);
}
public void publishedMethod2(){
handler.sendEmptyMessage(2);
}
}
4. AsyncQueryHandler
AsyncQueryHandler是用于在ContentProvider上面执行异步的CRUD操作的工具类。
CRUD操作会被放在一个单独的子线程中执行,当操作结束获取到结果后,将通过消息的方式传递给调用AsyncQueryHandler的线程,通常是主线程。
5. IntentService
Service的每个生命周期函数都是运行在主线程的,因此它本身不是一个异步处理技术。
为了能够在Service中实现在子线程中处理耗时任务。Android引入了一个Service的子类:IntentService。
6. Executor Framework 线程池
创建和销毁对象(例如线程),是存在开销的。
如果应用中频繁出现线程的创建和销毁,那么会影响到应用的性能。
使用Java Executor框架可以通过线程池等机制解决这个问题。
Executor框架为开发者提供了如下能力:
- 创建工作线程池,同时通过队列来控制能够在这些线程执行的任务的个数。
- 检测导致线程意外终止的错误
- 等待线程执行完成并获取执行结果
- 批量执行线程,并通过固定的顺序获取执行结果。
- 在合适的时机启动后台线程,从而保证线程执行结果可以很快反馈给用户
Executor框架的基础是Executor接口
Executor的主要目的是分离任务的创建和它的执行。
public interface Executor {
void execute(Runnable command);
}
开发者可以通过实现Executor接口并重写execute方法从而实现自己的Executor类。但实际应用中需要增加类似队列,任务优先级的功能,最终实现一个线程池。
线程池是任务队列和工作线程的集合,这两者组合起来实现生产者消费者模式。
Executor框架为开发者提供了预定义的线程池实现:
- 固定大小的线程池:通过Executors.newFixedThreadPool(n)创建,其中n是线程池中线程的个数
- 可变大小的线程池:通过Executors.newCachedThreadPool()创建。当有新的任务需要执行时,线程池会创建新的线程来处理它,空闲的线程会等待60s来执行新任务,当没有任务可执行时自动销毁,因此可变大小线程池会根据任务队列的大小而变化。
- 单个线程的线程池:通过Executors.newSingleThreadExecutor()创建,这个线程池中永远只有一个线程来串行执行任务队列中的任务。
预定义的线程池都是基于ThreadPoolExecutor类之上构建的,
而通过ThreadPoolExecutor可以自定义线程的一些行为。
ThreadPoolExecutor自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
int corePoolSize, //核心线程数,核心线程会一直存在于线程池中,即使当前没有任务需要处理;当线程数小于核心线程数时,即使当前有空闲的线程,线程池也会优先创建新的线程来处理任务。
int maximumPoolSize,//最大线程数,当线程数大于核心线程数,且任务队列已经满了,这时线程池就会创建新的线程,知道线程数量达到最大线程数为止。
long keepAliveTime,//线程的空闲存活时间,当线程的空闲时间超过这个时间,线程会被销毁,直到线程数等于核心线程数。
TimeUnit unit,//keepAliveTime的单位,可选的有TimeUnit类中的单位
BlockingQueue workQueue);//线程池所使用的任务缓冲队列。
7. AsyncTask
AsyncTask是在Executor框架基础上进行的封装,它将耗时任务移动到工作线程中执行,同时提供方便的接口实现工作线程和主线程的通信。
import android.os.AsyncTask;
public class FullTask extends AsyncTask{
//Params 执行AsyncTask时需要传入的参数,可用于在后台任务中使用
//Progress 后台任务执行时,如果需要在界面上显示当前的进度,则使用这里的泛型当进度单位。
//Result 当任务执行完成后,需要对结果进行返回,则这里泛型作为返回值单位。
@Override
//会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作。比如显示一个进度条对话框
protected void onPreExecute() {
super.onPreExecute();
}
@Override
//在子线程运行,用于处理耗时操作
//任务一旦执行完,就通过return语句返回任务的执行结果
//不可UI操作,如果反馈当前任务的执行速度,调用publishProgress(Progress ...)方法
protected Result doInBackground(Params... params) {
return null;
}
@Override
//当后台任务中调用publishProgress,onProgressUpdate会很快被调用
//可进行UI操作
protected void onProgressUpdate(Progress... values) {
super.onProgressUpdate(values);
}
@Override
//当后台任务执行完毕并通过return语句返回时,调用该方法。
//可UI操作,可用于提醒任务执行的结果,以及关闭掉进度条对话框
protected void onPostExecute(Result result) {
super.onPostExecute(result);
}
@Override
protected void onCancelled() {
super.onCancelled();
}
}
使用
new AsyncTask().execute();
//不同系统版本 AsyncTask的execute和executeOnExecutor方法的运行有差别(串行和并行的区别)。不同的版本不同。
一个应用中使用的所有AsyncTask实例会共享全局的属性,即所有AsyncTask实例会共享一个线程池。
如果AsyncTask中的任务是串行执行的,那么应用中所有的AsyncTask会进行排队,只有等前面的任务执行完成后才会执行下一个。
如果AsyncTask是异步执行的话,那么在四核CPU系统上,最多也只有五个任务可以同时进行,其他任务需要在队列中排队,等待空闲的线程。
AsyncTask内部实现
AsyncTask内部封装了Thread和Handler,通过AsyncTask可以更加方便地执行后台任务以及主线程中访问UI。
但是AsyncTask并不适合特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池