Android開発まとめ　個人メモ

# 概要
数年ぶり(2017年12月現在)にAndroid開発をすることになりました。
当時はEclipseの時代でしたが、色々変わってることが多々あり、
現状のベストプラクティスを模索しています。
今後自分がAndroidの開発をしていく上で調査した内容などを個人的な見解でまとめます。

# 注意 
個人メモのため記事は都度成長していきます。
分割して別の記事になる可能性もあります。

# 開発環境

* Android Studio 3.0
* Kotlin 1.2
* MacOS 10.12

# Android Studio 

## よく使う設定

### Android Studioのテーマの変更

http://color-themes.com/?view=index
で好きなテーマをダウンロードする。
Obsidianかな。
http://color-themes.com/?view=theme&id=563a1a6180b4acf11273ae3d
テーマ名前.jarが落ちてくるので
File -> import settigns からjarをインポート。再起動を求められるので再起動する。

### Android Studioで使用していないImportを自動削除する
http://tatuas.hatenablog.com/entry/2015/05/26/135640
Settingを開き、Editor > General > Auto Importで、Javaカテゴリの「Optimize imports on the fly」チェックボックスをつける。

## Parcelableを自動生成する

※ParcelableはBundelに設定できるオブジェクト。

Preferences > Plugin > Brouse repositories...ボタン 
[parcelable]で検索
Android Parcelable code generatorをインストール
※kotlinの場合は for kotlinをインストール

dataクラスで コマンド+N　でgenerateを表示し parcelableで生成できる。

# ライブラリの利用　Gradle

build.gradleはProject配下とapp配下がある。
どちらに配置するべきか要検討

## 今後要調査

dependenciesにおける
* classpath
* compile
* impementation
の違い

dependenciesのkaptを指定する意味

## HTTPクライアント

### Retrofit2 + GSON

Retrofit2公式
http://square.github.io/retrofit/

GSONのgithub
https://github.com/google/gson

```build.gradle
dependencies {
    compile 'com.squareup.retrofit2:retrofit:2.3.0'
    compile 'com.squareup.retrofit2:converter-gson:2.3.0'
    compile 'com.google.code.gson:gson:2.8.2'
}
```

## MVVM

### Rx+DataBinding 
* RxJava 
* RxKotlin 
* DataBinding 
* RxProperty

```build.gradle
dependencies {
    implementation 'io.reactivex.rxjava2:rxjava:2.1.7'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
    implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
    implementation 'com.jakewharton.rxbinding2:rxbinding-kotlin:2.0.0'
    implementation 'me.tatarka.bindingcollectionadapter2:bindingcollectionadapter:2.2.0'
    implementation 'me.tatarka.bindingcollectionadapter2:bindingcollectionadapter-recyclerview:2.2.0'
    kapt "com.android.databinding:compiler:$android_plugin_version"
    compile 'com.github.k-kagurazaka.rx-property-android:rx-property:4.0.0'
    // If you want to use Kotlin syntax
    compile 'com.github.k-kagurazaka.rx-property-android:rx-property-kotlin:4.0.0'
}
```
## DB

### RealmJava (Database)

```build.gradle(Project)
apply plugin: 'realm-android'
dependencies {
    classpath 'io.realm:realm-gradle-plugin:4.3.1'
}
```

```build.gradle(app)
apply plugin: 'realm-android'
```

# よく使う実装

## ボタンクリック RxCommand + Api実行のObservable連携 

例 LoginAPIを実行するログインボタンのObservableストリーム実装

### WebApiの呼び出し Retrofit2 +  Gson + Rx

```ApiService.kt
interface ApiService {
    @Headers("Accept: application/json", "Content-type: application/json")
    @POST("/api/auth/login")
    fun postAuthLogin(@Body body: Map<String,String>): Observable<LoginData>
}

class ApiInvoker {
    fun login(loginId: String):Observable<LoginData> {
        val map = mapOf("loginId" to loginId)
        val retrofit =  buildRetrofit()
        return retrofit.create(ApiService::class.java)
                .postAuthLogin(map)
                .delay(1, TimeUnit.SECONDS)
                .threadManage()
    }

    private fun <T> Observable<T>.threadManage():Observable<T> {
        return this.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
    }

    private fun buildRetrofit(): Retrofit {
        return Retrofit.Builder()                .client(OkHttpClient.Builder().build())
                .baseUrl("http://192.168.0.2")
                .addConverterFactory(GsonConverterFactory.create(Gson()))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build()
    }
}
```

#### ApiServiceにおけるPOINT
* HTTPメソッド指定
* URL指定
* リクエストヘッダー指定
* リクエストパラメータ指定
* レスポンスの型をObservableにする。ジェネリックタイプはレスポンスJSONを表す型

#### ApiInvokerにおけるPOINT
* RetffitのBuilderの使い方( buildRetrofit()に閉じ込める)
* subscribeOnでioスレッドを指定（newThread() でもよい)しバックグラウンド実行
* observableOnでメインスレッドに戻す。

### ViewとViewModelの連携

``` layout/activity_login.xml
<!-- 肝心なところだけ抜粋 -->
<layout xmlns:app="http://schemas.android.com/apk/res-auto" >
    <data>
        <variable name="vm" type="ViewModels.LoginViewModel" />
    </data>
    <Button android:id="@+id/loginButton"
        ....
        rxCommandOnClick="@{vm.signinCommand}" />
</layout>

```

``` LoginActivity.kt 
var viewModel: LoginViewModel = LoginViewModel(this)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding = DataBindingUtil.setContentView<ActivityLoginBinding>(this, R.layout.activity_login)
        val emitter: Subject<NoParameter> = PublishSubject.create()
        binding.loginButton.setOnClickListener { view -> emitter.onNext(NoParameter.INSTANCE) }
        viewModel.loginCommand.bindTrigger(emitter)
        binding.vm = viewModel
    }
```

#### Activity.kt(Fragment.kt) におけるPOINT

RxCommandはイベントの橋渡ししかしないのでClickListenerとの連携は独自で実装する。
これをやらないと実行時にDatabindingにてVoidからNoParameterの型違い関連のビルドエラーが出る。

### ViewModel　

``` LoginViewModel.kt
val loginId = RxProperty<String>("")
            .setValidator { value -> if(value.isEmpty()) "必須項目" else null }
            .asManaged()

val loginCommand: RxCommand<NoParameter> = loginId.onHasErrorsChanged()
            .map { !it }
            .toRxCommand<NoParameter>(!loginId.hasErrors())
            .asManaged()

init {
    val d1 = loginCommand
            .doOnNext { progressAction.set(true) }
            .concatMap { invoker.login(loginId.get()) }
            .subscribe {
                progressAction.set(false)
            }
}
```

#### ViewModelにおけるPOINT

* commandをプロパティで宣言するとき、入力チェックしたいRxPropertyのValidationと連動できる。
*  .map { !it } にて入力チェックが正常だったことを保証する。
*  toRxCommand<NoParameter>では初期値を渡す。
*  コンストラクタ(init)にて 後続処理のイベントストリームを作成し、プログレスバーの表示やAPI実行を繋ぐ。最終的にsubscribeが実行される。


## Databindingカスタムセッター

準備中・・・

## RecyclerViewでのMVVM

要調査