Android多线程

文章详细解释了Handler如何与消息队列和线程关联,Looper的工作原理以及如何通过AsyncTask进行UI线程的异步操作,包括doInBackground、onPostExecute等关键方法的实现和注意事项。
摘要由CSDN通过智能技术生成

所以最终,创建Handler的同时则自动在当前线程sThreadLocal中设置Looper对象,并获取该对象,进而获取消息队列。这样一来,Handler自然和消息队列以及线程关联起来了。

消息处理


Looper通过loop()方法建立消息循环:

public static void loop() {

//里面调用了sThreadLocal.get()获得刚才创建的Looper对象

final Looper me = myLooper();

if (me == null) {//如果Looper为空则会抛出异常

throw new RuntimeException(“No Looper; Looper.prepare() wasn’t called on this thread.”);

}

final MessageQueue queue = me.mQueue;

for (;😉 {

//这是一个死循环,从消息队列不断的取消息

Message msg = queue.next(); // might block

if (msg == null) {

//由于刚创建MessageQueue就开始轮询,队列里是没有消息的,等到Handler sendMessage enqueueMessage 后队列里才有消息

return;

}

msg.target.dispatchMessage(msg);

}

}

在上面的loop方法中,我们看到通过dispatchMessage方法来处理消息,根据适才看过的Message代码,target是Handler类型,所以说,Handler将消息投递到消息队列后,消息队列有将消息分发给Handler来处理。我们来看看消息处理函数的具体实现:

public void dispatchMessage(Message msg) {

if (msg.callback != null) {

//callback在message的构造方法中初始化或者使用handler.post(Runnable)时候才不为空

handleCallback(msg);

} else {

if (mCallback != null) {

//mCallback是一个Callback对象,通过无参的构造方法创建出来的handler,该属性为null,此段不执行

if (mCallback.handleMessage(msg)) {

return;

}

}

handleMessage(msg);//最终执行handleMessage方法

}

}

private static void handleCallback(Message message) {

message.callback.run();

}

如果message的Runnable类型变量callback不为空,即对应上述Handler投递方式中的post方法,则调用handleCallBack()进而调用callback的run()。如果callback为空,则检查mCallBack是否为null,不为空就调用mCallBack的handleMessage()方法。

Android中的AysncTask

============================================================================

为了更方便地在子线程中进行更新 UI 操作,Android 基于异步处理消息机制封装了一个工具类 AsyncTask。AsyncTask可以很容易且正确地使用UI线程,AsyncTask允许进行后台操作,并在不显示使用工作线程或Handler机制的情况下,将结果反馈给UI线程。但是AsyncTask只能用于短时间的操作(最多几秒就应该结束的操作),如果需要长时间运行在后台,就不适合使用AsyncTask了,只能去使用Java提供的其他API来实现。

AysncTask的使用


由于AsyncTask是一个抽象类,所以我们必须通过继承使用AsyncTask,下面我们给出一个例子,下载的异步任务,点击“开始下载”,开始下载,下载后显示下载文件名称以及字节数。

public class MainActivity extends Activity implements Button.OnClickListener {

TextView textView = null;

Button btnDownload = null;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

textView = (TextView)findViewById(R.id.textView);

btnDownload = (Button)findViewById(R.id.btnDownload);

Log.i(“iSpring”, "MainActivity -> onCreate, Thread name: " + Thread.currentThread().getName());

}

@Override

public void onClick(View v) {

//要下载的文件地址

String[] urls = {

“https://blog.csdn.net/fjnu_se/article/details/80965142”,

“https://blog.csdn.net/fjnu_se/article/details/80965139”,

“https://blog.csdn.net/fjnu_se/article/details/80965134”,

“https://blog.csdn.net/fjnu_se/article/details/80965133”,

“https://blog.csdn.net/fjnu_se/article/details/80933809”

};

DownloadTask downloadTask = new DownloadTask();

downloadTask.execute(urls);//执行一个异步任务

}

/**

  • public abstract class AsyncTask<Params, Progress, Result>

  • 三种泛型类型分别表示:参数类型、后台任务执行的进度类型、返回的结果类型

  • 在此例中,Params泛型是String类型,Progress泛型是Object类型,Result泛型是Long类型

*/

private class DownloadTask extends AsyncTask<String, Object, Long> {

/**

  • 在execute(Params…params)被调用后立即执行,执行在UI线程,

  • 一般用来在执行后台任务对UI做一些标记。

*/

@Override

protected void onPreExecute() {

Log.i(“iSpring”, "DownloadTask -> onPreExecute, Thread name: " + Thread.currentThread().getName());

super.onPreExecute();

btnDownload.setEnabled(false);

textView.setText(“开始下载…”);

}

/**

  • 在onPreExcecute()完成后立即执行,用于执行较为耗时的操作,此方法将接受输入参数返回计算结果。

  • 在执行过程中可以调用publishProgress(Progress…values)来更新进度条信息。

  • @param params

  • @return

*/

@Override

protected Long doInBackground(String… params) {

Log.i(“iSpring”, "DownloadTask -> doInBackground, Thread name: " + Thread.currentThread().getName());

//totalByte表示所有下载的文件的总字节数

long totalByte = 0;

//params是一个String数组

for(String url: params){

//遍历Url数组,依次下载对应的文件

Object[] result = downloadSingleFile(url);

int byteCount = (int)result[0];

totalByte += byteCount;

//在下载完一个文件之后,我们就把阶段性的处理结果发布出去

publishProgress(result);

//如果AsyncTask被调用了cancel()方法,那么任务取消,跳出for循环

if(isCancelled()){

break;

}

}

//将总共下载的字节数作为结果返回

return totalByte;

}

/**

  • 下载文件后返回一个Object数组:下载文件的字节数以及下载的博客的名字

*/

private Object[] downloadSingleFile(String str){

Object[] result = new Object[2];

int byteCount = 0;

String blogName = “”;

HttpURLConnection conn = null;

try{

URL url = new URL(str);

conn = (HttpURLConnection)url.openConnection();

InputStream is = conn.getInputStream();

ByteArrayOutputStream baos = new ByteArrayOutputStream();

byte[] buf = new byte[1024];

int length = -1;

while ((length = is.read(buf)) != -1) {

baos.write(buf, 0, length);

byteCount += length;

}

String respone = new String(baos.toByteArray(), “utf-8”);

int startIndex = respone.indexOf(“”);

if(startIndex > 0){

startIndex += 7;

int endIndex = respone.indexOf(“”);

if(endIndex > startIndex){

//解析出博客中的标题

blogName = respone.substring(startIndex, endIndex);

}

}

}catch(MalformedURLException e){

e.printStackTrace();

}catch(IOException e){

e.printStackTrace();

}finally {

if(conn != null){

conn.disconnect();

}

}

result[0] = byteCount;

result[1] = blogName;

return result;

}

/**

  • 执行在UI线程。在调用publishProgress(Progress…progress)时,此方法被执行,

  • 直接将进度信息更新到UI组件上。

  • @param values

*/

@Override

protected void onProgressUpdate(Object… values) {

Log.i(“iSpring”, "DownloadTask -> onProgressUpdate, Thread name: " + Thread.currentThread().getName());

super.onProgressUpdate(values);

int byteCount = (int)values[0];

String blogName = (String)values[1];

String text = textView.getText().toString();

text += “\n博客《” + blogName + “》下载完成,共” + byteCount + “字节”;

textView.setText(text);

}

/**

  • 执行在UI线程。当后台操作结束时,此方法将会被调用,

  • doInBackground()函数返回的计算结果将作为参数传递到此方法中,直接将结果显示到UI组件中。

  • @param aLong

*/

@Override

protected void onPostExecute(Long aLong) {

Log.i(“iSpring”, "DownloadTask -> onPostExecute, Thread name: " + Thread.currentThread().getName());

super.onPostExecute(aLong);

String text = textView.getText().toString();

text += “\n全部下载完成,总共下载了” + aLong + “个字节”;

textView.setText(text);

btnDownload.setEnabled(true);

}

@Override

protected void onCancelled() {

Log.i(“iSpring”, "DownloadTask -> onCancelled, Thread name: " + Thread.currentThread().getName());

super.onCancelled();

textView.setText(“取消下载”);

btnDownload.setEnabled(true);

}

}

}

在上面的每个方法中的注解可得知,当我们使用AsyncTask时,需要在代码中调用execute()方法,创建异步任务类继承AsyncTask后,doInBackground是一个抽象方法,必须覆写,而onPreExecute、onProgressUpdate、onPostExecute、onCancelled这几个方法是空方法,需要的时候也可以覆写它们,至于publishProgress是final修饰的方法,只能调用,一般在doInBackground中调用来更新进度条,这些方法顺序为:onPreExecute–>doInBackground–>publishProgress–>onProgressUpdate–>onPostExecute

使用同时,我们需要注意几点:

  • 异步任务的实例必须在UI线程中创建。

  • execute方法必须在UI线程中调用。

  • 不能在doInBackground中更改UI组件。

  • 一个任务实例只能执行一次。

AysncTask的底层原理


先看AsyncTask的源码解析:

当我们执行异步任务时,将在主线程调用execute方法,该方法源码如下:

//该方法运行在主线程

@MainThread

public final AsyncTask<Params, Progress, Result> execute(Params… params) {

//将任务放到线程池中执行,线程池中线程都是子线程

return executeOnExecutor(sDefaultExecutor, params);

}

可见,调用execute后,进而调用了executeOnExecutor方法,传进去的参数有两个:sDefaultExecutor以及params,params是参数值,sDefaultExecutor是ThreadPoolExecutor的实例,用于管理提交AsyncTask的任务,源码如下:

// We want at least 2 threads and at most 4 threads in the core pool,

// preferring to have 1 less than the CPU count to avoid saturating

// the CPU with background work

private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));

private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;

private static final int KEEP_ALIVE_SECONDS = 30;

//生产线程的工厂

private static final ThreadFactory sThreadFactory = new ThreadFactory() {

private final AtomicInteger mCount = new AtomicInteger(1);

public Thread newThread(Runnable r) {

return new Thread(r, “AsyncTask #” + mCount.getAndIncrement());

}

};

//存放任务的阻塞队列

private static final BlockingQueue sPoolWorkQueue =

new LinkedBlockingQueue(10);

public static final Executor THREAD_POOL_EXECUTOR

= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,

TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

//线性执行的执行器

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

//内部交互的handler

private static final InternalHandler sHandler = new InternalHandler();

//默认的Executor(顺序执行)

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

/**

*顺序执行的Executor

*/

private static class SerialExecutor implements Executor {

final ArrayDeque mTasks = new ArrayDeque();

Runnable mActive;

public synchronized void execute(final Runnable r) {

mTasks.offer(new Runnable() {

public void run() {

try {

r.run();

} finally {

scheduleNext();

}

}

});

if (mActive == null) {

scheduleNext();

}

}

protected synchronized void scheduleNext() {

if ((mActive = mTasks.poll()) != null) {

//将任务交给THREAD_POOL_EXECUTOR执行

THREAD_POOL_EXECUTOR.execute(mActive);

}

}

}

可见,sDefaultExecutor中有一个execute方法,该方法负责将异步任务分发给THREAD_POOL_EXECUTOR线程池执行。

我们继续接着往下看,进入ExecuteOnExecutor之后的源码:

//该函数体运行在主线程

@MainThread

/**

*@param exec执行任务的线程池

*@param params参数

*@return 该AsyncTask实例

*/

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,

Params… params) {

if (mStatus != Status.PENDING) {//状态检测,只有在PENDING状态下才能正常运行,PENDING:待定状态

switch (mStatus) {

case RUNNING://执行中

throw new IllegalStateException(“Cannot execute task:”

  • " the task is already running.");

case FINISHED://执行完成

throw new IllegalStateException(“Cannot execute task:”

  • " the task has already been executed "

  • “(a task can be executed only once)”);

}

}

mStatus = Status.RUNNING;

onPreExecute(); // 执行任务前的准备处理

mWorker.mParams = params;//UI线程、传递过来的参数

exec.execute(mFuture);//交给线程池管理器进行调度,参数为FutureTask类型,构造mFuture时mWorker被传了进去

return this;

}

程序先判断了AsyncTask的状态,初始情况下,AsyncTask的状态为PENDING。调用execute方法时会判断该任务的状态,如果是非PENDING状态就会抛出异常,所以AsyncTask实例只能运行一次。随后,改变AsyncTask的状态以及执行onPreExecute方法。后面,我们可以看到,参数sDefaultExecutor执行了它的execute方法,不过,它传进去的参数是mFuture又是什么呢,还有mWorker又是什么呢,我们可以从AsyncTask的构造函数中得到答案:

public AsyncTask() {

mWorker = new WorkerRunnable<Params, Result>() {

public Result call() throws Exception {

mTaskInvoked.set(true);

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

return postResult(doInBackground(mParams));//调用doInBackground函数,并将最后结果通过postResult投递出去。

}

};

mFuture = new FutureTask(mWorker) {

@Override

protected void done() {

try {

final Result result = get();

postResultIfNotInvoked(result);

} catch (InterruptedException e) {

android.util.Log.w(LOG_TAG, e);

} catch (ExecutionException e) {

throw new RuntimeException(“An error occured while executing doInBackground()”,

e.getCause());

} catch (CancellationException e) {

postResultIfNotInvoked(null);

} catch (Throwable t) {

throw new RuntimeException("An error occured while executing "

  • “doInBackground()”, t);

}

}

};

}

//用getHandler()把设置message,并发送。

private Result postResult(Result result) {

Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,

new AsyncTaskResult(this, result));

message.sendToTarget();

return result;

}

mFutrue: mFutrue是AsyncTask的抽象内部类FutureTask的实例对象,创建mFuture实例时,将mWorker传进来作为参数,并调用mWorker完成后台任务,完成后调用done方法。

mWorker: mWorker是AsyncTask的抽象内部类WorkerRunnable的实例对象,它实现了Callable接口中的call方法,在call方法中调用doInBackground函数,并将最后结果通过postResult投递出去。在postResult中,出现了内部封装的Handler,我们看看它相关的代码:

private static class InternalHandler extends Handler {

@SuppressWarnings({“unchecked”, “RawUseOfParameterizedType”})

@Override

public void handleMessage(Message msg) {

AsyncTaskResult result = (AsyncTaskResult) msg.obj;

switch (msg.what) {

case MESSAGE_POST_RESULT:

// There is only one result

result.mTask.finish(result.mData[0]);

break;

case MESSAGE_POST_PROGRESS:

result.mTask.onProgressUpdate(result.mData);

break;

}

}

}

private void finish(Result result) {

// 先判断是否调用了Cancelled()

// 1. 若调用了则执行我们复写的onCancelled()

// 即 取消任务时的操作

if (isCancelled()) {

onCancelled(result);

} else {

// 2. 若无调用Cancelled(),则执行我们复写的onPostExecute(result)

// 即更新UI操作

onPostExecute(result);

最后附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总)

面试成功其实是必然的,因为我做足了充分的准备工作,包括刷题啊,看一些Android核心的知识点,看一些面试的博客吸取大家面试的一些经验,下面这份PDF是我翻阅了差不多1个月左右一些Android大博主的博客从他们那里取其精华去其糟泊所整理出来的一些Android的核心知识点, 全部都是精华中的精华,我能面试到现在资深开发人员跟我整理的这本Android核心知识点有密不可分的关系,在这里本着共赢的心态分享给各位朋友。

这份PDF囊括了JVM,Java集合,Java多线程并发,Java基础,生命周期,微服务, 进程,Parcelable 接口,IPC,屏幕适配,线程异步,ART,架构,Jetpack,NDK开发,计算机网络基础,类加载器,Android 开源库源码分析,设计模式汇总,Gradle 知识点汇总…

由于篇幅有限,就不做过多的介绍,大家请自行脑补
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
Cancelled(result);

} else {

// 2. 若无调用Cancelled(),则执行我们复写的onPostExecute(result)

// 即更新UI操作

onPostExecute(result);

最后附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总)

[外链图片转存中…(img-57g7YDJf-1714755941928)]

面试成功其实是必然的,因为我做足了充分的准备工作,包括刷题啊,看一些Android核心的知识点,看一些面试的博客吸取大家面试的一些经验,下面这份PDF是我翻阅了差不多1个月左右一些Android大博主的博客从他们那里取其精华去其糟泊所整理出来的一些Android的核心知识点, 全部都是精华中的精华,我能面试到现在资深开发人员跟我整理的这本Android核心知识点有密不可分的关系,在这里本着共赢的心态分享给各位朋友。
[外链图片转存中…(img-ui3BznAT-1714755941928)]

这份PDF囊括了JVM,Java集合,Java多线程并发,Java基础,生命周期,微服务, 进程,Parcelable 接口,IPC,屏幕适配,线程异步,ART,架构,Jetpack,NDK开发,计算机网络基础,类加载器,Android 开源库源码分析,设计模式汇总,Gradle 知识点汇总…

由于篇幅有限,就不做过多的介绍,大家请自行脑补
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值