20200926のAndroidに関する記事は7件です。

Androidエミュレータの通信をモニタリングしてみる

やりたいこと

  • AndroidエミュレータでHTTPのモニタリング
  • ホスト側のWiresharkでリアルタイムで受け取り

使うもの

すること

Android側のPcap Remoteで通信をモニタリングしその情報をSSHを介してWiresharkで受け取ります。

流れ

  1. AndroidにPcap Remoteをインストール
  2. SSHのPrivateKeyを入手(メニューのHelp/Howto内のWireshark部分にあるリンクからDL or アプリ設定のExport→ホストへ転送)
  3. ADBサーバーを起動し、ポート転送を設定 adb forward tcp:[Host Port] tcp:[Emulator Port]
  4. WiresharkインターフェースからSSHの設定をする。
    • IP:127.0.0.1 Port:設定したポート AuthタブPath to SSH private keyに保存したPrivate key
  5. 接続して完了!

Tips

疎通確認したい

WindowsでPortごとの疎通確認はPowershellにて行う

Test-NetConnection -ComputerName [IP] -Port [6100]

HTTPSの中身を見たい ->

Androidモニタリングアプリには中間者攻撃でHTTPSの中身を見れるものもある。
ただしAndroid 7 では対策されており難しい。 Android 5を使えば簡単にみられる。

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

【Flutter入門】短期集中講座やったので要点を整理してみた

Android/iOS向けアプリ作りたいけどなんかいい開発環境ないかなぁ。
Electronのスマートフォン版あったら便利なんだけどなぁ。
Flutter?聞いたことはあるけどDartが不人気なことで有名だしなぁ。
(念の為改めて調査してみる)
は?Dartめっちゃいいじゃん!なにこのC言語系の正当進化版みたいな可読性!?何が不人気なの?? →完全にただの記憶違いでしたm(_ _)m
Flutterもデスクトップ・モバイル・Webをカバーした理想的な環境っぽいなぁ。
よし、まずは入門だ!
(最終更新:2020.09.27)

作業した環境

  • OS:macOS Catalina バージョン10.15.7

集中講座の元動画様(英語)

Flutter Crash Course
この記事の位置づけとして、基本的に元動画様に沿って進めていって、補助として個人的に記録しておきたいと思った情報を日本語でわかりやすくざっくりまとめた感じです。

作るもの

シンプルな単語ペアジェネレータ。
ランダムに生成された単語ペアをリスト表示する。
気に入った単語ペアはタップで印をつけることができ、別ページで一覧を確認できる。

完成イメージ

Flutterとは

Google製UIツールキット。
一つの共通化されたコードをもとにモバイル・Web・デスクトップ向けのネイティブクロスプラットフォームアプリが開発できる。
パフォーマンスが極めて高い。

Dartとは

Flutterが採用しているオブジェクト指向型プログラミング言語。
UIプログラミングに最適化されている。
すべてのプラットフォームで高速に動作する。
文法的には、例えるならJavaの要素を持つJavaScript。

Widgets

DartではすべてのパーツがWidgetである。
マテリアルデザインを採用している。
Scaffold(土台)、AppBar、Container、Image、Icon、などなど。
StatelessとStatefulがある。
build関数で作成する。

Stateless Widgets

状態を持たない静的なWidgetのこと。
実行中変化せず、自身を再描画する必要がないUIに使われる。

Statefull Widgets

状態を持っている動的なWidgetのこと。
実行中に変化し、自身を再描画する必要があるUIに使われる。

開発環境構築

ざっくりと今回必要な環境をまとめておきます。

  • Flutter本体

    • Flutterから環境にあったFlutterをインストール(圧縮ファイルを解凍して任意の場所に配置)
    • flutter/binにパスを通す
  • Visual Studio Code

    • Visual Studio Codeからインストール
    • Flutterエクステンションを追加
    • Dartエクステンションを追加
  • Xcode

    • AppStoreから最新のXcodeをインストール
    • 下記コマンドを実行してXcodeを設定
    • sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
    • sudo xcodebuild -runFirstLaunch
  • Android Studio

    • Android Studioから最新のAndroid Studioをインストール
    • Flutterプラグインを追加
    • 仮想デバイスを作成

開発環境構築チェック

テンプレートアプリを動かして開発環境が構築されたことを確認します。
※ここまでで詳細な手順やトラブルシューティングは省いているので、適宜自分で調べながらになるかと思います。

ターミナルで作業ディレクトリに移動して下記コマンドを実行

flutter create wordpair_generator
cd wordpair_generator
code .

★ターミナルからcodeコマンドが使えない場合は一度VSCodeを開いて、

Cmd+Shift+P
shell と入力
Shell Command: Install 'code' command in PATH を選択

とすると使えるようになる。

wordpair_generatorプロジェクトをVSCodeで開けたら、右下の「No Device」をクリックして「Start iOS Simulator」を選択し、シミュレータを立ち上げます。

上部のメニューから、
Run > Start Debugging > Dart & Flutter
と選択してデバッグ起動します。

テンプレートアプリが無事起動し、右下の+ボタンをクリックしたら画面中央の数字が増えていく、という挙動を確認できればOKです。

テンプレートアプリ

コーディング準備

コードを打ち込んで行く前に、いくつか準備しておきます。

私の場合、Dartのおすすめセッティングとかいうものを適用した結果、保存のたびに強制フォーマットされてめちゃくちゃやりにくかったのでコマンドパレットCmd+Shift+Pからopen settingsと打って設定ファイル(json)を開き、設定を一部OFFにしました。

また、動画とは順番が前後しますが、事前に依存パッケージをインストールしておくとスムーズかもしれません。

pubspec.yaml
# 〜(省略)〜
dependencies:
  flutter:
    sdk: flutter
  english_words: ^3.1.5 # この行を追加
# 〜(省略)〜

コーディング(main.dart)

エントリポイントのある、メインファイルです。
これだけだとまだ動きません。

main.dart
import 'package:flutter/material.dart';
import './random_words.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(primaryColor: Colors.purple[900]),
      home: RandomWords()
    );
  }
}

コーディング(random_words.dart)

画面の構成やランダムな単語ペアの生成・表示といったロジックを担当するファイルです。

random_words.dart
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

class RandomWords extends StatefulWidget {
  @override
  RandomWordsState createState() => RandomWordsState();
}

class RandomWordsState extends State<RandomWords> {
  final _randomWordPairs = <WordPair>[];
  final _savedWordPairs = Set<WordPair>();

  Widget _buildList() {
    return ListView.builder(
      padding: const EdgeInsets.all(16),
      itemBuilder: (context, item) {
        if (item.isOdd) {
          return Divider();
        }

        final index = item ~/ 2;

        if (index >= _randomWordPairs.length) {
          _randomWordPairs.addAll(generateWordPairs().take(10));
        }

        return _buildRow(_randomWordPairs[index]);
      }
    );
  }

  Widget _buildRow(WordPair pair) {
    final alreadySaved = _savedWordPairs.contains(pair);

    return ListTile(
      title: Text(pair.asPascalCase, style: TextStyle(fontSize: 18.0)),
      trailing: Icon(
        alreadySaved ? Icons.favorite : Icons.favorite_border,
        color: alreadySaved ? Colors.red : null),
      onTap: () {
        setState(() {
          if (alreadySaved) {
            _savedWordPairs.remove(pair);
          }
          else {
            _savedWordPairs.add(pair);
          }
        });
      }
    );
  }

  void _pushSaved() {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (BuildContext context) {
          final Iterable<ListTile> tiles = _savedWordPairs.map((WordPair pair) {
            return ListTile(
              title: Text(pair.asPascalCase, style: TextStyle(fontSize: 16.0))
            );
          });

          final List<Widget> divided = ListTile.divideTiles(
            context: context,
            tiles: tiles
          ).toList();

          return Scaffold(
            appBar: AppBar(title: Text('Saved WordPairs')),
            body: ListView(children: divided)
          );
        }
      )
    );
  }

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('WordPair Generator'),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.list),
            onPressed: _pushSaved)
        ]),
      body: _buildList()
    );
  }
}

特に気になった文法

Dartの文法はJavaやJavaScriptの流れを組んだような、素直でわかりやすい文法だと感じていますが、初見で特に気になったりわからなかったものについてまとめます。

  • 「~/」除算、整数(int)の結果を返す
  • 「_」アンダースコア始まりの識別子でプライベートアクセス指定を表す

トラブルシューティング

  • 実行したままコードをいじるとエラーが出て何も表示されなくなる
    • 原因はわかりませんが、実行中のアプリを再起動すると解決しました。

あとがき

Dartについて、よく考えられた直感的な命名の各種機能がそろっていて、文法も素直で堅実な印象で書いていて楽しいと感じました。
ただ、UIに最適化された言語だからなのか、コンストラクタにコンストラクタをどんどん重ねて作っていくやりかたがやりやすいので、気をつけないとついつい"ダーティー"なコードを書いてしまいがちとも思いました。
そこがまたいいのかもしれませんね。

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

AndroidStudioで(ほぼ)無限にUndoする

Android Studio 唯一の問題点

AndroidStudio大好きです。年々改善が施されてきて史上最強のAndroid開発環境となりつつあります。Android開発者なら「必ず」入れているであろうこのAndroidStudioですが、とてもとても残念極まりない問題点があります。それが......

Undoが全然できない問題

です。開発者なら「あぁぁ!やり直しだくそやろう!!」なんて場面は嫌ほどあったでしょうが、そのたびに直面するこの問題。Gitにコミットしているなら別に問題はないですが、コミットから編集し初めて結構経った後のあの場面に戻りたい!!なんてことはよくあります。イライラしながら必死にCtrl+Zを押すわけですが、戻る戻る......と思った次の瞬間、Undoが停止します。そうです。AndroidStudioのUndoは100件までが上限なのです。「あぁぁぁぁぁぁ...」と絶望し、直前のコミットから再編集をし始めるのでした......

なんて悲劇を二度と繰り返さないよう、そしてAndroidStudioを完璧なエディタに仕上げるために、Undoを(ほぼ)無限にできるようにしたいと思います。

(ほぼ)無限にUndoする

STEP 1

まずAndroidStudioを開き「アクションの検索」ダイアログを出します。ショートカットの場合はCtrl+Shift+Aです。マウスで操作する場合はヘルプ->アクションの検索をクリックすると出ます。
STEP1

STEP 2

検索バーに「レジストリー」と入力します。私は日本語化しているので日本語で入力していますが、英語版の方は英語でregistryと入力します。そして出てきた検索結果をクリックしてください。
STEP2

STEP 3

スクロールして「undo.documentUndoLimit」という項目を探します。右がその値で、Undoの上限数です。デフォルトでは100回が上限となっています。この値はint値ですので、上限の2147483647をぶち込みます。無限にUndoできたらいいなと思っていたのですが、-1や0を入力してもエラーになるだけでうまくいきませんでした。Googleさん。無限にUndoできるようにしてください。
STEP3

Undoライフを楽しんで

これで操作は以上です。一つ注意として、この値を変更したからと言ってこの値を変更する以前までUndoできるわけではありません。変更を行って初めてDoの記録を始めるので、2147483647回戻れるようになるには2147483647回何かをDoしなければなりません。それでは、良いUndoライフを!

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

通信処理で重くなり過ぎないようにするには

API呼び出しはコストのかかる処理で、呼び過ぎるとユーザーの回線に余計な負担を与える。また時間がかかるため、反応が遅いと感じられUXに悪影響を与えることがある。そのためどこでどう呼ぶかを考える必要がある。

呼ぶAPIの数の制限

例えばアプリなどで一画面で呼ぶAPIが多すぎると遅くなってしまう。そのため、1画面で呼ぶAPIは2〜3個までなどある程度基準を設け、一度に呼ぶAPI数が多くなり過ぎないように設計する。

回線状況の考慮

できれば、ユーザーがそのアプリ等を使う状況を考慮に入れ、通信回線がどのくらい良いかも念頭に置いておくと良い。例えば、電車の案内のアプリであれば、電車内とりわけ地下鉄など電波状況の悪い場所で使う場合もあることから、そのような場面であまり時間のかかる処理を入れるのは好ましくない。

APIを呼ばなくて済むのではないか検討

同じデータを取ってくるだけなのに何度もAPIを呼ぶのは無駄である。そのため、一度取ってきたデータをデバイスのメモリに一時保存、あるいはストレージに恒久保存して対処する事ができないかを検討する。

また一部のデータが欲しいだけなのに、多くのデータを取ってくるAPIを呼ぶなどもやはり無駄となる。

APIを呼んでいる最中の対応

APIを呼んでいる最中、UIまでも止まってしまう(タップとかクリックとかユーザーの反応を受け付けなくなる)とフリーズしたかのような印象をユーザーに与えてしまう。そのため、通信処理とUI処理のスレッドを分けるなどして対処する。

また、通信処理が終わるまでの間、100ms~200ms程度異常かかるようであればローディングの表示(グルグル回るアイコン、またはゲージが徐々に満タンに近づくバーなど)をするのが望ましい。この場合、通信の開始時にローディング表示を開始する。そして通信が成功しようと失敗しようと、通信が終了すればローディング表示を止めることになる。

参考

MdN Design -[優れたUXを目指して]アプリの性能について:第3回
Android Developers - Keeping your app responsive

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

テストキーを使ってPlatform署名アプリをつくってみる

はじめに

製品版ではつかえません。エミュレータ等で確認してください。

テストキーからPlatfoem署名用のkeystoreを作成する

AOSPの中にプラットフォームのテストキーがあります。
https://android.googlesource.com/platform/build/+/master/target/product/security

  • platform.x509.pem (公開鍵証明書)
  • platform.pk8 (秘密鍵)

上記をつかってplatform.jks(keysoreファイル)を作成します。

//  DER 形式の platform.pk8 を PEM 形式の platform.pem に変換
$ openssl pkcs8 -in platform.pk8 -inform DER -outform PEM -out platform.pem -nocrypt

// PEM 形式の platform.pem から PKCS12 platform.p12を作成
// alias: platform, key store password および key password: android
$ openssl pkcs12 -export -in platform.x509.pem -inkey platform.pem -out platform.p12 -name platform -password pass:android

// platform.jks(keysoreファイル作成)
$ keytool -J-Duser.language=en -importkeystore -destkeystore platform.jks -srckeystore platform.p12 -srcstoretype PKCS12 -deststorepass android -srcstorepass android

platform.jksを使ってアプリに署名をつける

AndroidManifest.xmlにキーを登録

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        debug {
            signingConfig signingConfigs.debug
        }
    }

    signingConfigs {
        debug {
            storeFile file("platform.jks")
            keyAlias "platform"
            keyPassword "android"
            storePassword "android"
        }
    }

Platform署名が付いていることを確認

アプリをインストール後、psコマンドを使ってアプリの素性をみてみます。

//platform.jksを使わないでインストールした場合
# ps -AZ | grep platformtest   
u:r:untrusted_app:s0:c512,c768 u0_a78        4102  1479 1434508  67872 ep_poll      f6ec1af0 S com.example.platformtest

//platform.jksを使ってインストールした場合
# ps -AZ | grep platformtest                                                                                                                                
u:r:platform_app:s0:c512,c768  u0_a75         942  1541 1391208  66196 ep_poll      eb908af0 S com.example.platformtest

もちろん、Platform署名をつけていれば、Systemアプリでしか使えない機能も使えるようになっています。

最後に

まあ、製品版で使えないならなんの意味もないんですけどねー

参考

https://codechacha.com/en/android-sign-app-with-platform-key/

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

Android 11 対応

とあるアプリをAndroid11に対応した際に起こった問題を書いていきたいと思います。

外部ストレージにファイル出力できなくなった...

原因

外部ストレージ直下に独自のディレクトリを作成してファイル出力しているが、
ターゲットAPIレベルを29としたため、外部ストレージへのアクセス強化により出来なくなった。

対処

外部ストレージでも、対象範囲別外部ストレージと呼ばれる場所へのファイル出力は引き続き可能であり、
こちらへファイル出力するように変更した。
また、対象範囲別ストレージへのアクセス権は自動で付与されるため、
WRITE_EXTERNAL_STORAGEパーミッションの確認が不要になった。

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

【Flutter】Locale data has not been initialized, call initializeDateFormatting(<locale>). の解決

error内容

Locale data has not been initialized, call initializeDateFormatting(<locale>). 

これは言語情報の初期化がされていないとのこと。

DateFormat.MMMM('id')

上記のようにlocale情報を扱う前には、'initializeDateFormatting'を呼び出す必要があるとのことです。

対応

アプリの初期起動時のmain()処理内に以下を追記して対応しました。

initializeDateFormatting('id'); //locale情報は各々変更してください。

参考文献

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