code

2018年6月21日 星期四

Android Architecture Components - Life Cycle Aware Components

簡介

這是Android官方推薦“正確寫一個Android App"的方法,這個官方推薦的方法由一些非常基本library以及pattern組成,稱為Architecture components,包括以下:

Lifecycles: 每個app必考慮的東西
Lifecycle-aware observables: 觀察lifecycles的方法
Lightweight ViewModel: 這是獨立於Activity和Fragment之外,可以單獨測試邏輯之處
Object mapping for sqlite


要解決什麼問題?

通常programmer把所有的邏輯(商業/UI/Data/與OS互動等)都寫在activity或是fragment中,不過activity事實上是設計來操縱UI與OS互動,越簡單越能避免life cycle造成的問題,主要是因為activity / fragment 這類型的class,programmer並沒有絕對掌控權,從instance creation到destruction都是OS決定,所以要盡量減少對這些class的dependency。

此外model應該永遠獨立於view之外,永遠不受到OS相關決定(回收記憶體之類)的影響。


Life-cycle Aware Components

我們通常會把某些邏輯或是object寫在一個activity或是fragment裡面,應且在life cycle callback中作出對應的動作。例如以下MyLocationListener instance會在MyActivity的onStart()和onStop() 時也去做start() / stop() 動作:

class MyLocationListener {
    public MyLocationListener(Context context, Callback callback) {
        // ...
    }

    void start() {
        // connect to system location service
    }

    void stop() {
        // disconnect from system location service
    }
}

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    @Override
    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, (location) -> {
            // update UI
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        myLocationListener.start();
        // manage other components that need to respond
        // to the activity lifecycle
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
        // manage other components that need to respond
        // to the activity lifecycle
    }
}

但是如果有很多類似的component在這個activity裡面,那他的life cycle callbacks就會充滿這類code,變得複雜難maintain。

此外我們不能保證async operations會在我們要的life cycle callbacks回來:

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, location -> {
            // update UI
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        Util.checkUserStatus(result -> {
            // what if this callback is invoked AFTER activity is stopped?
            if (result) {
                myLocationListener.start();
            }
        });
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
    }
}
可以看到在onStart()中,此myLocationListener要等待某個operation result回來為true才會啟動,但是有可能此Activity onStop()先發生了,然後此operation result才回來,所以又啟動了這個myLocationListener。這有可能造成了這個listener在我們未預期的狀況下活動。

android.arch.lifecycle提供了一些design pattern來幫助我們免於煩這些(鳥)事。


Life Cycle

LifeCycle這個class把某個有life cycle概念的component (e.g. Activity / Fragment / Service) 的life cycle給抽象化了,它有states和events:



所以對這個component life cycle有興趣的人,就可以implement LifeCycleObserver:

public class MyObserver implements LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void connectListener() {
        ...
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void disconnectListener() {
        ...
    }
}

myLifecycleOwner.getLifecycle().addObserver(new MyObserver());


Life Cycle Owner

擁有LifeCycle的components必須implement LifeCycleOwner interface (AppCompatActivity的某個super class是implement這個interface的),所以最快的讓自己的activity或是fragment變成life cycle owner就是去繼承有implement這個interface的base class:
class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
            // update UI
        });
        Util.checkUserStatus(result -> {
            if (result) {
                myLocationListener.enable();
            }
        });
  }
}
剛剛的MyLocationListener就可以implement LifeCycleObserver:

class MyLocationListener implements LifecycleObserver {
    private boolean enabled = false;
    public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
       ...
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void start() {
        if (enabled) {
           // connect
        }
    }

    public void enable() {
        enabled = true;
        if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
            // connect if not connected
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void stop() {
        // disconnect if connected
    }
}

可以看到MyLocationListener並沒有override什麼interface method,而是依賴於annotations。我們說MyLocationListener是一個life cycle aware object。


如果不繼承已知的life cycle owner class的話,也可以自己去implement LifeCycleOwner interface,但是要透過以下的templates:

public class MyActivity extends Activity implements LifecycleOwner {
    private LifecycleRegistry mLifecycleRegistry;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mLifecycleRegistry = new LifecycleRegistry(this);
        mLifecycleRegistry.markState(Lifecycle.State.CREATED);
    }

    @Override
    public void onStart() {
        super.onStart();
        mLifecycleRegistry.markState(Lifecycle.State.STARTED);
    }

    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        return mLifecycleRegistry;
    }
}


2018年6月10日 星期日

Google IO 2018 - Android Vitals

startup time

1. slow cold start: cold start超過五秒者:


定義為:
1. app 不在memory
2. app經過onCreate() -> Activity running

2. slow warm start: app已經在memory,重新叫起超過2秒者:


定義為:
1. app 在memory
2. app經過onCreate() -> Activity running

3. slow hot start: 某個已經launched activity又restart 到 onResume的時間的時間超過1.5秒者:


定義為:
1. app 在memory
2. app經過onRestart() -> Activity running

Detecting Crashes

Android Vitals是一個platform tool,所以可以在3rd party crash-detection sdk (e.g. Fabric Crashlytics)還沒起來前,就能偵測到crash!


Battery

這是最重要的效能指標,因為mobile device是很resource-limited。


1. 不要直接使用wake lock api,有可能因為bug造成螢幕/CPU永不關閉。改用:



2. 盡量少wake up

Permission

統計看來,user並不喜歡grant permissions:


Vitals會把這些permission列出來:


不過目前應該還是在beta吧?! 我們的app都看不到這些資訊。奇怪。


ANRs

這個是Android系統中惡名昭彰的代名詞,android vitals也是有偵測這個東西,這怎麼發生呢?

當main thread (UI thread) 被block住,無法處理render / ui input時,系統偵測到此狀態維持幾秒鐘之後,就會出現ANR警告。block的原因可能包括:

1. I/O operations: network, disk


上面這個function call看似沒有read/write operations,但實際上一但被instantiated,就馬上有read operations (SharedPreference implementation就是如此)。所以看似無害的code,實際上在main thread做了I/O operation了。

下面是另一個I/O example:


上面的code實際上在 access headerFields就會真的去讀取connection,甚至在某些版本的Android,連if ()中的URL equality comparison就會進行IO了!

不過developers哪有辦法知道所有這些眉角?????
還好Android提供一個debug api,可以讓我們在debug time抓出這些在main thread不該做的事:



penaltyDeath() 會造成runtime crash,所以記得不要在release版本時候打開!!!當違反strict mode crash之後,可以從log找到發生的行數:



2. long computation

這個就不用多說了,肯定是block。
要常態性使用Android Profiler來看哪些code有可能造成long computation。
Strict mode api也提供自己加入註解一段slow code,讓strict mode偵測到此slow code被main thread上執行時,可以penalty crash。

3. Interprocess communication (IPC)

既然另外一個process (通常是另外一個app)不受我們控制,我們當然不能在main thread去做IPC,否則有可能造成main thread上做一些blocking operations。

4. Multithreaded programming

使用一些synchronization primitives的時候,要很確定自己知道自己在幹嘛,否則有可能造成dead lock => ANR

android vitals的log可以幫助我們找出這類型的問題:



5. Slow BroadcastReceiver handling
我們宣告BroadcastReceiver在manifest中,如果一個broadcast進來,handler是在main thread上跑的! 所以不該做任何以上四點提到的operations。如果你的handler在10秒內沒exit,OS會殺掉整個process (app)。

所以要在handler中用另一個thread來做上面四點的事:




Crashes

我們常常為了怕某些狀況下會crash,而做一堆null check或是try-catch,不過目前Android官方建議全部使用Jetpack的架構來解決這些問題:



另外使用Kotlin的built-in nullity check,也是有幫助。

Google IO 2018 - What's new in Android TV

1. Hardware Adoption倍數成長




2. 還是稀少的apps,約3600個。


3. Google assistant 生態系一員:



有Google Assistant的幫助,大大降低操作的門檻,特別是search functionality。主要三個功能面向:

(1) Content Search
(2) Deep links
(3) Playback control


4. Content-first design

每個app有channel概念,可以拉出一部份重點放在Android TV首頁,而使用者也可以自己決定顯示哪些channel。



每個channel中可以有很多個"program",例如上面的每個電影是一個program。


看起來也沒太大改變。

2018年5月16日 星期三

Google IO 2018 - 跟Android有關的talk

Android

App performance:
Android vitals: debug app performance and reap rewards
What's new in Android Runtime
Drawn out: how Android renders
Understanding Android memory usage
Best practices using compilers in Android Studio
Best practices for text on Android
What's new with the Android build system
Don't let your app drain your users' battery



App development
What's new in Android development tools
The future of apps on Android and Google Play: modular, instant, and dynamic
Build the new, modular Android App Bundle
Protips: a fresh look at advanced topics for Android experts
Android Jetpack: what's new in Architecture Components
Effective ProGuard keep rules for smaller applications
What's new with ConstraintLayout and Android Studio design tools
Android Slices: build interactive results for Google Search
Building AR apps with the Sceneform SDK



App Testing
Frictionless Android testing: write once, run everywhere
Autonomous and customized pre-launch testing in the Google Play Console


App Security
What's new in Android security


App Release Management
Release management: successful launches and updates on Google Play


Android TV: What’s new with Android TV

Google Play
Analyze your audience and benchmark metrics to grow on Google Play
Google Play Instant: how app developers are finding success


Android Car
What’s new in automotive

Wear OS
What's new in Wear OS by Google



Google IO 2018 - .app domain

目的

提高https普及率。
此為全世界第一個open (任何人都可以註冊) TLD (top-level domain) with full HTTPS policy。

此地要分清楚Registry和Registrar的不同。看以下連結:

https://help.one.com/hc/en-us/articles/115005588149-What-is-a-registry-registrar-and-registrant-

Registry: 此TLD的創建者/管理者/經營者,以本例子來說是Google
Registrar: 銷售TLD的經銷商,例如goDaddy

民眾通常不會跟Registry打交道,而是透過Registrar。


Target Users

主要瞄準所有跟app development相關的從業人員。

此外還有行銷上的好處:好記較短的網址,例如momoapp.com可以簡化成 momo.app

HTTPS 好處

1. Authenticity: 公開wifi提供者常會對http網站內容做修改(因為掌握底層傳輸機制),所以失去網站的真實性,常見的非惡意的做法是塞入廣告版面。



2. 新的Browser APIs只限制要使用HTTPS傳輸

3. Chrome URL bar會顯示「不安全」給HTTP網站


4. Android P (9.0) default就要HTTPS connection between App and backend


HSTS (HTTP Strict Transport Security)

這是一個讓server告知browser說所有server content壹定要在HTTPS connection上傳輸。



HSTS可以避免connection被ISP downgrade 導向www server的HTTP content (如果有的話),稱為protocol downgrade attack。

再來就是可以做HSTS preloading,也就是一個browser list記載某些TLDs壹定要用HTTPS connection,即便連第一個request header都還沒看到。

由於.app domain已經加入HSTS preload list,所以使用.app domain的website不用花心力去configure HSTS,或是花幾個月的時間去申請加入HSTS preload list。

此外也可以節省website loading時間,如果已經加入HSTS preload list的話,就不會經歷HTTP redirect到HTTPS content的過程。


結論

加入.app domain的好處:
1. marketing advantages:較短且好記的名稱,例如momoecapp.com => momoec.app
2. .app domain已經被加入HSTS preload list,所以server不用花時間心力去configure HSTS以及申請加入preload list,馬上就可以得到HSTS的好處。


2018年1月15日 星期一

Cloud Computing Infrastructure 3 - storages

Storage types and protocols

從low-level access到high level access看起的話:

1. block storage: data bytes以block為單位儲存在裝置中,硬碟或SSD就是此類。如果直接(非透過網路)連接storage,稱為DAS (direct attached storage)。
block storage的速度會以IOPS (IO operations per sec)來判定。

DAS採用的access protocols包括 SATA/USB/SCSI。
非DAS採用的protocols包括iSCSI (internet SCSI) /SAN (storage area network)。


2. file storage: 人類會以files/folders來理解操作storage。
DAS對應的file-level protocol包括FAT/NTFS
非DAS對應的protocol: NFS/SMB等,這類storage又稱為NAS (network attached storage)。

3. object storage: 這又更高階抽象化的儲存單元,這在cloud applications中特別常見,protocols主要的設計方向就是horizontal scaling,這符合cloud的精神。


DAS Storages in the Cloud

<Storage for virtual servers>
cloud vendor為了達成share resources,會採用block storage,實際上是一個DAS,但對每一個virtual machine來說看到的是"local storage instance"。

雖然block storage擁有最快速的access time,但由於一個storage instance是shared resource虛擬化出來的,所以其儲存時間是暫時性的,一旦virtual server關閉,此shared storage instance也將會被釋放資源,回歸shared pool,使用者必須把data儲存在更為persistent的storage上。

當然如果client使用的是bare metal架構,那要裝幾個DAS都可以,也可以組成RAID。

<Storage for containers and serverless computing>
containers採用的是file storage,虛擬化之後containers看到的也是namespace中的temporary local storage,不過containers之間可以share storage。

serverless computing通常不會有storage (因為這是一種state),不過cloud vendor有可能給予暫時性的storage。



SAN storage in the cloud

當然cloud也可以佈署SAN (storage area networks),這是block storage的protocol。SAN storage可以跟virtual server分開佈署,所以不受限於virtual server的life cycle。

不過每次跟某一個virtual server連接(邏輯上的)時,這個SAN storage就必須要format/partition並且重新mount file systems,所以一次只能跟一個virtual server attach。

SAN storage的IOPS取決於網路的速度。


NAS storage in the cloud

SAN storage要format/partition,當然cloud vendors也可以提供快速mounting的file system,前提就是non-attached。


Object storage in the cloud

object就是non-structured data,俗稱blobs,通常是media/backups 等binary raw data。
blob會友伴隨著meta data,方便search和indexing。

S3 (simple storage service)就是Amazon的object storage implementation。

由於blobs通常檔案size很大(例如照片影像),所以storage consistency通常可以容忍delay,以秒為計算單位,這在internet application (e.g. FB)是可以接受的時間,但不適合需要極度synchronization的applicatoin,例如金融或是安全applications。


CDN (content delivery network)

CDN是比storage更上位的單位,通常是為了加速access而將資訊複製在不同geological的data center中,看資訊型態而言,通常會以object storage的方式在低階儲存,因為CDN通常是serve media。


2018年1月8日 星期一

Cloud Computing Infrastructure 2 - virtual server introduction

Virtual Server

virtual server就是hypervisor software創造出來的虛擬的邏輯運算單元,彷彿一個真的獨立運行的機器,在其上可以安裝不同的OS,即便完全跟host machine的OS不同也沒關係。

有兩種架構:


第一種是bare metal,hypervisor軟體直接跑在硬體上,另一種是hosted,hypervisor軟體仍然跑在host machine的OS之上。

資源如何分享呢? host machine上的CPU通常是多核心的,每個核心當然又可以跑多執行緒,而hypervisor軟體利用intel的hyperthreading技術,把每個thread可以視為一個"virtual cpu",所以這樣所有的virtual server都有了運算資源,當然記憶體和storage的虛擬化分享也可以類似泡製。

每個host machine也配備多個網路卡介面(NICs),所以virtual servers也能有獨立的網路資源。


Auto scaling

virtual server可以依照客戶使用量做自動的資源增減。
由於virtual server是虛擬的單元,這樣的scaling是橫向的,也就是需要的時候就多開幾個virtual servers,而不是去縱向置換成硬體能力更強大的server。

橫向的scaling才會沒有限制。

Load balancing

利用一台gateway機器來分配incoming requests給不同的virtual server,避免某幾台virtual server負擔過重。

分配不受地理條件的限制,亦即有可能分配到不同地理區域的host machine上的virtual servers。


Bare metal servers

意思是cloud vendor可以出租整個physical host machine給需要的客戶,也就是非分享的machine,所以不會有"noisy neighbors effect",這讓客戶可以建立off-premises private cloud,有充分的主控權。


Colocation

這算是一種機房配置,客戶可以把自己的私人機器放入cloud vendor的機房內,享有cloud vendor的硬體配置與安全保護,但又有自己的私人機器安全性與隱私。

舉例來說,安裝一個colocation的gateway來連接on-premises與off-premises 的hybrid cloud。


Containers

containers跑在OS上面,一個container image封裝了需要執行某個application所需要的一切runtime libraries,而且被高度的isolated,只看到自己的namespace下的file system (in memory)。

有名的container management software為Docker。

containers和virtual server的異同:



container算是sandboxes,可以快速deploy,不像provision virtual server那樣花費時間,但也由於share同一個host OS,這也樣containers受限了,也就是一台機器上不能有不同OS 的application sandboxes。

containers適合用來開發micro services,因為不同的micro services之間的開發時間和流程不一樣,用containers來deploy就不會影響彼此services之間的版本衝突等。(?! 好像有點牽強,不熟悉這種開發過程)


Server-less computing

這是最lightweight的compute (意思是computation unit),這不用host server / container apps,而是只做request/event handlers,可以稱為Function as a Service, FaaS。

之前在erlang有玩過,因為erlang沒有state,沒有mutables,所以在語法層面可以支援stateless server,也就是server-less。

AWS的implementation為 AWS Lambda。

Cloud Computing Infrastructure 1 - cloud service model overview

Cloud computing實現的背後技術

1. virtualization: 透過hypervisor software來把資源邏輯化切割成可以量化, on-demand的資源。

2. internet: 任何地方都可以存取

Cloud services models

綜觀如下:



on-premises意思是公司有機房自己host infrastructure,從左到右公司需要維護的infrastructure越來越少,當然掌控程度也越來越低。

Cloud Deployment Models

public cloud: 跟所有人share virtualized resources,資源使用性可能會被其他使用者影響到(noisy neighbor effect)。

private cloud: 不跟任何人share virtualized resources,可以自建機房(on-premises),或是託管 (off-premises)。也可以自己安裝與掌控virtualization software,例如OpenStack, VMWare vCenter。

Hybrid cloud: 以上兩種的混合,可以把適合的application放上public cloud。



2018年1月4日 星期四

iOS testing 3 - XCode 9 new testing features

這是WWDC 2017 #409 "What's new in testing" 筆記。

New Async waiting API

通常會用到async API的情況如下:



之前Xcode6 我們需要這樣做async waiting:



上面程式要開啟遠端某個文件, 在實際上開啟之前(document.open()),我們先定義一個expectation,然後為此async operation設定expectation,當此operation真的完成的話,在success closure裡面我們就要關閉或說fulfill 這個expectation。

限制如下:
1. timeout會被判定成failure
2. waiting (e.g. waitForExpectations()) 是XCUITest instance method,綁死了
3. 沒有nested waiting


XCTWaiter
這是針對上面限制的改良,主要是把waiting從XCUITest分離出來:


timeout的處理方式提供了delegate或是 closure,或是XCTWaiter intance,更加彈性了。

另外記得去看XCTestExpectation的new features。

Multiple App UI Testing

現在可以在不同的target app間測試互動情況,有需要再研究。



不知道可不可以launch 3rd party來測試?

Performance of UI Testing

要找到畫面中的一個ui element,我們必須去做query。XCUIQuery是需要用到accessibility data,舊的做法是test app藉由interprocess communication,去要某個時間點target app的serialized "snapshot",就是app state snapshot:


這個做法在state相當龐大的時候(例如app使用超多記憶體),會有testing performance impact,甚至會造成某些testing timeout,使得test case被誤判成failure。更極端有可能因為query使用太多記憶體而process被killed。

要到snapshot,判別query的責任是落在test app身上。


xcode9的做法是:
(1) remote query: 減少interprocess data



現在改成只傳query data給target app,然後query在target app身上做(那還不是會造成target app performance impact?!),最後再把query results傳回給testing app。

所以這樣能夠減少testing app負擔,以及減少interprocess communication,這得到30%的記憶體與速度改善:





(2) query analysis: 透過分析來減少一個query要實現需要的最小的app states snapshot,這樣當然也可以減少運算時間與memory。



(3) programmer tools: 透過programmer對不同測試情境的知識,可以決定是何時可以做較短的query,提供"firstMatch"這類method讓query不用走完整個query data hierarchy。


firstMatch是改善最多的,但這是需要programmer對ui layout的了解才有辦法達成!

一個原則就是,你query的越精確,越會有好的testing performance,所以這樣也同樣無法依賴所謂的UI Recording,基本上是個廢物說實在的:




新架構避免使用的寫法

不要用block(closure) based NSPredicate,因為block不能被serialized,也不能在runtime被optimize:


上面說道的optimization都不能用了,所以避免使用block based NSPredicate 。



更好的test report: Activities / Attachments / Screen shots

activity是把一堆test command group起來,變成一個存活較久的test:



這個好處是讓test report看起來很簡潔,也比較有表達性:





XCode 9提供了screenshot api,可以讓XCUIElement或是整個screen都擷取快照圖片。
attachment則是讓test report把一些(包括screen shots) 想要跟activity 一起呈現在test report中的data包裹起來:



所以在test report中就會看到activity包含了attachments (此例中是screenshots):




動工吧!!!!