Android 从一次apk迁移窥看Android JellyBean(4.1)的变化
平台的版本的变化会引入新的特性,和对现有API的优化。
对于Android 4.1 (JellyBean)的新特性请参考官方文档android-4.1,由于本文的重点不是对4.1新特性进行介绍,所以暂时省略这部分内容。
众所周知对于已有项目的维护,有一项必不可少的工作就是对app进行平台版本的迁移工作,对于身处移动平台的Android更是如此。这期间会碰见许多"莫名其妙"的奇怪问题,下文会结合一次实际的平台迁移工作小结下一部分4.1下API的变化以及可能引起的问题。
案例1. 莫名其妙的 "java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics......"
相信各位看官对这个exception一定不会陌生,正如exception 本身所描述的那样,当我们试图在Canvas上Draw一个回收过的bitmap时就会抛出这样的异常。
同样的apk在4.1版本以下运行都是没有问题的,但一旦运行到4.1版本的操作系统下就会抛出这个异常,这应该是一个由版本迁移引起的API变化的问题,那么到底是哪个API引起的问题呢?怎样定位到这个有问题的API呢?
bitmap.recycle()!既然是回收bitmap引起的问题,那就从所有调用bitmap.recycle()方法的地方入手,根据重现步骤反相定位有问题的代码。
按照这样的解决思路我很快的揪出了这个引起问题的API
Bitmap result = Bitmap.createScaledBitmap(bitmap, dstWidth, dstHeight, true);
首先说说这个API在JellyBean下发生了什么样的变化?
这个Bitmap的静态方法createScaledBitmap(......)是用来根据一个源bitmap,和缩放的大小来创建一个缩放的bitmap。
那么请考虑这样的一种特殊情况,当作为参数传进来的源bitmap的宽度高度与同样是参数的缩放的宽度高度一致的时候(换句话说就是没有对源bitmap进行缩放的时候),这个API会返回什么样的结果呢?
Source bitmap hash code | Returned bitmap hash code | |
JellyBean | 42041a00 | 42041a00 |
ICS | 41cd8888 | 41dfa4c8 |
Gingerbread | 41d37750 | 41c2a3d1 |
各位看官看出有什么问题么?
对。那就是在JellyBean之前的版本返回的这个实际上没有缩放的bitmap与源bitmap实际上并不是同一个对象实例,但JellyBean对于这样的特殊用例给出了优化方案,那就是将这个源bitmap实例返回,减少创建不必要的内存。
那么为什么会引起draw 回收bitmap的问题呢?
请各位再次看看这个出问题的API,实际上当调用这个API的时候是想获得一个新的bitmap对象,这个新的bitmap对象可能是以缓存的形式存在,在某个时点会为了对bitmap资源进行管理,会调用bitmap。recycle() 方法进行释放。
注意:而实际上并不希望回收源bitmap,如果回收了这个源bitmap很有可能就会造成上面提到的那个Exception。
说到现在,那么这个问题就很好解释了,在JellyBean下如果调用方法,并将这个返回的bitmap缓存起来,在某个时点调用bitmap.recycle()方法进行手动回收,回收这个缓存的同时也会将源bitmap回收掉了,悲剧就发生了。
怎样解决?
两种解决方案;
1. 保持原先代码逻辑不变,只是对变化了实现的API进行一层方法封装
public Bitmap createScaledBitmap(Bitmap sourceBitmap, int scaledWidth, int scaledHeight, boolean filter){Bitmap scaledBitmap = null;if (null != sourceBitmap){if(sourceBitmap.getWidth() == scaledWidth && sourceBitmap.getHeight() == scaledHeight){scaledBitmap = Bitmap.createBitmap(sourceBitmap);}else{scaledBitmap = Bitmap.createScaledBitmap(sourceBitmap, scaledWidth, scaledHeight, filter);}}return scaledBitmap;
}
2. 设计好bitmap资源缓存回收机制,依赖GC和缓存回收机制回收资源;不再调用bitmap。recycle()方法回收bitmap。
案例2. ListView 中的RadioButton 点击后选中状态下UI没有更新
如同上个案例,这个问题同样也只会在JellyBean 下发生,在排除了UI展示所需的数据错误后,这个问题最终定位在了UI层中ListView中的UI未被更新。
为什么会在JellyBean中出现这个问题呢?
有两方面的原因:
首先,ListView中在处理点击事件的部分,framework层的实现有了变化,在JellyBean之前ListView在触发了Click事件后会调用Listview的requestLayout()方法,从而可能会导致ListView item的UI更新,而JellyBean出于优化的考虑,不再主动调用requestLayout()方法更新UI,也就是是说需要开发者自己负责去在数据变化的时候更新UI。
其次,基于第一个原因,当用户点击ListView中的item导致数据变化的时候没有调用adapter的notifyDatasetChanged()方法通知UI改变,这个原因是自身代码的问题,今后要引以为戒。
案例3. One table, many user
多可用户可以共享一个平板,这就像Windows下面的多个用户一样,不同的用户可以有不同的桌面,不同的widgets,不同的界面设置等信息,不同用户之间的信息是分隔开的,也就是说,这个用户的配置不会影响到其他用户。
每一个用户会共享应用程序,A用户装了一个程序,B用户也要可以装,但实现在平板上面,只会有一个程序,所有用户都共享这个程序。
对于开发者来说,一般我们不需要针对多用户做特别的操作,这里要说一点,特别重要:
在4.2上面,应用程序的数据存储路径再也不是/data/data/package-name了,而是/data/user/user-id/package-name。
因此,我们程序中如果需要存储一些配置文件到/data目录,一定不能直接是写死的字符串,而是始终都应该从程序中去动态获取,获取方法如下:
String dataDir = context.getApplicationInfo.dataDir
记住:
1,不要把存储路径写死。
2,不要把存储路径写成绝对路径。
3,动态根据程序来取存储路径,这样可以应对不同的系统版本。
总结
1,在Jelly Bean下,Bitmap.createScaledBitmap并不会总是返回一个新的Bitmap,如果指定的大小与源图片一样,那么它直接返回源图片。
2,在Android开发过程中,尽量不要自己去调用Bitmap.recycle()方法来回收bitmap所占的内在,因为,你很有可能不清楚这个bitmap是否还有对象在使用,比如如果这个bitmap设置为View的背景图片时。
3,在AdapterView(ListView, GridView, Gallery)在,如果当我们更改了数据后,一定要调用BaseAdapter#notifyDatasetChange()方法来更新UI,这一点务必明确。
Android 从一次apk迁移窥看Android JellyBean(4.1)的变化相关推荐
- android aapt下载,获取apk信息工具(android SDK的aapt工具)
aapt命令是android SDK 中的一个工具,功能强大,比如在windows平台获取apk包的信息. 使用该工具准备条件,也即获取aapt.exe文件的方式(2选1即可): 安装android ...
- android手机存储大小设置在哪里看,Android 最新获取手机内置存储大小,SD卡存储空间大小方法...
在以前我们都是这样获取的 //Android API18之前:fs.getAvailableBlocks()*fs.getBlockSize() 目前: /** 得到系统可用内存 **/ @Suppr ...
- android换iphone教程,换手机必看 Android转iPhone换机教程
2.短信 微信只有备份通讯录的功能,第三方软件包括QQ同步助手等虽然支持通话记录.短信的备份,但是无法再Android和iOS之间互通,想要在不越狱的情况下完成短信的备份有些繁琐,接下来我们就为大家演 ...
- android app安装,Android App更新安装APK
原标题:Android App更新安装APK 概要 一般地, Android App 都会被要求在App内进行软件更新提示, 让用户下载apk文件, 然后更新安装新版本, 一般过程如下: 检测是否有新 ...
- 解决 Android N 7.0 上 报错:android.os.FileUriExposedException
解决 Android N 上 安装Apk时报错:android.os.FileUriExposedException: file:///storage/emulated/0/Download/appN ...
- android下载后的app自动安装,Android 7.0 下载APK后自动安装
随着Android版本越来越高,Android对隐私的保护力度也越来越大.这些隐私权限的更改在为用户带来更加安全的操作系统的同时也为开发者带来了一些新的任务.如何让你的APP能够适应这些改变而不是崩溃 ...
- android APK内存多少字节,Android apk安全测评、应用加固、字节对齐、二次签名(有这一篇就够了)...
本文将通过前言.apk安全测评.应用加固.字节对齐.二次签名.总结共6大版块来阐述 一.前言 评判一个App是否出色,除了看它的性能.体验外,本人认为最为重要的是它的安全性.市面上apk安全评测.加固 ...
- android 电视安装apk文件损坏,android - 无法在Android电视盒上安装APK - 堆栈内存溢出...
我正在为Android TV编写应用程序. 它可以在模拟器上正常运行,并在Android电视盒上以调试模式运行. 但是,当我使用Android Studio生成apk并尝试将该apk安装在同一盒子上时 ...
- 从源码角度看Android系统SystemServer进程启动过程
SystemServer进程是由Zygote进程fork生成,进程名为system_server,主要用于创建系统服务. 备注:本文将结合Android8.0的源码看SystemServer进程的启动 ...
最新文章
- zynq tcp如何从网口发数据_ZYNQ_PL与PS的DDR交互
- 吃货告诉你IaaS、PaaS、SaaS之间的区别
- 3、4TP之url和路由
- 软件测试之三——路径覆盖
- Canal 1.1.5 启动报错:caching_sha2_password Auth failed
- webpack 保存文件后自动打包_继webpack后又一打包神器Parcel
- python实现一个小程序
- python3的面向对象_python3学习之面向对象
- Markdown 调整图片位置与大小
- html模板引擎 字符串长度,javascript轻量级模板引擎juicer使用指南
- DNF自动获取C语言,VC++开发硬件DNF搬砖项目全新发车,想上车的要赶紧
- 3GQQ幻想西游攻略
- Spring validation框架简介
- c语言strlen转义字符,转义字符 sizeof strlen
- php 处理eml,php读取eml实例、php解析eml、eml解析成网页
- Update Strategy小记
- powerdesigner中把用例关系线设置成直线
- python抢mac微信红包群_要抢红包?Python来帮你~
- android应用自启分析与S4启动列表
- 用计算机弹暖暖数字代码,奇迹暖暖网页版计算器