Android Weekly Issue #225
October 2nd, 2016
Android Weekly Issue #225
本期内容包括: Android 7.0的Quick Settings; Firebase; 兼容旧版本的shared element transition; Wear; ORM: 用ActiveAndroid做数据库存储; 崩溃报告工具对比; Google Cast API介绍; Google的播放器库ExoPlayer 2.x发布; 项目的包结构整理; Task API的使用等等.
ARTICLES & TUTORIALS
Android 7.0的快速设置 Quick Settings Tiles
从Android 7.0 (API 24)开始, 任何app都可以创建一个quick settings tile, 快速访问关键功能.
它除了是一个展示最新信息的UI, 点击一个片还可以trigger后台任务, 打开dialog或activity.
一个好的quick settings tile:
决定是否要建立这样一个tile时, 主要考虑紧急性和频繁性两个方面.
每一个tile和一个TileService关联. 和其他service一样, 它需要在manifest中注册, 它的label和icon就是显示在quick settings上的文字和图片.
TileService的生命周期:
TileService是一个bound service, 它的生命周期主要由系统控制. 主要有三个阶段: being added, listening, being removed.
-
onTileAdded()
: 当用户添加这个tile到quick settings. -
onStartListening()
: tile变为可见. -
onStopListening()
: tile变为不可见. -
onTileRemoved()
: 用户移除这个tile.
以上这是默认模式, 如果你准确地知道何时更新, 你可以使用active mode.
此时更新的回调onStartListening()
是通过静态方法主动触发的.
更新UI:
UI是Tile, 主要包含icon, label, description和state. 最后必须调用updateTile()
方法.
处理点击:
在onClick()
回调触发的时候, 我们可以启动一些后台工作, 或者showDialog()
, 或者startActivityAndCollapse()
.
对于锁屏的机器有一些限制, 不能打开dialog, 并且activity需要有一个特定的flag, 有一个unlockAndRun()
方法可以让用户先解锁后做一些工作.
长按tile默认会打开app的app info屏, 当然这个行为也可以override. 只要给你想打开的activity加上ACTION_QS_TILE_PREFERENCES
.
Android开发最佳实践 Android Development Best Practices
关于性能:
Best Practices for Performance;
Performance and Optimization
关于架构:
android-architecture
写单元测试和UI测试.
使用Proguard, Stetho.
复用布局, 使用标签.
reusing-layouts.
把launcher icons放在mipmap文件夹下.
多用shape和selector而不是图片.
避免深层次的布局.
向Intent或Bundler传数据时, 使用Parcelable
而不是Serializable
. 因为后者使用反射而比较慢.
不要在UI线程进行文件操作.
明白Bitmaps. 因为它们占用很多memory. Displaying Bitmaps
使用style来避免重复的属性设置.
需要时使用Fragment.
明白Activity的生命周期.
使用得到公认的libraries而不是自己的实现.
在各种机器上测试.
Recap Of Google Launchpad Build Lagos : All About Firebase
作者参加了一个叫Google Launchpad Build的会议, 这篇文章是总结, 全部是关于Firebase的.
Android Shared-Element Transitions for all
在Lollipop+的设备上, shared element的transition动画很好实现, 但是在旧的版本上该怎么办呢? 作者展示了他的方法:
- Activity A捕捉origin view的初始值, 通过Intent把它们传给Activity B;
- Activity B完全透明地启动;
- Activity B读取bundle中的值, 准备场景;
- Acitivty B运行shared element动画.
几个实现细节:
需要知道View在B中的位置, 时机是layout之后, 但是draw之前, 即onPreDraw()
.
返回时只需要把这个动画反向播放即可.
Writing Better Adapters
(这个上一期刚讲过, 不知道为什么重复了. )
就是关于RecyclerView的Adapter, 作者认为多种View类型时, Adapter中太多的instance of和强制类型转换不是一种好做法, 于是提出了他的做法.
Android Wear: Accessing the Data Layer API
Data Layer API是Google Play services的一部分, 用于不同设备(手机和手表)间的数据交换.
作者先提供了代码, 发送和存储数据, 监听数据变化.
问题是, 如果Wear第二次向mobile请求数据, mobile发送了和上一次一样的数据, Wear并不会进入onDataChanged()
, 因为数据并没有变化.
所以作者想知道如何从Data Layer API来获取数据, 并展示了他的方法在不同情形下的应用.
Espresso Tests For TextSwitcher
作者想给TextSwitcher写Espresso测试.
从Android Studio 2.2开始, 你可以录制你的操作, IDE将会自动为你生成Espresso测试代码. 但是作者录了一个有关TextSwitcher的测试之后, 跑失败了.
这是因为TextSwitcher
继承了ViewSwitcher
, 其实现其实是把两个TextView加到了布局里.
所以Espresso抛出了AmbiguousViewMatcherException
.
所以作者根据可见性区分了它俩, 修复了测试.
还可以根据child view的index来区分.
Animating Android Activities and Views with Slide Animations
作者展示了如何给Activity和View加上左右滑动的动画.
Guide to ORM using ActiveAndroid: Part 1
这是一个系列教程, 相关的代码在: ActiveAndroid-Tutorial
什么是ORM(Object-Relational Mapping)呢?
a technique to convert between incompatible type-systems in an object-oriented programming language.
在面向对象的语言中, 转换不兼容的类型的技术.
ActiveAndroid是一个ORM(object relational mapper), 让你不用写SQL语句, 就可以读写数据库.
A Comparison of Android Crash Reporting Tools
作者对比了几种崩溃报告工具, 并介绍了如何使用.
包括: Firebase, Crashlytics, Apteligent, Bugsnag.
Google Play Services: Google Cast v3 and Media
Google Cast是一个让用户把网上的内容发送到设备上的技术. 通常用来和TV交换内容.
作者详细地介绍了如何使用Google Cast SDK来创建应用.
注: 要建造客户端程序, 首先需要注册: https://cast.google.com/publish/.
这是收费的.
ExoPlayer 2.x - It’s here (plus FAQs)!
Google的库google/ExoPlayer升级到v2.x了.
(它是一个Media Player, YouTube用的就是它.)
这次是个重大更新, 添加了很多新功能, 推荐大家以后用新版.
How We Rethought our Complete Package Structure for Buffer on Android
作者他们重新整理了项目的包结构, 总结了整个过程还有从中学到的东东.
作者他们之前的包结构是按类型的, 有activities, fragments, adapters等包. 因为类名以类型终结, 所以索性就按整个分组.
当app变得越来越大, 这种组织方式发现就不太好, 感觉很难找东西, 并且感觉没什么结构.
经过改变之后, 作者他们采用了一种更加整洁并且易于导航的结构.
新结构中, 当添加一个新的feature, 就保持在同一个目录中, 这样就不用来回切换目录.
作者他们的新结构有四个总目录:
- data
- ui
- injection
- util
data中包含网络请求及相关的models, preferences, database, data models, 还有其他和数据直接关联的东西.
其中和不同API关联的models又分别组织在子目录下.
ui目录中包含所有和UI相关的组件, 在这个包中按照功能又拆分了子目录. 其中有base包, 用来盛放Fragment, Activity和MVP的基类, 接口等; 还有common包, 用来盛放公共控件.
injection中包含所有依赖注入的类, 分component, module和scope的子目录.
util中含有Helper和Utility类.
Become a Firebase Taskmaster! (Part 3)
这是系列文章的第三篇, 这个系列是关于Play services的Task API.
如果项目里已经依赖了Firebase, 变自动包含了Task API, 如果不想用Firebase, 可以单独添加依赖:compile 'com.google.android.gms:play-services-tasks:9.6.1'
创建新的Task可以用下面这两个方法:
Task<TResult> call(Callable<TResult> callable)
Task<TResult> call(Executor executor, Callable<TResult> callable)
第一个call()
方法在主线程执行任务, 第二个call()
方法可以把工作提交给一个Executor
.
Callable有点类似于Runnable:
public class CarlyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "Call me maybe";
}
}
参数制定了方法的返回值的类型, 进而也是创建出Task的类型.
Task<String> task = Tasks.call(new CarlyCallable());
想要链式执行, 进行后续操作, 可以用Continuation.
public class SeparateWays implements Continuation<String, List<String>> {
@Override
public List<String> then(Task<String> task) throws Exception {
return Arrays.asList(task.getResult().split(" +"));
}
}
它继承接口时指定了输入和输出的类型, 它的输入来自于Task的输出.
可以多写几个Continuation类然后连起来:
Task<String> playlist = Tasks.call(new CarlyCallable())
.continueWith(new SeparateWays())
.continueWith(new AllShookUp())
.continueWith(new ComeTogether());
playlist.addOnSuccessListener(new OnSuccessListener<String>() {
@Override
public void onSuccess(String message) {
// The final String with all the words randomized is here
}
});
LIBRIARIES & CODE
groupie
显示和管理复杂的RecyclerView布局, 把你的items按照逻辑分组管理.
android-junit5
Gradle插件, 用JUnit5做Android的单元测试.
epoxy
用来构建复杂的RecyclerView屏.