20190215のAndroidに関する記事は4件です。

Nexus7(2013)に TWRP で Android 9.0.0(Pie) をインストール後、Open GApps の error70 を回避する方法

原因:インストール作業領域のシステムパーティションが容量フルで空きが無いため(英文詳細)
対策:初回にパーティションリサイズツール sysrepart_1280_120.zip で変更する。

インストールの順番(参考)

adb push sysrepart_1280_120.zip /sdcard/
adb push AospExtended-v6.2-deb-20190208-1518-UNOFFICIAL.zip /sdcard/
adb push open_gapps-arm-9.0-nano-20190209.zip /sdcard/

上記作業は、Nexus7(2013年モデル)にAndroid 9.0.0(Pie)をインストールしようのサイトが参考になる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

buildSrcでDependencyをIDE補完で記述する

はじめに

AndroidアプリのDependencyを下記のように記述していないでしょうか?

build.gradle
...

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

このように記述すると、バージョン変更時に一つずつ編集しなければならなかったり、そもそも可読性が低いように感じます。
そこで、buildSrcでこれらを保持する方法が良さそうだったのでメモしておきます。

参考:
Kotlin + buildSrc for Better Gradle Dependency Management

やり方

1. buildSrcフォルダを作成

モジュールと同じようにプロジェクトルートにbuildSrcという名前のフォルダを作成します。

2. build.gradle.ktsを作成

作成したbuildSrcフォルダにbuild.gradle.ktsという名前のkotlin-scriptファイルを作成します。
この段階ではまだ何も記述しなくていいです。

3. Sync Gradle !

sync gradleしましょう。buildSrc内に以下のようなファイルが生成されれば成功です。

4. dslを記述

build.gradle.ktsに以下を記述してsync gradleしましょう。

build.gradle.kts
plugins {
    `kotlin-dsl`
}

5. Dependencyを保持するオブジェクトクラスを作成

buildSrc内にsrc/main/java/Dependencies.ktを作成します。

一旦今のDependencyを全て保持させておきたいので、下記のようにします。

Dependencies.kt
object Dependencies {
    object Version {
        val kotlin = "1.2.71"
        val support = "28.0.0"
        val constraint = "1.1.3"
        val runner = "1.0.2"
        val espresso = "3.0.2"
    }

    val kotlinLib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Version.kotlin}"
    val appCompat = "com.android.support:appcompat-v7:${Version.support}"
    val constraintLayout = "com.android.support.constraint:constraint-layout:${Version.constraint}"
    val junit = "junit:junit:4.12"
    val testRunner = "com.android.support.test:runner:${Version.runner}"
    val espressoCore = "com.android.support.test.espresso:espresso-core:${Version.espresso}"
}

6. 実際に使う

これで使えるようになりました! 実際にbuild.gradleに適用しましょう!

build.gradle
...

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation Dep.kotlinLib
    implementation Dep.appCompat
    implementation Dep.constraintLayout
    testImplementation Dep.junit
    androidTestImplementation Dep.testRunner
    androidTestImplementation Dep.espressoCore
}

以前と比べて何を読み込んでいるか分かりやすくなり、IDE補完が効くので編集もしやすくなったかと思います。

サンプル

今回のプロジェクトをGitHubに上げたので、よければ参考にしてください。
https://github.com/TaigaNatto/BuildSorceSample

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android】dependenciesをIDE補完で記述する

はじめに

Androidアプリのdependenciesを下記のように記述していないでしょうか?

build.gradle
...

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

このように記述すると、バージョン変更時に一つずつ編集しなければならなかったり、そもそも可読性が低いように感じます。
そこで、buildSrcでこれらを保持する方法が良さそうだったのでメモしておきます。

参考:
Kotlin + buildSrc for Better Gradle Dependency Management

やり方

1. buildSrcフォルダを作成

モジュールと同じようにプロジェクトルートにbuildSrcという名前のフォルダを作成します。

2. build.gradle.ktsを作成

作成したbuildSrcフォルダにbuild.gradle.ktsという名前のkotlin-scriptファイルを作成します。
この段階ではまだ何も記述しなくていいです。

3. Sync Gradle !

sync gradleしましょう。buildSrc内に以下のようなファイルが生成されれば成功です。

4. dslを記述

build.gradle.ktsに以下を記述してsync gradleしましょう。

build.gradle.kts
plugins {
    `kotlin-dsl`
}

5. Dependencyを保持するオブジェクトクラスを作成

buildSrc内にsrc/main/java/Dependencies.ktを作成します。

一旦今のDependencyを全て保持させておきたいので、下記のようにします。

Dependencies.kt
object Dependencies {
    object Version {
        val kotlin = "1.2.71"
        val support = "28.0.0"
        val constraint = "1.1.3"
        val runner = "1.0.2"
        val espresso = "3.0.2"
    }

    val kotlinLib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Version.kotlin}"
    val appCompat = "com.android.support:appcompat-v7:${Version.support}"
    val constraintLayout = "com.android.support.constraint:constraint-layout:${Version.constraint}"
    val junit = "junit:junit:4.12"
    val testRunner = "com.android.support.test:runner:${Version.runner}"
    val espressoCore = "com.android.support.test.espresso:espresso-core:${Version.espresso}"
}

6. 実際に使う

これで使えるようになりました! 実際にbuild.gradleに適用しましょう!

build.gradle
...

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation Dependencies.kotlinLib
    implementation Dependencies.appCompat
    implementation Dependencies.constraintLayout
    testImplementation Dependencies.junit
    androidTestImplementation Dependencies.testRunner
    androidTestImplementation Dependencies.espressoCore
}

以前と比べて何を読み込んでいるか分かりやすくなり、IDE補完が効くので編集もしやすくなったかと思います。

サンプル

今回のプロジェクトをGitHubに上げたので、よければ参考にしてください。
https://github.com/TaigaNatto/BuildSorceSample

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Kotlin Coroutineの悪い例と解決策

昨日、Coroutineを理解するために以下の記事を書いた

Kotlin Coroutinesパターン&アンチパターン - Qiita
https://qiita.com/ikemura23/items/fb8caeba4c35fcd85644

ただ書いただけではなく、本当にそうなのか実装して確かめてみた。

例外処理をtry/catchに頼らない

エラーが発生するdoWork()をtry / catchしてもダメ

class MainActivity : AppCompatActivity() {

    private val job = Job()
    private val scope = CoroutineScope(Dispatchers.Default + job)

    override fun onResume() {
        super.onResume()

        // Throw Exceptionの可能性がある処理
        fun doWork(): Deferred<String> = scope.async { throw UnknownError() }

        scope.launch {
            try {
                doWork().await() // ここで落ちる
            } catch (e: Exception) {
                Log.d(TAG, "ここに到達しない")
            }
        }
    }

結果

実行すると、trt / catchの意味なくエラーでアプリが落ちる

E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
    Process: com.ikemura.android_kotlin_lab, PID: 9287
    java.util.concurrent.TimeoutException
        ...

解決策

SupervisorJobで子のエラーを回避する。

class MainActivity : AppCompatActivity() {

    private val job = SupervisorJob() // <= SupervisorJobに変更
    private val scope = CoroutineScope(Dispatchers.Default + job)

    override fun onResume() {
        super.onResume()

        // Throw Exceptionの可能性がある処理
        fun doWork(): Deferred<String> = scope.async { throw UnknownError() }

        scope.launch {
            try {
                doWork().await() // ここで落ちる
            } catch (e: Exception) {
                Log.d(TAG, "キャッチされる")
            }
        }
    }

注意すべきasync

↑で示した解決は、SupervisorJobを使用してCoroutineScopeで非同期を明示的に実行した場合にのみ機能する。

以下の async はSupervisorJobを使用してもエラーとなる。

    private val job = SupervisorJob()
    private val scope = CoroutineScope(Dispatchers.Default + job)

    // エラーの可能性がある処理
    fun doWork(): Deferred<String> = throw TimeoutException()

    fun loadData() = scope.launch {
        try {
            async {
                doWork() // ここで落ちる
            }
        } catch (e: Exception) {
            Log.d(TAG, "キャッチされない")
        }
    }

理由は、async内で別のcoroutineが作成され、その範囲内で起動されるから。(ちょっと曖昧)

解決策

coroutineScopeを使ってasyncをラップする。

    // エラーの可能性がある処理
    fun doWork(): Deferred<String> = throw TimeoutException()

    fun loadData() {

        scope.launch {
            try {
                coroutineScope { // 追加
                    async {
                        doWork()
                    }
                }
                Log.d(TAG, "ここは実行されない")
            } catch (e: Exception) {
                Log.d(TAG, "キャッチされる")
            }
        }
    }

上記コードは分かりにくいので、こう見やすくできる。

    // エラーの可能性がある処理
    suspend fun doWork(): Deferred<String> = coroutineScope {
        async { throw TimeoutException() }
    }

    fun loadData() {
        scope.launch {
            try {
                doWork() // coroutineScopeとasyncをdoWorkに移動させた
                Log.d(TAG, "ここは実行されない")
            } catch (e: Exception) {
                Log.d(TAG, "キャッチされる")
            }
        }
    }

try/catchばかり使わない

SupervisorJobを作り、try/catchでラップしておけば大概のエラーはキャッチできるはず。
だが毎回try/catchを使うのではなく、コルーチンの例外をキャッチしてくれるCoroutineExceptionHandlerが用意されているので、こちらを使う。

CoroutineExceptionHandlerを個別に指定する例

    // エラーハンドリングを作成
    private val exceptionHandler: CoroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
        Log.e(TAG, "例外キャッチ $throwable") // ログ出力のみ
    }
    private val job = SupervisorJob()
    private val scope = CoroutineScope(Dispatchers.Default + job)

    // エラーの可能性がある処理
    fun doWork(): Deferred<String> = throw TimeoutException()

    fun loadData() {
        scope.launch(exceptionHandler) { // エラーハンドリングを指定
            doWork()
        }
    }

ログ

E/MainActivity: 例外キャッチ java.util.concurrent.TimeoutException

try/catchがなくなり、スッキリした。

CoroutineExceptionHandlerを共通で使い回す

CoroutineExceptionHandlerを個別に指定するのもいいが、CoroutineScopeに渡せば共通で使い回せる。

private val exceptionHandler: CoroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
        Log.e(TAG, "例外キャッチ $throwable")
    }
    private val job = SupervisorJob()
    private val scope = CoroutineScope(Dispatchers.Default + job + exceptionHandler) // exceptionHandlerを渡す

    // エラーの可能性がある処理
    fun doWork(): Deferred<String> = throw TimeoutException()

    fun loadData() {
        scope.launch { // exceptionHandler指定が無くなった
            doWork()
            Log.d(TAG, "到達しない")
        }
    }

普段は共通でしておき、必要な時だけ個別に例外を指定するのがベストだろう。

実装したコード

https://github.com/banbara23/Android-Kotlin-Lab/tree/coroutine-sample

参考リンク

https://proandroiddev.com/kotlin-coroutines-patterns-anti-patterns-f9d12984c68e

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む