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的更多相关文章

  1. Android Weekly Notes Issue #230

    Android Weekly Notes Issue #230 November 6th, 2016 Android Weekly Issue #230. Android Weekly笔记, 本期内容 ...

  2. Android Weekly Notes Issue #227

    Android Weekly Issue #227 October 16th, 2016 Android Weekly Issue #227. 本期内容包括: Google的Mobile Vision ...

  3. Android Weekly Notes Issue #237

    Android Weekly Issue #237 December 25th, 2016 Android Weekly Issue #237 这是本年的最后一篇issue, 感谢大家. 本期内容包括 ...

  4. Android Weekly Notes Issue #229

    Android Weekly Issue #229 October 30th, 2016 Android Weekly Issue #229 Android Weekly笔记, 本期内容包括: 性能库 ...

  5. Android Weekly Notes Issue #221

    Android Weekly Issue #221 September 4th, 2016 Android Weekly Issue #221 ARTICLES & TUTORIALS And ...

  6. Android Weekly Notes Issue #219

    Android Weekly Issue #219 August 21st, 2016 Android Weekly Issue #219 ARTICLES & TUTORIALS Andro ...

  7. Android Weekly Notes Issue #236

    Android Weekly Issue #236 December 18th, 2016 Android Weekly Issue #236 本期内容包括: Google的物联网平台Android ...

  8. Android Weekly Notes Issue #235

    Android Weekly Issue #235 December 11th, 2016 Android Weekly Issue #235 本期内容包括: 开发一个自定义View并发布为开源库的完 ...

  9. Android Weekly Notes Issue #234

    Android Weekly Issue #234 December 4th, 2016 Android Weekly Issue #234 本期内容包括: ConstraintLayout的使用; ...

随机推荐

  1. javaScript基础详解(1)

    javaScript基础详解 首先讲javaScript的摆放位置:<script> 与 </script> 可以放在head和body之间,也可以body中或者head中 J ...

  2. PrefixSpan算法原理总结

    前面我们讲到频繁项集挖掘的关联算法Apriori和FP Tree.这两个算法都是挖掘频繁项集的.而今天我们要介绍的PrefixSpan算法也是关联算法,但是它是挖掘频繁序列模式的,因此要解决的问题目标 ...

  3. canvas小程序-快跑程序员

    canvas不用说html5带来的好东西,游戏什么的,么么哒 记得有一天玩手机游戏,就是一个跳跃过柱子那种,其实元素很简单啊,app能开发,借助html5 canvas也可以啊, 于是就开始了. -- ...

  4. 真分布式SolrCloud+Zookeeper+tomcat搭建、索引Mysql数据库、IK中文分词器配置以及web项目中solr的应用(1)

    版权声明:本文为博主原创文章,转载请注明本文地址.http://www.cnblogs.com/o0Iris0o/p/5813856.html 内容介绍: 真分布式SolrCloud+Zookeepe ...

  5. js精要之模块模式

    // 模块模式是一种用于创建拥有私有数据的单件对象的模式,基本做法是使用立调函数(IIFE)来返回一个对象 var yourObjet = (function(){ // 私有数据 return { ...

  6. windows下安装redis以及redis扩展,设置redis为windows自启服务

    windows下安装reids windows下redis下载地址:https://github.com/MSOpenTech/redis/releases. 启动redis服务:在redis目录下启 ...

  7. spring 定时任务 taskScheduler详解

    spring 3.0版本后,自带了一个定时任务工具,而且使用简单方便,不用配置文件,可以动态改变执行状态.也可以使用cron表达式设置定时任务. 被执行的类要实现Runnable接口 TaskSche ...

  8. 64位win2003 IIS6运行32位的.NET程序

    做web服务迁移,从32位win2003迁移到64位win2003,数据库是32位Oracle在另外一台服务器上. 迁移之后数据库各种连不上,oracle的客户端32位的装完装64位的,odp.net ...

  9. plupload插件的错误SCRIPT601

    在网上copy 别人的demo来用结果发生这个问题.浪费半天时间才找到问题. 在IE8下下提示这个异常. SCRIPT601: 未知的运行时错误plupload.full.min.js, 行15 字符 ...

  10. 【android开发笔记】为Button的背景图片添加边框式样式效果

    现在做的项目遇到一个问题,设计给过来的图片只有一种状态,但是实现的需求是要求有两个状态,另一种选状态为图片背景加边框.如图: 刚开使用使用ImageView ,ImageViewButton 效果不是 ...