虚苦心観察ブログ

ブログ管理者である虚苦心が私利私欲に基づいて書いているブログです。主にガジェットのレビューだったり、画像処理のことだったりを記事にしています。

なぜかあまり報じられないがAndroidがその座をFuchsiaに譲るかもしれない。

はじめに

以前にも同じ意見はあった
www.gizmodo.jp

決して私だけが思い込んでいるわけではない。

根拠となるニュースや情報など

これらの内容から、開発者側はAndroidアプリを開発しながらFuchsiaへ移行可能になり、Androidユーザーは既存のアプリをFuchsiaでも使うことができる。この二つがそろうことはとても大きい。既存の開発者もAndroidユーザーもそのままFuchsiaに引き込むことができるから。

移行するわけないだろうという反対意見もある

FuchsiaがAndroidを置き換えるかもという意見に対して否定的な意見もあるが、 既存ユーザーを気にかけているような内容がかかれているので、 それはAndroidアプリのRuntimeが追加されたことで解決されてしまったことになる。
Is Fuchsia OS the next Android? - Dignited

私見とまとめ

ここで書きたいことはAndroidプラットフォームは今後縮小する可能性が高い、ということはもちろんだが、今後Androidアプリを開発しようとしている開発者はAndroid SDKで直に開発するのではなく、Flutterを使うべき、ということだ。

Flutterを使うべき理由はFuchsiaのアプリ開発にスムーズに移行できることは当たり前だが、AndroidのViewは複雑すぎ独自のViewを正しく作ることが難しく、明記されていない仕様、Android OSのバージョンや端末ごとで異なる動作など問題が多いので、Flutterを使ってこのような問題とは無縁な状態で開発するべきだと考えているからだ。
ソースコードを見ればわかる?そうではない。Viewのそもそもの設計や画面制御が良くないと感じているのでより良いものがあるならそんなものは使うべきではない、そう考えているだけだ。

反発する意見はもちろんあると思うが、まじめに開発しているAndroid開発者ほど意味不明なバグに悩まされてきたと思うので、それらが少しでも解決されて欲しいと思っているなら・・・・AndroidがFuchsiaにその座を譲る(かもしれない)こと、Flutterで開発すべきという内容に納得できるのではないかと思う。

Flutterでネットワーク通信

はじめに

覚書のために書いています。

使うパッケージ

  • http

使い方

  • GET get()

  • POST post(, body: )

戻り値は Future< Response >
Responseの処理はFutureのthenメソッドにCallbackとして渡す。

余談

パッケージのインポートは

import 'package:http/http.dart' as http;

と書くが、

import 'package:http/http.dart'

と書くとネームスペースがグローバルになるみたい。
Dartの勉強がまだまだ。

FlutterのWidgetについて勉強を始める

FlutterのUI作成の特徴

画面を表示

  • すべてコードで書く
  • Widgetの構成単位が細かい
  • AndroidでいうところのLayoutに当たるものがほとんど。
  • StatelessWidgetとStateWidgetがある
    • まだ理解していない
  • Androidでいうところのlayout_gravityやgravityもWidgetとして提供されている
  • とてもシンプルなデコレーションパターンな印象
  • それ故にコード量は多い
  • しかし、それを補って余りある強力なUI表現が得られる
  • UIをコードに落とし込むときも単純な考え方で記述できるので苦労しなさそう
  • Androidでよくある「え、標準UIでこれができないの?」がほとんどない気がする
    • Androidの標準のViewは機能が豊富過ぎるわりに必要な機能を網羅できていなくて使いにくかった

画像を表示

  • ローカルで表示する場合
    • Image.asset(<画像のパス>)で表示
    • プロジェクト直下にディレクトリを作成。名前はなんでもOK
    • 表示したい画像が入ったディレクトリはpubspec.yamlに記載
    • ディレクトリだけでなくファイルも指定可能
flutter:
  assets:
    - assets/ # ディレクトリ指定
    - assets/images/image.jpg
  • ネットワーク経由で表示する場合
    • Image.network()で表示できる
  • よくある角丸はClipRRect Widgetの子に指定することで簡単に

リスト表示

ListView.builder(
  itemBuilder: (buildContext, index) => Text("current $index"),
  itemCount: 10,
)
  • 書き方はRecyclerViewととても似ている。

Flutterを始める

つまづく

UIをぽちぽちしてプロジェクトを作ってRunしただけなのに動かない・・・

やったこと

Androidアプリ開発で陥りやすいバグやあまり知られてないけど有益な実装方法をまとめる。

忘れることが多いのでちょくちょくメモしていく。

  • ダウンロードは別スレッドで行う。メインスレッドを止めてしまうためANRが発生する可能性が高い。(activity,service)
  • 画像表示は縮小した画像で行う。Android8から大きすぎる画像を表示しようとすると例外が発生する場合がある。また縮小することで大幅な高速化が可能。ただし、縮小する方法は考えること。単純な実装だとむしろ重くなってしまう場合がある。
  • recyclerviewの各ViewHolderには直接listenerを実装せずにリストの各要素にそれぞれリスナーを保持する
  • ViewHolderで画像表示やネットワーク通信をするときはViewHolderが再利用されるときに画像の解放やネットワーク通信のキャンセルをすること。
  • mvpパターンはandroidフレームワークとロジックを分離するための雛形。
  • ネットワーク通信は通信内容の処理のほかに通信失敗の処理も必須。ネットワーク通信が失敗する可能性は0ではない。
  • FcmIdRecieverServiceはDeprecated。FirebaseMessageRecieverServiceでFCMトークンの更新処理を行うこと。
  • PendingIntentに設定したTaskBackStackが反映されない場合はアプリを再インストールすると治ることがある。アプリのキャッシュやデータを削除しても治らない。(このことからわかるようにAndroidにはアプリから触れることのできないアプリにかかわる領域がある)
  • Retrofit2のマルチパートでファイルとその他をアップロードする場合はPartとPartMapだけしか使えない。PartMapはBodyにハッシュを指定する感覚で使える。
  • KotlinからRetrofit2でPartMapを使う場合、RequestBodyがワイルドカードで表現されてしまう場合がある。その時はPartMapのValueに@JvmSuppressWildcardsを付ける。参照
  • EditTextのimeOptionsにactionSearchを指定してはいけない。ソフトウェアキーボードからのエンターキーのイベントはsetOnEditorActionListenerで設定できるが、ハードウェアキーボードからのイベントは処理できずアプリが落ちてしまう。単純に検索窓として使いたいならSupport LibraryのSearchViewを使おう。継承することで見た目をカスタマイズすることもできる。どうしてもEditTextを使いたい場合はimeOptionsにactionDoneを設定すること。見た目は検索と異なってしまうが、落ちることはない。
  • RecyclerViewのOnScrollListenerに依存した初期化処理を書いてはいけない。タイミングによってイベントが発火しないときがある。なんとかして初期化処理は能動的に実行すること
  • RecyclerViewのスクロール位置復元は明示的に行う必要がある。RecyclerViewのスクロール位置を保存したいに書かれていることをonSaveInstanceStateで保存し、onRestoreInstanceStateで復元する。
  • RecyclerViewの無限スクロールにはこれがよい。無限スクロールの状態をリセットし、一旦停止した無限スクロールを再開することもできる。使い方もしっかり書かれている。
  • アスペクト比を固定するにはConstraintLayoutが使える。stackoverflow よくあるアスペクト比固定のImaveViewもImageViewを拡張せずに使うことができる。
  • デバッグ時はアプリの起動と再起動を開発者オプションの「アクティビティを保持しない」の有効無効を切り替えて確認すること。アクティビティが一時的に破棄され、復元された場合のチェックが抜けていることがよくある。
  • Maps SDK for Androidで地図をLite Modeで表示するにはViewのサイズが完全に固定されている必要がある。固定されない場合、地図が表示されない、タップしたときだけ地図が表示される、など表示崩れが発生する。サンプルコードのLayoutをそのまま使ってもうまく動作しないことがある。その時は、Viewのサイズが変わらないよう、不要な要素を排除、もしくはサイズが変わらないように変更しよう。
  • Activity間のデータやイベントのやり取りはやってはいけない。短時間での使用ではモンダイなさそうでも、長期間利用した場合にAndroidのライフサイクルと合わず、おかしな動作をしてしまう可能性が高い。
  • 逆の考えでActivityの実装を制限してデータやイベントのやり取りを可能にすることはできるかもしれないが、普通ではない実装であるが故に普通の実装をしようとしたときに後悔することになりかねない。
  • FLAG_ACTIVITY_REORDER_TO_FRONTを使って複数のActivityを切り替えて表示すると、オーバービュー画面(アプリの使用履歴が見れる画面)で最後に表示されたActivityのスクリーンショットが表示されず、どこかのタイミングで表示されたActivityの画面になってしまうことがある。Android 8のNexus 5Xで確認。改修方法はわからず。
  • RecyclerView and java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder のようなエラーが出た場合はAdapterの更新でnotifyItemRangeChangedが呼び出されているところをチェックしよう。StackOverFlowで説明されているエラーとは異なる。このエラーはsupport library のバージョンが23.1.1で発生していたもので、すでに修正済みである。ではなぜ同様のエラーが発生しているかというと、おそらくRecyclerView.AdapterのnotifyItemRangeChangedを呼び出していながら表示要素が減っていることが原因だ。notifyItemRangeChangedは表示要素が増えた場合は対応できるが、減った場合は対応できていないようだ。
  • Activity間での変更通知はLocalBroadCastManagerを使うことができる。しかし、これは本来的な使い方ではない。本来はActivityとServiceの通信で使うものである。よく検討し、ライフサイクルを考慮した実装をしなければただのバグの温床にしかならない。あるデータの変更通知を行いたいのであれば、データベースに変更日時を記録すれば、データの取得日時がそれよりも後か前かで更新すべきかどうか判断できる。もしくはデータをServiceなどで一括管理するなどが必要だろう。
  • 通知からCustomChromeTabを直接起動するとBackStackBuilderで構築したバックスタックが反映されず、CustomChromeTabが終了するとアプリも終了してしまう場合がある。Android API Level 25のバーチャルマシンで確認した。21、23、25、27と確認してのことである。解決方法はIntent.ACTIVITY_FLAG_NO_HISTORYで起動した何もしないActivityからCustomChromeTabを起動する。これでAPI LEVEL 25のバーチャルマシンでも問題なくBackstackが構築された。
  • IntentはParcelを継承しているのでそのままIntentにputExtraで保存することができる。これを利用すると何もしないACTIVITY_FLAG_NO_HISTORYで起動するActivityにIntentをExtraとして渡すことでRedirectのような動作を実現することができる。
  • Activity遷移のAnimationはNULLにしない。空のAnimationをセットすること。端末によってはNULLにしたことによってActivityの表示前に黒い画面が一瞬表示されてしまう。
  • RecyclerViewの各アイテムにもAnimationがある。ItemAnimatorを使って実現している。