Android Weekly Notes Issue #241
Android Weekly Issue #241
January 22nd, 2017
Android Weekly Issue #241
本期内容包括: 经典导航模式Master/Detail的设计和实现; APK的大小讨论和增量下载大小的预估工具; Model-View-Intent模式的讨论和实现; 分多个modules对build时间的影响; 测试中能够利用的一些Android特有的接缝设计(manifest, build config, resource).
ARTICLES & TUTORIALS
Case Study. Master/Detail Pattern Revisited
Master/Detail是一种经典的导航流, master屏包含一个list, detail显示某一项的详细信息. Android Doc.
作者讲了他适配多种屏幕(包括平板)的设计, 以及简单的实现.
Tracking app update sizes
以前作者有一系列的文章讲过apk的组成以及如何减少apk的大小.
事实上app的大小可以分下面四种:
- 提交到Google Play的APK文件大小.
- 初始的下载大小.
- 在设备上的安装大小.
- 更新下载大小.
之前的一些文章可能都在讨论如何减少初始的大小, 但是大多数情况用户可能只安装你的应用一次, 之后就只是从Play Store更新, 所以应用的更新大小也是很重要的.
事实上Android Studio(2.2+)改善了打包apk的机制, (apk packaging), 使得每一个build都尽可能地相似, 这样Play Store就能计算出一个较小的delta更新. 另外, Play Store也引入了新的算法, 比如最近的File-By-File patching, 同样也有效减小了更新的大小.
所以我们要注意的就是不要介入和干扰当前Android Studio和Play Store的这些优化, 比如不要用自定义的ZIP加密设置来自己压缩APK. 也不要用Zopfli来再次压缩APK.
Play Store上会显示应用的下载大小, 如果用户已经安装了, 则显示的是更新大小.
对于开发者来说, 如果能在发布前知道这些信息就更好了, 所以作者他们开源了这个库: apk-patch-size-estimator
这是一个命令行的工具, 可以集成到CI里, 也可以手动比较两个apk文件.
这个工具实现了当前Play Store的算法, 可以帮你估计出初始的apk下载大小和更新下载大小.
(注意下载大小和apk文件大小不同因为Play Store可能会做进一步压缩.)
同样, Android Studio中也有一个图形化的 APK Analyzer工具, 可以做apk的比较, 让你看到到底是哪一部分的尺寸增长了.
Reactive Apps with Model-View-Intent - Part 2
上一篇文章讨论了一个好的Model层可以解决很多问题. 这篇来介绍Model-View-Intent
模式.
Model-View-Intent模式
Model-View-Intent模式是在一个JavaScript的framework cycle.js
中提出的.
intent()
: 这个方法接收用户输入, 然后输出将会作为参数传给model()
.model()
: 接收intent()
的输出作为自己的输入, 来操纵Model, 这个方法的输出是一个新的Model(状态变化). 所以它不应该更新一个已经存在的Model. 因为我们想要不可变性. 注意这里是唯一一个允许创建新Model的地方.view()
: 接收model()
方法返回的model作为输入, 然后将其展示出来.
用RxJava连接
我们希望数据流是单向的, 于是我们用了RxJava, 它很适合这种基于事件的编程, 在这里主要是UI事件.
作者之后举了一个实现的例子, 在这个例子中他们的Model层用了ViewState后缀. SearchInteractor
用来执行搜索, 返回的结果是Observable<SearchViewState>
.
这个模式中定义的View接口里包含了render()
方法, 根据传入的状态model显示UI; 这个View接口其实还包含了intent()
的方法, 返回的是一个Observable
, UI中用了RxBinding.
最后一步就是, 如何将View的intent和业务逻辑联系起来呢? 这里用到了一个额外的组件: Presenter
.
这个Presenter看起来像这样:
public class SearchPresenter extends MviBasePresenter<SearchView, SearchViewState> {
private final SearchInteractor searchInteractor;
@Override protected void bindIntents() {
Observable<SearchViewState> search =
intent(SearchView::searchIntent)
.switchMap(searchInteractor::search) // I have used flatMap() in the video above, but switchMap() makes more sense here
.observeOn(AndroidSchedulers.mainThread());
subscribeViewState(search, SearchView::render);
}
}
MviBasePresenter
是mosby中的一个类.
这个类做的事情就是当View第一次attach到Prensenter上时, 调用bindIntent()
方法将来自view的intent绑定到业务逻辑上, 只有第一次会绑定, 当View再次attach时不会发生.
而subscribeViewState()
方法则处理了定于管理, 避免内存泄露(具体原因见原文).
How modularization affects build time of an Android application
一个Android应用至少有一个application module, build这个module之后得到一个.apk文件.
application module之间不能相互依赖, 它只能依赖于library, build library module的结果是得到一个.aar(Android Archive Library)文件.
build的过程可以粗略分为5个阶段:
- 1.准备依赖.
- 2.Merge资源和manifest.
- 3.编译. 从annotation processors开始, 把源码编译成字节码.
- 4.后处理. 所有以
transform
开头的gradle tasks都属于这个阶段. 其中最重要的是transformClassesWithMultidexlist
和transformClassesWithDex
, 它们生成了.dex文件. - 5.打包发布. 对library来说是生成.aar, 对application来说是生成.apk.
我们都知道gradle只有在输入变化了的情况下才会重跑task. 而且如果一个module没有变化, 也不会被重新build, 那么就出现了一种假设: 多个module应用的增量build要比单个module的快, 因为只有被改变了的module才会重新编译.
作者想验证这种假设是否正确.
他用的工具就是:
./gradle assembleDebug --profile
做了一系列实验之后证明这个假设还是有道理的.
实验过程中的一些发现:
1.当应用被拆分为多个modules之后, 改变application module中的代码, build时间会减少; 但是library中的代码, build时间反而会增加. 这是因为library build的时候debug和release的tasks都执行了(并不知道为什么).
当library module被这样添加的时候:
dependencies {
compile project(path: ':app2')
compile project(path: ':app3')
}
不管app当前的build type是什么, app永远依赖的是library的release版本.
这是一个Gradle当前的限制. 参见Library-Publication.
幸运的是, 我们可以改变这一行为:
首先在library中添加:
android {
defaultConfig {
defaultPublishConfig 'release'
publishNonDefault true
}
}
让它也可以发布debug版.
在app中依赖的时候:
dependencies {
debugCompile project(path: ':app2', configuration: "debug")
releaseCompile project(path: ':app2', configuration: "release")
debugCompile project(path: ':app3', configuration: "debug")
releaseCompile project(path: ':app3', configuration: "release")
}
这样debug和release都只依赖各自对应版本的library了.
2.不管我们改动的是library中的代码还是application中的代码, application module永远都会被重新编译, 所以减小app module的尺寸很有意义.
3.上面这些都是library之间互相独立的情况, 如果library之间还有相互依赖, 那么build时间也会变长.
4.如果应用超出了DEX的方法数限制, 用了multidex, 也会增加build时间, Android 5.0开始使用了一个叫做ART的runtime, 在这方面有一些优化, 可以减少build时间, 所以我们可以在开发的时候设置最小API是21: Optimize multidex in development builds.
Exploiting Android Seams for Testing and Flexibility
如何让Android应用代码可测试? 答案是创建一些接缝. 这篇文章中, 作者将一些Android特有的接缝, 来让我们的应用更加灵活和易测.
Manifest接缝
使用Merge rule markers可以方便地更改manifest.
比如在build variant是mock的时候, 由于我们在src/mock/AndroidManifest.xml里这样写:
<!-- src/mock/AndroidManifest.xml -->
<activity
android:name=".StubConfigActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".MainActivity">
<intent-filter tools:node="remove">
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
所以在build mock的时候, 启动Activity会被替换成上面这个StubConfigActivity
.
还有更多的可能值得探索, 比如你可以替换filter的内容, 从而改变默认intent启动的Activity.
BuildConfig接缝
在gradle中可以根据不同的build variant来定义BuildConfig
中的变量值.
默认情况下BuildConfig
中会包含一些有用的变量比如DEBUG
和FLAVOR
.
我们可以创建更多额外的变量:
productFlavors {
mock {
buildConfigField('Boolean', 'MOCK', "true")
}
}
一个简单的应用case是我们可以定义不同的base url:
defaultConfig {
buildConfigField('String', 'API_BASE', '\"api.awesomecompany.com\"')
}
productFlavors {
sandbox {
buildConfigField('String', 'API_BASE', '\"localhost:8080\"')
}
}
Resource接缝
不同build variants的资源就像manifest一样, 最后会被merged. 但是对于资源我们没有markers可以控制它们如何merge.
我们可以利用默认的merge行为: Resource merging.
优先级是这样的:
build variant > build type > product flavor > main source set > library dependencies
所以我们可以把默认的资源放在main里, 然后在特定的build variant再创建一份覆盖它们.
LIBRARIES & CODE
Reptar
RxJava2.x的有用的类的集合.
Toasty
前面加了一个icon的Toast, 带背景颜色, 除了内置的error, info, success, warning等几种形式, 还可以自定义.
Google-Actions-Java-SDK
非官方的Google Actions Java SDK.
Android Weekly Notes Issue #241的更多相关文章
- 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的使用; ...
随机推荐
- cin和scanf的速度差别
好长时间没有遇到这种问题了,以前虽然知道scanf比cin快,但是没想到快这么多,见图. 50万的数据. scanf输入: cin输入: 网上说用std::ios::sync_with_stdio(f ...
- FZU 2122 又见LKity【字符串/正难则反/KMP/把一个字符串中某个部分替换为另一个部分】
嗨!大家好,在TempleRun中大家都认识我了吧.我是又笨又穷的猫猫LKity.很高兴这次又与各位FZU的ACMer见面了.最近见到FZU的各位ACMer都在刻苦地集训,整天在日光浴中闲得发慌的我压 ...
- 天天算法————快排及java实现。
快排说的很邪乎,原理懂了,实现自然也就出来了: public void static quickSorted( int[] a ,int low ,int high){ //递归结束条件 if(low ...
- Jackson反序列化错误:com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field的解决方法
说明:出现这种问题的情况是由于JSON里面包含了实体没有的字段导致反序列化失败. 解决方法: // 第一种解决方案 // ObjectMapper对象添加 mapper.configure(Deser ...
- Eclipse安装Spring工具套件
前言: 安装spring工具套件是为了更快捷的使用spring,但是我觉得既然已经有了maven,工具套件其实不那么重要. 而且装好后我发觉没什么两样,只是新建bean文件时比较爽一点. 安装步骤: ...
- SQL-基础学习3--通配符:LIKE,%,(_); 拼接:+,||,concat;
第六课 用通配符进行过滤 6.1 LIKE操作符 通配符本身实际上是SQL的WHERE子句中有特殊含义的字符,SQL支持几种通配符.为在搜索子句中使用通配符,必须使用LIKE操作符.LIKE指示DB ...
- 邁向IT專家成功之路的三十則鐵律 鐵律二十二:IT人升遷之道-無為
升遷管道是許多人求職時相當重要的考量之一,畢竟人除了很愛錢之外更愛顯赫的頭銜,然而在企業中越顯赫的頭銜,其背後通常有更多的罵名,因為許多人的高官厚爵都是踩著一群人的頭頂爬上去的,隨時哪一天跌了下來,都 ...
- js:深入继承
/** * js实现继承: * 1.基于原型链的方式 * 2.基于伪造的方式 * 3.基于组合的方式 */ 一.基于原型链的方式 function Parent(){ this.pv = ...
- bootstrap3分页
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"% ...
- Linux基础(1)- 命令和目录文件
1.开启Linux操作系统,要求以root用户登录GNOME图形界面,语言支持选择为汉语 Linux操作界面如图: 右击桌面,点击打开终端 输入“su”,点击回车键,出现密码,输入密码,点击回车键,显 ...