Android Weekly Notes Issue #248
Android Weekly Issue #248
March 5th, 2017
Android Weekly Issue #248.
本期内容包括: 为什么有时候应该让你的应用崩溃(而不是一味保护); Trello离线模式实现中两个id的问题; 如何让Dagger的component按照scope保存, 在屏幕旋转时不重建; 用Dagger构建Realm的数据库迁移逻辑;
利用各种mock工具写单元测试; Map上markers的动画实现; JUnit5中@DisplayName的使用; RxJava中的Single和Completable使用; 举例说明如何给FindBugs写自定义的探测器; Android中静态代码分析工具的使用; Trello离线实现中sync失败情况的处理.
ARTICLES & TUTORIALS
Why your app should crash
作者认为有时候让应用崩溃反而是有好处的.
以NPE为例, 有时候我们会习惯性地加很多null判断, 有的是多余的, 有的防御型的代码反而会掩盖了真实问题所在. 比如当一个不合理的情况发生时, 让用户看到一个不可理解的页面, 比如空白, 然后我们开发者根本不知道这种情况的发生.
与其这样掩盖错误, 不如让应用崩溃, 让开发者立即知道问题的原因.
实用建议:
- 永远让应用对外来输入(比如service的响应, UI的输入, 进来的intents)保持健壮性.
- 在程序的入口点保证数据的完整性. 这样不合理的数据就不会到处都是, 所以你不用到处检查.
- 如果你不确定某个错误是否会在某个地方发生, 先假装它不会发生, 在测试阶段再验证.
- 如果某个方法在产品环境不能被调用, 或者只能被调用一次等, 抛出
IllegalStateException
. - 永远在发布之前进行完整测试, 这样你就会在用户之前, catch住可怕的崩溃.
The Two ID Problem
还是Trello开发离线模式的系列文章, 本篇讲他们遇到的一个很tricky的问题: id问题.
在他们的项目里, 所有的models都有一个id, 用以和server通信, 以及定义model之间的关系.
如果是在离线模式下, 就不能依靠server来提供这个id, 客户端需要自己生成.
所以离线模式下有两种id, 一种是本地生成的, 一种是用来和server通信的.
他们想过几个办法, 比如在sync的时候将local的id转化为server id; 或者干脆存储一个id的Pair类, 但是都有难以维护或者性能缺陷等种种问题.
最后他们提出了一个叫local-server barrier
的解决方案. 基本的原则就是, 在app中, 只使用local的id, 同server通信时, 使用server id. 好处: 首先保证了客户端代码的简洁, 只有网络通信层需要考虑到server id; 重构代码量小.
Retaining Dagger components
如果你用dagger创建了component, scope是Activity或者Fragment, 那么你可能遇到过这个问题: 旋转屏幕之后, 所有的依赖都重建了, 因为你创建了一个新的component.
如果你想要在configuration变化的时候不重建, 你就需要把component存储在一个全局的地方, 但是这样的话, 当你真的结束你的Activity和Fragment的时候, 你如何释放这些component呢?
你需要分层地(hierarchical)存储, service-tree就是用来做分层存储东西的一个工具.
文中基本思想是把Application的component作为根节点, 然后Activity和Fragment的component作为树形结构的叶子节点逐级存储. Activity和Fragment的节点什么时候移除, 有一些判断条件和时机选择, 详见原文代码.
The Burden of Knowledge
鼓励在team里分享知识.
Realm Migrations Supercharged with Dagger
使用Dagger2可以大幅度改善Realm中的数据迁移处理. 具体的做法是把每一步的迁移处理都放在一个统一接口的实现类里, 然后注入它们.
@Module
public class MigrationsModule {
@Provides
@IntoMap
@IntKey(1)
static VersionMigration provideVersion1Migration() {
return new Version1Migration();
}
@Provides
@IntoMap
@IntKey(2)
static VersionMigration provideVersion2Migration() {
return new Version2Migration();
}
}
所以最后的迁移类看起来就是这样:
@Reusable
public class Migration implements RealmMigration {
private Map<Integer, Provider<VersionMigration>> versionMigrations;
@Inject
Migration(Map<Integer, Provider<VersionMigration>> versionMigrations) {
this.versionMigrations = versionMigrations;
}
@Override
public void migrate(final DynamicRealm realm, long oldVersion, long newVersion) {
for (int i = (int) oldVersion; i < newVersion; i++) {
final Provider<VersionMigration> provider = versionMigrations.get(i);
if (provider != null) {
VersionMigration versionMigration = provider.get();
versionMigration.migrate(realm, i);
}
}
}
}
这样做的好处:
- 1.以后再有数据库升级也不需要再改这个类了.
- 2.不需要逐个check每个版本号, 自动只从需要的版本号开始做迁移.
- 3.每个迁移模块都变成了可测试的单元.
我觉得作者的这种处理结构很好, 不仅仅限于Realm数据库的迁移, 其他的数据库迁移也可以用类似的结构来处理.
How to be a Mock-Star…
介绍如何用mockito来测试一个MVP的程序.
首先测试Presenter的部分, 这里Mock了数据源和各种错误响应.
测试Repository, 需要用到MockWebServer, 来模拟测试环境下的响应.
Animating Markers with MapOverlayLayout
作者App的动画实现讨论第二发, 如何让地图上的markers带缩放和渐变动画 -> 用MapOverlayLayout
.
文中有详细的实现代码, 基本思路就是在这个MapOverlayLayout
中保存一个View的列表, 然后在自定义View中实现每个marker在相应动作时的动画.
What Unit Tests are Trying to Tell us About Activities Pt 2
以Activity/Fragment作为基本构建单元, 让程序难以测试, 本文举例说明了这一点.
JUnit 5: DisplayName
JUnit 5提供了@DisplayName
, 这样测试报告里case显示的名字将是@DisplayName
定义的字符串.
相比原先的方法名来说, 这个字符串是可以带空格的, 所以比之前的可读性增强了.
Clearer RxJava intentions with Single and Completable
RxJava中我们经常用到的类就是Observable
, 然后处理三个事件: onNext()
, onError()
和onCompleted()
.
但是有些时候我们并不需要关心全部这三个事件, 这时候我们就可以用Single<T>
和Completable
.
Single<T>
返回一个值或者一个error.
它和Observable
之间可以互相转换: 用toObservable()
和singleOrError()
方法.
Completable
, 只有onCompleted()
和onError()
. 它不发射任何值, 可以在它之后用andThen()
来添加另一个Observable, 进行后续其他操作.
Observable
不能直接转换为Completable
, 因为不知道Observable
到底会不会停止. 可以把Single
转换为Completable
, 用toCompletable()
方法.
Custom FindBugs detectors in Android
Android中有两种工具可以做进一步的编译期检查: Android Lint和FindBugs.
FindBugs是一个静态的分析工具. 本文的主要任务是讲解如何实现一个自定义的检测器来检测一种特定的错误.
作者的例子是try-with-resources
模式的代码.
这是Java 7新加的模式try-with-resources:
try (Cursor c = db.query(...)) {
c.moveToFirst();
while (!c.isAfterLast()) {
return new Foo(
c.getString(c.getColumnIndex(...))
...
);
}
}
try语句中声明的资源将会在这个block结束的时候自动close.
但是这个特性最低需要API 19.
所以为了兼容旧版本, 我们不得不使用finally来自己close:
Cursor c = db.query(...);
try {
c.moveToFirst();
while (!c.isAfterLast()) {
return new Foo(
c.getString(c.getColumnIndex(...))
...
);
}
} finally {
c.close();
}
但是有时候我们会忘记close导致了泄露, 所以需要实现一个自定义的findbugs检测器来检查这种错误. 具体实现步骤和讨论见原文.
Static Code Analysis Tools
Android中流行的静态代码检测工具:
- Lint
- PMD
- FindBugs
本文介绍它们如何配置和使用.
Sync Failure Handling
Trello离线模式文章, sync失败的处理.
在发请求的时候可能会发生各种各样的错误, 分为暂时性的和永久性的两类.
对于永久性的错误, 我们可以直接放弃delta; 但是对于暂时性的错误, 我们需要重试. 这里就需要考虑重试的时间和重试的次数.
另外还有一种情况是客户端发了请求, server也收到了, 但是客户端在收响应的时候失败了, 所以客户端可能会找机会重新发请求, 为了保证幂等性, 我们的每一个请求都有一个唯一的id, 如果server发现同样的id, 只处理第一个.
对于多个用户编辑的冲突处理, 当前用的是简单的以后者为准的方式.
撤销本地不合理数据, 以server数据为准, 更新本地数据, 这就需要在sync开始的时候先讲本地改动上传.
DESIGN
LottieFiles
免费的Lottie动画.
LIBRARIES & CODE
DiscreteScrollView
可滚动的列表, 中间的项目放大. (基于RecyclerView, 长得有点像ViewPager.)
SimpleRatingBar
五星评价View, 用kotlin实现的.
InstaCropper
剪切图像的View, 类似于Instagram的crop.
GuildWars2_APIViewer
一个app, 用来查看Guild Wars 2的API响应.
用了Dagger2, Retrofit2, RxJava2, MVVM架构.
here-be-dragons
一个Intellij/Android Studio插件, 你可以在一个方法上标记@SideEffect
, 之后你调用这个方法的代码行左边会显示出一个龙的图标.
RoboGif
一个python的小工具, 可以把Android设备上的录屏生成一个GIF图.
service-tree
一个存储service的树形结构. (本期文章Retaining Dagger components有讲.)
Android Weekly Notes Issue #248的更多相关文章
- Android Weekly Notes Issue #230
Android Weekly Notes Issue #230 November 6th, 2016 Android Weekly Issue #230. Android Weekly笔记, 本期内容 ...
- Android Weekly Notes Issue #227
Android Weekly Issue #227 October 16th, 2016 Android Weekly Issue #227. 本期内容包括: Google的Mobile Vision ...
- Android Weekly Notes Issue #237
Android Weekly Issue #237 December 25th, 2016 Android Weekly Issue #237 这是本年的最后一篇issue, 感谢大家. 本期内容包括 ...
- Android Weekly Notes Issue #229
Android Weekly Issue #229 October 30th, 2016 Android Weekly Issue #229 Android Weekly笔记, 本期内容包括: 性能库 ...
- Android Weekly Notes Issue #221
Android Weekly Issue #221 September 4th, 2016 Android Weekly Issue #221 ARTICLES & TUTORIALS And ...
- Android Weekly Notes Issue #219
Android Weekly Issue #219 August 21st, 2016 Android Weekly Issue #219 ARTICLES & TUTORIALS Andro ...
- Android Weekly Notes Issue #236
Android Weekly Issue #236 December 18th, 2016 Android Weekly Issue #236 本期内容包括: Google的物联网平台Android ...
- Android Weekly Notes Issue #235
Android Weekly Issue #235 December 11th, 2016 Android Weekly Issue #235 本期内容包括: 开发一个自定义View并发布为开源库的完 ...
- Android Weekly Notes Issue #234
Android Weekly Issue #234 December 4th, 2016 Android Weekly Issue #234 本期内容包括: ConstraintLayout的使用; ...
随机推荐
- mesos 资源分配
Mesos 资源分配 众所周知, Mesos在运行时使用wDRF( Dominant Resource Fairness)算法进行一级资源分配, 通过应用程序(Framework)运行时使用资源进行二 ...
- EntityFramework Core Raw Query再叙注意事项后续
前言 话说通过EntityFramwork Core进行原始查询又出问题,且听我娓娓道来. EntityFramework Core Raw Query后续 当我们进行复杂查询时我们会通过原始查询来进 ...
- tbl.js div实现的表格控件,完全免费,no jquery
html上现在有比较好用的表格控件是datatable,但是编辑.按钮等部分是收费的,只有基础功能免费.而且尺寸发生变化时需要手工刷新等繁琐操作较多.所以我开发一个免费的供大家使用. 本项目已用于“虚 ...
- 王爽汇编语言(第三版)环境搭建(附PDF及工具下载)
一.前言 最近在学习汇编语言,使用的是读者评价非常高的王爽老师写的<汇编语言>(第三版),为了适应现在各个版本的windows操作系统,所以采用VMWare虚拟机来搭建纯DOS环境. 二. ...
- C++编程练习(5)----“实现简单的循环队列的顺序存储结构“
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表. 队列是一种先进先出(First In First Out)的线性表,简称FIFO.允许插入的一端称为队尾,允许删除的一端 ...
- matlab中hold指令、figure指令及subplot指令的使用
一.hold指令使用 正常情况下,plot指令显示figure时,以前的数据丢失了.使用hold on指令后,此后添加的一系列plot曲线将叠加在前一个图上当使用hold off后,恢复为默认状况,p ...
- 反汇编看c++引用
继续反汇编系列,本次使用vc2008在x86体系下分析c++中的引用. 定义一个引用类型和将一个变量转换成引用类型一样吗? 引用比指针安全,真的是这样吗,对引用不理解的话比指针还危险. 为什么要用常量 ...
- 内存管理 (C++)
转:http://hi.baidu.com/%D0%A1%B0%FC%D7%D349/blog/item/de1a8e4fa5eeafc3d0c86a68.html1.进程地址空间 Window ...
- 读书笔记 effective c++ Item 16 成对使用new和delete时要用相同的形式
1. 一个错误释放内存的例子 下面的场景会有什么错? std::]; ... delete stringArray 一切看上去都是有序的.new匹配了一个delete.但有一些地方确实是错了.程序的行 ...
- Win32/MFC/COM学习推荐书籍
以前有不少朋友问关于学习各种技术的推荐书籍的问题,这里把我觉得比较好的一些书籍列一下,希望能起到抛砖引玉的作用就好了:) Win32开发 Programming Windows by Charles ...