Android异步任务与多线程,金九银十正确打开方式

  • b.线程缺乏统一管理,可能无限制新建线程,相互之间竞争,即可能占用过多系统资源导致死机或oom.

  • c.缺乏更多功能,如定时执行、定期执行、线程中断。

3.1、缓存线程池


newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,如无可回收,则新建线程。

private void testCache() {

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

for (int i = 0; i < 10; i++) {

final int index = i;

try {

Thread.sleep(2000);

} catch (InterruptedException e) {

e.printStackTrace();

}

cachedThreadPool.execute(new Runnable() {

@Override

public void run() {

Log.i(Constant.TAG, Thread.currentThread().getName() + " " + index);

}

});

}

}

3.2、定长线程池


newFixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

private void testFixed() {

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);

for (int i = 0; i < 10; i++) {

final int index = i;

fixedThreadPool.execute(new Runnable() {

@Override

public void run() {

Log.i(Constant.TAG, Thread.currentThread().getName() + " " + index);

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

});

}

}

3.3、单个线程池


newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。

private void testSingle() {

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

for (int i = 0; i < 10; i++) {

final int index = i;

singleThreadExecutor.execute(new Runnable() {

@Override

public void run() {

Log.i(Constant.TAG, Thread.currentThread().getName() + " " + index);

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

});

}

}

3.4、定时线程池


newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行。

private void testScheduled() {

Log.i(Constant.TAG, “开始执行 testScheduled”);

ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

// 定时3秒后执行

// scheduledExecutorService.schedule(new Runnable() {

// @Override

// public void run() {

// Log.i(Constant.TAG, “延时3秒执行”);

// }

// },3, TimeUnit.SECONDS);

// 定时3秒后执行,每隔2秒执行一次

scheduledExecutorService.scheduleAtFixedRate(new Runnable() {

@Override

public void run() {

Log.i(Constant.TAG, “每隔2秒执行一次”);

}

}, 3, 2, TimeUnit.SECONDS);

}

4、异步消息处理机制

==========

当我们学会使用多线程编程以后,就需要思考一个问题:线程之间如何通讯?

4.1、分析


详细了解Android SDK提供的几个线程间通讯的类.

1、Handler

Handler在android里负责发送和处理消息,通过它可以实现其他线程与Main线程之间的消息通讯。

2、Looper

Looper负责管理线程的消息队列和消息循环。

3、Message

Message是线程间通讯的消息载体。两个码头之间运输货物,Message充当集装箱的功能 ,里面可以存放任何你想要传递的消息.

4、MessageQueue

MessageQueue是消息队队列,先进先出,它的作用是保存有待线程处理的消息.

它们四者之间的关系是,在其他线程中调用Handler.sendMsg()方法(参数是Message对象),

将需要Main线程处理的事件添加到Main例的MessageQueue中,Main线程通过MainLooper从消息队列中取出Handler发过来的这个消息时,会回调Handler的handlerMessage()方法。

4.2、用法


4.2.1、send

1、sendEmptyMessage(int)

2、sendMessage(Message)

3、sendMessageAtTime(Message , long)

4、sendMessageDelayed(Message , long)

sendMessage类方法允许你安排一个带数据的Message对象到队列中,等待处理。

4.2.2、post

1、post(Runnable)

2、postAtTime(Runnable, long)

3、postDelayed(Runnable, long)

post方法允许你排列一个Runnable对象到主线程队列中,等待执行。

4.3、总结


1、传递Message。用于接受子线程发送的数据,并用此数据配合主线程更新UI。

在Android中,对于U1的操作通常需要放在主线程中进行操作。如果在子线程中有关于UI的操作,

那么就需要把数据消息作为一个Message对象发送到消息队列中,然后,用Handler中的handleMessge方法处理传过来的数据信息,并操作UI,类似sendMessage(Message msg)

方法实现发送消息的操作。在初始化Handler对象时重写的handleMessage方法来接收Message并进行相关操作。

2、传递Runnable对象。用于通过Handler绑定的消息队列,安排不同操作的执行顺序。Handler对象在进行初始化的时候,会默认地自动绑定消息队列。利用类post方法,可以将Runnable对象发送到消息队列中,按照队列的机制按顺序执行不同的Runnable对象中的run方法.

4.4、补充


4.4.1、View的post()方法

public boolean post(Runnable action) {

//1 View自己创建的Handler

Handler handler;

if (mAttachInfo != null) {

//2 获取View所依附的Thread的Handler

handler = mAttachInfo.mHandler;

} else {

getRunQueue().post(action);

return true;

}

//3 执行post方法 所以View.post方法本质上还是handler.post

return handler.post(action);

}

4.4.2、Activity的runOnUiThread方法

@Override

public final void runOnUiThread(Runnable action) {

if (Thread.currentThread() != mUiThread) {

mHandler.post(action);

} else {

action.run();

}

}

4.5、代码


4.5.1、MainActivity.java

public class MainActivity extends AppCompatActivity {

private Button btn1;

private Button btnTestPb;

private ProgressBar pb;

private int progress = 0;

@SuppressWarnings(“HandlerLeak”)

private final Handler handler = new Handler() {

// 接收消息等待处理

@Override

public void handleMessage(@NonNull Message msg) {

switch (msg.what) {

case 1:

btn1.setText(“12345”);

break;

case 2:

String str = (String) msg.obj;

btn1.setText(str);

break;

case 3:

if (progress < 100) {

progress += 10;

pb.setProgress(progress);

handler.sendEmptyMessageDelayed(3, 2000);

}

break;

default:

}

}

};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

btn1 = findViewById(R.id.btn_test1);

btnTestPb = findViewById(R.id.btn_test_pb);

pb = findViewById(R.id.pb);

btn1.setOnClickListener(view -> test1());

btnTestPb.setOnClickListener(view -> testTimer());

findViewById(R.id.btn_test_post).setOnClickListener(view -> testPost());

}

private void testPost() {

new Thread(new Runnable() {

@Override

public void run() {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

// handler.post(new Runnable() {

// @Override

// public void run() {

// btn1.setText(“6789”);

// }

// });

// View的post方法

// btn1.post(new Runnable() {

// @Override

// public void run() {

// btn1.setText(“View 6789”);

// }

// });

// Activity的runOnUiThread

runOnUiThread(new Runnable() {

@Override

public void run() {

btn1.setText(“runOnUiThread 6789”);

}

});

}

}).start();

}

private void testTimer() {

pb.setVisibility(View.VISIBLE);

handler.sendEmptyMessageDelayed(3, 2000);

}

private void test1() {

new Thread(new Runnable() {

@Override

public void run() {

// 模拟一个网络请求

try {

Thread.sleep(2000);

} catch (InterruptedException e) {

e.printStackTrace();

}

// handler.sendEmptyMessage(1);

// handler.sendEmptyMessageDelayed(1,2000);

Message message = Message.obtain();

message.what = 2;

message.obj = “ABC”;

handler.sendMessage(message);

// handler.sendMessageDelayed(message,2000);

}

}).start();

}

}

4.5.2、布局文件

<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:app=“http://schemas.android.com/apk/res-auto”

xmlns:tools=“http://schemas.android.com/tools”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

tools:context=“.MainActivity”>

<TextView

android:id=“@+id/tv_mark”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“MainActivity”

android:textSize=“30sp”

app:layout_constraintLeft_toLeftOf=“parent”

app:layout_constraintRight_toRightOf=“parent”

app:layout_constraintTop_toTopOf=“parent” />

<Button

android:id=“@+id/btn_test1”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“测试 Handler”

android:textAllCaps=“false”

android:textSize=“20sp”

app:layout_constraintLeft_toLeftOf=“parent”

app:layout_constraintRight_toRightOf=“parent”

app:layout_constraintTop_toBottomOf=“@+id/tv_mark” />

<Button

android:id=“@+id/btn_test_pb”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“测试 进度条”

android:textAllCaps=“false”

android:textSize=“20sp”

app:layout_constraintLeft_toLeftOf=“parent”

app:layout_constraintRight_toRightOf=“parent”

app:layout_constraintTop_toBottomOf=“@+id/btn_test1” />

<ProgressBar

android:id=“@+id/pb”

android:layout_width=“match_parent”

android:layout_height=“20dp”

android:max=“100”

android:visibility=“gone”

style=“?android:attr/progressBarStyleHorizontal”

app:layout_constraintLeft_toLeftOf=“parent”

app:layout_constraintRight_toRightOf=“parent”

app:layout_constraintTop_toBottomOf=“@+id/btn_test_pb”

/>

<Button

android:id=“@+id/btn_test_post”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“测试 post”

android:textAllCaps=“false”

android:textSize=“20sp”

app:layout_constraintLeft_toLeftOf=“parent”

app:layout_constraintRight_toRightOf=“parent”

app:layout_constraintTop_toBottomOf=“@+id/pb” />

</androidx.constraintlayout.widget.ConstraintLayout>

5、异步任务AsyncTask

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

为了更加方便我们在子线程中更新UI元素,Android 从 1.5版本就引入了一个AsyncTask类,使用它就可以非常灵活方便地从子线程切换到UI线程。

AsyncTask :异步任务,从字面上来说,就是在我们的UI主线程运行的时候,异步的完成 一些操作。

AsyncTask允许我们执行一个异步的任务在后台。我们可以将耗时的操作放在异步任务当中来执行,

并随时将任务执行的结果返回给我们的UI线程来更新我们的U1控件。通过AsyncTask我们可以轻松地解决多线程之间的通信问题。

怎么来理解AsyncTask呢?通俗一点来说,AsyncTask就相当于Android给我们提供了一 个多线程编程的一个框架,其介于Thread和Handler之间,我们如果要定义一个AsyncTask,就需要定义一个类来继承AsyncTask这个抽象类,并实现其唯一的一个 doInBackgroud 抽象方法。

5.1、用法


首先来看一下AsyncTask的基本用法,由于AsyncTask是一个抽象类,所以如果我们想使用它,就必须要创建

一个子类去继承它。在继承时我们可以为AsyncTask类指定三个泛型参数,这三个参数的用途如下:

1. Params

在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。

2. Progress

后台任执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位.

3. Result

当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型.

因此,一个最简单的自定义AsyncTask就可以写成如下方式:

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {…}

5.2、分析


这里我们把AsyncTask的第一个泛型参数指定为Void ,表示在执行AsyncTask的时候不需 要传入参数给后台任务.

第二个泛型参数指定为Integer,表示使用整型数据来作为进度显示单位。

第三个泛型参数指定为Boolean ,则表示使用布尔型数据来反馈执行结果。

当然,目前我们自定义的DownloadTask还是一个空任务,并不能进行任何实际的操作. 我们还需要去重写AsyncTask中的几个方法才能完成对任务的定制。经常需要去重写的方 法有以下四个:

1. onPreExecute()

这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示 一个进度条对话框等。

2. doInBackground(Params…)

这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务.任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void ,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用 publishProgress(Progress…)方法来完成.

3. onProgressUpdate(Progress…)

当在后台任务中调用了publishProgress(Progress…)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。

4. onPostExecute(Result)

当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据采进行一些UI操作,比如说提醒任务执行的结果,以及关闭进度条对话框等.

5.3、总结


AsyncTask和Handler对比

  1. AsyncTask实现的原理和适用的优缺点

AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程。

使用的优点:

简单,快捷,过程可控

使用的缺点:

在使用多个异步操作,并需要进行Ui变更时,就变得复杂起来.

2)Handler异步实现的原理和适用的优缺点

在Handler异步实现时,涉及到Handler, Looper, Message,Thread四个对象,实现异步的流程是主线程启动Thread (子线程),子线程运行并生成Message通过Handler发送出去,然后Looper取出消息队列中的Message再分发给Handler进行UI变更。

使用的优点:

结构清晰,功能定义明确。对于多个后台任务时,简单,清晰

使用的缺点:

在单个后台异步处理时,显得代码过多,结构过于复杂。

5.4、代码


5.4.1、MainActivity.java

public class MainActivity extends AppCompatActivity {

private Button btn1;

private ProgressBar pb;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

btn1 = findViewById(R.id.btn_test1);

pb = findViewById(R.id.pb);

btn1.setOnClickListener(view -> test1());

}

private void test1() {

new TestTask().execute();

}

class TestTask extends AsyncTask<Void, Integer, Boolean> {

int progress;

// 当前还在主线程中,做一些准备工作

@Override

protected void onPreExecute() {

super.onPreExecute();

Log.i(Constant.TAG, “onPreExecute: 准备下载”);

pb.setVisibility(View.VISIBLE);

}

// 在异步线程里面执行

@Override

protected Boolean doInBackground(Void… voids) {

Log.i(Constant.TAG, “doInBackground: 正在下载”);

try {

// 永久循环,模拟下载文件

while (true) {

// 每隔1秒下载10%

Thread.sleep(1000);

progress += 10;

// 通知主线程当前的进度是多少

publishProgress(progress);

if (progress >= 100) {

break;

}

}

} catch (InterruptedException e) {

e.printStackTrace();

return false;

}

return true;

}

// 当前切换到了主线程,可以根据传递的参数做UI的更新

@Override

protected void onProgressUpdate(Integer… values) {

Log.i(Constant.TAG, "onProgressUpdate: 下载回调 " + values[0]);

super.onProgressUpdate(values);

pb.setProgress(values[0]);

}

// 切换到主线程里面执行 doInBackground中返回的数据就到了这里

@Override

protected void onPostExecute(Boolean bool) {

if (bool){

Log.i(Constant.TAG, “onPostExecute: 下载成功”);

pb.setVisibility(View.GONE);

}else {

Log.i(Constant.TAG, “onPostExecute: 下载失败”);

}

}

}

}

5.4.2、布局文件

<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:app=“http://schemas.android.com/apk/res-auto”

xmlns:tools=“http://schemas.android.com/tools”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

tools:context=“.MainActivity”>

<TextView

android:id=“@+id/tv_mark”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“MainActivity”

android:textSize=“30sp”

app:layout_constraintLeft_toLeftOf=“parent”

app:layout_constraintRight_toRightOf=“parent”

app:layout_constraintTop_toTopOf=“parent” />

<Button

android:id=“@+id/btn_test1”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“测试 Handler”

android:textAllCaps=“false”

android:textSize=“20sp”

app:layout_constraintLeft_toLeftOf=“parent”

app:layout_constraintRight_toRightOf=“parent”

app:layout_constraintTop_toBottomOf=“@+id/tv_mark” />

<ProgressBar

android:id=“@+id/pb”

android:layout_width=“match_parent”

android:layout_height=“20dp”

android:max=“100”

android:visibility=“gone”

style=“?android:attr/progressBarStyleHorizontal”

app:layout_constraintLeft_toLeftOf=“parent”

app:layout_constraintRight_toRightOf=“parent”

app:layout_constraintTop_toBottomOf=“@+id/btn_test1”

/>

</androidx.constraintlayout.widget.ConstraintLayout>

6、设计自己的图片轮播器

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

6.1、加载轮播图片


1、本地加载图片

2、异步获取网络图片

6.2、滑动轮播图片


同种无限循环轮播的实现方式:

1、欺骗适配器

在适配器中将getcount的值设置为无限大

2、拖造数据源

有4张图片,霁现无限循环。在viewpager中设置6个view ,第一个为4张图片的最后1张,第6张为4张图片

的第1张。图片顺序如下数字:

3-0-1-2-3-0

0-1-2-3为正常的4个图片。3,0为添加的两个图片view

滑动的顺序:进入页面显示0图片,向右滑动到0时,将0页设置为0 ,则可以继续向右滑动。

同理当向左滑动到3时,将3页设置为3。

6.3、代码


6.3.1、BitmapTask.java

public class BitmapTask extends AsyncTask<Integer, Void, Bitmap> {

@SuppressLint(“StaticFieldLeak”)

private final Context context;

private int res;

@SuppressLint(“StaticFieldLeak”)

private final ImageView imageView;

public BitmapTask(Context context, ImageView imageView) {

this.context = context;

this.imageView = imageView;

}

@Override

protected Bitmap doInBackground(Integer… integers) {

res = integers[0];

// 根据图片ID获取到对应的bitmap

Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), res);

return bitmap;

}

@Override

protected void onPostExecute(Bitmap bitmap) {

// 显示加载成功的图片

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

总而言之,Android开发行业变化太快,作为技术人员就要保持终生学习的态度,让学习力成为核心竞争力,所谓“活到老学到老”只有不断的学习,不断的提升自己,才能跟紧行业的步伐,才能不被时代所淘汰。

在这里我分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司19年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。需要的朋友可以私信我【资料】或者 点这里 免费领取

还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。 领取地址: Android学习PDF+架构视频+最新面试文档+源码笔记

text.getResources(), res);

return bitmap;

}

@Override

protected void onPostExecute(Bitmap bitmap) {

// 显示加载成功的图片

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-81Rp7J8e-1710567580429)]
[外链图片转存中…(img-LDsF7q72-1710567580429)]
[外链图片转存中…(img-I7rPCfWk-1710567580429)]
[外链图片转存中…(img-i0RCv9gf-1710567580430)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-eHMGBjlE-1710567580430)]

最后

总而言之,Android开发行业变化太快,作为技术人员就要保持终生学习的态度,让学习力成为核心竞争力,所谓“活到老学到老”只有不断的学习,不断的提升自己,才能跟紧行业的步伐,才能不被时代所淘汰。

在这里我分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司19年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。需要的朋友可以私信我【资料】或者 点这里 免费领取

[外链图片转存中…(img-tuyODsu1-1710567580430)]

[外链图片转存中…(img-OlH3Yujz-1710567580431)]

[外链图片转存中…(img-PJPnpDBY-1710567580431)]

还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。 领取地址: Android学习PDF+架构视频+最新面试文档+源码笔记

  • 23
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值