shion のアバター画像もくもくログページ一覧ブログ一覧技術メモ一覧ライセンス表記利用規約外部リンクconnpassGitHubSpeaker DeckTwitter[工事中]Shion のもくログバージョン1.2.5

もくもくログ

主にIT 関連のメモをもくもくと綴っていきます

最終更新日:2020/01/02 06:23:322019/12/28 ~ 2020/01/03 のもくもく日記進捗管理

もくもく 前回までは

(3行で)まとめると……

  • REPLInDocker シリーズを整備した
  • いろいろな調査を進めた
    • Gradle Bill of Materials
    • Hyperion Android で独自プラグイン作り方法を試した
    • Kotlin Coroutines
  • とりこぼした……
    • マンデルブロ集合をJavaScript, TypeScript で実装する
    • モバイルネイティブアプリ用画像の変換ツールの開発

詳細は2019/12/21 ~ 12/27 のもくもく日記 はご覧ください。

今回の目標

  • Android ライブラリの公開方法を試してみる
  • Kotlin Coroutines のサンプルを実装する
  • REPLInDocker シリーズの整備
  • 本の買い出し
  • 臨時でがやがや会を開催する

途中経過

その1

フェアリーペンギン、ヒナかと思ったら、解説 によると成体らしい。

フェアリーペンギン

個人的にペンギンといえばこのあたりのイメージが強かったので、意外だった。

個人的なペンギンのイメージその1
個人的なペンギンのイメージその2

その2

2019年に取り組んだAndroid の開発戦術はこんな感じだった。

  • OpenAPI(Swagger) を書いてサーバー側実装を並走できるように努力した
  • PlantUML を書いて機能認識を揃えるように努力した
  • 実装は分解に注力したので、どう短時間で作るかは今後に課題

戦術は引き続き整備するとして、今後は戦略レベルのものも混ぜていきたいところ。

・新卒、中途、派遣などの人員関連
・育成関連
・営業関連

頭痛い……。

その3

OpenAPI 3.0.0 で記述したものを、Swagger Editor でNode のサーバーコード生成したんだけど、なぜか起動しなかったorz
OpenAPI 3.0.1 を指定すると動いたので、何かあるのかもしれない。

あと生成コードで、swagger .params['body'] .value がエラーになって落ちるんだけど、どうすれば良いのだろう?

module.exports.v1TodoIdPUT = function v1TodoIdPUT (req, res, next) {
  var body = req.swagger.params['body'].value;
  var id = req.swagger.params['id'].value;
  Default.v1TodoIdPUT()
    .then(function (response) {
      utils.writeJson(res, response);
    })
    .catch(function (response) {
      utils.writeJson(res, response);
    });
};

その4

Android Jetpack のNavigation ってパッケージ名が変わっていたので注意した方がいいかも。
おそらくFragment のパッケージ名変更に伴うものだと思うけど、文献は見つけられなかった。

バージョン パッケージ名 依存関係1 依存関係2
1.0.0 android.arch.navigation navigation-runtime support-fragment
2.1.0 androidx.navigation fragment navigation-runtime

その5

OkHttp 4 は、Java 8 が必要のようだ。
設定忘れて悩んでしまったw

Cash App Code Blog | OkHttp 3.13 Requires Android 5+

その6

Kotlin Coroutines を使ってみた感想。

  • CodeLab の下記実装を自作するべきか悩ましい

      package com.example.android.advancedcoroutines.util
    
      import kotlinx.coroutines.CancellationException
      import kotlinx.coroutines.Deferred
      import kotlinx.coroutines.async
      import kotlinx.coroutines.supervisorScope
      import kotlinx.coroutines.sync.Mutex
      import kotlinx.coroutines.sync.withLock
    
      /**
       * Cache the first non-error result from an async computation passed as [block].
       *
       * Usage:
       *
       * ```
       * val cachedSuccess: CacheOnSuccess<Int> = CacheOnSuccess(onErrorFallback = { 3 }) {
       *     delay(1_000) // compute value using coroutines
       *     5
       * }
       *
       * cachedSuccess.getOrAwait() // get the result from the cache, calling [block], or fallback on
       *                            // exception
       * ```
       *
       * @param onErrorFallback: Invoke this if [block] throws exception other than cancellation, the
       *        result of this lambda will be returned for this call to [getOrAwait] but will not be
       *        cached for future calls to [getOrAwait]
       * @param block Suspending lambda that produces the cached value. The first non-exceptional value
       *        returned by [block] will be cached, and future calls to [getOrAwait] will return the
       *        cached value or throw a [CancellationException].
       */
      class CacheOnSuccess<T: Any>(
          private val onErrorFallback: (suspend () -> T)? = null,
          private val block: suspend () -> T
      ) {
          private val mutex =  Mutex()
    
          @Volatile
          private var deferred: Deferred<T>? = null
    
          /**
           * Get the current cached value, or await the completion of [block].
           *
           * The result of [block] will be cached after the fist successful result, and future calls to
           * [getOrAwait] will return the cached value.
           *
           * If multiple coroutines call [getOrAwait] before [block] returns, then [block] will only
           * execute one time. If successful, they will all get the same success result. In the case of
           * error it will not cache, and a later call to [getOrAwait] will retry the [block].
           *
           * If [onErrorFallback] is not null, this function will *always* call the lambda in case of
           * error and will never cache the error result.
           *
           * @throws Throwable the exception thrown by [block] if [onErrorFallback] is not provided.
           * @throws CancellationException will throw a [CancellationException] if called in a cancelled
           *          coroutine context. This will happen even when reading the cached value.
           */
          suspend fun getOrAwait(): T {
              return supervisorScope {
                  // This function is thread-safe _iff_ deferred is @Volatile and all reads and writes
                  // hold the mutex.
    
                  // only allow one coroutine to try running block at a time by using a coroutine-base
                  // Mutex
                  val currentDeferred = mutex.withLock {
                      deferred?.let { return@withLock it }
    
                      async {
                          // Note: mutex is not held in this async block
                          block()
                      }.also {
                          // Note: mutex is held here
                          deferred = it
                      }
                  }
    
                  // await the result, with our custom error handling
                  currentDeferred.safeAwait()
              }
          }
    
          /**
           * Await for a deferred, with our custom error logic.
           *
           * If there is an exception, clear the `deferred` field if this is still the current stored
           * value.
           *
           * If the exception is cancellation, rethrow it without any changes.
           *
           * Otherwise, try to get a fallback value from onErrorFallback.
           *
           * This function is thread-safe _iff_ [deferred] is @Volatile and all reads and writes hold the
           * mutex.
           *
           * @param this the deferred to wait for.
           */
          private suspend fun Deferred<T>.safeAwait(): T {
              try {
                  // Note: this call to await will always throw if this coroutine is cancelled
                  return await()
              } catch (throwable: Throwable) {
                  // If deferred still points to `this` instance of Deferred, clear it because we don't
                  // want to cache errors
                  mutex.withLock {
                      if (deferred == this) {
                          deferred = null
                      }
                  }
    
                  // never consume cancellation
                  if (throwable is CancellationException) {
                      throw throwable
                  }
    
                  // return fallback if provided
                  onErrorFallback?.let { fallback -> return fallback() }
    
                  // if we get here the error fallback didn't provide a fallback result, so throw the
                  // exception to the caller
                  throw throwable
              }
          }
      }
  • Mockito Kotlin はCoroutines 正式リリース後にアップデートがないので、他のライブラリを使った方がいいかも

その7

下記の本を買ってきた。

その8

Android Q でRobolectric を使う場合はJava 9 の設定が必要みたいだ。
ただ、その環境をサクッと揃える方法が思いつかなかったので、採用は見送った……。

今回の成果

関連リンク

成果物

参考文献

一覧に戻る