xutilsbitmaputils改造以加入drawable支持

mthoutai mthoutai     2022-08-31     750

关键词:

=== XUtilsBitmapUtils 改造以加入drawable支持 ===

 

# XUtils 简单介绍

XUtils 是一套少有的早期国产安卓框架, 其源于AFinal, 文件夹结构也与之相似, 可是代码却进行了大量的重构, 使得XUtils更加现代, 攻克了AFinal 的OOM等问题.

眼下 XUtils 已经支持 API 8(android 2.2) 至 API 21(android 5.0.x).

XUtils 主要内置了DbUtils 模块, ViewUtils 模块, HttpUtils 模块, BitmapUtils 模块.

对于新手来说, 这些功能着实使用并且强大, 为我们省下了不少的功夫 去处理业务.

同类的框架, 国外流行的的有androidannotations, roboguice, androidquery,droidparts等, 当然国内也有不少竞争者,

ThinkAndroid,UltimateAndroid, LoonAndroid, KJFrameForAndroid, SmartAndroid, 都是能够能够用来借鉴的. 本文临时专注于XUtils的使用.

 

详细的细化模块能够參考 官方地址(wyouflf/xUtils):

https://github.com/wyouflf/xUtils

 

做过android的同学一定都知道安卓处理Bitmap可谓一绝, Bitmap绝对是吃内存的大户, 并且Dalvik虚拟机(临时不考虑ART技术)垃圾回收常常不及时, 所以图片处理不当,常常会出现OOM(out of memory), 即内存溢出的情况. 在接触XUtils等框架之前, 非常多人都是自己通过BitmapFactory.Options 来解决燃眉之急, 网上也有非常多对策, 可是这样非常不系统, 并且有些方案, 比如使用软引用或者弱引用, 已经在安卓4之后不再被推荐(事实上com.lidroid.xutils.bitmap.core.AsyncDrawable.java 还是用了弱引用), 仍然可能会出现OOM. 所以一款流行的, 稳定的, 现代的代码框架是不可缺少的. XUtils 恰恰满足了这一点.

 

XUtils 的图片处理存在缓存, 主要是内存缓存和外存缓存. 可是这不是今天本文的重点, 可是以后会提及. 今天主要说说XUtils不太好的方面, 首先直接上改动过的官方代码:

 

// this 是一个 Context

BitmapUtils bitmapUtils = new BitmapUtils(this);

 

// 载入网络图片

bitmapUtils.display(testImageView,"http://www.52deng.com/logo.png");

bitmapUtils.display(testImageView,"ftp://www.52deng.com/logo.png");

 

// 载入本地图片, 路径以/开头, 须要填写绝对路径

bitmapUtils.display(testImageView,"/sdcard/dengdeng/test.jpg");

 

// 载入assets中的图片, 路径以assets开头

bitmapUtils.display(testImageView,"assets/dengdeng/wallpaper.jpg");

 

// 使用ListView等容器展示图片时, 可通过PauseOnScrollListener在滑动和高速滑动过程中控制暂停载入图片

listView.setOnScrollListener(newPauseOnScrollListener(bitmapUtils, false, true));

listView.setOnScrollListener(newPauseOnScrollListener(bitmapUtils, false, true, customListener));

 

凝视已经被我优化, 相信结合代码, 语义应该更加明朗了. (← 你够了, 语文渣)

 

 

可是细致观察会发现, 事实上UXtils还是有不完美的地方: 貌似并不支持从项目中的drawable获取图片进行展示, 这样岂不是遇到大图片又要回归BitmapFactory.Options等基础方案了吗?

这里给大家推荐一下还有一个安卓专攻图片处理的框架Android-Universal-Image-Loader, 官方地址例如以下:

https://github.com/nostra13/Android-Universal-Image-Loader,

看关注度就能看出来, 它在Github上处于垄断地位, 当然还有其它的专攻网络和图片异步的框架(国外的有glide, ion, Picasso,volley等), 都是非常厉害和出名的. 那么我们来看看 他支持的图片处理方案, 不改了, 直接引用官方的样例:

 

"http://site.com/image.png"// from Web

"file:///mnt/sdcard/image.png"// from SD card

"file:///mnt/sdcard/video.mp4"// from SD card (video thumbnail)

"content://media/external/images/media/13"// from content provider

"content://media/external/video/media/13"// from content provider (video thumbnail)

"assets://image.png"// from assets

"drawable://"+ R.drawable.img// fromdrawables (non-9patch images)

 

NOTE: Use drawable:// only if you really need it! Always considerthe native way toload drawables -ImageView.setImageResource(...) instead of using of ImageLoader.

 

看, 他是 支持多种图片协议或者存储路径的, 也包含drawable, 可是值得注意的是, 他事实上并不推荐缓存drawable, 依据我的理解, 毕竟有一些drawable非常小, 直接使用 ImageView 等空间自带的放置图片的方法就可以. 可是遇到OOM的话, 该出手时就出手. 因为时间紧迫, 临时不细研究这款开源项目的设计, 直接扒代码. 检出项目之后, Ctrl+H选择项目, 全文搜索 keyword”drawable://”. 结果出来一堆东西, 换个思路, 搜索”assets://”, 竟然找到的是样例, 再换思路, 搜索”assets:”, ok, 侥幸找到了核心代码 (事实上他是通过 scheme 匹配传递的url的协议的):

 

// com.nostra13.universalimageloader.core.download.BaseImageDownloader.java

 

       @Override

       public InputStream getStream(String imageUri,Objectextrathrows IOException{

              switch (Scheme.ofUri(imageUri)) {

                     case HTTP:

                     case HTTPS:

                            return getStreamFromNetwork(imageUri,extra);

                     case FILE:

                            return getStreamFromFile(imageUri,extra);

                     case CONTENT:

                            return getStreamFromContent(imageUri,extra);

                     case ASSETS:

                            return getStreamFromAssets(imageUri,extra);

                     case DRAWABLE:

                            return getStreamFromDrawable(imageUri,extra);

                     case UNKNOWN:

                     default:

                            return getStreamFromOtherSource(imageUri,extra);

              }

       }

 

好吧, 把DRAWABLE看看:

       protected InputStream getStreamFromDrawable(String imageUri,Objectextra) {

              String drawableIdString = Scheme.DRAWABLE.crop(imageUri);

              int drawableId =Integer.parseInt(drawableIdString);

              return context.getResources().openRawResource(drawableId);

       }

 

OK了, 那么来匹配一下 Xutils, 开启Ctrl+H全文搜索打开, 搜索”assets”, 轻松找到了:

 

// com.lidroid.xutils.bitmap.download.DefaultDownloader.java

        if (uri.startsWith("/")) {

                FileInputStream fileInputStream =newFileInputStream(uri);

                fileLen = fileInputStream.available();

                bis = new BufferedInputStream(fileInputStream);

                result = System.currentTimeMillis() + this.getDefaultExpiry();

            } else if (uri.startsWith("assets/")) {

                InputStream inputStream = this.getContext().getAssets().open(uri.substring(7,uri.length()));

                fileLen = inputStream.available();

                bis = new BufferedInputStream(inputStream);

                result = Long.MAX_VALUE;

            } else {

                final URLurl =newURL(uri);

                urlConnection =url.openConnection();

                urlConnection.setConnectTimeout(this.getDefaultConnectTimeout());

                urlConnection.setReadTimeout(this.getDefaultReadTimeout());

                bis = new BufferedInputStream(urlConnection.getInputStream());

                result = urlConnection.getExpiration();

                result = result < System.currentTimeMillis() ?System.currentTimeMillis() +this.getDefaultExpiry() :result;

                fileLen = urlConnection.getContentLength();

            }

}

 

有了前几步的经验, 轻松改造, 加一个else if, 代码 參考之前的Android-Universal-Image-Loader的核心代码:

if (uri.startsWith("/")) {

                FileInputStream fileInputStream =newFileInputStream(uri);

                fileLen = fileInputStream.available();

                bis = new BufferedInputStream(fileInputStream);

                result = System.currentTimeMillis() + this.getDefaultExpiry();

            } else if (uri.startsWith("assets/")) {

                InputStream inputStream = this.getContext().getAssets().open(uri.substring(7,uri.length()));

                fileLen = inputStream.available();

                bis = new BufferedInputStream(inputStream);

                result = Long.MAX_VALUE;

            } else if (uri.startsWith("drawable://")) {//赤裸裸地抄袭,我也用这个协议

                String drawableIdString = uri.substring(11,uri.length());//注意别算错了

                int drawableId =Integer.parseInt(drawableIdString); //还原原始的 id

                InputStream inputStream = this.getContext().getResources().openRawResource(drawableId);

                fileLen = inputStream.available();//抄上面的

                bis = new BufferedInputStream(inputStream); //抄上面的

                result = Long.MAX_VALUE;//抄上面的,先这么写,以后讨论

            } else {

                final URLurl =newURL(uri);

                urlConnection =url.openConnection();

                urlConnection.setConnectTimeout(this.getDefaultConnectTimeout());

                urlConnection.setReadTimeout(this.getDefaultReadTimeout());

                bis = new BufferedInputStream(urlConnection.getInputStream());

                result = urlConnection.getExpiration();

                result = result < System.currentTimeMillis() ?System.currentTimeMillis() +this.getDefaultExpiry() :result;

                fileLen = urlConnection.getContentLength();

            }

       }

 

轻松改造, TEST!!

String uri = "drawable://" +R.drawable.super_larger_logo;

BitmapHelp.getBitmapUtils(this).display(largePic_imgV, uri);

 

OK, 測试通过, 图片出现, 未出现OOM. 至此, 成功地加入drawable支持. 同理, 我们也能够抄抄”content”等协议的代码, 本文就不赘述了, 注意源码的协议, 引用或者改动都要留出处啊.

 

2015-03-14 PS: 注意不要对 *.9.png 套用, 这非常没有必要, *.9.png 通常非常小. 此外 对图片实时性要求高的也不可用上面的方案, 尽管能解决 内存溢出和卡顿, 可是 播放连贯性 和 用户体验等会打大折扣的.

 

Presented by imknown

2015-03-13

如何为这个 Json 文件制作模型以进行改造?

】如何为这个Json文件制作模型以进行改造?【英文标题】:HowcanimakeamodelforthisJsonfileforRetrofit?【发布时间】:2020-12-3017:39:50【问题描述】:Json文件是:"data":["name":"key","error":"keyisnotvalid","name":"package_name","error":"packagenameisnotvalid"],"... 查看详情

我如何处理改造错误或获取 URL 请求以检测问题

】我如何处理改造错误或获取URL请求以检测问题【英文标题】:Howcanihandleretrofiterrorsorgeturlrequestfortodetectproblems【发布时间】:2021-12-1902:33:26【问题描述】:我正在尝试学习开发android应用程序,并且正在尝试实施最新的方法。所... 查看详情

如何通过改造发布数据并以正确的格式获得响应?

】如何通过改造发布数据并以正确的格式获得响应?【英文标题】:HowtoPOSTdatawithretrofitandgettheresponseinproperformat?【发布时间】:2019-06-1518:21:01【问题描述】:我遇到了异常java.lang.IllegalStateException:ExpectedBEGIN_OBJECTbutwasSTRINGatline2colu... 查看详情

重载运算符 const char* 以进行就地改造?

】重载运算符constchar*以进行就地改造?【英文标题】:Overloadoperatorconstchar*forretrofittingin-place?【发布时间】:2015-03-0203:43:58【问题描述】:我正在处理一堆现有的测试,如下所示(实际上有几百个):boolTest_X(constchar*file)...它会... 查看详情

如何改造基于非通用的 EF Core 代码以使用通用 IQueryable

】如何改造基于非通用的EFCore代码以使用通用IQueryable【英文标题】:HowtoretrofitNonGenericbasedEFCorecodetouseGenericIQueryable【发布时间】:2022-01-1708:13:41【问题描述】:我正在将旧的EF解决方案更新到EFCore,我想删除大部分不使用泛型(T)... 查看详情

如何正确地将数据解析到我的改造客户端中以执行发布请求[重复]

】如何正确地将数据解析到我的改造客户端中以执行发布请求[重复]【英文标题】:Howtoparsedataintomyretrofitclientcorrectlyinordertopreformapostrequest[duplicate]【发布时间】:2021-05-1921:31:49【问题描述】:我正在尝试通过POST请求将JSON发送到... 查看详情

谷歌地球模型改造

】谷歌地球模型改造【英文标题】:googleearthmodeltransformation【发布时间】:2012-03-1007:38:51【问题描述】:我想在googleearth中为部分collada几何图形添加地标。为此,我需要转换xml中的几何坐标以匹配转换到谷歌地球中的模型。给定... 查看详情

如何在不使用 GSON 或 android 中的任何其他库的情况下使用改造以字符串形式获得响应 [重复]

】如何在不使用GSON或android中的任何其他库的情况下使用改造以字符串形式获得响应[重复]【英文标题】:HowtogetresponseasStringusingretrofitwithoutusingGSONoranyotherlibraryinandroid[duplicate]【发布时间】:2015-12-1313:57:15【问题描述】:我正在... 查看详情

以编程方式创建和加入hyperledgerfabric通道

...组织的现实区块链网络中这是如何有意义的吗?至少应该加入该频道的所有同伴都需要某种方式来检查加入频道和创建频道是否在他们意义上。有没有办法以舒适和安全的方式进行创作和加入?答案无论如何,您需要足够的签名... 查看详情

dra7xx:configureforrtosusecasetobulild(代码片段)

exampleforinhlosaddrtosusecasetobuild.commitf01f061b6db87566e9623ebf43b2466586e1f9fbDate:FriMay1017:06:402019+0800VSDKbuild:addRTOSusecasestobuilddiff--gita/apps/Makefileb/apps/Makefileindex033b932..0 查看详情

32面向对象设计模式之工厂方法模式——工厂方法对之前的例子进行改造(代码片段)

简单工厂模式的不足  在简单工厂模式中,只提供了一个工厂类,该工厂类处于对产品类进行实例化的中心位置,它知道每个产品对象的创建细节,并决定何时实例化哪个产品类。简单工厂模式的最大的缺点是有当有新产品要... 查看详情

过滤节点并加入其他元素以获得标题

】过滤节点并加入其他元素以获得标题【英文标题】:FilteringNodesandJoinwithotherelementtogetcaption【发布时间】:2021-07-2605:56:08【问题描述】:我有这个xml<?xmlversion="1.0"encoding="UTF-8"?><BRDatasetNodeRootNodeType="Entersoft.Framework.Platform.Cu... 查看详情

Laravel 加入以检索总数

】Laravel加入以检索总数【英文标题】:LaravelJoinstoretrievetotalcount【发布时间】:2020-12-1517:59:26【问题描述】:我一直试图通过文档和多次尝试来解决这个问题,但我无法解决这个非常简单的问题。我正在使用Laravel7。我有3张桌子... 查看详情

Kotlin Coroutines viewModelScope 中的改造调用

】KotlinCoroutinesviewModelScope中的改造调用【英文标题】:RetrofitcallinKotlinCoroutinesviewModelScope【发布时间】:2020-07-2716:02:18【问题描述】:最近我更新了我的ViewModel以使用新的viewModelScope。从它的实现中,我看到Dispatchers.Main.immediate被... 查看详情

mysql如何加入以获取数据[重复]

】mysql如何加入以获取数据[重复]【英文标题】:mysqlhowtojointofetchdata[duplicate]【发布时间】:2012-12-1414:13:19【问题描述】:可能重复:HowcananSQLqueryreturndatafrommultipletables我有3张桌子属性attr_id|attr_name1|oval<2|white产品product_id|product_... 查看详情

改造-http 405 响应?

】改造-http405响应?【英文标题】:Retrofit-http405response?【发布时间】:2017-05-2122:33:44【问题描述】:我正在尝试创建一个登录页面,并且我使用django作为后端。使用的身份验证是基于令牌的。我试图发布用户名和密码,这给了我... 查看详情

替换 Union All 加入以提高性能

】替换UnionAll加入以提高性能【英文标题】:ReplaceUnionAlltojointoimproveperformance【发布时间】:2018-04-0308:08:29【问题描述】:我有一个工作查询需要20分钟才能返回数据。我想优化它。我有桌子激励措施:Transaction_ID|Incentive_On_A|Incent... 查看详情

MS Access 加入以创建新字段

】MSAccess加入以创建新字段【英文标题】:MSAccessJoinstocreateanewfield【发布时间】:2011-02-0200:09:41【问题描述】:我在MicrosoftAccess中有三个表。我有一个查询,它在一个属性上连接前两个,但如果Table2中给定名称的MeasurementType为“... 查看详情