eaglesakuraの技術ブログ

技術的な話題とか、メモとか。

Flutter iOSでファイルの書き込みが静かに失敗する不具合の対応

flutter doctor

$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.5.3, on macOS 12.0.1 21A559 darwin-arm, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
[✓] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
[✓] Android Studio (version 2020.3)
[✓] Android Studio (version 2020.3)
[✓] IntelliJ IDEA Ultimate Edition (version 2021.3)
[✓] IntelliJ IDEA Ultimate Edition (version 2021.3)
[✓] IntelliJ IDEA Ultimate Edition (version 2021.2.3)
[✓] VS Code (version 1.62.3)
[✓] Connected device (2 available)

発生した事象

iOSのアプリCacheディレクトリにファイルを書き込むと、低確率でファイル書き込みが静かに失敗する。

file.writeAsBytes( binary, flush: true);

Consoleログ出力にはエラーはなく、例外が発生しない。 書き込みは失敗するため、書き込んでもファイルが存在しない(File.existsがfalseとなる)し、appendモードの場合はファイル長(File.length())が想定よりも短くなる。

Issue

おそらくこれ

github.com

対応

たまに静かにランダムに失敗するので、成功が確定するまで書き込み続ける。

ログファイルのように次々appendするようなパターンでは二重書き込みが怖いけど、今回の自分のユースケースでは書き込みをリトライすることでワークアラウンドした。

その他

正しい対応の仕方があれば教えてほしい(マジで困る)

AndroidアプリのKotlin 1.4/1.5から1.6へのマイグレーション

AndroidX Roomのコンパイラが不正なJavaクラスを吐き出す

  • Entity(だいたいdata class)に複数のコンストラクタがある場合(Entityをnewする際のサポートのために作ったコンストラクタ)、Kotlin 1.6からRoom Compilerが不正なコードを吐き出すようになった
  • RoomのR/Wに使われないコンストラクタに @Ignore アノテーションをつければ良い
    • メッセージからして、多分1.4までが不正な状態で、1.6で想定通りに機能するようになったと思われる

String.toLowerCase(Locale)が非推奨になった

  • ガイドに従って String.lowercase(Locale) に書き換えれば良い

when分岐でに足りない候補がある場合に(seled classとか)警告が出るようになった

  • elseブロックを追加する
  • 丁寧にやるなら、全部の候補を列挙した方が将来自分が困らなくていいかもしれない(意図しないwhen分岐漏れを防ぐための警告なので)

Channel.offerがtrySendに変更された

  • 戻り値がObjectになり、詳細な結果が取れるようになった

Koinが内部で参照しているClassが消された

  • Time/Duration系の参照が一部変更されたらしい
  • KoinのLoggerレベルを下げてワークアラウンド
  • 追記: Square Wireライブラリ(Proto3のSquare実装)も同じくtime.Instantに関わる問題でクラッシュする
    • 3.7.1では対応済なのでバージョン更新

stackoverflow.com

改善策

  • Android Desugaringライブラリが古いとProguardかけた場合だけ問題が発生する場合がある
  • 1.1.5に更新したら治った

developer.android.com

M1 Max Macbook Pro(14inch)の所感

購入したスペック

  • M1 Max
  • 10 CPU
  • 32 GPU
  • 64GB RAM
  • 512GB SSD

環境構築で躓いたところ

Android SDKAndroid Studioからインストールも認識もできない

  • なにかに引っかかってエラーらしい
  • IntellijAndroid Pluginに付属しているGUIでインストールするとAndroid Studioからも認識される
    • Apple Silicon環境ではemulatorコマンドが消されているので、Android Studioインストーラーがコケる
    • emulatorだけcanaryチャンネル(--channel=3)で手動でインストールすると以後正常動作する

javacがクラッシュする

  • JDK9から付属しなくなったいつものjavax
  • ごちゃごちゃやってるうちに治った

Android Emulatorが起動しない

  • Android StudioのAVD Managerで作成すると常に失敗する
  • コマンドラインで作成すると成功する
  • stableチャンネルに"emulator"がない(Apple Siliconだけ?)ようなので、canaryから取り寄せる
sdkmanager --channel=3 emulator
avdmanager create avd -n instrumentation -k "system-images;android-31;google_apis_playstore;arm64-v8a" -d "Nexus 9"

Flutterアプリビルド速度

  • VSCodeからdebugビルドが30秒〜15秒 -> 9秒〜10秒に短縮

Androidアプリビルド

  • Gradleコマンドでフルビルド75秒 -> 65秒に短縮

費用対効果

  • ビルド速度だけを見たら、40万円出すのは微妙と思うかも
  • 何やってもほぼファンはまわらず、静かに作業できる
    • 全部のUnit Testを並列実行させたらファンが回ったので、ファンは壊れてなさそう
  • 作業中も概ねキビキビ動いてくれる
    • 総合的な快適性はかなり上がった
    • iOSアプリ開発しないのであれば、40万円でデスクトップ構築したほうが安い
  • 逆に言えば、40万円払って出先でもデスクトップ(より多少劣るが)並のスペックを手に入れたと思えばまあ良いかも
  • アップグレード先としてはアリ、高性能なMacを必要とせず既にデスクトップで強力な母艦があるなら微妙
    • けど超静か
    • 静かで強力な開発環境がほしいなら(ARMでよければ)アリ

Android StudioとVSCodeの開発効率のメモ

コード生成

  • equals系のオーバーライドで差が出る
    • Android Studioは割と素直にできるし、data classは自動的に行われる
    • VSCode + Dartは標準機能に無いのでExtensionを追加
      • ただし、Classの記述状況によってはExtensionが生成してくれないので自分でどうにかすることがまれによくある
      • VSCode+Dartは基本機能が(ASに比べて)プアなので、Extensionで機能を拡張していく必要がある
        • Projectで必要なExtensionは標準設定としてシェアできるので、誰かがちゃんと整備すれば実用上あまり問題にならない
  • VSCode + Dart Extensionは型の単純なtypedefを認識してくれないので、一部コード補完が効かなくなる

リファクタリング

プロジェクト環境整備

  • VSCode圧勝。JSONで記述できるので、必要なExtensionやTaskを登録しておきやすい
    • Intellijのプロジェクトシェアは結構面倒

IDEの快適さ

  • 基本的にメモリを積んでCPUで叩けば何ら問題ない
  • VSCodeは参照の検索が遅い
  • VSCodeIDEのレイアウトが限られているのが難点
    • Terminalを上に持っていきたいが、できない
    • Intellijは自由にレイアウトできる
  • VSCodeはRemote Developmentが強い
    • なれたWindows母艦からリモートに接続して開発できる
    • WindowsiOSアプリを開発できる気になれる

Flutterアプリ開発環境の長所短所

M1X出ないのかよ!!

Apple発表会で出なかったので、手持ちの環境の再整備を行うことにした

各環境のレビュー

Intel Mac mini 2018 + Windows

  • i7 6C12T / RAM 64GB
  • 従来の開発環境

iOSアプリビルドにはmacが必須なので用意した環境。 WindowsからVSCode Remoteで接続して開発。

  • 利点
    • AndroidiOSもWebも開発できる
    • VMを立ち上げればWindows AppもLinux Appもビルドできるパーフェクト環境
  • 欠点
    • 根本的にi7が遅いのでビルドも遅い。
    • 騒音がひどい。

Intel Mac Book Pro 2020

  • i7 4C8T / RAM 32GB
  • 持ち運び用

リモートワークで自宅内ノマドする機会が増えたので用意した環境。

  • 利点
    • AndroidiOSもWebも開発できる
    • VMを立ち上げればWindows AppもLinux Appもビルドできるパーフェクト環境
  • 欠点
    • 根本的にi7が遅いのでビルドも遅い。
      • Mac miniと比べてもパワー不足。Ryzenとは比べられるレベルじゃない。
    • 騒音がひどい。

Windows

  • Ryzen 3950X 16C32T / RAM 64GB

  • 利点

    • ビルドが早い
    • Macと比べて動作が静か
  • 欠点
    • iOSビルドができない
    • Flutter以外の周辺ツールと日本語環境のかみ合わせが悪い
      • よく文字化けする
    • flutter pub getでmklinkできずにコケる
      • 管理者権限でpub getすれば通るけど気持ち悪い

Ryzen 3950X + Windows / WSL2

  • 利点
    • ビルドが早い
    • Macと比べて動作が静か
  • 欠点
    • iOSビルドができない
    • adb接続にトリッキーなことをしなければならない
    • Android Studioとか開けないのでAndroid Kotlin側のコードをいじるときに不便

Ryzen 3950X + Ubuntu

  • 利点
    • ビルドが早い
    • Macと比べて動作が静か
  • 欠点
    • iOSビルドができない
    • Windows系のツール(Photoshopとか)が使えない
    • ZoomやDiscordが安定して接続できない

最終的に

Android12対応でやったこと

もうすぐAndroid 12がリリースされるので、最低限対応した内容をメモする。

Android Studio AF対応

  • 新AndroidStudioにビルドツールを切り替えた

UnitTestをRobolectricからInstrumentation Testをメインに変更

  • Android Studio AFから、Robolectricが実行しづらくなった(できなくはないが、使いにくい)
  • なので、Instrumentation Testのみを行う方向に切り替えた
  • それに合わせてCIも変更した

Lint対応

  • Android 12でLint系が更新されたので、チクチクと変更

Activity.exported 属性の対応

  • AndroidManifest.xmlのActivityタグで、exported属性設定が必須になった
  • 内部・外部を問わず、aar内部で利用されているActivityにも適用されるので、ライブラリが古いとビルドが通らない場合がある
  • ビルドツールチェインがタコなので、問題が起きてももとのライブラリ名が不明

抽出

  • API 30の状態でapkをビルド -> 分解してAndroidManifest内のActivityを総チェック
  • exported属性がついていないActivityを洗い出して修正
  • androidx.test が内部で使用しているActivityも、古いバージョンだとexported属性が付いてないのでバージョンを更新

PendingIntentのフラグが厳密化されたので対応

  • androidx.work ライブラリはalpha版に切り替えないといけないので、stableが早く出てほしい

SONYのHDDレコーダーのリモート予約が正常動作しなくなったのでエンジニア的カンで直した

利用機器

  • SONY製 BDR-BDZ-FW2000(容量2TB, ホームサーバー機能対応のモデル。以下レコーダー)
  • マンション標準のネットワーク(グローバルIPは割り当てられない, 1家庭500~800MBps程度の速度が最大)
  • Google Nest Wi-Fi(ただしレコーダーはハブを介して有線接続)

事象

  • レコーダーのリモート予約機能が「レコーダーがネットワークに繋がっていない」という旨のエラーを表示してしまった
  • SONYのQ&Aはすべて試したが改善しない
  • 利用価値の半分がリモート予約(外出先で子供が「コレ予約してよ!」って言うことが多い)

観察

  • レコーダーのネットワークチェックは正常
  • そもそもホームサーバー機能の一部であるリモート視聴は行えるので、ネットワークは接続できている
  • 無線・有線を切り替えたが共に問題ない
  • iOS / Android両方で同じ事象

考察

  • アプリの挙動を見ると、リモート予約(POST/PUT)系だけではなく、「予約済み番組一覧」のGETが行えない
  • アプリのレビューやTwitterの検索をしてみるが、一斉不具合は起きていない
  • レコーダー <--> SONYサーバー <--> アプリの経路のうち、レコーダー <--> SONYサーバーの部分に(俺の環境のいずれか)問題がある恐れが濃厚である

更なる考察

  • アプリ側 / 経路内のプログラムの問題として、Invalidな予約データを受け取る -> 例外吐いて停止している恐れがある
  • レコーダーは年単位で予約している番組(ガイアの夜明けとか)が存在している
  • 古い番組のDate系に問題があるのではないか?

行動

  • 一旦すべての予約データを(後で治せるようにメモして)削除する
  • レコーダーを再起動する
  • これで無事に復旧した
    • 正確なところはわからないが、サーバーやアプリ側のValidationに問題があったのではないだろうか?