安卓线程异步处理的小结
概念了解
Android会存在两种线程:一种是UI主线(UI Thread)程一种是工作线程(Work Thread)
在子线程中处理耗时的任务,任务完成后通过Handler通知UI主线程更新UI界面
主线程不允许进行耗时的操作(比如说网络请求和访问),否则容易出现ANR现象
ANR
anr:application not reponse:应用程序无响应
主线程:UI线程
anr产生的原因:主线程需要做很多重要的事情,响应点击事件,更新ui,如果在主线程里面阻塞时间过久,应用程序就会无响应,为了避免应用程序出现anr,所有的耗时的操作,都应该放在子线程中执行。
Android 异步消息处理机制解析
Android异步消息处理主要分为四个部分,Message、Handler、MessageQueue、Looper。
Message
Message 是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。通常使用 Message 的 what 字段携带命令,除此之外还可以使用 arg1 和arg2 字段来携带一些整形数据,使用 obj 字段携带一个 Object 对象
使用示例
var message=Message()
message.what=0x123
message.obj=1
handlerMess.sendMessage(message)
//在主线程中创建一个Handler
var handlerMess: Handler = object : Handler() {
//处理消息
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
0x123 -> {
Log.i("Message","Message="+msg.obj)
}
}
}
}
返回结果:
Message=1
Handler
Handler 顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用 Handler 的 sendMessage()方法,而发出的消息经过一系列地辗转处理后,最终会传递到 Handler 的 handlerMessage()方法中。
MessageQueue
MessageQueue 是消息队列的意思,它主要用于存放所有通过 Handler 发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个 MessageQueue 对象。
Looper
Looper 是每个线程中的 MessageQueue 的管家,调用 Looper 的 loop() 方法后,就会进入到一个无限循环当中,然后每当发现 MessageQueue 中存在一条消息,就会将它取出,并传递到 Handler 的 handleMessage() 方法中。每个线程中也只会有一个 Looper 对象。
Android提供了四种常用的操作多线程的方式
1. Thread
使用示例
Thread{
tv_statue.post(Runnable {
tv_statue.text = "下载中"
ToastUtils.show(baseContext, "下载中")
})
Thread.sleep(2000)
tv_statue.post(Runnable {
tv_statue.text = "下载完成"
ToastUtils.show(baseContext, "下载完成")
})
}.start()
2. Thread+Handler
优点
1. Handler用法简单明了,可以将多个异步任务更新UI的代码放在一起,清晰明了。
2. 处理单个异步任务代码略显多。
适用范围
3. 多个异步任务的更新UI。
使用示例
Thread {
handler.sendEmptyMessage(0)
Thread.sleep(3000)
handler.sendEmptyMessage(1)
}.start()
----------------------------------------
var handler = object : Handler() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
0 -> {
ToastUtils.show(baseContext, "开始下载")
}
1 -> {
ToastUtils.show(baseContext, "下载完成")
}
}
}
}
3. AsyncTask
异步任务 执行完一个,再执行下一个。 在线程池中执行后台任务
优点
方便实现异步通信,节省资源
1. 处理单个异步任务简单,可以获取到异步任务的进度
2. 可以通过cancel方法取消还没执行完的AsyncTask
3. 处理多个异步任务代码显得较多
适用范围
1. 单个异步任务的处理
使用示例
var time = 3
val asyncTask: AsyncTask<Int, Int, String?> =
object : AsyncTask<Int, Int, String?>() {
override fun doInBackground(vararg params: Int?): String? {
for (i in params[0]!! downTo 1) {
try {
Thread.sleep(1000)
publishProgress(i) //调用onProgressUpdate方法
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
return "计时结束"
}
override fun onPostExecute(result: String?) {
//结束--返回doInBackground里面返回的信息
super.onPostExecute(result)
tv_statue.text = result
Log.i("AsyncTask==","onPostExecute"+result)
}
override fun onProgressUpdate(vararg values: Int?) {
//过程更新
super.onProgressUpdate(*values)
tv_statue.text = values[0].toString()
Log.i("AsyncTask==","onProgressUpdate"+values[0].toString())
}
}
asyncTask.execute(time)
返回:
AsyncTask==: onProgressUpdate3
AsyncTask==: onProgressUpdate2
AsyncTask==: onProgressUpdate1
AsyncTask==: onPostExecute计时结束
4.ThreadPoolExecutor
线程池:可以管理多个线程并行执行
虽然线程的创建销毁的开销相对较小,但是频繁得创建和销毁也会消耗有限的资源,从而带来性能上的浪费,也不够高效。因此线程池的出现就是为了解决这一问题,即在初始状态创建并维护一定数量的空闲线程,当有需要执行的任务,就交付给线程中的一个线程,任务执行结束后,该线程也不会死亡,而是回到线程池中重新变为空闲状态。
优点
减少线程频繁创建销毁的资源开销,同时能够有效控制系统中并发线程的数量,防止系统性能的剧烈下降。
适用范围
1. 批处理任务
newSingleThreadExecutor
单个线程的线程池,即线程池中每次只有一个线程工作,单线程串行执行任务 这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
使用示例
inner class MyThread : Thread() {
override fun run() {
super.run()
var name=currentThread().name
println(name + "正在执行....");
}
}
//创建一个可重用固定线程数的线程池
val pool: ExecutorService = Executors.newSingleThreadExecutor()
//创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口;
val t1: Thread = MyThread()
val t2: Thread = MyThread()
val t3: Thread = MyThread()
val t4: Thread = MyThread()
val t5: Thread = MyThread()
//将线程放到池中执行;
pool.execute(t1)
pool.execute(t2)
pool.execute(t3)
pool.execute(t4)
pool.execute(t5)
//关闭线程池
pool.shutdown()
返回结果:
pool-1-thread-1正在执行…
pool-1-thread-1正在执行…
pool-1-thread-1正在执行…
pool-1-thread-1正在执行…
pool-1-thread-1正在执行…
newFixedThreadPool
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。 线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
使用示例
//创建一个可重用固定线程数的线程池
var pool: ExecutorService? =
Executors.newFixedThreadPool(3)
//创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口;
val t1: Thread = MyThread()
val t2: Thread = MyThread()
val t3: Thread = MyThread()
val t4: Thread = MyThread()
val t5: Thread = MyThread()
//将线程放到池中执行;
pool.execute(t1)
pool.execute(t2)
pool.execute(t3)
pool.execute(t4)
pool.execute(t5)
//关闭线程池
pool.shutdown()
返回结果:
pool-2-thread-2正在执行…
pool-2-thread-3正在执行…
pool-2-thread-1正在执行…
pool-2-thread-2正在执行…
pool-2-thread-3正在执行…
newCachedThreadPool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
使用示例
//创建一个可重用固定线程数的线程池
val pool =
Executors.newCachedThreadPool()
//创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口;
val t1: Thread = MyThread()
val t2: Thread = MyThread()
val t3: Thread = MyThread()
val t4: Thread = MyThread()
val t5: Thread = MyThread()
//将线程放到池中执行;
pool.execute(t1)
pool.execute(t2)
pool.execute(t3)
pool.execute(t4)
pool.execute(t5)
//关闭线程池
pool.shutdown()
范湖结果:
pool-3-thread-4正在执行…
pool-3-thread-3正在执行…
pool-3-thread-2正在执行…
pool-3-thread-5正在执行…
pool-3-thread-1正在执行…
newScheduledThreadPool
大小无限制的线程池,支持定时和周期性的执行线程
使用示例
var exec: ScheduledThreadPoolExecutor = ScheduledThreadPoolExecutor(1)
exec.scheduleAtFixedRate(
Runnable //每隔一段时间就触发异常
{
// TODO Auto-generated method stub
println("111111111111")
}, 1000, 5000, TimeUnit.MILLISECONDS
)
exec.scheduleAtFixedRate(
Runnable //每隔一段时间打印系统时间,证明两者是互不影响的
{
// TODO Auto-generated method stub
println(System.nanoTime())
}, 1000, 2000, TimeUnit.MILLISECONDS
)
返回结果:
111111111111
23119318857491
23121319071841
23129318176148
23123319007891
111111111111
23125318176937
23127318190359
111111111111
23131318344312
23133318465896
111111111111
23135319645812
5.IntentService
IntentService继承自Service,是一个经过包装的轻量级的Service,用来接收并处理通过Intent传递的异步请求。客户端通过调用startService(Intent)启动一个IntentService,利用一个work线程依次处理顺序过来的请求,处理完成后自动结束Service。
IntentService封装了HandlerThread和Handler,但是它继承了Service,所以导致它的优先级比单纯线程要高,所以IntentService适合执行一些高优先级的后台任务。
特点
一个可以处理异步任务的简单Service
当任务执行完后,IntentService 会自动停止,不需要我们去手动结束。
使用示例
public class MyIntentService extends IntentService {
private int count = 10;
private LocalBroadcastManager mLocalBroadcastManager;
public MyIntentService(String name) {
super(name);
}
public MyIntentService() {
super("someName");// 关键是这句话
}
@Override
public void onCreate() {
super.onCreate();
mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
try {
Thread.sleep(1000);
while (count!=0){
count--;
sendThreadStatus("倒计时中...", count);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 发送进度消息
*/
private void sendThreadStatus(String status, int progress) {
Intent intent = new Intent("action.type.thread");
intent.putExtra("status", status);
intent.putExtra("progress", progress);
mLocalBroadcastManager.sendBroadcast(intent);
}
}
//注册广播--IntentService
mLocalBroadcastManager = LocalBroadcastManager.getInstance(this)
mBroadcastReceiver = MyBroadcastReceiver()
val intentFilter = IntentFilter()
intentFilter.addAction("action.type.thread")
mLocalBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter)
/**
* 非静态内部类,关键字inner
*可以访问外部类的成员变量
*/
inner class MyBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
when (intent.action) {
"action.type.thread" -> {
//更改UI
val progress = intent.getIntExtra("progress", 0)
tv_statue.text = progress.toString()
if (progress == 0) {
tv_statue.text = "下载结束"
}
}
}
}
}
//开启
val intent = Intent(this, MyIntentService::class.java)
startService(intent)
返回结果:
下载
9
8
7
6
5
4
3
2
1
下载结束
参考文献
Java-线程池 ThreadPool 专题详解 (美团面试题)