从最简单的图片加载,教你Android实现异步!
异步,在安卓开发中简直是再熟悉不过了。
说到异步,脑海中立马浮现的就是多线程开发,Thread、Handler啥的一一涌上心头…
我们知道在Android开发中不能在非UI线程中更新UI,但是,有的时候我们需要在代码中执行一些诸如访问网络、查询数据库等耗时操作,为了不阻塞UI线程,我们时常会开启一个新的线程(工作线程)来执行这些耗时操作,然后我们可能需要将查询到的数据渲染到UI组件上,那么这个时候我们就需要考虑异步更新UI的问题了。
今天我们从一个简单的业务需求,给大家介绍几种实现异步的方式,最后两个简直爽到不行。
业务是这样的:需要根据文件地址,加载本地图片,最后在ImageView上显示。当然了,从文件中加载图片,是一个耗时操作,必须在子线程中执行,ImageView显示图片呢,又属于UI操作,需要回到主线程。接下来列举几种实现方式:
Thread+Handler
使用Thread+Handler是最传统的实现异步方式了,看下代码:
new Thread(new Runnable() { @Override public void run() { Bitmap bitmap = getBitmapFromFile(PATH); handler.post(new Runnable() { @Override public void run() { imageView.setImageBitmap(bitmap); } }); } }).start();
如果熟悉Lambda表达式的话,也可以这样写:
new Thread(() -> { Bitmap bitmap = getBitmapFromFile(PATH); handler.post(() -> imageView.setImageBitmap(bitmap)); }).start();
这样看来代码干净了许多。
除了实现Runnable,还可以继承Thread,实现run方法来做到开启子线程。但由于Java的单继承多实现,所以还是使用实现Runnable方式更实用一些。handler的post方法可以将消息发送回主线程,以实现线程间切换。
这种方式在需要的地方new一个对象,使得代码繁乱,不易管理,对系统资源也不便管理。
AsyncTask
AsyncTask提供了方便的接口实现工作线程和主线程的通信。先贴代码:
class BitmapAsyncTask extends AsyncTask<String, Integer, Bitmap> { @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); } @Override protected Bitmap doInBackground(String... strings) { // 在doInBackground方法中执行耗时操作 Bitmap bitmap = getBitmapFromFile(strings[0]); return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); // 在onPostExecute方法中进行ui操作 imageView.setImageBitmap(bitmap); } } new BitmapAsyncTask().execute(PATH);
AsyncTask就是一个封装过的后台任务类,顾名思义就是异步任务。AsyncTask定义了三种泛型类型 Params,Progress和Result。
doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。
onPostExecute(Result) 相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回。
这种方式使用了线程池+Handler实现,较好得管理分配资源,还可以拿到进度回调,有较高的拓展性。但需要创建新类,代码也会随之增加,对于简单的异步操作,这种方式有些繁琐。
RxJava
主要还是用到了RxJava的Scheduler(调度器)来实现线程切换,看下代码:
Observable observable = Observable.create(new Observable.OnSubscribe<Bitmap>() { @Override public void call(Subscriber<? super Bitmap> subscriber) { Bitmap bitmap = getBitmapFromFile(PATH); subscriber.onNext(bitmap); } }); observable.subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程 .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程 .subscribe(new Subscriber<Bitmap>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Bitmap bitmap) { imageView.setImageBitmap(bitmap); } });
使用Observable.create创建Observable,在call方法中进行耗时操作,执行完成后发送消息,在观察者中的onNext中处理。
使用subscribeOn和observeOn进行线程切换。
使用RxJava的好处是很轻松得实现线程切换,还可以指定线程,有异常捕获机制。但对于不熟悉RxJava的朋友来说会有些…
Kotlin协程
最后要安利一个非常酷炫的方式,那就是Kotlin协程。
越来越多的公司和项目开始使用Kotlin编码,毕竟Kotlin得到了谷歌爸爸的支持,而且Kotlin的优秀语言特性,使得它受到开发者的广泛欢迎。
今天介绍Kotlin的一个概念,叫做协程。协程是由程序直接实现的,是一种轻量级线程,kotlin也为此提供了标准库和额外的实验库。标准库为kotlin.coroutines.experimental(写作时使用kotlin-1.20版本),可见仍然还是一个实验性功能。
看下代码
先定义一个后台CoroutineContext,协程上下文,很容易理解,就是执行环境。
val Background = newFixedThreadPoolContext(2, "bg") mWriteJob = launch(Background) { var bitmap = getBitmapFromFile(PATH); launch(UI){ imageView.setImageBitmap(bitmap) } }
最后会返回一个Job对象,可以调用方法将其任务停止:
if (mWriteJob != null && mWriteJob!!.isActive) { mWriteJob!!.cancel() }
不由得想感叹一下,使用协程做轻量的异步操作,简直爽到不行。
但毕竟协程可能还是了解不多,不免会有一些坑的出现,但多去了解和使用,想必也是很酷的。
小结
从个人感觉来说,我比较推荐使用RxJava和协程来实现,处理周密的话,轻松避免资源浪费和内存泄漏。
Android中的异步操作,实现方式有好多种,各有利弊,就需要我们针对具体业务需求来选择合适的方式,使得功能完成的前提下,优化性能,优化代码。
给看到最后的朋友们发一波福利;
现在加Android开发群;701740775,可免费领取一份最新Android高级架构技术体系大纲和视频资料,以及五年积累整理的所有面试资源笔记。加群请备注csdn领取高级大纲。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Android版中文分词:原理、接入和启动优化
中文分词功能是一项常用的基础功能,有很多开源的工程实现,目前能应用于Android手机端的中文分词器没有很完善的版本。经过调研,我选择了结巴分词,该开源工程思路简单,易于理解,分词效果也还不错,目前有众多语言版本,PYTHON、C++、JAVA、IOS等,暂时还没有Android版本,所以我在Java版本的基础上进行了移植,开发了适用于Android手机的结巴分词Android版。 相比于Java版本的实现,Android版将字典文件存放在Asset目录下进行读取,同时对字典加载速度进行了大幅优化。原始的Java版本加载完整的字典文件在测试手机上需要28秒,时间太长,经过优化,成功将加载时间降到1.5秒,分词速度1秒以内,满足了Android手机的启动速度要求。 本文将结合代码通过以下三个方面展开介绍:结巴分词的基本原理,Android版的接入方式,以及启动速度优化的实现。 结巴分词的原理 结巴分词采用两种方式进行分词,基于字典的分词和基于HMM(隐马尔科夫模型)的分词。模型会首先加载词典文件生成一个字典树,并利用该字典树进行一段中文的分词,比如“我要去五道口吃肯德基”被分词...
- 下一篇
ADB 你想找的命令都在这里
一、ADB 简介 ADB(Android Debug Bridge) 是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试应用,并提供对 Unix shell(可用来在模拟器或连接的设备上运行各种命令)的访问。该工具作为一个客户端-服务器程序。 客户端,该组件发送命令。客户端在开发计算机上运行。您可以通过发出 adb 命令从命令行终端调用客户端。 后台程序,该组件在设备上运行命令。后台程序在每个模拟器或设备实例上作为后台进程运行。 服务器,该组件管理客户端和后台程序之间的通信。服务器在开发计算机上作为后台进程运行。 adb 工具路径android_sdk/platform-tools/ 二、ADB的工作方式 1. 连接 Android 模拟器 ADB与本地 TCP 端口 5037 绑定,并侦听从 adb 客户端发送的命令—所有 adb 客户端均使用端口 5037 与 adb 服务器通信。然后,服务器设置与所有运行的Android模拟器/Android 设备连接。 2.USB 连接 Android 机器 a. 打开开发...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Hadoop3单机部署,实现最简伪集群
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS8编译安装MySQL8.0.19
- SpringBoot2配置默认Tomcat设置,开启更多高级功能