虚苦心観察ブログ

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

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とPartだけしか使えない。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で確認。改修方法はわからず。

sklearnのChange Logまとめ

バージョンアップでモジュールのパスなり引数の名前が変わってしまっていて、本を使って勉強をしているときに困ることが多いのでメモ。

  • パス
old path new path changed
sklearn.cross_validation sklearn.model_selection 0.18.0
sklearn.grid_search sklearn.model_selection 0.18.0
sklearn.learning_curve sklearn.model_selection 0.18.0
  • 引数 | function | old argument | new argument | changed | | :-- | :-- | :-- |

随時追加していこうかと思います。 載っていないものがあればコメントいただければ追記します。

Visual Studio 2015 で LLVM/Clang 3.6.2 をコンパイルする

LLVM/Clangはコンパイルのハードルが高いと思っていたのですが、そこまで大変ではないらしいことを知りました。

KMC Staff Blog:LLVM/Clang 3.6.2をVisualStudio 2013 Expressでビルド

上記の記事よりVisual Studioのバージョンが新しいのですが、コンパイルを試みました。 そしたらなぜかコンパイルエラーが。

Severity Code Description Project File Line Error C2664 'void llvm::BitstreamWriter::EmitRecordWithBlob<uint64_t>(unsigned int,llvm::SmallVectorImpl<uint64_t> &,llvm::StringRef)': cannot convert argument 3 from 'clang::serialization::DeclOffset *' to 'llvm::StringRef' clangSerialization  C:\Users\kyokushin\ProgramingLibraries\llvm-3.6.2.src\tools\clang\lib\Serialization\ASTWriter.cpp   2815

Visual Studioのバグでdataというテンプレート関数が別のインクルードファイルに含まれているテンプレート関数を呼び出してしまっているらしい。
エラー文からいくつかキーワードを引っ張ってググってみるとそれっぽいバグ報告を見つけた。

Bug 25650 – Cannot compile clang 3.7.0 with msvc 14 (2015)

このバグ報告によれば新しいClangでは直っているようです。なのでClang3.8.1のソースをダウンロードしてどのような変更があったのかを直接みました。 Clang3.8.1ではdataというテンプレート関数をbytesという名前に変更していることがわかり、同様の変更を加えてみました。

変更するファイルは一つ。
clang/lib/Serialization/ASTWriter.cpp

ファイル先頭にあるテンプレート関数dataを引数などはそのままで関数名のみをbytesに変更します。 同じファイル内でテンプレート関数dataを呼び出しているところをbytesに変更します。 一括置換で簡単に変更することができますが、dataというメソッド名も存在するので注意。 先頭にスペースを入れた" data("を検索文字列にすると対象となるテンプレート関数だけが置換できます。

変更後コンパイルしてみましたが、エラーなくコンパイルが終了しました。
こういうことがあると本当にVisual Studioはクソだなーって感じますね。

へっぽこ知識で作る画像処理プログラム 「自炊本のページ抜けチェック」 3.5日目

はじめに

2.5日目につづき今回もPython版を作成したので解説していこうと思います。 ただし、アルゴリズムについてはC++版を参考にしてもらうことにして、ここではC++版とPython版での違いについて紹介していきます。

C++Pythonでのピクセルアクセス方法の違い

C++ではOpenCVのクラスcv::Matに画像データを保持しますが、Pythonにはcv::Matは存在しません。
では何を使うのかというと、Python数値計算ライブラリであるnumpyのndarrayを利用します。

numpyのデータ構造の一つであるndarrayはC++のcv::Matに比べてかなり柔軟な書き方ができます。
例えば今回の場合だと、文字のない範囲を塗りつぶす、という処理を行っているわけですが、C++版とPython版を比べてみましょう。

C++

//文字のない範囲を3チャンネルの原画像に書き込む
    for (size_t i = 0; i < verticalRanges.size(); i++){
        Range& r = verticalRanges[i];
        //文字のない範囲を3チャンネルの原画像から切り出す
        cv::Rect rect(0, r.start, verticalRangeDst.cols, r.end - r.start + 1);
        cv::Mat roi(verticalRangeDst, rect);
        //切り出した画像を1色で塗りつぶす
        roi = cv::Scalar(0, 0, 255);
    }

Python

    for r in verticalRanges:
        verticalRangeDst[r.start:r.end, :, :] = (0, 0, 255)

C++版はちょっと冗長な書き方をしていますが、、、どうでしょう。Python版はものすごくシンプルで、なんと矩形の塗りつぶしだけなら1行で書けてしまいます!
ndarrayでの各ピクセルへのアクセス方法はPythonの配列と同等の方法になります。
そして":"はPythonのスライスと呼ばれる書き方です。 スライスを使うことでアクセスしたい配列の範囲を指定することができます。 スライスで指定した範囲に対して上記のように代入することができるため、C++に比べて非常に柔軟な書き方が可能です。

別の例として1チャンネルの画像から3チャンネルの画像を作る方法。cv::mergeを使わずに実装することができます。

color = numpy.zeros((gray.shape[0], gray.shape[1], 3), dtype=gray.dtype)
color[:,:,0] = gray
color[:,:,1] = gray
color[:,:,2] = gray

ただPython版のOpenCVにはcv2.merge関数が用意されていてそっちの方が簡単です。あくまで参考。

Pythonではrangeという組み込み関数が存在するので注意

Python版を作るうえで今回注意した点が一つあります。 それはrangeという名前の関数や変数を作らないことです。 なぜかというと、Pythonにはrangeという組み込み関数が存在し、誤って上書きしてしまうとrange関数を使っている場所で予期しない動作をする可能性があります。 今回の場合はrangeという変数名の代わりに頭文字のrをつかって対処しました。 PythonC++に比べて柔軟な書き方が出来て便利な反面、こういった落とし穴もあるので注意が必要です。

出来上がったプログラム

今回もタグを用意しています。

github.com

今回はここまで。