Android Weekly Issue #235
December 11th, 2016
本期内容包括: 开发一个自定义View并发布为开源库的完整流程介绍; 用AnimatedVectorDrawable
实现的动画; 什么样的程序是可测试的; DownloadManager
介绍; Okhttp的重试; Android 7取消了file://
; Android Studio即将推出的build cache功能; 支持离线模式的app构架; 如何写自定义的lint规则; Epoxy, 一个处理复杂RecyclerView屏的库; FragmentPagerAdapter
和FragmentStatePagerAdapter
的比较等. ARTICLES & TUTORIALS
作者开发了一个环形的SeekBar, 并把它作为一个库发布到了JCenter.
作者首先讲了自定义View的实现:
首先是关于View生命周期的介绍, 在写自定义View的时候有几个关键的生命周期回调需要处理:作者实现的几个关键步骤讲解:
- 自定义属性并获取.
- 在
onMeasure()
中控制尺寸. - 在
onDraw()
中绘制: 避免在onDraw()
中分配内存; 用invalidate()
方法来激发重绘. - 在
onTouchEvent()
处理用户手势. 在他的环形SeekBar的实现里, 这里涉及到了点击坐标到角度的转换.
将自定义View库开源到Github:
开源到Github有个好的README很重要, 这里有几个tips:- 提供截图, Gif或者Video.
- 提供安装/使用说明. 作者自己的库:
发布库:
- 去注册.
- 创建repository, package, 和版本号.
- 生成并上传, 用了.
- 添加到Jcenter.
- 被接受之后收到邮件, 就可以使用了.
用AnimatedVectorDrawable
实现的一个很fancy的位置标志动画.
如果程序的架构不适合测试, 那么硬要写一些测试很可能就会面临这样的局面: 要么就是发现没法写测试, 要么就是为了写测试而破坏了代码, 做了一些奇怪的事情.
那么到底是什么样的程序才是适合写测试, 或者是可测试的呢?
有一个有趣的定义是seam(接缝), 在接缝处你可以改变程序的行为, 而不用编辑当前程序. 如果程序没有接缝, 你将无法设置测试的初始条件和验证测试结果.
本文中举了一个实际的例子, 开始的时候程序没有seam, 所以导致无法测试, 后来把静态方法改为实例的方法之后, 我们就可以通过Mockito来模拟行为, 设置条件, 最后通过验证某一方法的调用与否来进行验证.
用DownloadManager
来处理下载.
我们可以查询到文件的一些信息, 比如MIME type, 文件尺寸, 下载状态等.
我们还可以用getUriForDownloadedFile()
方法来获取一个URI, 配合MIME type, 发送Intent, 来打开一个相关的查看程序.
关于储存文件的合适地点:
- 文件小, 仅app自己使用 -> 私有数据区域(默认行为).
- 文件大, 仅app自己使用 -> 外部存储的私有数据区域(不需要权限).
setDestinationInExternalFilesDir()
. - 文件需要被别的应用访问 -> 外部存储的共有区域, 需要
WRITE_EXTERNAL_STORAGE
权限.setDestinationInExternalPublicDir()
.
在网路较慢或不稳定的时候, OkHttp有可能会重复发送请求, 直到成功.
这个重试的逻辑是通过实现的.
那么, 我们可以关掉这个重试行为吗? 有一些issues就在讨论这个问题: . 后来有两个pull requests: 和改进了这个问题, 减少(但并没有消除)了不必要的retry请求.
全局关闭重试行为: OkHttpClient.Builder .retryOnConnectionFailure()
设置为false. 但是注意这样是很粗暴并具有破坏性的, 消除了retry逻辑带来的好处:
- 如果Url有多个IP, 失败了一个还可以试另一个.
- 连接池中的连接偶尔会time out, 减少这种意外导致的后果.
- 可以顺次查找多个代理, 如果都失败了再转向直接连接.
解决真正的问题: 关闭静默重试在某些情形下有帮助, 但是其实它隐藏了真正的问题, 就是你的API是否是幂等的. server端可以根据客户端的GUID来检测重复, 这样server就不会多次执行操作, 会通知发送者.
Android N (Nougat, API 24)开始, 不再允许发送file://
的Intent, 将会直接抛出FileUriExposedException
异常.
所以当你把targetSdkVersion
改为24之后, 你必须要确保你修复了这些问题再发布.
解决方案是什么呢? 用content://
, 结合FileProvider
:
然后在res\xml\provider_paths.xml
文件里指明路径:
最后, 把
Uri photoURI = Uri.fromFile(createImageFile());
改为
Uri photoURI = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", createImageFile());
然后放在Intent里发送就好了.
注意, 如果你的targetSdkVersion
还没有更新到24, 那么即便是在Nougat的手机上file://
也仍然是能正常使用的.
Android Studio当前的最新版是2.3 Canary 2. 有一些新的改进, 但是其中最吸引人的是这个. 它会使你的clean build更快.
本文后面解析了build cache的工作原理.
一个好的应用应该在网络不好甚至离线的时候仍然可以使用, 我们应该做些什么呢?
- 确定连接状况. 可以使用这个. 如果你使用的是Okhttp, 可以加一个Intercepter来进行采样.
- 有效地缓存. 从网络取数据很慢并且昂贵, 所以有效地利用之前取到的数据是很关键的优化. (Cache-Control, Etag).
- 在本地操作, 在全局同步. 等网络请求的时候可以先显示本地数据, 而不是loading.
- 有效地处理线程.
- 优化图片. 网络不好的时候先用RGB_565, 等网络变好了再取高质量图片.
- 使用大Cookie. 尽量一次传输更多的数据(big cookie), 而不是频繁发送一些小请求(small cookies).
如何创建自定义的lint规则.
事情的由来是作者发现了一个死循环调用, 然后他想做一个什么标记以防以后其他人会犯同样的错误.然后他想到的是, 的检查, 实质是依靠来实现的.
于是他自己写了一个自定义的lint规则, 来提示使用用他的注解@CarefulNow
标记的方法时应当注意.
是一个Android库, 用来处理复杂的RecyclerView屏. 本文介绍了它在项目中实际的使用.
可能有很多Android开发者对于
和的区别不是太清楚或根本不知道, 本文作者就具体介绍了二者的不同.基本不同
FragmentPagerAdapter
FragmentManager
中移除, 直到Activity被销毁. 当Fragment不见的时候, 仅仅是onDestroyView()
被调用, 当fragment再次回来时, 再调用onCreateView()
.
FragmentStatePagerAdapter
FragmentManager
移除. 被移除的fragment实例的状态由FragmentStatePagerAdapter
保存, 当你再次回到该项的时候, fragment会重建新实例, 并且状态被恢复. 所以这种adapter适用于项目个数不确定或的情况. 所以使用FragmentPagerAdapter
的时候需要注意内存问题.
notifyDatasetChanged()的问题.
notifyDataSetChanged()
是用来处理数据集变化的情况, 比如一些项目增删的情况. 这个方法不是用来刷新当前显示的Fragment或其中的Views的.
文章中还有一些关于数据改变实现以及现有issue的讨论. 为了解决issue作者还发布了一个库.
LIBRARIES & CODE
一个quick settings tile来开启"Don't keep activities".
一个波形的loading图, 水面上涨代表loading程度.
Simple MVWhatever for Android.
一个处理复杂的RecyclerView屏的库.
录屏脚本.