vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

【Kotlin】【Android】RxJavaとUniRxの対応表を作ってみたい 1

はじめに

この記事はRxJava Advent Calendar 2016の2日目に勢いのみでつっこもうとしている記事です。

それはさておき。

先月参加したKansai.kt #2でReactiveProgrammingとRxJavaの話を聞いて以降、を読みつつコードを書いてみたりしています。

その中で、(お仕事ではUnityを使うことが多く、UniRxも導入しているため)UniRxと同じような処理から書いてみるととっつきやすいんじゃね?と思い立ったため、思いつく順番でそれぞれのコードを書いてみたいと思います。

なおRxJavaの方はAndroid環境で実行しており、言語はKotlinを使います。

環境

  • Unity 5.5
  • IntelliJ IDEA 2016.3

  • UniRx 5.5

  • Kotlin v1.0.5-release-IJ2016.3-2

準備

UniRx

UniRxをインポートしたUnityプロジェクトを作成します。

RxJava

後述しますが、RxJava、RxAndroid、RxBindingを使います(ついでにDataBindingも有効にしています)。

build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.0"
    defaultConfig {
        applicationId "jp.masanori.kandroidtest"
        minSdkVersion 19
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }
    dataBinding {
        enabled = true
    }
    packagingOptions {
        exclude 'META-INF/rxjava.properties'
    }
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.0.1'
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
    compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7-kotlin:1.0.0'
    compile 'io.reactivex.rxjava2:rxjava:2.0.1'
    testCompile 'junit:junit:4.12'
    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    kapt 'com.android.databinding:compiler:1.0-rc5'
}
repositories {
    mavenCentral()
}
kapt {
    generateStubs = true
}

ボタンクリックのイベント

なんとなくGUIから始めます。

ボタンをクリックしたときのイベントを取得してみます。

UniRx

using UnityEngine;
using UnityEngine.UI;
using UniRx;

public class MainCtrl: MonoBehaviour{
    public Button startButton;
    private void Start(){
        // ボタンにクリックイベントを追加する.
        startButton.onClick.AsObservable()
            .Subscribe(_ => Debug.Log("クリックされました"));
    }
}

RxJava

RxJavaでは上記のようにボタンクリックを取得することはできないようです。
まぁAndroid用に作られたものではないため、当然といえば当然なのですが。。。

ではどうするかといいますと、RxBindingを使用します。

準備のところで書いたように、「compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7-kotlin:1.0.0'」を追加します。
なおJavaで書く場合は「-kotlin」の部分を削除してください。

ここでRxJava2を使っていて、「compile ~」だけを追加した場合に「com.android.builder.packaging.DuplicateFileException」が発生します。

どうもRxJava1系と2系が競合してしまうらしく、下記を追加する必要があります。

~省略~
android{
    ~省略~
    packagingOptions {
        exclude 'META-INF/rxjava.properties'
    }
}
~省略~

あとは下記のようにボタンのクリックイベントを取得できるようになります。

import android.databinding.DataBindingUtil
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.jakewharton.rxbinding.view.RxView
import io.reactivex.disposables.CompositeDisposable
import jp.masanori.kandroidtest.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    lateinit private var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        // ボタンにクリックイベントを追加する.
        RxView.clicks(binding.startButton).subscribe { Log.d("RxTest", "クリックされました") }
    }
}

一定時間ごとに処理を行う

次は一定時間ごとに処理を行ってみます。

UniRx

private void Start () {
    // 500ミリ秒ごとにログ出力.
    var intervalObservable = Observable.Interval(System.TimeSpan.FromMilliseconds(500d))
        .Subscribe(time => Debug.Log("経過: " + time));
    
    // 画面遷移などのタイミングでDisposeを実行して接続を解除する.
    // intervalObservable.Dispose();
}

RxJava

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.jakewharton.rxbinding.view.RxView
import rx.Observable
import rx.schedulers.Schedulers
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 500ミリ秒ごとにログ出力.
        val intervalObservable = Observable.interval(500, TimeUnit.MILLISECONDS, Schedulers.io())
                .subscribe { Log.d("RxTest", "経過:" + it) }

        // 画面遷移などのタイミングで接続を解除する.
        // intervalObservable.unsubscribe()
    }
}

RxJavaの方ではIDisposableが継承されていないようで、DisposeはできずUnsubscribeするのみとなっています。
処理の書き方自体はかなり近い感じですね。

一定時間後に処理を行う

一定時間が経過したら処理を行います。

UniRx

private void Start () {
    var timerObservable = Observable.Timer(System.TimeSpan.FromSeconds(3d))
            .Subscribe(time => Debug.Log("3秒経過しました"));
            
    //timerObservable.Dispose();
}

RxJava

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        val timerObservable = Observable.timer(3, TimeUnit.SECONDS)
                .subscribe { Log.d("RxTest", "3秒経過しました") }
        // timerObservable.unsubscribe()
}

おわりに

細かい違いはともかく、同じものを元にしているだけに大部分の書き方や考え方は共通化されていて助かります。

とりあえずしばらくはこんな感じでバラバラと挙げていって、量が溜まってきたら別途整理、といったところでしょうか。

参考

RxJava

UniRx

RxAndroid

RxBinding