Android异步任务与多线程,一个月成功收割腾讯、阿里、字节offer

1.4、利用线程池提高性能

2、多线程的创建

2.1、继承Thread实现多线程

2.2、实现Runnable接口实现多线程

2.3、实现Runnablej接口和继承Thread的区别

2.4、代码实战

2.4.1、Constant

2.4.2、MyThread

2.4.3、MyRunnable

2.4.4、SaleTicket

2.4.5、MainActivity

2.4.6、布局文件

3、线程池的应用

3.1、缓存线程池

3.2、定长线程池

3.3、单个线程池

3.4、定时线程池

4、异步消息处理机制

4.1、分析

4.2、用法

4.2.1、send

4.2.2、post

4.3、总结

4.4、补充

4.4.1、View的post()方法

4.4.2、Activity的runOnUiThread方法

4.5、代码

4.5.1、MainActivity.java

4.5.2、布局文件

5、异步任务AsyncTask

5.1、用法

5.2、分析

5.3、总结

5.4、代码

5.4.1、MainActivity.java

5.4.2、布局文件

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

6.1、加载轮播图片

6.2、滑动轮播图片

6.3、代码

6.3.1、BitmapTask.java

6.3.2、Images.java

6.3.3、ViewPagerAdapter.java

6.3.4、MainActivity.java


相关资料:

=====

https://www.bilibili.com/video/BV1m4411r73w?p=3https://www.bilibili.com/video/BV1m4411r73w?p=3https://www.bilibili.com/video/BV1m4411r73w?p=3

1、多线程的意义

========

1.1、为什么要使用多线程


为什么要使用多线程?

a)提高用户体验或避免ANR

在事件处理中需要使用多线程,否则会出现ANR,或者因为响应较慢导致用户体验很差。

b)异步

应用中有些情况并不一定需要同步阻塞去等待返回结果,可以通过多线程来实现异步,例如你的应用的某个Activity需要从云端

获取一些图片,加载图片比较耗时,这时需要使用异步加载,加载完成一个图片刷新一个。

c)多任务

例如多线程下载

1.2、为什么通过多线程可以提高用户体验避免ANR


1.2.1、什么是ANR

ANR全程Application Not Responding,意思是程序未响应,如果一个应用无法响应用户的输入,系统就会弹出一个ANR对话框,用户可以自行选择继续等待亦或停止当前程序。

1.2.2、深入了解

我们继续来了解一下Android应用程序的main线程,它负责处理UI的绘制,Android系统为了防止应用程序较慢导致系统无法正常运行做了一个处理,一种情况是当用户输入事件在5秒内无法得到响应,那么系统会弹出ANR对话框,由用户决定继续等待还是强制结束应用程序。(另一种情况是BroadcastReceiver超过10秒没有执行完也会弹出ANR对话框,见文章 ANR 弹窗的显示原理

ANR 的四种场景:

  1. Service TimeOut:  service 未在规定时间执行完成:前台服务 20s,后台 200s

  2. BroadCastQueue TimeOut: 未在规定时间内未处理完广播:前台广播 10s 内, 后台 60s 内

  3. ContentProvider TimeOut:  publish 在 10s 内没有完成

  4. Input Dispatching timeout:  5s 内未响应键盘输入、触摸屏幕等事件

ANR 的根本原因是:应用未在规定的时间内处理 AMS 指定的任务才会 ANR。

另外,人眼可以分辨的时间的160毫秒,超过这个时间就可以感到卡顿,所以要控制好这个时间。

1.2.3、事件处理原则

事件处理的原则:所有可能耗时的操作都放到其他线程去处理。

Android中的main线程的事件处理不能太耗时,否则后续的事件无法在5秒内得到响应,就会弹出ANR对话框。那么哪些方法会在main线程执行呢?

1)Activity的生命周期方法,例如:onCreate()、onStart()、onResume()等

2)事件处理方法,例如onClick()、onItemClick()等

通常Android基类中的以on开头的方法是在main线程被回调的。

提高应用的响应性,可以从这两方面入手。

一般来说,Activity的onCreate()、onStart()、onResume()方法的执行时间决定了你的应用首页打开的时间,这里要尽量把不必要的操作放到其他线程去处理,如果仍然很耗时,可以使用SplashScreen。使用SplashScreen最好用动态的,这样用户知道你的应用没有死掉。

1.2.4、实际操作

当用户与你的应用交互时,事件处理方法的执行快慢决定了应用的响应性是否良好,

这里分两种情况:

1)同步,需要等待返回结果.例如用户点击了注册按钮,需要等待服务疏返回结果,那么需要有一个进度条来提示用户你的程序正在运行没有死掉。一般与服务端交互的都要有进度条,例如系统自带的浏览器,URL跳转时会有进度条.

2)异步,不需要等待返回结果。例如微博中的收藏功能,点击完收藏按钮后是否成功执行完成后告诉我就行了,我不想等它,这里最好实现为异步的. 无论同步异步,事件处理都可能比较耗时,那么需要放到其他线程中处理, 等处理完成后,再通知界面刷新。

这里有一点要注意,不是所有的界面刷新行为都需要放到Main线程处理, 例如Textview的setText()方法需要在Main线程中,否则会抛出 CalledFromWrongThreadException ,而ProgressBar的setProgress()方法 则不需要在Main线程中处理,当然你也可以把所有UI组件相关行为都放到Main线程中处理,没有问题。可以减轻你的思考负担,但你最好了解他们之间的差别,掌握事物之间细微差别的是专家。把事件处理代码放到其他线程中处理,如果处理的结果需要

刷新界面,那么需要线程间通讯的方法来实现在其他线程中发消息绐 Main线程处理.

1.3、如何实现多线程之间的通讯


1.3.1、Handler

Handler、Looper、Message、MessageQueue

1.3.2、AsyncTask

AsyncTask是Android框架提供的异步处理的辅助类,它可以实现耗时操作 在其他线程执行,而处理结果在Main线程执行,对于开发者而言,它屏蔽掉了多线程和后面要即Handler的概念。你不了解怎么处理线程间通讯也 没有关系,AsyncTask体贴的帮你做好了。不过封装越好施高级的API,对初级程序员反而越不利,就是你不了解它的原理.当你需要面对更加复杂的情况,而高级API无法完成得很好时,你就杯具了.所以,我们也要掌握功 能更强大,更自由的与Main线程通讯的方法:Handler的使用.

1.4、利用线程池提高性能


这里我们建议使用线程池来管理临时的Thread对象,从而达到提高应用程序 性能的目的.

线程池是资源池在线程应用中的一个实例.了解线程池之前我们首先要了解 一下资源池的概念在JAVA中,创建和销毁对象是匕阳消耗资源的。我们 如果在应用中需要频繁创建销毁某个类型的对象实例,这样会产生很多临时 对象,当失去引用的情时对象较多时,虚拟机会进行垃圾回收(GC), CPU在 进行GC时会导致应用程序的运行得不到相应,从而导致应用的响应性降低。

资源池就是用来解决这个问题,当你需要使用对象时,从资源池来获取.资 源池负责维护对象的生命周期。了解了资源池,就很好理解线程池了,线程 池就是存放对象类型都是线程的资源池.

2、多线程的创建

========

2.1、继承Thread实现多线程


继承Thread类的方法尽管被我列为一种多线程实现方式,但Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extends Thread ,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。

2.2、实现Runnable接口实现多线程


如果自己的类extends另一个类,就无法直接extends Thread ,此时 必须实现一个Runnable接口。同时为了启动MyThread ,需要首先实例化一个Thread ,用传入自己的已经实现好Runnable接口的目标对象。

2.3、实现Runnablej接口和继承Thread的区别


1 ,一个类只能继承一个父类,存在局限;一个类可以实现多个接口

2 ,在实现Runable接口的时候调用Thread(Runnable target)创建进程时 ,使用同一个Runnable实例,则建立的多线程的实例变量也是共享的。 但是通过继承Thread类是不能用一个实例建立多个线程,故而实现 Runnable接口适合于资源共享。当然,继承Thread类也能够共享变量, 能共享Thread类的static变量;

3 , Runnable接口和Thread之间的联系:

public class Thread extends Object implements Runnable可以看出Thread类也是Runnable接口的子类;

2.4、代码实战


2.4.1、Constant

public class Constant {

public static final String TAG = “Multi_Thread”;

}

2.4.2、MyThread

public class MyThread extends Thread {

@Override

public void run() {

Log.i(Constant.TAG, Thread.currentThread().getName() + “.run()”);

}

}

2.4.3、MyRunnable

public class MyRunnable implements Runnable{

@Override

public void run() {

Log.i(Constant.TAG, Thread.currentThread().getName() + “.run()”);

}

}

2.4.4、SaleTicket

public class SaleTicket implements Runnable {

private int ticket = 20;

@Override

public void run() {

while (true) {

synchronized (this){

if (ticket > 0) {

Log.i(Constant.TAG, Thread.currentThread().getName() + “卖出了第” + (20 - ticket + 1) + “张票”);

ticket–;

} else {

break;

}

}

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

2.4.5、MainActivity

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

findViewById(R.id.btn_test_thread).setOnClickListener(view -> testThread());

findViewById(R.id.btn_test_runnable).setOnClickListener(view -> testRunnable());

findViewById(R.id.btn_test_sale).setOnClickListener(view -> testSale());

}

private void testSale() {

SaleTicket saleTicket = new SaleTicket();

Thread thread1 = new Thread(saleTicket, “A代理”);

Thread thread2 = new Thread(saleTicket, “B代理”);

Thread thread3 = new Thread(saleTicket, “C代理”);

Thread thread4 = new Thread(saleTicket, “D代理”);

thread1.start();

thread2.start();

thread3.start();

thread4.start();

}

private void testRunnable() {

MyRunnable runnable1 = new MyRunnable();

Thread thread1 = new Thread(runnable1);

MyRunnable runnable2 = new MyRunnable();

Thread thread2 = new Thread(runnable1);

thread1.start();

thread2.start();

}

private void testThread() {

MyThread thread1 = new MyThread();

MyThread thread2 = new MyThread();

thread1.start();

thread2.start();

}

}

2.4.6、布局文件

<?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_test_thread”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“测试Thread”

android:textAllCaps=“false”

android:textSize=“26sp”

app:layout_constraintLeft_toLeftOf=“parent”

app:layout_constraintRight_toRightOf=“parent”

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

<Button

android:id=“@+id/btn_test_runnable”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“测试Runnable”

android:textAllCaps=“false”

android:textSize=“26sp”

app:layout_constraintLeft_toLeftOf=“parent”

app:layout_constraintRight_toRightOf=“parent”

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

<Button

android:id=“@+id/btn_test_sale”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“测试卖票”

android:textAllCaps=“false”

android:textSize=“26sp”

app:layout_constraintLeft_toLeftOf=“parent”

app:layout_constraintRight_toRightOf=“parent”

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

</androidx.constraintlayout.widget.ConstraintLayout>

3、线程池的应用

========

new Thread的弊端:

  • a.每次new Thread新建对象性能差.

  • 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() {

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

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

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

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

题外话

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在IT学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多程序员朋友无法获得正确的资料得到学习提升,故此将并将重要的Android进阶资料包括自定义view、性能优化、MVC与MVP与MVVM三大框架的区别、NDK技术、阿里面试题精编汇总、常见源码分析等学习资料免费分享出来。
Android学习PDF+学习视频+面试文档+知识点笔记

【Android思维脑图(技能树)】

知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。

需要的朋友,可以点赞关注+转发”前往免费领取!

e

public void run() {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

// handler.post(new Runnable() {

// @Override

// public void run() {

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

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

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

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

题外话

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在IT学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多程序员朋友无法获得正确的资料得到学习提升,故此将并将重要的Android进阶资料包括自定义view、性能优化、MVC与MVP与MVVM三大框架的区别、NDK技术、阿里面试题精编汇总、常见源码分析等学习资料免费分享出来。
Android学习PDF+学习视频+面试文档+知识点笔记

【Android思维脑图(技能树)】

知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。

[外链图片转存中…(img-GlAkvFlx-1711048107935)]

需要的朋友,可以点赞关注+转发”前往免费领取!

希望我能够用我的力量帮助更多迷茫、困惑的朋友们,帮助大家在IT道路上学习和发展~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值