temporaryなめも帳

だらだらと備忘録とか。誰かの為になることをねがって。

RxJavaでエラーが発生したときに◯回リトライする

ただのメモ書き。

やりたいこと

エラーが発生したときに◯回リトライして、エラー終了 or 正常終了する 全てはこのあたりにまとまっている。ただの蛇足

qiita.com

qiita.com

試したこと

目的は、Httpリクエストにつけてるtokenの期限切れ時にリトライするようなパターンを想定している。 書いてみたら、結局は上記の記事の内容で十分網羅出来ている

正常終了のパターン

@RunWith(JUnit4::class)
class MyTest {

    fun someFunction() {
        System.out.println("some")
    }

    @Test
    fun hoge() {
        val exception = mock(HttpException::class.java).apply {
            `when`(code()).thenReturn(HttpURLConnection.HTTP_UNAUTHORIZED)
        }

        Observable.create<String> {
            it.onNext("1")
            it.onNext("2")
            it.onError(exception)
        }.retryWhen { upstream ->
            upstream.take(3).doOnNext {
                if (it is HttpException) {
                    when (it.code()) {
                        HttpURLConnection.HTTP_UNAUTHORIZED -> someFunction()
                    }
                }
            }
        }.subscribe({
            System.out.println(it)
        }, {
            System.out.println("error")
        }, {
            System.out.println("completed")
        })
    }
}

結果

1
2
some
1
2
some
1
2
some
completed

エラー終了のパターン。こちらはcomposeを利用するようにしてみてる

@RunWith(JUnit4::class)
class MyTest {

    fun someFunction() {
        System.out.println("some")
    }

    @Test
    fun fuga() {
        val exception = mock(HttpException::class.java).apply {
            `when`(code()).thenReturn(HttpURLConnection.HTTP_UNAUTHORIZED)
        }

        Single.create<String> { emitter ->
            System.out.println("st")
            emitter.onError(exception)
        }
                .compose(TokenRefreshTransformer<String>())
                .subscribe({
                    System.out.println(it)
                }, {
                    System.out.println("error occurred")
                })
    }
}
class TokenRefreshTransformer<T> : SingleTransformer<T, T> {
    override fun apply(upstream: Single<T>): SingleSource<T> =
            upstream.retry { count, it -> refresh(count, it) }

    private fun refresh(count: Int, throwable: Throwable): Boolean {
        if (count < 3) {
            if (throwable is HttpException) {
                when (throwable.code()) {
                    HttpURLConnection.HTTP_UNAUTHORIZED -> someFunction()
                }
            }
            return true
        }
        return false
    }

    private fun someFunction() {
        System.out.println("super!")
    }
}

結果

st
super!
st
super!
st
error occurred

The experts' guide to Android development toolsで紹介されてたASショートカット(気になったのメモ

Motivation

Google I/O 2016のセッションを少しずつ消化しているのだけれど、「The experts' guide to Android development tools」のセッションで紹介されていたAndroidStudioのショートカットが有用っぽいので備忘録がてらまとめておく。
とりあえず「ctrl + shift + a」と「local history」は必修ぽい。

www.youtube.com

  • Motivation
    • 「Join Line」 Ctrl + Shift + J
    • 「Show Intention Actions」 option + enter
    • サジェスト時にEnter使うかTab使うか
    • 「Select Next Occurrence」ctrl + G / ctrl + shift + 右クリック
    • 「Find Action」cmd + shift + A
    • 「File Structure」cmd + F12
    • 「Insert Live Template」 cmd + j
    • 「Refactor > Extract > Method...」option + cmd + M
    • 「Local History」
続きを読む

Android Performance Patterns その2

その2

例のごとく、英語の勉強もかねてるので合ってるかは知らない。

今日のお題は Rendering Performance 101 派生してる次のも一緒に見ておいた。

Why 60fps

アプリケーションは60Hzで画面更新してる。つまりは、16ms毎にGPUの更新処理を終えないとフレーム落ちしちゃうことになる。

なぜ60fpsなのかというと、人間の目の性能的にマッチしている値だからだそう。 24fpsだと荒く見えちゃうし、60超えてても大きな差はあまり認識できないらしい。

Understanding VSYNC

16ms毎にViewの更新処理をなぜ行わなければいけないかというと、VSYNCの問題があるから。ラグになるよって話かな。
Refresh-Rate(画面の更新頻度)とFrame-Rate(GPUの絵の作成時間)の話を冒頭に、まだ出来上がってない絵を描いちゃうテアリングの話と、出来上がってない時に前の絵を描くVSYNCの話がまとまってた。

Android, UI and the GPU

layout.xmlに書いたUIをどうやって描画しているのかって話。
そのコアになる処理がrasterizationでこの処理はGPUでサポートされて高速化されてる。 OpenGLESをつかって描画するときは、CPUでポリゴンやテクスチャに変換して、GPUに転送しないといけない。OpenGLES APIにはGPU Bufferにテクスチャなどを置いておけるAPIがある。これを利用して都度の転送を減らすことが高速描画につながる。
アプリケーションのthemeのリソースは一つのテクスチャにまとめられてGPUにアップロードされている。これらを使う分には高速に描ける。 画像を描画するときはCPUでイメージをロードして(画像、Path、文字、アニメーションに言及)〜といった具合にCPUとGPUの処理内容をイメージできるといいよって話なのかな。

Understanding Overdraw

何回も同じpixelにdrawするのはもったいないよって話。
Overdrawを確認する時は、DeveloperOptionのShow GPU OverdrawをONにする。 するとOverdrawの回数によって、pixelに色がつくようになる。

  • overdraw無し: true color(色がつかない)
  • 1x: 青
  • 2x: 緑
  • 3x: ピンク
  • 4x: 赤

これを確認して、不必要なoverdrawは無くしていくべき。

Views, Invalidation and Perfowmance

Viewのプロパティを変更したときにAndroidの中で何が起きているのかって話。
まずは、Android UI and the GPUのおさらい。 GPUで描画するViewは全てDisplay Listで管理されていて、描画するときにOpenGLで描画される。 初めてViewが描画されるときにDisplay Listは作成され、それを実行する形でGPUに描画命令を出す。
Viewの位置変更のような変更はもう一度Display Listの実行命令を出すだけですむが、Viewの表示内容に関わる変更の場合はそれではすまない。 もう一度Display Listの作成を行った上で描画命令を出す必要が出てくる。
Viewの表示内容に関わる変更は描画パイプラインへの影響が大きい。 他の例を挙げると、TextViewの大きさを二倍に変更したときに、parentのコンテナの位置調整が起こり、関連するViewの描画位置の変更が発生することがある。 Viewの大きさを変えると、大きさの計算処理(Measure処理)がView Hierarchyに対して走り、それぞれのView毎に新たなサイズを計算する。さらに位置が変更されたときは、位置の計算処理(Layout処理)が走ることになる。 その後にDisplay Listを再構築して、描画命令が出る形になる。
大きさ変えたり、位置変えたりはコストでかいから初期位置には注意を払ってねってお話。
Viewのinvalidate処理のデバッグをするなら GPU View Updates が役に立つ。
LayoutやMeasure処理に時間がかかるようなら、Hierarchy Viewerを使ってViewの階層構造を確認してみるといいよ。

思ったより長くなったけど描画周りの勉強になった気がする。

Android Perfomance Pattern その1

Android Performance PatternってYoutubeのチャンネルが2015年から始まってたみたい。 google for Mobileで知ったのでぼちぼちと見始めてみる。 英語聞く勉強がてらなので、合ってるかどうかはわかんない。

Android Performance Pattern

#Cachematters for networking (Android Performance Patterns Season 4 ep1)

#Cachematters for networking (Android Performance Patterns Season 4 ep1)

AndroidアプリはデフォルトでHTTPのキャッシュ機能がOFFになっている。 cacheをONにする時は、HttpResponseCacheクラスを利用する。

protected void onCreate(Bundle savedInstanceState) {
    ...
    try {
        File httpCacheDir = new File(context.getCacheDir(), "http");
        long httpCacheSize = 10 * 1024 * 1024;
        HttpResponseCache.install(httpCacheDir, httpCacheSize);
    catch(IOException e){

    }
}

protected void onStop() {
    ...
    HttpResponseCache cache = HttpResponseCache.getInstalled();
    if(cache != null){
        cache.flush();
    }
}

これで全てのHTTPリクエストがキャッシュされることになる。 全部が全部キャッシュするのはばからしいので、2つ組み込むと良い手法があるよ。

  • 独自のCache Managerつくる
  • Chacheのロジックをカスタマイズする

サンプルあるから見るといいよ

そのほかに、Networkのライブラリ使うのもよいよ

ネットワークのリソースについてデバッグするなら、Network Traffic Toolがあるよ。

デバイスの送信情報とかを深くおいたいならARO Toolがあるよ。

AndroidのToolbarのTitleをXMLから指定したい

別にJavaからでもいいんだけど、ソース読んだらこうだったのでできるはずだよね?と試行錯誤。

結果として、以下でいけるぽい。 Themeもちょっとちゃんと理解してたらそんなにハマらなかったんじゃないかなと。

layout.xml

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:theme="@style/AppTheme.MyToolbar"
        android:background="?attr/colorPrimary"/>

style.xml

    <style name="AppTheme.MyToolbar" parent="ThemeOverlay.AppCompat.ActionBar">
        <item name="title">@string/app_name</item>
        <item name="subtitle">@string/hoge</item>
        <item name="android:textColorPrimary">@color/white</item>
        <item name="android:textColorSecondary">@color/white</item>
    </style>

別にParentのThemeは何でもよいと思う。サンプルだから適当です。 他にもいろいろ要素あるので、この辺みていじれば良いんじゃないかな。

android-DeviceOwner(GoogleのSample)を動かしてみる

これで正しいのか、まだよく分かってませんが動かすことは出来たのでおいておきます。 このやり方だと、デバッグが超大変なんだけど、、 もう少しコード読む必要がありそう。

アプリケーションを落としてきてビルドする

必要だったのは、以下の二つのアプリケーション。

https://github.com/googlesamples/android-DeviceOwner https://github.com/googlesamples/android-NfcProvisioning

落としてきて、AndroidStudioに食わせてビルドするだけ。 ただし、DeviceOwnerの方はAPKのHashとか作っておく必要があるので、Signed-APKを作成してHashを以下コマンドで作成した。

cat Application-release.apk | openssl dgst -binary -sha1 | openssl base64 | tr &#039;+/&#039; &#039;-_&#039; | tr -d &#039;=&#039; > app_hash.txt

参考Link

DeviceOwnerなアプリケーションを入れ込む準備

DeviceOwnerなアプリケーションは単純にインストールするだけでは、動作しません。 こんなUIが出るだけ。

Screenshot_2015-06-07-21-09-11

FactoryReset直後にNFC(AndroidBeam)でパッケージ名、apkファイルのDL先、apkファイルのHashを送りつけるといいらしい。 この辺はAndroid5.0の時の記事が参考になりました。

Android 5.0 の Device Owner を有効にする方法

ただ、Googleさんのサンプルにはこの「DL先」と「Hash」を入れているところがなさそう。 これを追記してやる。

--- a/Application/src/main/java/com/example/android/nfcprovisioning/NfcProvisioningFragment.java
+++ b/Application/src/main/java/com/example/android/nfcprovisioning/NfcProvisioningFragment.java
@@ -129,6 +129,12 @@ public class NfcProvisioningFragment extends Fragment implements
             properties.put(DevicePolicyManager.EXTRA_PROVISIONING_LOCAL_TIME,
                     String.valueOf(System.currentTimeMillis()));
         }
+
+        // I just use sample DeviceOwner.apk from google.
+        properties.setProperty(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME, "com.example.android.deviceowner");
+        properties.setProperty(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION, "http://192.168.65.2:8080/Application-release.apk");
+        properties.setProperty(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM, "LMhnJMzo4gstAYyKPrSshH4tPSc");
+
         try {
             properties.store(stream, getString(R.string.nfc_comment));
             NdefRecord record = NdefRecord.createMime(

Hashは先ほどのコマンドで生成したものを利用する。 DOWNLOAD_LOCATIONの部分はPythonのSimpleHTTPServerで適当に建てておく。IPは宅内のLANのもの。

DeviceOwnerアプリのapkファイルのある箇所で、以下コマンドを叩くとサーバーが起きる。PCのウィルス対策ソフトがポート開けるのに邪魔したりするので、お気をつけを。。(しっかりハマった。)

$ ls
Application-release.apk
$ python -m SimpleHTTPServer 8080

これで準備完了。

インストールの実施

インストール先の端末はFactoryReset直後の状態(GoogleのSetupアプリ起動中)で行った。 GoogleのSetupアプリを抜けた後だと、「Oops!」になってしまう。たぶん、このアプリでの設定が終わった後に、どこぞのFlagが書き換えられているのだと思う。 この辺見てるとそんな感じ。

https://android.googlesource.com/platform/packages/apps/ManagedProvisioning/+/android-m-preview/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java#115

手順をまとめると以下。 1. インストール対象のデバイス(A)をFactoryResetする 2. もう一つのデバイス(B)にandroid-NfcProvisioningアプリをインストールする(上記の修正は盛り込んでおく。) 3. android-DeviceOwnerアプリのapkを任意の場所において、そこでサーバーを起動しておく 4. FactoryReset直後の画面でデバイスB->デバイスAにAndroidBeamを行う。この時、デバイスBにはandroid-NfcProvisioningを起動しておく。 5. 上手くいっていれば、デバイスAの暗号化を行うように画面が進むので、その通りにすすめる。 6. 再起動して抜けてくれば、インストール済みの状態になっている。

これで、DeviceAdminなアプリケーションとしてインストールが出来ている。 通常のものと違い、ユーザーがOFFに出来ないのがポイントなんでしょうね。 Screenshot_20150607-214044

結局DevicePolicyManager経由でコントロールするのなら、開発中は前に調べたやり方でやるのがよいのかなー。