Android Weekly Notes Issue #225
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 (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
.
寫單元測試和UI測試.
使用Proguard, Stetho.
複用佈局, 使用
把launcher icons放在mipmap資料夾下.
多用shape和selector而不是圖片.
避免深層次的佈局.
向Intent或Bundler傳資料時, 使用Parcelable
而不是Serializable
. 因為後者使用反射而比較慢.
不要在UI執行緒進行檔案操作.
使用style來避免重複的屬性設定.
需要時使用Fragment.
明白Activity的生命週期.
使用得到公認的libraries而不是自己的實現.
在各種機器上測試.
作者參加了一個叫Google Launchpad Build的會議, 這篇文章是總結, 全部是關於Firebase的.
在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()
.
返回時只需要把這個動畫反向播放即可.
(這個上一期剛講過, 不知道為什麼重複了. )
就是關於RecyclerView的Adapter, 作者認為多種View型別時, Adapter中太多的instance of和強制型別轉換不是一種好做法, 於是提出了他的做法.
Data Layer API是Google Play services的一部分, 用於不同裝置(手機和手錶)間的資料交換.
作者先提供了程式碼, 傳送和儲存資料, 監聽資料變化.
問題是, 如果Wear第二次向mobile請求資料, mobile傳送了和上一次一樣的資料, Wear並不會進入onDataChanged()
, 因為資料並沒有變化.
所以作者想知道如何從Data Layer API來獲取資料, 並展示了他的方法在不同情形下的應用.
作者想給TextSwitcher寫Espresso測試.
從Android Studio 2.2開始, 你可以錄製你的操作, IDE將會自動為你生成Espresso測試程式碼. 但是作者錄了一個有關TextSwitcher的測試之後, 跑失敗了.
這是因為TextSwitcher
繼承了ViewSwitcher
, 其實現其實是把兩個TextView加到了佈局裡.
所以Espresso丟擲了AmbiguousViewMatcherException
.
所以作者根據可見性區分了它倆, 修復了測試.
還可以根據child view的index來區分.
作者展示瞭如何給Activity和View加上左右滑動的動畫.
什麼是ORM(Object-Relational Mapping)呢?
a technique to convert between incompatible type-systems in an object-oriented programming language.
在面向物件的語言中, 轉換不相容的型別的技術.
ActiveAndroid是一個ORM(object relational mapper), 讓你不用寫SQL語句, 就可以讀寫資料庫.
Google Cast是一個讓使用者把網上的內容傳送到裝置上的技術. 通常用來和TV交換內容.
作者詳細地介紹瞭如何使用Google Cast SDK來建立應用.
注: 要建造客戶端程式, 首先需要註冊: https://cast.google.com/publish/.
這是收費的.
Google的庫google/ExoPlayer升級到v2.x了.
(它是一個Media Player, YouTube用的就是它.)
這次是個重大更新, 添加了很多新功能, 推薦大家以後用新版.
作者他們重新整理了專案的包結構, 總結了整個過程還有從中學到的東東.
作者他們之前的包結構是按型別的, 有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類.
這是系列文章的第三篇, 這個系列是關於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
顯示和管理複雜的RecyclerView佈局, 把你的items按照邏輯分組管理.
Gradle外掛, 用JUnit5做Android的單元測試.
epoxy
用來構建複雜的RecyclerView屏.