iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >深入了解Android中的AsyncTask
  • 806
分享到

深入了解Android中的AsyncTask

asynctaskAndroid 2022-06-06 04:06:13 806人浏览 独家记忆
摘要

AsyncTask,即异步任务,是Android给我们提供的一个处理异步任务的类。通过此类,可以实现UI线程和后台线程进行通讯,后台线程执行异步任务,并把结果返回给UI线程。&

AsyncTask,即异步任务,是Android给我们提供的一个处理异步任务的类。通过此类,可以实现UI线程和后台线程进行通讯,后台线程执行异步任务,并把结果返回给UI线程。  我们知道,Android中只有UI线程,也就是主线程才能进行对UI的更新操作,而其他线程是不能直接操作UI的.这样的好处是保证了UI的稳定性和准确性,避免多个线程同时对UI进行操作而造成UI的混乱。  但Android是一个多线程操作系统,我们总不能把所有的任务都放在主线程中进行实现,比如网络操作,文件读取等耗时操作,如果全部放到主线程去执行,就可能会造成后面任务的阻塞。Android会去检测这种阻塞,当阻塞时间太长的时候,就会抛出Application Not Responsed(ANR)错误.所以我们需要将这些耗时操作放在非主线程中去执行.这样既避免了Android的单线程模型,又避免了ANR。  虽说现在做网络请求有了Volley全家桶和OkHttp这样好用的库,但是在处理其他后台任务以及与UI交互上,还是需要用到AsyncTask。任何一个用户量上千万的产品绝对不会在代码里面使用系统原生的AsynTask,因为它蛋疼的兼容性以及极高的崩溃率实在让人不敢恭维。  AsyncTask到底是什么呢?很简单,它不过是对线程池和Handler的封装;用线程池来处理后台任务,用Handler来处理与UI的交互。线程池使用的是Executor接口,我们先了解一下线程池的特性。

jdk5带来的一大改进就是Java的并发能力,它提供了三种并发武器:并发框架Executor,并发集合类型如ConcurrentHashMap,并发控制类如CountDownLatch等;尽量使用Exector而不是直接用Thread类进行并发编程

AsyncTask内部也使用了线程池处理并发;线程池通过ThreadPoolExector类构造,这个构造函数参数比较多,它允许开发者对线程池进行定制,我们先看看这每个参数是什么意思,然后看看Android是以何种方式定制的。

ThreadPoolExecutor的其他构造函数最终都会调用如下的构造函数完成对象创建工作:


  public ThreadPoolExecutor(int corePoolSize,
       int maximumPoolSize,
       long keepAliveTime,
       TimeUnit unit,
       BlockingQueue<Runnable> workQueue,
       ThreadFactory threadFactory,
       RejectedExecutionHandler handler);

corePoolSize: 核心线程数目,即使线程池没有任务,核心线程也不会终止(除非设置了allowCoreThreadTimeOut参数)可以理解为“常驻线程”

maximumPoolSize: 线程池中允许的最大线程数目;一般来说,线程越多,线程调度开销越大;因此一般都有这个限制。

keepAliveTime: 当线程池中的线程数目比核心线程多的时候,如果超过这个keepAliveTime的时间,多余的线程会被回收;这些与核心线程相对的线程通常被称为缓存线程

unit: keepAliveTime的时间单位

workQueue: 任务执行前保存任务的队列;这个队列仅保存由execute提交的Runnable任务

threadFactory: 用来构造线程池的工厂;一般都是使用默认的;

handler: 当线程池由于线程数目和队列限制而导致后续任务阻塞的时候,线程池的处理方式。

如果线程池中线程的数目少于corePoolSize,就算线程池中有其他的没事做的核心线程,线程池还是会重新创建一个核心线程;直到核心线程数目到达corePoolSize(常驻线程就位)。如果线程池中线程的数目大于或者等于corePoolSize,但是工作队列workQueue没有满,那么新的任务会放在队列workQueue中,按照FIFO的原则依次等待执行。当有核心线程处理完任务空闲出来后,会检查这个工作队列然后取出任务默默执行去,如果线程池中线程数目大于等于corePoolSize,并且工作队列workQueue满了,但是总线程数目小于maximumPoolSize,那么直接创建一个线程处理被添加的任务。如果工作队列满了,并且线程池中线程的数目到达了最大数目maximumPoolSize,那么就会用最后一个构造参数handler处理;**默认的处理方式是直接丢掉任务,然后抛出一个异常。总结起来,也即是说,当有新的任务要处理时,先看线程池中的线程数量是否大于 corePoolSize,再看缓冲队列 workQueue 是否满,最后看线程池中的线程数量是否大于 maximumPoolSize。另外,当线程池中的线程数量大于 corePoolSize 时,如果里面有线程的空闲时间超过了 keepAliveTime,就将其移除线程池,这样,可以动态地调整线程池中线程的数量。

AsyncTask里面有“两个”线程池;一个THREAD_POOL_EXECUTOR一个SERIAL_EXECUTOR;之所以打引号,是因为其实SERIAL_EXECUTOR也使用THREAD_POOL_EXECUTOR实现的,只不过加了一个队列弄成了串行而已。AsyncTask里面线程池是一个核心线程数为CPU + 1,最大线程数为CPU * 2 + 1,工作队列长度为128的线程池;并且没有传递handler参数,那么使用的就是默认的Handler(拒绝执行)。如果任务过多,那么超过了工作队列以及线程数目的限制导致这个线程池发生阻塞,那么悲剧发生,默认的处理方式会直接抛出一个异常导致进程挂掉。假设你自己写一个异步图片加载的框架,然后用AsyncTask实现的话,当你快速滑动ListView的时候很容易发生这种异常;这也是为什么各大ImageLoader都是自己写线程池和Handlder的原因。这个线程池是一个静态变量;那么在同一个进程之内,所有地方使用到的AsyncTask默认构造函数构造出来的AsyncTask都使用的是同一个线程池,如果App模块比较多并且不加控制的话,很容易满足第一条的崩溃条件;如果你不幸在不同的AsyncTask的doInBackgroud里面访问了共享资源,那么就会发生各种并发编程问题。

在AsyncTask全部执行完毕之后,进程中还是会常驻corePoolSize个线程;在Android 4.4 (api 19)以下,这个corePoolSize是hardcode的,数值是5;API 19改成了cpu + 1;也就是说,在Android 4.4以前;如果你执行了超过五个AsyncTask;然后啥也不干了,进程中还是会有5个AsyncTask线程。

      AsyncTask里面的handler很简单,如下(API 22代码):


   private static final InternalHandler sHandler = new InternalHandler();
    public InternalHandler() {
    super(Looper.getMainLooper());
   }

注意,这里直接用的主线程的Looper;如果去看API 22以下的代码,会发现它没有这个构造函数,而是使用默认的;默认情况下,Handler会使用当前线程的Looper,如果你的AsyncTask是在子线程创建的,那么很不幸,你的onPreExecute和onPostExecute并非在UI线程执行,而是被Handler post到创建它的那个线程执行;如果你在这两个线程更新了UI,那么直接导致崩溃。这也是大家口口相传的AsyncTask必须在主线程创建的原因。另外,AsyncTask里面的这个Handler是一个静态变量,也就是说它是在类加载的时候创建的;如果在你的APP进程里面,以前从来没有使用过AsyncTask,然后在子线程使用AsyncTask的相关变量,那么导致静态Handler初始化,如果在API 16以下,那么会出现上面同样的问题;这就是AsyncTask必须在主线程初始化 的原因。事实上,在Android 4.1(API 16)以后,在APP主线程ActivityThread的main函数里面,直接调用了AscynTask.init函数确保这个类是在主线程初始化的;另外,init这个函数里面获取了InternalHandler的Looper,由于是在主线程执行的,因此,AsyncTask的Handler用的也是主线程的Looper。这个问题从而得到彻底的解决。

AsyncTask的使用较为简单,只需要关注三个参数和四个方法即可。

AsyncTask<Params,Progress,Result>是一个抽象类,通常用于被继承.继承AsyncTask需要指定如下三个泛型参数:

     Params:启动任务时输入的参数类型.
     Progress:后台任务执行中返回进度值的类型.
     Result:后台任务执行完成后返   回结果的类型.

AsyncTask主要有如下几个方法:

     doInBackground:必须重写,异步执行后台线程要完成的任务,耗时操作将在此方法中完成.      onPreExecute:执行后台耗时操作前被调用,通常用于进行初始化操作.      onPostExecute:当doInBackground方法完成后,系统将自动调用此方法,并将doInBackground方法返回的值传入此方法.通过此方法进行UI的更新.
     onProgressUpdate:当在doInBackground方法中调用publishProgress方法更新任务执行进度后,将调用此方法.通过此方法我们可以知晓任务的完成进度.

下面通过代码演示一个典型的异步处理的实例--加载网络图片.网络操作作为一个不稳定的耗时操作,从4.0开始就被严禁放入主线程中.所以在显示一张网络图片时,我们需要在异步处理中下载图片,并在UI线程中设置图片。

MainActivity.java


import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
 private Button btn_image;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  btn_image = (Button) findViewById(R.id.btn_image);
  btn_image.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    startActivity(new Intent(MainActivity.this,ImageActivity.class));
   }
  });
 }
}

ImageActivity.java


import android.app.Activity;
import android.graphics.*;
import android.os.*;
import android.view.View;
import android.widget.*;
import java.io.*;
import java.net.*;
public class ImageActivity extends Activity {
 private ImageView imageView ;
 private ProgressBar progressBar ;
 private static String URL = "http://tupian.baike.com/a2_50_64_01300000432220134623642199335_jpg.html?prd=so_tupian";
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.image);
  imageView = (ImageView) findViewById(R.id.image);
  progressBar = (ProgressBar) findViewById(R.id.progressBar);
  //通过调用execute方法开始处理异步任务.相当于线程中的start方法.
  new MyAsyncTask().execute(URL);
 }
 class MyAsyncTask extends AsyncTask<String,Void,Bitmap> {
  //onPreExecute用于异步处理前的操作
  @Override
  protected void onPreExecute() {
   super.onPreExecute();
   //此处将progressBar设置为可见.
   progressBar.setVisibility(View.VISIBLE);
  }
  //在doInBackground方法中进行异步任务的处理.
  @Override
  protected Bitmap doInBackground(String... params) {
   //获取传进来的参数
   String url = params[0];
   Bitmap bitmap = null;
   URLConnection connection ;
   InputStream is ;
   try {
    connection = new URL(url).openConnection();
    is = connection.getInputStream();
    //为了更清楚的看到加载图片的等待操作,将线程休眠3秒钟.
    Thread.sleep(3000);
    BufferedInputStream bis = new BufferedInputStream(is);
    //通过decodeStream方法解析输入流
    bitmap = BitmapFactory.decodeStream(bis);
    is.close();
    bis.close();
   } catch (IOException e) {
    e.printStackTrace();
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   return bitmap;
  }
  //onPostExecute用于UI的更新.此方法的参数为doInBackground方法返回的值.
  @Override
  protected void onPostExecute(Bitmap bitmap) {
   super.onPostExecute(bitmap);
   //隐藏progressBar
   progressBar.setVisibility(View.GoNE);
   //更新imageView
   imageView.setImageBitmap(bitmap);
  }
 }
}
您可能感兴趣的文章:Android AsyncTask使用以及源码解析Android屏幕旋转 处理Activity与AsyncTask的最佳解决方案Android AsyncTask实现机制详细介绍及实例代码Android中使用AsyncTask实现文件下载以及进度更新提示详解Android中AsyncTask的使用方法Android中通过AsyncTask类来制作炫酷进度条的实例教程详解Android App中的AsyncTask异步任务执行方式Android AsyncTask完全解析 带你从源码的角度彻底理解Android使用AsyncTask实现多线程下载的方法Android AsyncTask用法巧用实例代码


--结束END--

本文标题: 深入了解Android中的AsyncTask

本文链接: https://www.lsjlt.com/news/22555.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 深入浅析Android中的AsyncTask
    这篇文章给大家介绍深入浅析Android中的AsyncTask,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。1、Asynctask简介1.1 使用方法简介Asynctask作为Android的基础之一,怎么使用就不多讲...
    99+
    2023-05-31
    android asynctask cta
  • 深入了解Android中GestureDetector的定义与使用
    目录简介赋予widget可以点击的功能会动的组件可删除的组件总结简介 之前我们介绍了GestureDetector的定义和其提供的一些基本的方法,GestureDetector的好处...
    99+
    2023-01-31
    Android GestureDetector使用 Android GestureDetector
  • 深入了解Android IO的底层原理
    目录前言一、应用层1. IO的分类1.1 缓冲和直接1.2 阻塞和异步2. IO流程二、sysCall系统调用三、虚拟文件系统1. VFS结构2. VFS中的缓存3. IO流程四、文...
    99+
    2024-04-02
  • Android 中ThreadLocal的深入理解
    ThreadLocal前言:    ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,ThreadLocal是一个线程内部的数据存储类...
    99+
    2023-05-30
    android threadlocal roi
  • android中AsyncTask类的示例分析
    这篇文章将为大家详细讲解有关android中AsyncTask类的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。AsyncTask也叫做“异步任务”,是一个抽象类   Asyn...
    99+
    2023-05-30
    android
  • 深入了解Node中的Buffer
    最开始的时候 JS 只在浏览器端运行,对于 Unicode 编码的字符串容易处理,但是对于二进制和非 Unicode 编码的字符串处理困难。并且二进制是计算机最底层的数据格式,视频/音频/程序/网络包都是以二进制来存储的。所以 Node 需...
    99+
    2023-05-14
    前端 Node.js
  • 浅谈Android中AsyncTask的工作原理
    目录概述AsyncTask使用方法AsyncTask的4个核心方法AsyncTask的工作原理概述 实际上,AsyncTask内部是封装了Thread和Handler。虽然Async...
    99+
    2024-04-02
  • 深入了解Python中的变量
    目录1 Python变量概述2 Python变量的命名3 Python变量赋值3.1 Python赋值概述3.2 Python变量的基本格式3.3 Python变量的其他赋值格式3....
    99+
    2024-04-02
  • Android开发之Gradle 进阶Tasks深入了解
    目录前言定义Taskregister与create的区别查找Task配置Task将参数传递给Task构造函数Task添加依赖Task排序Task添加说明跳过Task使用onlyIf使...
    99+
    2024-04-02
  • 深入了解zhparser
    瀚高数据库 目录 环境 文档用途 详细信息 环境 系统平台:N/A 版本:14 文档用途 本文参考《zhparser全文检索》,文章ID:051686104;对其进行互补,对zhparser进行进一步...
    99+
    2023-10-27
    postgresql 数据库 php
  • 带你深入了解Android的事件分发机制
    Android的事件分发机制是指在Android系统中,如何将用户的触摸事件、按键事件等传递给正确的View进行处理的一套机制。它是Android应用程序中实现交互的重要部分,确保用户的操作能够被正确地捕获和处理。 Android的事件分发...
    99+
    2023-08-18
    android android studio ui 知识图谱 事件分发
  • 深入了解JavaScriptPromise
    目录一 什么是 Promise?二 为什么有 Promise?三 Promise常用api四 Promise常用的两个用法总结一 什么是 Promise? 一个 Promise 对...
    99+
    2024-04-02
  • 如何深入了解Redis中的Codis
    这篇文章给大家介绍如何深入了解Redis中的Codis,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。场景在大数据高并发场景下,使用单个redis实例,即使redis的性能再高,也会变的...
    99+
    2024-04-02
  • 深入了解Golang中的run方法
    Go是一种快速,可靠和开源的编程语言。Go语言通过其高效的并发性和垃圾回收器以及C的速度,用于构建高效和可扩展的网络服务器和系统编程。让我们深入了解Golang中的run方法。run()方法是golang中重要的一种方法,可以用于创建新的协...
    99+
    2023-05-14
  • python中jieba模块的深入了解
    目录一、前言        二、模块的安装三、jieba模块具体讲解3.1分词模式3.2cut()、lcut()3.2.1cut(s...
    99+
    2024-04-02
  • 深入了解HTML中的video元素
    HTML中video视频标签详解 HTML5中的video标签是一种用于在网页上播放视频的标签。它可以使用不同的格式来呈现视频,例如MP4、WebM、Ogg等等。在本篇文章中,我们将详...
    99+
    2024-02-24
    html 标签 video
  • 深入了解Python中的os.path.join函数
    深入了解Python中的os.path.join函数 1. 引言 在Python中,处理文件和目录路径是常见的任务。为了简化路径的拼接和操作,Python提供了os.path模块,其中的join函数是...
    99+
    2023-09-08
    python 开发语言
  • 深入了解PHP中的Request概念
    标题:深入了解PHP中的Request概念 在PHP编程中,Request是一个非常重要的概念,它代表着客户端向服务器发送的请求。了解Request的机制可以帮助我们更好地处理用户输入...
    99+
    2024-02-27
    php request 概念 表单提交
  • 深入了解golang中的的泛型(Generic)
    本篇文章给大家带来的内容是介绍深入理解golang中的泛型?泛型怎么使用?有一定的参考价值,有需要的朋友可以参考一下,希望对你们有所助。什么是泛型泛型(Generic)是一种编程技术。在强类型语言中, 允许编写代码时使用以后才指定的类型, ...
    99+
    2023-05-14
    Go 后端
  • 深入了解Spring中的@Autowired和@Resource注解
    目录@Autowired@Resource总结应用场景@Resource和@Autowired是Spring Framework中两种常用的注入方式,它们的作用是在Spring容器中...
    99+
    2023-05-17
    Spring @Autowired和@Resource Spring @Autowired Spring @Resource
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作