Sean's Note: 10月 2016

2016年10月17日 星期一

Android ReactiveX 新手入門

Getting Started With ReactiveX on Android


前言

在開發 Android 時,常常為了處理網路連線、後端 API 呼叫與 UI 的更新,而寫出一堆 callback,甚至是 nested callbacks,這些程式碼不僅難看,也容易寫錯。使用 ReativeX 可以讓程式碼更清楚明瞭,更可以節省開發的時間。

設定 RxAndroid

compile 'io.reactivex:rxandroid:1.2.1'
// Because RxAndroid releases are few and far between, it is recommended you also
// explicitly depend on RxJava's latest version for bug fixes and new features.
compile 'io.reactivex:rxjava:1.2.1'
PS: 可以到 GitHub 上查看目前最新的版本

基礎:Observables 與 Observers 

當在開發 ReactiveX 時,我們會不斷的使用 observables 和 observers,因為 ReativeX 正正是套用 Observer Design Pattern 的設計,Observable 是可被觀察的對象,也就是真正在做事的人,Observer 是觀察者,聆聽與接收事件發生的人。

建立一個簡單只傳遞出 "Hello" 的 Observable:
Observable<string> myObservable = Observable.just("Hello"); // Emits "Hello"

建立一個 Observer:
Observer<string> myObserver = new Observer<string>() {
    @Override
    public void onCompleted() {
        // Called when the observable has no more data to emit
    }
 
    @Override
    public void onError(Throwable e) {
        // Called when the observable encounters an error
    }
 
    @Override
    public void onNext(String s) {
        // Called each time the observable emits data
        Log.d("OBSERVER1", s);
    }
};
在沒有 Observer 觀察者的時候,Observable 是不會傳遞出資料的,想要 Observable 傳遞出資料,就必須要先向其訂閱 (Subscribe):
Subscription mySubscription = myObservable.subscribe(myObserver);
此時 myObserver 就會接收到 "Hello" 字串。
除了 observer 可以 subscribe 以外,有時我們不需要 onCompletedonError 時,也可以用 Action1 的介面來 subscribe:
Subscription mySubscription = myObservable
.subscribe(new Action1<string>() {
    
        @Override

        public void call(String s) {
        
            Log.d("ACTION1", s);
    
        }

});

使用 Operators

from

ReactiveX 提供了許多對 observables 操作的運算方法,先來介紹 Observable.from 的用法,from 可以讓你提供一組數據資料,使每個資料都成爲一個 Observable,而 Observer 接受到的資料就會是一個接著一個來,而不是一次收到整組數據。
Observable<Integer> myArrayObservable 
    = Observable.from(new Integer[]{1, 2, 3, 4, 5, 6}); // Emits each item of the array, one at a time
 
myArrayObservable.subscribe(new Action1<Integer>() {
    @Override
    public void call(Integer i) {
        Log.d("My Action", String.valueOf(i)); // Prints the number received
    }
});

map

透過 map 可以將原本 Observable 的結果,做想要的運算,再回傳另一個新的 Observable。
        myArrayObservable
                .map(new Func1<Integer, Integer>() {
                    @Override
                    public Integer call(Integer integer) {
                        return integer * integer;
                    }
                })
                .subscribe(new Action1<Integer>() {
                    @Override
                    public void call(Integer i) {
                        Log.d("ACTION1", i.toString());
                }
        });

skip 

skip 可以用來忽略前面幾個的回傳結果,例如 skip(2) 回傳的結果就會是 3, 4, 5, 6
        myArrayObservable
                .skip(2)
                .subscribe(new Action1<Integer>() {
                    @Override
                    public void call(Integer i) {
                        Log.d("ACTION1", i.toString());
                }
        });

filter 

filter 如同其名,可以用來過濾掉不要的 observables,例如下方的範例就會過濾的奇數的值。
        myArrayObservable
                .filter(new Func1<Integer, Boolean>() {
                    @Override
                    public Boolean call(Integer integer) {
                        if (integer % 2 == 0) {
                            return true;
                        }
                        return false;
                    }
                })
                .subscribe(new Action1<Integer>() {
                    @Override
                    public void call(Integer i) {
                        Log.d("ACTION1", i.toString());
                    }
                });

處理 Asynchronous 任務

剛剛的任務都是執行在 UI Thread(Main Thread) 上的,如果我們要指定 observable 要在哪個 Thread 上執行任務,就要用 subscribeOn,subscribeOn(Schedulers.newThread) 或 subscribeOn(AndroidSchedulers.mainThread)。如果是要指定 observer 在那個 Thread 上接受資料,就要用 observeOn,如果沒有指名 observeOn,observer 就會在 subscribeOn 所指定的 thread 上接收到資料。
只是要傳遞簡單的資料,我們可以用前面介紹的 just 和 from 方法,但大多情況我們都需要執行其他的方法後,才能取得資料,此時就可以用 create 來建立 Observable,並實作 OnSubscribe 介面。
Observable<String> fetchFromGoogle = Observable.create(new Observable.OnSubscribe<String>() {
    @Override
    public void call(Subscriber<? super String> subscriber) {
        try {
            String data = fetchData("http://www.google.com");
            subscriber.onNext(data); // Emit the contents of the URL
            subscriber.onCompleted(); // Nothing more to emit
        }catch(Exception e){
            subscriber.onError(e); // In case there are network errors
        }
    }
});

zip

有時在處理網路任務時,會需要等待兩個以上的任務同時完成,並處理結果,這不但要添加不少程式碼,也容易寫錯,此時 ReactiveX 所提供的 zip 方法便帶來了這樣的好處:
// Fetch from both simultaneously
Observable<String> zipped 
    = Observable.zip(fetchFromGoogle, fetchFromYahoo, new Func2<String, String, String>() {
        @Override
        public String call(String google, String yahoo) { 
            // Do something with the results of both threads
            return google + "\n" + yahoo;
        }
    });

concat

concat 的功能和 from 很像,會把 observable 的結果一個接著一個回傳。
        
Observable observable1 = Observable.just("Hello");
Observable observable2 = Observable.just("World");

// Fetch from both simultaneously
Observable.concat(observable1, observable2)
          .subscribe(new Subscriber<string>() {
              @Override
              public void onCompleted() {}

              @Override
              public void onError(Throwable e) {}

              @Override
              public void onNext(String s) {
                  Log.d("Sean", s);
              }
});

Ref: https://code.tutsplus.com/tutorials/getting-started-with-reactivex-on-android--cms-24387

2016年10月14日 星期五

[Git] 如何更改 push 後的 commit 內容?

How to change git commit message after push?


若只是要更改近幾次的 commit message 可以用以下指令:
git commit --amend
然後在 force push 到 remote server:
git push --force <repository> <branch>
如果同時也有人在修改該 branch 時,最好是用 --force-with-lease 比較安全,因為如果 upstream 已經有更改時,push 會被 abort:
git push --force-with-lease <repository> <branch>
Ref: http://stackoverflow.com/questions/8981194/changing-git-commit-message-after-push-given-that-no-one-pulled-from-remote

2016年10月13日 星期四

[Android 4.4 Issue] TextView 無法垂直置中

The TextView cannot center vertically:

<TextView 

                android:id="@+id/toolbar_title" 
                android:layout_width="match_parent" 
                android:layout_height="match_parent"
                android:visibility="visible" 
                tools:text="Hello" 
                android:layout_gravity="center_vertical" 
                android:gravity="center_vertical" 
                android:textSize="@dimen/font_slarge"/> 

It seems an OS issue that android:gravity="center_vertical" doesn't work when android:layout_height="match_parent". The issue can be fixed when giving the specific height.


Ref: https://code.google.com/p/android/issues/detail?id=59368

2016年10月3日 星期一

[Mac] 如何在 ZSH 下設定 adb 指令?

How to add ADB command to ZSH?


在 ~/.zshrc 檔裡加入:
export PATH=$PATH:"$HOME/Library/Android/sdk/platform-tools"