2015年8月29日下午,腾讯Bulgy移动开发者沙龙第三期在车库咖啡与大家如约相见。本期,我们分享的主题是安卓应用机型适配之痛。适配性问题作为安卓开发者中的老大难问题,几乎困扰过每一个安卓开发者。如何在保证快速的研发中,又能做好服务的质量保证呢?来听听我们的嘉宾带来的精彩分享。

一、我们服务过的海量优秀产品

二、个性化十足的Launcher

快捷方式虽然看起来只是一个很小的功能点,但是他涉及到的机型适配问题很多。

快捷方式创建代码:

ntent addShortCut = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");

addShortCut.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);

// 不允许重复创建

addShortCut.putExtra("duplicate", false);

addShortCut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon);

addShortCut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);

sendBroadcast(addShortCut);

1无法创建快捷方式

越来越多的手机厂商取消了快捷方式的概念,导致我们无法通过代码创建一个我们需要的快捷方式,根据我们所掌握的数据显示,这样的手机约占 13%。

2重复创建快捷方式

通常情况下,我们是不希望自己的快捷方式被重复创建:

使用 addShortCut.putExtra("duplicate", false);方法就能达到目的,但是市面上的手机至少有 8%的手机即使设置了 duplicate 为 false 还是可以重复创建快捷方式的。

代表手机品牌为:华为 中兴 HTC

Android Launcher 源码:

2.1 重复创建快捷方式的解决方案 V1.X

我们最早使用的解决快捷方式重复创建的方法是:在创建快捷方式前先执行删除操作,这种方式其实很聪明,因为即使是在快捷方式不存在的情况下执行删除操作也不会有任何异常,这样看来问题解决地太轻松了,但是遗憾的是删除快捷方式同样存在适配问题,我们掌握的数据显示大约 21%的手机无法正常删除快捷方式。

另外一种方法是:自行保存快捷方式的创建记录,通过一个字段来记录快捷方式是否已经创建过了,以此来决定是否创建新的快捷方式,这种做法也是因为出现快捷方式无法删除情况后对解决方案进行了一个小的升级,虽然可以解决问题,但是如果程序被清除了数据,那么一切都乱了,还是无法彻底的规避重复的问题。

2.2 重复创建快捷方式的解决方案 V2.X

遇到难解的问题还是看看源码吧,Android 的 Launcher 源码在创建快捷方式的时候不仅会判断 duplicate 的值,还会在数据库中查询一下将要被创建的快捷方式是否已经存在,我们也照做就 OK 了。

我们注意到,查询数据库的时候访问地址URI 是一个很重要的因素,问题是数据库的URI比较多,Android 标准的URI 就有3 个:

2.2 版本以前的URI 是:content://com.android.launcher.settings/favorites?notify=true

2.2~4.3 版本的URI 是:content://com.android.launcher2.settings/favorites?notify=true

4.4 版本以上的目前都是:content://com.android.launcher3.settings/favorites?notify=true

不仅仅Android 自己的Launcher 数据库地址众多,厂商自己定义的地址就更加丰富多彩,如OPPO R827T 的访问URI 为:content://com.oppo.launcher.settings /favorites?notify=true

HTC Z715e 的访问地址为:

content://com.htc.launcher.settings/favorites?notify=true

远远不止这些,还有不计其数的第三方Launcher 应用,很多开发者也会修改数据库访问地址,目前仅我们掌握的不同访问地址就有多达40 种左右。

通过权限查询URI:

通过数据库的读写权限来查询对应的URI 相信大家也不陌生,感觉上像是找到了终极的解决方案,且看下去...

问题一:如果使用完整的权限进行查询--权限众多,我们目前掌握的超过 50 种。

问题二:如果使用不完整的权限进行查询(READ_SETTINGS)对应关系复杂,大约有 32%的手机会对应两个以上的 URI。

例如:

GT-I8262D:

authority:com.sec.android.app.launcher.settings

ReadPermission:com.android.launcher.permission.READ_SETTINGS

authority:com.sec.android.app.launcher.settings.id

ReadPermission:com.android.launcher.permission.READ_SETTINGS

Lenovo A278t:

authority:com.aspire.mm.Settings ReadPermission:com.aspire.mm.permission.READ_SETTINGS

authority:com.huaqin.launcherEx.settings

ReadPermission:com.huaqin.launcherEx.permission.READ_SETTINGS

authority:com.huaqin.thememgr.Settings

ReadPermission:com.huaqin.thememgr.permission.READ_SETTINGS

三、多姿多彩的 Camera

1Intent 调用手机内相机程序

如果我们设置了照片的存储路径,那么很可能会遇到一下三种问题:

问题一:onActivityResult 方法中的data 返回为空(数据表明,93%的机型的data 将会是Null,所以如果我们指定了路径,就不要使用data 来获取照片,起码在使用前要做空判断)

问题二:照片无法存储

如果自定义存储路径是/mnt/sdcard/lowry/,而手机SD 卡下在拍照前没有名为lowry 的文件夹,那么部分手机拍照后图片不会保存,导致我们无法获得照片,大多数手机的相机遇到文件夹不存在的情况都会自己创建出不存在的文件夹,而个别手机却不会创建,其代表机型为:三星I8258、华为H30-T00、红米等。解决的方法就是在指定存储路径前先判断路径中的文件夹是否都存在,不存在先创建再调用相机。

问题三:照片可以存储,但是名字不对

file:///mnt/sdcard/123 1.jpg,由于Uri 的fromFile 方法会将路径中的空格用“%20”取代。其实对于大多数的手机这都不算事,手机在解析存储路径的时候都会将“%20”替换为空格,这样实际上最终的照片名字还是我们当初指定的名字:123 1.jpg,遗憾的是个别手机(如酷派7260)系统自带的相机没有将“%20”读成空格,拍照后的照片的名字是123%201.jpg,我们用路径“file:///mnt/sdcard/123 1.jpg”能找到照片才怪!!

总结:

(1)使用 onActivityResult 中的 intent(data)前要做空判断。

(2)指定拍照路径时,先检查路径中的文件夹是否都存在,不存在时先创建文件夹再调用相机拍照。

(3)指定拍照存储路径时,照片的命名中不要包含空格等特殊符号。

2通过 Camera 的 open 方法调用手机摄像头

2.1 连续自动对焦 crash

原因:第一次对焦未结束,应用层又发起的第二次对焦,引起对焦失败。

解决方案一:传入 AutoFocusCallback

解决方案二:延时操作

解决方案三:异常捕获

2.2 摄像头个数判断错误

现象:当我们使用 Camera.getNumberOfCameras()方法检测摄像头数量时返回的结果不准确,如果我们尝试打开一个不存在的摄像头肯定会抛出异常,这也提醒我们在开启 Camera 摄像头时需要加异常保护。

代表机型:联想 278t 酷派 8022

2.3 闪光灯的判断

我们常用的判断手机是否有闪光灯的方法应该有以下两种:

判断是否支持闪光灯方法一:使用getSupportedFlashModes 方法

判断是否支持闪光灯方法二:通过PackageManager 判断

方法一有 3.7%的机器结果错误,无法准确地判断出手机是否有闪光灯,主要的品牌包含:酷派、天语、联想、三星等。

方法二有 9.7%的机器结果错误,主要品牌包含:VIVO、金立、酷派、天语、朵唯、三星等。我们建议在判断手机是否有闪光灯的时候将这两种方法联合使用,出现错误的概率将大大降低。

2.4 常亮状态与其他状态间的切换

前提条件是我们设置闪光灯为常亮(Parameters.FLASH_MODE_TORCH),并且闪光灯成功常亮,此时我们在设置闪光灯模式为 Parameters.FLASH_MODE_AUTO 后闪光灯依然常亮,这样的机型约占热门机型的12% ,遇到这种情况我们需要先设置闪光灯模式为Parameters.FLASH_MODE_OFF 关闭闪光灯后再设置其他模式。

2.5 释放 Camera 后闪光灯依旧闪亮

既然开了,我们就要负责关,说实话,以前这个问题根本不在我的考虑范内,因为我们在使用 Camera 的时候都会在Activity 被销毁或者暂停时释放 Camera,这个时候无论闪光灯是什么状态,都会随着 Camera 的释放而关闭,直到我遇见了 OPPO R815T,我的世界观发生了变化,这货如果设置了闪光灯常亮,即使释放了 Camera 闪光灯依旧稳稳地亮着,而且由于Camera 被释放掉了,你再也没办法关闭闪光灯了,关闭 App、卸载 App,你还是扣电池关机吧.....所以,如果你的程序中有设置闪光灯为常亮状态的操作,建议在释放 Camera 前先将闪光灯设置为关闭(Parameters.FLASH_MODE_OFF)状态。

2.6 CameraInfo 的另类情况

官方文档中有关于调整相机预览角度的例子

在这个例子中CameraInfo 非常重要,最终的角度计算就是根据CameraInfo 中orientation 值得到的,所以如果这个值不准确的话那么我们的角度就有可能出现错误。

VIVO V1 手机第一次我们获取CameraInfo 的orientation 值是90,而当执行了mCamera =Camera.open();之后再获取CameraInfo 的orientation 值就是0,而且以后获取的都是0 ,除非重启手机。

无论是这款手机上的哪个应用,只要执行了一次Camera.open()之后,其他所有程序中获取CameraInfo 的orientation 都是是0。

机自带的相机却能很好的使用反编译系统相机后果然发现系统相机并没有像官方给出的例子来进行角度的矫正。

解决方案:

1.按照此手机系统相机的做;

2.对该手机CameraInfo 的orientation 值写死为90。

四、不止是2 的双卡

双卡的问题解决的基本思路:

1.推断:手机内置的系统APP 都可以正常使用这些功能,因此肯定存在厂商自定义API 来实现这些功能;

2.反编译:Framework、系统APP、系统数据库;

3.定位:TelephoneManager 扩展、SMSManager 扩展、电话服务扩展、短信服务扩展、数据

库字段扩展。

五、UI适配

说到UI 适配其实很是让人头疼,下面的图片是某个产品为了进行UI 适配所做的工作,可以看出相当繁琐。

除了分辨率的适配,有时候布局文件中的某个标签还会引起一些问题,我们先看下面一段布局代码:

正确结果:

错误结果:

这就是因为Android3.0 一下版本在FrameLayout 中使用layout_marginTo 标签必须要设置gravity 才能生效。

那么如果解决这个问题呢:

在设置android:layout_marginTop 的组件中再设置一下android:layout_gravity="top"

六、还有更奇葩的

1厂商的抽象方法

如果你需要实现InputConnection 接口,那么你一定要注意下面这个很奇葩的异常:

反编译了下此款手机的framework,发现厂商在InputConnection 接口中增加了一个抽象方法performYLPrivateCommand

2距离传感器

2.1 不同手机event.values[0]值简直是千变万化

简单说几个有代表性的:

1)一部分手机比较正常,靠近时为0 远离时为1(0,1),么么哒~;

2)有点小个性的手机数值将变大,比如(0,100),(3,5),(3,100)等等;

3)213 手机的数值就比较莫名其妙,(1.001,5.003),你是表明精确度高呗??

2.2 数值与远近关系不统一

既然我们是通过数值来判断当前是否出于近耳状态,那么是不是应该这个数值的大小是有说道的,靠近时的数值小一点,远离时的数值大一些,起码我见过的99%的手机是这样子的,但是就有几款神经病手机(100W)偏偏是靠近时的数值比远离时的数值大,这是个坑,开发者要注意~~!!

2.3getMaximumRange 方法返回值不对

有一句API:

SensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY).getMaximumRange()文档解释这个应该获取的是传感器数值变化的最大范围,比如如果靠近时的值是0,远离时的值是1,那么getMaximumRange()的值应该是1 才不会影响我们的判断,我这里仅仅是从API 角度和我们日常的使用习惯来说的,如果不是这样的规律,就会对我们的编程造成麻烦。

酷派5890 手机:变化的数值为(3,5),getMaximumRange()的值为10;

华为Y320-T00 手机,变化的数值为(0,10),getMaximumRange()的值为20;

中兴U930HD 手机,变化的数值为(3,100),getMaximumRange()的值为1023;


作者简介:李忠丞 沈阳大学学士,腾讯优测产品支撑组组长;2011年加入腾讯,专注移动终端开发,先后参与过腾讯微博研发以及手机QQ浏览器、Qzone、魅拍等产品的机型适配工作。

【点击此处可下载反编译工具】

本文系腾讯bugly独家内容,转载请在文章开头显眼处注明注明作者和出处“腾讯Bugly(http://bugly.qq.com)”

【沙龙干货分享】你要知道的N个Android适配问题相关推荐

  1. 盘点安卓绿色联盟2018全年技术沙龙干货分享

    月度技术沙龙作为安卓绿色联盟为会员提供的重要服务之一,经过两年的发展,已经愈渐成熟,截止目前覆盖线上及线下开发者近百万人次. 2018年,联盟携手数千名开发者走进阿里.百度.华为.腾讯.网易.360. ...

  2. 完全相同的4个小矩形如图所示放置_分享 | 你想知道的100个桥梁知识点!

    1.组合梁桥 composite beam bridge 指以梁式桥跨作为基本结构的组合结构桥,既两种以上体系重叠后,整体结构的反力性质仍与以受弯作用负载的梁的特点相同.这类桥的特点主要表现在设计计算 ...

  3. 【Bugly干货分享】手把手教你逆向分析 Android 程序

    很多人写文章,喜欢把什么行业现状啊,研究现状啊什么的写了一大通,感觉好像在写毕业论文似的,我这不废话,先直接上几个图,感受一下. 第一张图是在把代码注入到地图里面,启动首页的时候弹出个浮窗,下载网络的 ...

  4. 【Bugly干货分享】手把手教你逆向分析android程序

    为什么80%的码农都做不了架构师?>>>    很多人写文章,喜欢把什么行业现状啊,研究现状啊什么的写了一大通,感觉好像在写毕业论文似的,我这不废话,先直接上几个图,感受一下. 第一 ...

  5. 干货分享:实用/高效/有逼格的Android Studio 常用配置/插件推荐

    注释模板 前言 Gradle Build Running 手动下载Gradle 正确导入他人项目 手动下载SDK 禁用HTTP代理 使用阿里maven仓库 JCenter/mavenCentral() ...

  6. 云顶之弈怎么防止被机器人拉_云顶之弈:只有钻石玩家才知道的12个小技巧,都是干货!...

    最近英雄联盟云顶之弈非常火爆,以前一些不上线好友的名字也纷纷亮起来了,而且放在他们名字上一看基本上都是在玩云顶之弈.笔者经常使用恶魔元素法师阵容,海岛枪剑阵容,四护卫阵容,终于非常幸运的上到了钻石段位 ...

  7. 【线下沙龙免费报名】像阿里巴巴一样工作_听干货分享欣赏阿里西溪园区风景

    阅读原文请点击 [杭州] Work Like Alibaba,你也可以,像阿里巴巴一样工作 云栖社区大数据云市场云服务阿里云创业大学 活动时间:2017-08-26 13:30 ~ 18:30 报名时 ...

  8. 记忆力测试软件 知乎,知乎干货分享:如何提升你的记忆力

    考研复习时间有限,关键是我们要找到方法提高复习的效率,提高记忆力.如何提升你的记忆力?新东方在线跟大家分享知乎大神的干货分享,教你提升记忆力: 一.多换几个学习场所 很多人肯定都告诉大家要固定学习环境 ...

  9. 分享 14 个你必须知道的 JS 函数

    英文 | https://javascript.plainenglish.io/you-must-understand-these-14-javasript-functions-1f4fa1c620e ...

  10. 知乎好物开通,以及实战攻略干货分享

    腾讯这一波操作6了 前段时间加入生财,分享了几篇文章,今天说一个近期在实战操作的事情(在操作之前,将星球关于知乎的文章都看了一遍,算是找一些思路,操作的基本原则),以及我做知乎到底奔着什么去的,我们要 ...

最新文章

  1. iOS开发- OpenGL ES屏幕截图
  2. Java配置环境变量及其意义
  3. shiro 字段不是username 和password_Shiro整合JWT
  4. Android初级教程获取手机系统联系人信息
  5. VMware出错 Reason Failed to lock the file
  6. 1-2-3 CodeForces - 863C(规律+思维)
  7. java中,如何实现输入一个正整数,并将这个数字反转输出,比如输入123,输出321
  8. junit mockito_使用JUnit 5在Mockito中方便地进行模拟–官方方式
  9. random对文件随机重命名
  10. vue自定义组件是.vue还是html,Vue自定义组件的四种方式示例详解
  11. Linux命令常用的快捷键
  12. 经济管理中的计算机应用笔试题,经济管理中的计算机应用笔试试卷及答案资料.doc...
  13. python入门经典-入门python有什么好的书籍推荐?
  14. 设置表格表头字体_Excel双栏和三栏斜线表头制作技巧
  15. Android SDK Manager Proxy on MAC
  16. 秩和比综合评价法(RSR)详解及Python实现和应用
  17. android 8.1.0编译以及卡刷包制作教程
  18. 如何安装 elasticsearch-head 插件?
  19. 微信小程序系列(7)如何用微信小程序写一个论坛?贴心代码详解(五)删帖页【完结篇】
  20. 【Shader进阶】Shader的Lod

热门文章

  1. python实现局域网文件互传_Python+pyftpdlib实现局域网文件互传
  2. 很荣幸,和各位一起创造了历史
  3. 【转】写给支持和反对《完全用Linux工作》的人们
  4. 计算机主板 上电顺序,BIOS很熟悉,电脑开机BIOS开机自检顺序你知道吗?
  5. 塔防游戏c语言源代码,转经典塔防游戏TowersTrap-[lua复刻版本,附全部lua源代码]
  6. 成功三大定律:荷花定律、金蝉定律、竹子定律
  7. 苹果 服务通知 V2
  8. Spring Validation 验证框架全面总结
  9. 阿里平头哥首次交货——玄铁910是个啥?是芯片吗?
  10. C++中的DLL调用0x00000000错误