本期內容包括: 開發一個自定義View併發布為開源庫的完整流程介紹; 用`AnimatedVectorDrawable`實現的動畫; 什麼樣的程式是可測試的; `DownloadManager`介紹; Okhttp的重試; Android 7取消了`file://`; Android Studio即... ...
Android Weekly Issue #235
December 11th, 2016
Android Weekly Issue #235
本期內容包括: 開發一個自定義View併發布為開源庫的完整流程介紹; 用AnimatedVectorDrawable
實現的動畫; 什麼樣的程式是可測試的; DownloadManager
介紹; Okhttp的重試; Android 7取消了file://
; Android Studio即將推出的build cache功能; 支持離線模式的app構架; 如何寫自定義的lint規則; Epoxy, 一個處理複雜RecyclerView屏的庫; FragmentPagerAdapter
和FragmentStatePagerAdapter
的比較等.
ARTICLES & TUTORIALS
Make an android custom view, publish and open source
作者開發了一個環形的SeekBar, 並把它作為一個庫發佈到了JCenter.
作者首先講了自定義View的實現:
首先是關於View生命周期的介紹, 在寫自定義View的時候有幾個關鍵的生命周期回調需要處理:
作者實現的幾個關鍵步驟講解:
- 自定義屬性並獲取.
- 在
onMeasure()
中控制尺寸. - 在
onDraw()
中繪製: 避免在onDraw()
中分配記憶體; 用invalidate()
方法來激發重繪. - 在
onTouchEvent()
處理用戶手勢. 在他的環形SeekBar的實現里, 這裡涉及到了點擊坐標到角度的轉換.
將自定義View庫開源到Github:
開源到Github有個好的README很重要, 這裡有幾個tips:
- 提供截圖, Gif或者Video.
- 提供安裝/使用說明.
作者自己的庫: SwagPoints
發佈庫:
- 去JFrog Bintray註冊.
- 創建repository, package, 和版本號.
- 生成並上傳, 用了這個library.
- 添加到Jcenter.
- 被接受之後收到郵件, 就可以使用了.
Animation: Jump-through
用AnimatedVectorDrawable
實現的一個很fancy的位置標誌動畫.
What makes Android Apps Testable
如果程式的架構不適合測試, 那麼硬要寫一些測試很可能就會面臨這樣的局面: 要麼就是發現沒法寫測試, 要麼就是為了寫測試而破壞了代碼, 做了一些奇怪的事情.
那麼到底是什麼樣的程式才是適合寫測試, 或者是可測試的呢?
有一個有趣的定義是seam(接縫), 在接縫處你可以改變程式的行為, 而不用編輯當前程式. 如果程式沒有接縫, 你將無法設置測試的初始條件和驗證測試結果.
本文中舉了一個實際的例子, 開始的時候程式沒有seam, 所以導致無法測試, 後來把靜態方法改為實例的方法之後, 我們就可以通過Mockito來模擬行為, 設置條件, 最後通過驗證某一方法的調用與否來進行驗證.
DownloadManager – Part 3
用DownloadManager
來處理下載.
首先它在設備上有自己的UI, 還有notification, 還有Downloads app能讓用戶管理下載文件.
我們可以查詢到文件的一些信息, 比如MIME type, 文件尺寸, 下載狀態等.
我們還可以用getUriForDownloadedFile()
方法來獲取一個URI, 配合MIME type, 發送Intent, 來打開一個相關的查看程式.
關於儲存文件的合適地點:
- 文件小, 僅app自己使用 -> 私有數據區域(預設行為).
- 文件大, 僅app自己使用 -> 外部存儲的私有數據區域(不需要許可權).
setDestinationInExternalFilesDir()
. - 文件需要被別的應用訪問 -> 外部存儲的共有區域, 需要
WRITE_EXTERNAL_STORAGE
許可權.setDestinationInExternalPublicDir()
.
OkHttp is quietly retrying requests. Is your API ready?
在網路較慢或不穩定的時候, OkHttp有可能會重覆發送請求, 直到成功.
這個重試的邏輯是通過RetryAndFollowUpInterceptor.java實現的.
那麼, 我們可以關掉這個重試行為嗎? 有一些issues就在討論這個問題: Issue # 1043. 後來有兩個pull requests: PR #1259和PR #2479改進了這個問題, 減少(但並沒有消除)了不必要的retry請求.
全局關閉重試行為: OkHttpClient.Builder .retryOnConnectionFailure()
設置為false. 但是註意這樣是很粗暴並具有破壞性的, 消除了retry邏輯帶來的好處:
- 如果Url有多個IP, 失敗了一個還可以試另一個.
- 連接池中的連接偶爾會time out, 減少這種意外導致的後果.
- 可以順次查找多個代理, 如果都失敗了再轉向直接連接.
解決真正的問題: 關閉靜默重試在某些情形下有幫助, 但是其實它隱藏了真正的問題, 就是你的API是否是冪等的idempotent. server端可以根據客戶端的GUID來檢測重覆, 這樣server就不會多次執行操作, 會通知發送者.
File scheme is now not allowed with Intent on N
Android N (Nougat, API 24)開始, 不再允許發送file://
的Intent, 將會直接拋出FileUriExposedException
異常.
所以當你把targetSdkVersion
改為24之後, 你必須要確保你修複了這些問題再發佈.
解決方案是什麼呢? 用content://
, 結合FileProvider
:
首先在manifest裡面聲明:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
然後在res\xml\provider_paths.xml
文件里指明路徑:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
最後, 把
Uri photoURI = Uri.fromFile(createImageFile());
改為
Uri photoURI = FileProvider.getUriForFile(MainActivity.this,
BuildConfig.APPLICATION_ID + ".provider",
createImageFile());
然後放在Intent里發送就好了.
註意, 如果你的targetSdkVersion
還沒有更新到24, 那麼即便是在Nougat的手機上file://
也仍然是能正常使用的.
Use Android Studio Gradle Build Cache for faster builds
Android Studio當前的最新版是2.3 Canary 2. 有一些新的改進, 但是其中最吸引人的是這個build cache. 它會使你的clean build更快.
本文後面解析了build cache的工作原理.
Offline App Architecture, build for the Next Billion
一個好的應用應該在網路不好甚至離線的時候仍然可以使用, 我們應該做些什麼呢?
- 確定連接狀況. 可以使用這個network-connection-class. 如果你使用的是Okhttp, 可以加一個Intercepter來進行採樣.
- 有效地緩存. 從網路取數據很慢並且昂貴, 所以有效地利用之前取到的數據是很關鍵的優化. (Cache-Control, Etag).
- 在本地操作, 在全局同步. 等網路請求的時候可以先顯示本地數據, 而不是loading.
- 有效地處理線程.
- 優化圖片. 網路不好的時候先用RGB_565, 等網路變好了再取高質量圖片.
- 使用大Cookie. 儘量一次傳輸更多的數據(big cookie), 而不是頻繁發送一些小請求(small cookies).
Writing custom lint rules and integrating them
如何創建自定義的lint規則.
事情的由來是作者發現了一個死迴圈調用, 然後他想做一個什麼標記以防以後其他人會犯同樣的錯誤.
然後他想到的是@Nullable註解, 的檢查, 實質是依靠lint來實現的.
於是他自己寫了一個自定義的lint規則, 來提示使用用他的註解@CarefulNow
標記的方法時應當註意.
詳細的實現方式請看原文.
Epoxy: Airbnb’s View Architecture on Android
epoxy是一個Android庫, 用來處理複雜的RecyclerView屏. 本文介紹了它在項目中實際的使用.
Adventures with FragmentStatePagerAdapter
可能有很多Android開發者對於
FragmentPagerAdapter和FragmentStatePagerAdapter的區別不是太清楚或根本不知道, 本文作者就具體介紹了二者的不同.
基本不同
FragmentPagerAdapter
適用於項目個數確定的情形.
為什麼呢? 因為一旦fragment的實例被創建, 它永遠也不會從FragmentManager
中移除, 直到Activity被銷毀.
當Fragment不見的時候, 僅僅是onDestroyView()
被調用, 當fragment再次回來時, 再調用onCreateView()
.
FragmentStatePagerAdapter
當fragment的實例不可達的時候, 實例就會立即從FragmentManager
移除. 被移除的fragment實例的狀態由FragmentStatePagerAdapter
保存, 當你再次回到該項的時候, fragment會重建新實例, 並且狀態被恢復. 所以這種adapter適用於項目個數不確定或的情況.
所以使用FragmentPagerAdapter
的時候需要註意記憶體問題.
notifyDatasetChanged()的問題.
notifyDataSetChanged()
是用來處理數據集變化的情況, 比如一些項目增刪的情況. 這個方法不是用來刷新當前顯示的Fragment或其中的Views的.
文章中還有一些關於數據改變實現以及現有issue的討論. 為瞭解決issue作者還發佈了一個庫UpdatableFragmentStatePagerAdapter.
LIBRARIES & CODE
KeepActivitiesTile
一個quick settings tile來開啟"Don't keep activities".
WaveLoading
一個波形的loading圖, 水面上漲代表loading程度.
coordinators
Simple MVWhatever for Android.
epoxy
一個處理複雜的RecyclerView屏的庫.
Screen Record for Android
錄屏腳本.