Android Weekly Notes Issue #222

Android Weekly Issue #222

September 11th, 2016
Android Weekly Issue #222html

ARTICLES & TUTORIALS

Forcing bytes download in Okio

這是Jake Wharton的文章, 關於Okio的BufferedSink.java

okio 是一個java io庫, 包裝了一套API用來讀寫和處理數據. 文檔見: okio doc.android

不少庫都是在其之上寫的, 好比okhttp, Retrofit, Moshi. 這裏有個視頻: A Few OK librariesios

有三個方法能夠強制把bytes放入底層的Sink.git

flush(), emit() and emitCompleteSegments()

flush()github

調用這個方法會使得全部緩存的字節移動到Sink, 而後Sink當即flush本身. 因此調用flush()方法返回後, 你能夠保證全部的字節都到了目的地Sink.web

當用多級buffer時, flush()會清除全部level的buffer. Okio中多級buffer的花銷很小, 一個flush會讓每一級把本身的segments移到下一級.數據庫

java io中的多級buffer每一級都本身管理本身的字節, 因此flush操做會讓每一級都拷貝數據到下一級.express

流類型的close()flush()的行爲相似, 在關閉流以前把全部的字節都寫了.編程

emit()

發射字節的行爲和flush相似, 可是不是遞歸的. 調用emit()會使得全部緩存的字節移動到Sink, 可是與flush()不一樣, 此時Sink並不會作其餘的操做.

emitCompleteSegments()

調用這個emitCompleteSegments()方法僅僅移動那些完整segment的字節移動到Sink.

segment是okio的一個概念.

原文中有動圖圖解, 看起來更清楚一些.

Use cases

Writing messages to a WebSocket -> flush()

Encoding a video to a file -> emitCompleteSegments() + emit()/flush() at the end

Serializing an object to JSON -> emit()

Using Android Studio's Performance Monitors

做者他的音樂應用遇到了不響應的問題, 而後他打開Android Studio的performance monitor看到了是內存問題, 而後他track了一下出問題的時候的memory allocation, 最後發現是有一張要下載的圖太大了.

The hidden cost of code coverage

測試覆蓋率是一個很好的方法來激勵你和你的團隊多寫一些測試, 可是你知道嗎, 打開測試覆蓋率的檢測會讓你的build變慢.

要測量build時間:

./gradlew clean assembleDebug --profile

而後做者發現其中:app:transformClassesWithJacocoForDailyDebug佔用的時間達到了build時間的14%.

就是由於這句:

buildTypes {
    debug {
        ...
        testCoverageEnabled true
    }
}

解決辦法
只在須要測試覆蓋率的時候用它, 因此加一個變量:
/gradlew -Pcoverage clean connectedDebugAndroidTest

gradle裏這樣寫:

buildTypes {
    debug {
        ...
        testCoverageEnabled (project.hasProperty('coverage') ? true : false)
    }
}

當你不加這個flag的時候就不會檢查測試覆蓋率.

Android Development Useful Tools

methodscount 能夠統計library的方法數, 由於Android有64k的方法數限制.

Stetho
能夠看網絡請求, 數據庫, shared preferences等.

LeakCanary 檢測內存泄露的庫.

apk-method-count 這個網站能夠檢測apk中的方法數, 根據包分別顯示.

Android Asset Studio
生成圖片, 9-patch等的一系列工具.

Buck Facebook開發的一套build系統.

Gradle, please 輸入你要的庫, 而後就能夠找到對應的dependencies裏應該寫的 compile xxx.

ProguardDexGuard

Genymotion

Material Design Icons

Introduction to Automated Android Testing - Part 6

系列文章的最後一篇, 做者講用Espresso寫UI測試.

若是數據是動態的, 那麼要測試View包含指定的信息是很難的. 數據有可能變化, 可是咱們的測試不能所以而失敗. 因此爲了測試可靠和可重複, 咱們不該該調用production的API.

因此第一步是mock API, 有幾種方法:

    1. WireMock (須要server).
    1. 用OkHttp的MockWebServer, 在你的設備上跑一個webserver.
    1. 建立一個Retrofit REST接口, 返回一些dummy對象.

Mock數據的時候利用了Gradle的flavor, 能夠建立一個mockDebug flavor, 而後在src目錄下建一個mock目錄, 放一些mock的代碼進去, 而後再建一個prod目錄, 把真實的實現移動到那裏去.

Espresso基礎

Espresso寫測試, 基本格式是這樣:

onView(withId(R.id.menu_search))      // withId(R.id.menu_search) is a ViewMatcher
  .perform(click())               // click() is a ViewAction
  .check(matches(isDisplayed())); // matches(isDisplayed()) is a ViewAssertion

ViewMatchers 是用來定位View的 有withId, withText, withTag等.

ViewActions 是用來跟View交互的, 好比點擊, 輸入等.

ViewAssertations 是用來作出斷言的.

這裏有個cheat-sheet能夠查: android-expresso-testing.pdf

Writing Espresso UI Tests

這部分具體講了做者例子的UI測試寫法, 見原文.
最後做者還看了一下測試覆蓋率.

What 2 years of Android Development Have Taught Me

做者講了他兩年來的心路歷程, 以及他總結的一些To do和Not to do.

  1. 不要重複造輪子.
    能夠用android-arsenal來查庫.

  2. 明智地選擇庫.
    選擇庫的時候看看星多很少, 做者有沒有什麼其餘流行的庫. 再看看issues, 有時間的話能夠看代碼.
    Dryrun能夠用來跑sample.

  3. 坐下來看代碼. 咱們應該花費多數的時間閱讀別人的代碼, 而不是寫本身的代碼.
    你能寫出來的代碼是你已經知道的東西的一個反映, 要不斷成長和提升本身, 只能不斷閱讀和學習別人的代碼.
    這裏有Library列表app列表.

  4. 保持一個好的代碼風格. 這裏有一些參考: code-style, android-guidelines.

  5. 用Proguard, 這樣release版本不但縮小了代碼也作了混淆. 這裏還有一個DexGuard.

  6. 使用一個合理的架構. 能夠用MVP來解耦, 這裏有個demo: Android-CleanArchitecture, 這裏是它相關的Guide文章: Guide on Clean Architecture.
    更多資源:
    androidmvp,
    mosby,
    android-architecture.

  7. 對於獨立工做的開發, 還須要注意一些UI/UX相關的原則. 能夠上DribbleMaterialUp多看看.

  8. 分析是你的好朋友. 分析包括了crash報告和app使用記錄, 能夠用Firebase.

  9. 作一個市場忍者. 對於獨立開發者來講, 須要marketing. 這是一個市場分析工具: sensor tower.

  10. 優化你的App. 推薦檢查內存泄露的工具: leakcanary.

  11. 優化gradle的build時間. 這裏有兩個Guides: speeding up gradle build, making gradle builds faster

  12. 多作測試. 發佈前花時間測試, 不要急.

  13. 兼顧多種機型, 包括屏幕尺寸, API level, 不一樣廠商的OS等.

  14. 用Git. 這是一個Git branch model. 若是你不能負擔github上的private repo, 你能夠試試BitBucket

  15. 讓黑客以爲難處理. Android很容易被攻擊, app能夠被輕易地反編譯和分析. 你須要知道如何處理你的API keys, 若是你須要處理用戶的敏感信息, 你應該知道如何加密. 祕鑰也要妥善存儲. 任何存儲在數據庫中的敏感信息也都要加密. 相關的資料能夠看Adding Tampering Detection to Your appHiding Secrets in Android Apps.

  16. 在低級設備上開發. 低級的設備容易暴露問題.

  17. 學習設計模式. 這裏有一個repo講了全部的Java中的設計模式: java-design-patterns. 另外還推薦書籍: GoF的設計模式, Martin Fowler的重構, Joshua Bloch的Effective Java.

  18. 貢獻本身的力量. StackOverflow, Github, blog posts...

Android Support Annotations

使用Annotation library:

compile 'com.android.support:support-annotations:<latest-library-version>'

若是你已經用了appcompat library, 你就已經能夠用annotations了, 由於appcompat本身就用了它.

annotations按照用法和功能來分組:

  • Nullness Annotations
  • Resource Annotations
  • Thread Annotations
  • Value Constraints Annotations
  • Others : Permissions Annotations, CheckResults Annotations and CallSuper Annotations.

Nullness Annotations

@Nullalbe@NonNull用來檢查變量, 參數和方法返回值爲null與否.

@NonNull表示變量, 參數或返回值不能爲null, 若是爲null了編譯器會給出警告.

@Nullalbe表示可能爲null, 用這個註解的時候表示代碼中應該加上null check.

Resource Annotations

由於資源號都是int值, 因此若是你把一個drawable的int傳給一個期待string resource的代碼, 編譯器是會接受的.
資源註解就是用來作這種狀況的類型檢查的.

好比用@StringRes來標記參數, 若是你傳入一個drawable的id, IDE就會把它標記出來.

每個Android的資源類型都有一個對應的資源註解, 好比類型是Foo, 那麼對應的資源類型註解就是FooRes.

有一個特殊的@AnyRes, 用來表示任意的資源類型.

Thread Annotations

這種註解用來檢查方法是否是在特定的線程調用的. 有:

  • @UiThread
  • @MainThread
  • @WorkerThread
  • @BinderThread

@UiThread@MainThread是可互換的.

若是一個類中的全部方法都是從一樣的線程調用搞得, 那麼能夠直接把類給標記了.

Value Constraints Annotations

@IntRange, @FloatRange@Size註解是用來驗證參數的值的. 好比@IntRange就驗證參數是在一個給定的int範圍以內.
好比下面的方法確保傳入的參數是0到255:

public void setAlpha(@IntRange(from=0, to=255) int alpha) {
    //set alpha
}

相應的@FloatRange檢查參數是在一個指定範圍內的浮點數.

@Size註解是用來檢查集合的大小, 還有字符串的長度. 好比@Size(min=1)用來檢查集合不爲空, @Size(2)檢查集合有兩個元素.

CheckResult Annotations

這個註解是用來檢查一個方法的返回值確實被使用了.
一個比較好的例子是String.trim方法, 當這個方法被@CheckResult標註, 若是它的返回值沒有被使用, IDE就會報錯.

另一些比較值得看的註解有@CallSuper, @Keep@RequiresPermission.
能夠直接查看support annotations的reference.

其餘參考資料:

Improve code Inspection with Annotations

Support Annotation documentation

Improving your code with android support annotations

ActivityTestRule: Espresso's Test "Lifecycle"

做者這篇文章的目的是講講用Espresso的ActivityTestRule寫的測試中的操做順序, 討論像beforeActivityLaunched(), afterActivityLaunched(), 和 afterActivityFinished()這些方法相對於測試和Activity的生命週期都是何時被調用的.

首先做者介紹了Espresso 2.0及以前的舊的ActivityInstrumentationTestCase2.

ActivityTestRule的"生命週期".

新的寫法是這樣:

@RunWith(AndroidJUnit4.class)
public MyNewTest {
  @Rule
  public MyCustomRule<MyActivity> testRule = new MyCustomRule<>(MyActivity.class);
  
  @Test
  public void testStuff() {
    // Wow where's all the boilerplate code?
    
    // Verify Oscar Grouch is no longer grouchy.
  }
}

而其中的MyCustomRule:

public class MyCustomRule<A extends MyActivity> extends ActivityTestRule<A> {
  public MyCustomRule(Class<A> activityClass) {
    super(activityClass);
  }
  
  @Override
  protected void beforeActivityLaunched() {
    super.beforeActivityLaunched();
    // Maybe prepare some mock service calls
    // Maybe override some depency injection modules with mocks
  }
    
  @Override
  protected Intent getActivityIntent() {
    Intent customIntent = new Intent();
    // add some custom extras and stuff
    return customIntent;
  }
  
  @Override
  protected void afterActivityLaunched() {
    super.afterActivityLaunched();
   // maybe you want to do something here 
  }
    
  @Override
  protected void afterActivityFinished() {
    super.afterActivityFinshed();
    // Clean up mocks
  }
}

ActivityTestRule: launchActivity=false;

ActivityTestRule的第三個參數容許開發者明確指定對每個test case啓動一個Activity.

public ActivityTestRule(Class activityClass, boolean initialTouchMode, boolean launchActivity).

把第三個參數設置爲false, 就能夠寫出這樣的測試:

@RunWith(AndroidJUnit4.class)
public class MultipleIntentsTest {
  @Rule
  public ActivityTestRule<MyActivity> testRule = new ActivityTestRule<>(MyActivity.class,
          false,    // initialTouchMode
          false);  // launchActivity. False to set intent per test);

  @Test
  public void testOscarGrouchy() {
    Intent grouchyIntent = new Intent();
    // intent stuff
    grouchyIntent.putExtra("EXTRA_IS_GROUCHY", true);
    testRule.launchActivity(grouchyIntent);
    // verify Oscar is grouchy
  }
  
  @Test
  public void testOscarNotGrouchy() {
    Intent happyIntent = new Intent();
    // intent stuff
    happyIntent.putExtra("EXTRA_IS_GROUCHY", false);
    testRule.launchActivity(happyIntent);
    // verify Oscar is not grouchy
  }
}

這個lauchActivity的值默認是爲true. 設置爲false以後咱們在每個test case裏面本身啓動activity. 對生命週期產生的影響做者也作了圖對比分析, 見原文.

做者最後還講了幾個點, 關於測試遷移, 以及Activity的生命週期方法中啓動Intent相關的須要注意的地方.

People and resources to learn Android programming from

做者分享了關於Android編程學習中他積累的人和資源.

Twitter

Chiu-Ki Chan

Donn Felker, 他的博客: blog. 他和Kaushik Gopal一塊兒弄了Fragmented Podcast. 這裏還有一個視頻教程的網站: Caster.io.

Jake Wharton, 這個你們都知道啦, 這是他的博客: blog.

Kristin V Marsicano, 這裏有一個她的關於Activity生命週期的演講Activities in the Wild, 可能有一些你沒有想過的東西.

Ryan Harter

The Practical Dev

最後這有一個列表: Tweet Android List

Podcasts

Fragmented 兩個獨立開發者辦的.

Android Developers Backstage 寫Android的那些人辦的.

Material 這不是一個技術廣播, 講一些Google新聞.

Videos

Caster.io

Realm.io

Android Dialogs (YouTube)

Newsletters

Android Weekly

Kotline Weekly

General Reading

Medium: 標籤androiddevandroid-app-development

Conferences

GoogleIO

360|AnDev

Droid Con NYC

ThirtyInch - a new MVP library for Android

近年來MVP已經變成了Android社區中很流行的一種設計模式, MVC和MVVM也有人用. 這些模式的共同點就是把業務邏輯從Activity中抽取來.

這樣作的好處首先是咱們能夠儘可能把須要測試的邏輯用JVM上的單元測試測, 而不是用模擬器上的androidTests.
固然有些須要UI測試的地方仍然會用Espresso.

做者介紹了他們的MVP庫: ThirtyInch.

這個庫開始的時候mosby尚未release, 建議讀一下mosby做者關於MVP的文章: mosby, 其中關於passive View的概念也在ThirtyInch中用到.

ThirtyInch:

ThirtyInch.

TiPresenter, TiView.
TiView是一個接口, 能夠被attach和detach.

TiPresenter有四個生命週期事件:

  • onCreate(): 初始化的時候調用一次, 此時view尚未attach.
  • onWakeUp(): view attach了, 而且對用戶變爲可見.
  • onSleep(): 在這個調用以後, view將被detach, 而且變爲對用戶不可見.
  • onDestroy(): 在Activity/Fragment徹底銷燬的時候調用一次.

onWakeUp()onSleep()對應了onStart()onStop(), onResume/onPause沒有對應的回調支持, 由於這些生命週期回調應該在View層處理, 見: Presenters dont need lifecycle

ThirtyInch有什麼不一樣

  • 可配置. 能夠傳TiConfiguration對象給TiPresenter, 去掉一些features.
  • 全部Presenter的生命週期都按照正確的順序調用, onCreate()onDestroy()只調用一次.
  • 不依賴RxJava. 它有一個獨立的Rx module.
  • View接口的方法註解. 好比@CallOnMainThread@DistinctUntilChanged.
  • Public API. 一些基層API能夠被全部人利用起來.
  • 不用繼承TiActivity. 你能夠利用CompositeAndrodi, 把plugin module做爲你的依賴, 而後把TiActivityPlugin加到你的Activity.

以後做者舉了一個Hello World的例子, 附圖講解很好, 見原文.

不是嚴格的MVP, MVVM也能夠

做者又列出了一個ViewModel的圖.
ViewModel存儲了當前UI的狀態數據.
當ViewModel中的數據變化時當即應用到View, 這裏@DistinctUtilChanged的使用避免了數據不變時候的重複操做.

測試, Keep Android At Arm's Length

MVP的初衷之一是爲了方便寫測試, 由於Android SDK中的一些方法和類很差mock, 因此Presenter中應該是沒有Android相關的東西的, 好比Context和Fragment等, Presenter只知道View接口和其中的方法, 是純java的.

這就是"Keep Android At Arm's Length."的意思, 不要把Android和邏輯代碼綁在一塊兒, 庫的名字ThirtyInch也是來自於這個原則, 由於三十寸是人類手臂, 肩膀到手指的平均長度.

How does the Presenter survive the configuration change?

Activity在屏幕方向旋轉時會被重建. 此時沒有被序列化保存的信息就會丟失, 網絡請求要麼被取消, 要麼被忽略, 從新請求.

序列化數據會費時, 並且在這種狀況下, 序列話的數據幾秒以後就要被反序列化.

Android Framework提供了兩個方法來避免這種沒必要要的序列化:

  • Fragment的setRetainInstance(true), 以前的那個Fragment實例會被保存.
  • 使用Activity#onRetainNonConfigurationInstance()Activity#getLastNonConfigurationInstance()來存儲和恢復對象. 這也是Android保存上面retained Fragments的方法. 這個方法最近被廢棄了.

在Java中還有一個比較簡單粗暴的方法是保存一個應用級別的單例.

ThirtyInch使用了上述的三種方法來確保TiPresenter在configuration變化的時候不死. 單例的解決方法盡在一些邊緣狀況必須.

當使用TiPresenter的時候不須要再實現onSaveInstanceState(Bundle)方法了, 由於數據都存在Presenter中.

Firebase Analytics VS Google Analytics

做者以前對比過 Firebase Crash Reporting和Crashlytics.

在這篇文章裏, 做者對比Firebase和Google的分析工具, 下面簡稱FA和GA.

首先GA是2005年就推出了, 那時候根本沒有Android和iOS, 因此GA最開始是網站用的, FA是今年推出的, 從一開始目標就是移動應用.

而後做者總結了FA的優點和當前存在的幾個不足.

最後的結論就是:
若是你只有app, 用FA; 若是你只有網站, 用GA; 若是你兩個都有, 則兩個都用.

VectorDrawable Fill Windings

做者有一個Sketch作的資源, 是一個空洞圖, 結果放在程序裏看的時候中間的洞沒有了, 變成實心的了.

做者分析並詳細解釋了出現這種問題的緣由, 並提供了兩種解決方案.

DESIGN

Basic Patterns for Mobile Navigation

這篇文章做者分析了三種導航模式: hamburger menu, tab bar和gesture-base navigation.

Hamburger Menu

Pros: 導航選項多, 設計乾淨, 給主要內容留出了更多空間.

Cons: 不易被發現.
在iOS實現時, 和iOS的基本導航元素衝突.
hamburger的icon並無給出上下文.
須要點兩下才到目標頁面.

Tips: 給選項排列優先級. 若是你的高優先級選項很少, 能夠考慮用tabs或者tab bar. 重審你的信息結構, 有沒有必要劃分紅多個簡單的app.

Tab Bar

Pros: Tab bar能夠反映出當前在哪. 它們是永久存在的, 用戶能夠單擊訪問.

Cons: 有限的選項數. iOS和Android可能會有不一樣的設計規範.

Tips: 讓點擊區域足夠大. icon要通過可用性測試. icon和label一塊兒用.

Gesture-Based Navigation

Pros: 移除了UI雜項, 節約了屏幕空間. 天然的人機交互接口.

Cons: 導航不可見. 增長了用戶教育成本.

Tips: 確保不要必須教給用戶一種全新的交互方式, 設計類似的體驗. 使用過程動畫的形式教用戶如何使用.

Design Reviews: Going beyond the surface

關於設計的review.

LIBRARIES & CODE

green-coffee

一個Android庫, 讓你能夠在instrumentation測試中跑Cucumber.

ThirtyInch

一個Android MVP庫.

Tools

Exynap

一個Android Studio插件, 能夠生成實現代碼.

相關文章
相關標籤/搜索