Android线程相关基础知识整理

本文介绍了Android系统中线程的运行机制,指出耗时操作会阻塞UI线程,需新开线程执行。同时详细阐述了Android中4种常用的多线程实现方式,包括Handler+Thread、AsyncTask、ThreadPoolExecutor和IntentService,还提及了Handler的内存泄漏问题及解决办法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有关安卓基础知识整理,自己能想到的整理一下,有写的不对的地方,欢迎指导

线程

Android系统本身是通过cpu产生数据,交给gpu进行渲染刷新页面,每16ms刷新一次界面以保证UI的流畅。
当启动一个APP,Android系统就会生成一个线程,APP中的所有组件都会在其中展示,运行。
而我们编写的代码,就运行在系统UI之间,夹缝中运行,就像人体的构造一样,一条主干,代码相当于血肉,穿插于脊椎周围,构成一个人。如果插入的代码耗时严重,就会阻塞UI线程,界面卡顿,如果卡顿5s,就会导致ANR(Activity响应时间超过5s,Broadcast在处理时间超过10s,Service处理时间超过20s)。
如果要执行耗时操作,就需要新开一个线程,就相当于主线程UI,相当于一条血管,如果耗时操作在主血管中进行,就会局部鼓胀,导致整个血管运行不畅。所以,可以把耗时部分的操作,新创建线程去运行。但是,因为所有的更新UI都要在主线程UI里面完成,所以,新线程处理完成以后,需要将结果,返回到主线程UI进行显示处理。

Android中提供4中常用的多线程

  1. Handler+Thread
  2. AsyncTask
  3. ThreadPoolExecutor
  4. IntentService

1.Handler+Thread

具体安卓端的实现:
创建handler对象——>线程耗时操作——>handler传递message——>更新主线程UI

Handler:用于传递Message对象,Runnable对象(传递印象能更深一点)

handler的主要目标知道子线程中的任务何时完成,又应该什么时候更新UI,又更新什么内容

Looper:每个线程只有一个Looper,相当于对接handler的接口,是ActivityThread默认初始化好的,
同时调用了Looper.prepare方法(将Looper与当前的线程进行绑定)
MessageQueue:消息对象队列
Message:消息对象。在发送Message时,建议使用Message.obtain()来获取Message实例,可以大大减少当有大量Message对象而产生的垃圾回收问题。
具体流程

handler的内存泄漏
原因:
非静态内部类(handler)会隐性地持有外部类的引用
(message持有handler,handler持有activity)
Handler对象隐性地持有了Activity的对象,当发生GC是以为 message – handler – acitivity 的引用链导致Activity无法被回收,所以发生了内存泄露的问题。

内存泄露
在 Android 开发中是一个比较严重的问题,系统给每一个应用分配的内存是固定的,一旦发生了内存泄露,就会导致该应用可用内存越来越小,严重时会发生 OOM导致 Force Close。

解决:

1.使用弱引用

java对于 强引用 的对象,就绝不收回,
对于 软引用 的对象,是能不收回就不收回,这里的能不收回就是指内存足够的情况,
对于 弱引用 的对象,是发现就收回,但是一般情况下不会发现。

原因就是,Handler 对 Activity 是强引用

解决:
静态内部类方式实现handler
在这里插入图片描述
2.及时清除消息
ac页面onDestroy的时候,清除handler和message
在这里插入图片描述

2.AsyncTask

AsyncTask封装了线程池和Handler,它主要是为了方便开发者在子线程中更新UI。
把AsyncTask看成一个工具类,使用的时候直接继承就可以了。

继承AsyncTask要重新定义他的三个泛型参数Params、Progress和Result
如果不需要定义参数的话,可以用void代替。

Params:启动任务输入的参数,例如HTTP请求的URL
Progress:表示后台任务的执行进度的类型,即后台任务执行的百分比
Result :后台执行任务最终返回的结果类型

同时我们要重写AsyncTask的四个核心方法:

1.onPreExecute(), 该方法在主线程中执行,将在execute(Params… params)被调用后执行,一般用来做一些UI的准备工作,如在界面上显示一个进度条。

2.doInBackground(Params…params), 该方法在线程池中执行,用于执行异步任务,如果有进度条实时展示需求,调用publishProgress(int);方法
	publishProgress(int)方法,可以直接调用onProgressUpdate,实现实时更新进度条显示。
	
3.onProgressUpdate(Progress…),在主线程中执行,一般用于更新UI进度,如更新进度条的当前进度。

4.onPostExecute(Result), 在主线程中执行,被UI线程调用,doInBackground 方法的返回值将作为此方法的参数传递到UI线程中,并执行一些UI相关的操作,如更新UI视图。

上面这几个方法,onPreExecute先执行,接着是doInBackground,最后才是onPostExecute。此外AsyncTask还提供了onCancelled()方法,它同样在主线程中执行,当异步任务被取消时,onCancelled()方法会被调用,在这个时候onPostExecute则不会调用。

注意:

不能多次执行同一个 AsyncTask 对象的 execute 方法,否则会直接抛出异常。

因为 AsyncTask 内部默认是串行执行任务,因此前一个任务没有执行完,新的任务处于等待过程。

3.ThreadPoolExecutor

顾名思义,线程池。

这里加入线程池是因为,对比线程,线程池有一下好处:

  • 避免线程频繁创建消毁。
    虽然采用Thread 创建线程可以实现耗时操作,但线程的大量创建和销毁,会造成过大的性能开销。

  • 避免系统资源紧张。
    当大量的线程一起运作的时候,可能会造成资源紧张,上面也介绍过线程底层的机制就是切分CPU的时间,而大量的线程同时存在时可能造成互相抢占资源的现象发生,从而导致阻塞的现象。

  • 更好地管理线程。
    以下载功能为例,一般情况下,会有限制最大并发下载数目,而利用线程池我们可以灵活根据实际需求来设置同时下载的最大量、串行执行下载任务顺序、实现队列等待等功能。

虽然开发中经常是利用handler,或AsyncTack。但是多了解一些没坏处,况且。需求多个线程进行并发下载的时候,还是线程池好用一下。

4.IntentService

Service 用于处理长期需要在后台执行的任务,但是不代表着可以执行耗时任务,内部的操作其实都是在主线程中完成的

因此 IntentService 诞生,既可以存在于后台,提高任务的存活率,又可以执行耗时操作。

IntentService 内部封装了 HanderThread 和 Handler。

  • 原理:
    内部维护了一个无线循环的消息队列,这样就可以在 HandlerThread 中创建 Handler,外部则通过消息的方式来通知 HandlerThread 执行一个具体的任务。

应用 ,自定义service继承IntentService,重写相关方法。在onHandleIntent做相关操作。(记得在清单文件中注册service)
在这里插入图片描述

相关代码上传至GitHub

https://github.com/wwwangcaesar/ThreadDemo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值