あれ、どっちがどっちだっけ?と迷った時に、よくこのブログを思い出して検索する
不意に読もうという気持ちになってさくっと購入して読んでみた。
さくっと読めるのでオススメ。この中に戦略的設計と戦術的設計が出てきたのでブログを検索して読み直してみたのだった。 そんな今日この頃。
今まではVysorを使っていた
Androidの実機をPCから操作したい時に主に使っていた。 ディスプレイにうつして他の人にデモしたり、テキストを入力する時に便利。
しかしながら、無料版だといくつかの欠点があった
特に音が出るアプリを開発している時は、スマホのボリュームをある程度上げている。その状態で音声つきの広告が再生されるとびっくりする事があった。 いろいろと考えてみると、OpenSTFを普通にMacにインストールして、自分専用にして使えばいいんじゃないか。。?と思ったので試してみた。
導入はMacであれば、基本的に
これを見ながらやればOK
よかったこと
わるかったこと
こんな感じ。わるかったことに関しては今後、対応出来るものであれば対応していきたいけど、ざっと調べた時は出来なさそうな感触ではあった。。。 そんな今日この頃。
subject.subscribe ({ number -> println(number.toString()) if (number == 3) throw RuntimeException() }, { println("onError") }, { println("onComplete") }) subject.onNext(1) subject.onNext(2) subject.onNext(3) subject.onNext(4) subject.onNext(5)
1 2 3 onError
4と5が表示されない
onErrorに入った時にちゃんと気がつけないと、何故かいきなり動かなくなってる。。。! という辛みがあるので注意。
http://mapyo.hatenablog.com/entry/2017/07/23/032630
この辺で日々のビルドをするためにfastlaneを使ったので、次はGoogle Playにアップするところで使ってみる。
いきなり本番は流石に辛いのでベータ版にアップする部分を自動化して、手元で確認してから、Developer Console上で操作して本番に公開という流れをとりたい。
fastlane的なbetaとFabricのBetaとデベロッパーコンソールのBetaといろいろ混同しそうなので注意。
基本的な導入周りはこの辺でいい感じに出来てる
http://qiita.com/gupuru/items/9ddeff0906b02d779ed9
公式ドキュメントも参照
https://docs.fastlane.tools/getting-started/android/setup/
https://docs.fastlane.tools/getting-started/android/setup/#collect-your-google-credentials
この辺を見ればOK。内容的には同じだけど、僕のメモを書いとく
という感じでもろもろ設定が終わり。権限を絞っているので、何か起きたとしてもアルファ版かベータ版がどうにかなるだけなので、ある意味安心出来る(笑)
ダウンロードしたjsonを好きな場所に置いて、
fastlane/Appfile
ここに追記すればOK
https://docs.fastlane.tools/getting-started/android/beta-deployment/#supported-beta-testing-services
この辺を見て設定する
今回追加したlaneはこちら
lane :googleplay_beta do gradle(task: "clean assembleRelease") supply(track: 'beta') end
ハマったところは、ここの設定じゃないんだけど、サンプル用にGoogle Play上にアプリを作ったんだけど、公開するために必要なものを一通り設定して、1回でも公開(ベータ版でも可)しないとapiでアップする事が出来なかった。。。
BITRISEを使っていると、 deploy-to-bitrise-ioというstepをとりあえず実行しとけば apkとかも含めてそのビルドに保存してくれるので楽。
これでgoogle playのベータにアップするところが自動化出来て最高。という感じ
現在開発中のdevelopブランチの最新を日々自動的に配布したい。 日々配布してQA担当の方にテストしていただいたり、今のアプリの動きどうなってるんだろ?と思った時にさくっと確認出来て便利。
developブランチにMergeしたタイミングでapk作るのはちょっと細かすぎるかな?という気持ちがあって、1日に1回。 でも、何もdevelopにMergeされてない時にもapkが作られるのでそれはそれで微妙かもしれない。
ざっくり書くと以下のような感じ。
bitriseを使う。workflowを簡単に設定できて非常に便利。しかしお金はかかりますよと。 毎日指定した時間に発動させる。
日々ビルドしてアップするので、一体どのビルドなんだろう。。。?? というのをわかるようにしないと混乱してしまう。
versionCodeとversionNameのどちらをカウントアップするのがいいのだろうか?
versionCode 1 versionName "1.0.0-alpha.1” // こちらをカウントアップするのがいいと考えた
軽く考えたところで、versionNameの方を変更する事にした。 気分的な問題だけど、versionCodeを日々カウントアップすると実際にアプリをリリースする時に無駄に増えた状態でリリースされてしまいそうと考えたからだ。
日々のビルドは、versionNameの最後に-alpha.1
とつけて配布する事にしてみる。
やろうと思えば、いろいろと方法があると思う。
gradleでやるには
https://stackoverflow.com/a/21951328/2172277
この辺を参考にすれば書けるかもしれない。
また、ちょっと話はそれるのだが、将来的にGoogle Playへのデプロイも自動化したいので、その辺も考慮に入れると、
Gradleのgradle-play-publisher vs fastlane
という感じになってくる
どっちがいいんだろうか。。。?と調べてみたところ、1年以上前だけど、いい比較記事を見つけた!
http://qiita.com/shiraji/items/518cb1a20671a6388dd7
fastlaneの方が確かに開発が活発そうな感じなのと、
https://docs.fastlane.tools/getting-started/android/setup/
この辺のドキュメントみたりして比較記事から1年以上たって、いろいろと機能が追加されてそうなので、fastlaneを使ってみる。GroovyよりもRubyの方が僕にとってはコードが読めそう。
fastlaneの準備とかもろもろはちょっとめんどくさくなったので省略する。 versionNameを更新するactionを以下のような感じで作ってみた。
https://gist.github.com/mapyo/e0387f94bf94f6bcb119be7b40ea23ac
fastlane使ったの初めてだし、action作ったのも初めてなのでそんな感じ。 後は、versionNameなどはこの表示形式にする。という決め打ち前提なので、あまり使い勝手は良くないかもしれない。
fastlaneのlaneの中で以下のような感じでやる
gradle(task: "clean assembleDebug crashlyticsUploadDistributionDebug")
crashlytics用のactionも用意されているっぽい感じだったけど、既にgradleで配布出来てたし、別に使わなくてもええやん。という気持ちで雑にgradleを使うようにした。
fastlane、今まで全然使った事なかったけど、Android開発者にとっても、便利そうだ!! そして、bitriseが結構便利です!!
今度はアプリをgoogle playに配布するとこら辺を自動化してみたい気持ち。
こんな感じのseald classを作る
sealed class SampleEvent(open val name: String) class SampleEventA( override val name: String ): SampleEvent(name)
こんな感じでJson作る
Gson().toJson(SampleEventA("aaaaa"))
気持ちとしては以下のようなjsonが出て欲しい
{"name":"aaaa"}
でもエラーが出る
SampleEventA declares multiple JSON fields named name
ほほう
Tools > Kotlin > Show Kotlin Bytecode
でBytecodeからのJavaにDecompileしてどんな感じか見る。
public final class SampleEventA extends SampleEvent { @NotNull private final String name; @NotNull public String getName() { return this.name; } public SampleEventA(@NotNull String name) { Intrinsics.checkParameterIsNotNull(name, "name"); super(name, (DefaultConstructorMarker)null); this.name = name; } } public abstract class SampleEvent { @NotNull private final String name; @NotNull public String getName() { return this.name; } private SampleEvent(String name) { this.name = name; } // $FF: synthetic method public SampleEvent(@NotNull String name, DefaultConstructorMarker $constructor_marker) { this(name); } }
abstract classと継承したclassの両方でnameが作られていてそれが原因でエラーが出てるようだ。
javaでabstractを使った時にちゃんと動かすならこんな感じで書けばよさそうだった
abstract class JavaSampleEvent { protected final String name; public JavaSampleEvent(String name) { this.name = name; } } class JavaSampleA extends JavaSampleEvent { public JavaSampleA(String name) { super(name); } }
String json = new Gson().toJson(new JavaSampleA("aaaa"));
結局、Seald Classを使わずにInterfaceを使って対応したのだけど、こういう場合はどうやってやるのがよかったのだろうか。。。??
Twitterでいい感じに出来る方法を教えて頂きました!!
sealed class SampleEvent { abstract val name: String } class SampleEventA( override val name: String ) : SampleEvent()
abstractでプロパティを宣言すれば大丈夫だった。 sealed class使う時にいつもopen使ってやってたけど、こっちの方がよさそうだなー
Observableな川の流れの中で、Completableを使った時に、どうやってまたObservableとして流せばいいのか?と考えてた。
andThenというオペレータを使えばよさそう。
具体的なコードはこんな感じ
Observable.just(1, 2, 3) .flatMap { number -> Completable.fromAction { showMessage("completableTest: " + number) Thread.sleep(300) }.andThen(Observable.just(number)) } .doOnNext { showMessage("doOnNext") } .doOnComplete { showMessage("doOnComplete") } .subscribe({ showMessage("onNext: " + it.toString()) }, { it.printStackTrace() showMessage("hoge") }, { showMessage("onComplete") })
実行結果はこんな感じ
main:completableTest: 1 main:doOnNext main:onNext: 1 main:completableTest: 2 main:doOnNext main:onNext: 2 main:completableTest: 3 main:doOnNext main:onNext: 3 main:doOnComplete main:onComplete
※mainはスレッドの名前を出しているだけ
どう書いたらいいかわからなかったので、いろいろと試行錯誤してた
.toObservable<Int>()
でObservableに変換するだけのコード
Observable.just(1, 2, 3) .flatMap { number -> Completable.fromAction { showMessage("completableTest: " + number) Thread.sleep(300) }.toObservable<Int>() // ここが変わっただけ } .doOnNext { showMessage("doOnNext") } .doOnComplete { showMessage("doOnComplete") } .subscribe({ showMessage("onNext: " + it.toString()) }, { it.printStackTrace() showMessage("hoge") }, { showMessage("onComplete") })
実行結果
main:completableTest: 1 main:completableTest: 2 main:completableTest: 3 main:doOnComplete main:onComplete
Completableの中身はちゃんと実行されているけど、onNextには何も値が流れてこない。
toObservableからの、startWith
Observable.just(1, 2, 3) .flatMap { number -> Completable.fromAction { showMessage("completableTest: " + number) Thread.sleep(300) }.toObservable<Int>() .startWith(number) } .doOnNext { showMessage("doOnNext") } .doOnComplete { showMessage("doOnComplete") } .subscribe({ showMessage("onNext: " + it.toString()) }, { it.printStackTrace() showMessage("hoge") }, { showMessage("onComplete") })
実行結果
main:doOnNext main:onNext: 1 main:completableTest: 1 main:doOnNext main:onNext: 2 main:completableTest: 2 main:doOnNext main:onNext: 3 main:completableTest: 3 main:doOnComplete main:onComplete
一見、onNextも呼ばれてそうだし、大丈夫そう!と思うのだが、順番がおかしい。 川の流れ的に、completableTestが呼ばれてからonNextが呼ばれてほしいが、逆になってしまっている。
一度singleに変換してobservableに変換したら上手く言った。 上手く言ったのでCompletableのところだけ貼る。
Completable.fromAction { showMessage("completableTest: " + number) Thread.sleep(300) } .toSingleDefault(number) .toObservable()
川を使いこなすにはまだまだ修行が必要だー
雑なサンプル置き場を作ってる https://github.com/mapyo/RxJavaSamples/blob/master/app/src/test/java/com/mapyo/rxjavasamples/RxExampleUnitTest.kt