20210305のAndroidに関する記事は8件です。

[Android]Glideのネットワークエラー後の画像再リクエスト機能について

Android 開発で Glide はよく使われる画像ローダーライブラリの一つです。

この Glide にはネットワークエラーで画像の取得に失敗した場合でも、通信状況が復帰したタイミングで自動的に画像の再リクエストを行う機能があります。

この機能について深堀りしていきたいと思います。

※ 動作確認は Glide v4.12.0 で行っています。

画像再リクエストが行われるまでの流れ

Glide では DefaultConnectivityMonitor というクラスでネットワーク状況の監視が行われています。

ネットワーク状況が回復したタイミングで、このクラスは登録されたリスナーに通知します。

  private final BroadcastReceiver connectivityReceiver =
      new BroadcastReceiver() {
        @Override
        public void onReceive(@NonNull Context context, Intent intent) {
          boolean wasConnected = isConnected;
          isConnected = isConnected(context);
          if (wasConnected != isConnected) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
              Log.d(TAG, "connectivity changed, isConnected: " + isConnected);
            }

            listener.onConnectivityChanged(isConnected);
          }
        }
      };

この通知は RequestManager.RequestManagerConnectivityListener#onConnectivityChanged メソッドで受け取り、このメソッド内で画像の再リクエストが行われます。

  private class RequestManagerConnectivityListener
      implements ConnectivityMonitor.ConnectivityListener {
    @GuardedBy("RequestManager.this")
    private final RequestTracker requestTracker;

    RequestManagerConnectivityListener(@NonNull RequestTracker requestTracker) {
      this.requestTracker = requestTracker;
    }

    @Override
    public void onConnectivityChanged(boolean isConnected) {
      if (isConnected) {
        synchronized (RequestManager.this) {
          requestTracker.restartRequests();
        }
      }
    }
  }

画像再リクエストが行われる条件

RequestManager のコンストラクタ にて ConnectivityMonitorFactoryConnectivityMonitor のインスタンスを生成します。

  RequestManager(
      Glide glide,
      Lifecycle lifecycle,
      RequestManagerTreeNode treeNode,
      RequestTracker requestTracker,
      ConnectivityMonitorFactory factory,
      Context context) {
    this.glide = glide;
    this.lifecycle = lifecycle;
    this.treeNode = treeNode;
    this.requestTracker = requestTracker;
    this.context = context;

    connectivityMonitor =
        factory.build(
            context.getApplicationContext(),
            new RequestManagerConnectivityListener(requestTracker));

    ...
  }

この ConnectivityMonitorFactory は特に何も設定しなければ DefaultConnectivityMonitorFactory が使われます。

このクラスを見ると、android.permission.ACCESS_NETWORK_STATE パーミッションが許可されている場合に DefaultConnectivityMonitor を、そうでない場合は何もチェックを行わない NullConnectivityMonitor のインスタンスを生成するようになっています。

  public ConnectivityMonitor build(
      @NonNull Context context, @NonNull ConnectivityMonitor.ConnectivityListener listener) {
    int permissionResult = ContextCompat.checkSelfPermission(context, NETWORK_PERMISSION);
    boolean hasPermission = permissionResult == PackageManager.PERMISSION_GRANTED;
    if (Log.isLoggable(TAG, Log.DEBUG)) {
      Log.d(
          TAG,
          hasPermission
              ? "ACCESS_NETWORK_STATE permission granted, registering connectivity monitor"
              : "ACCESS_NETWORK_STATE permission missing, cannot register connectivity monitor");
    }
    return hasPermission
        ? new DefaultConnectivityMonitor(context, listener)
        : new NullConnectivityMonitor();
  }

ですので AndroidManifest.xmlandroid.permission.ACCESS_NETWORK_STATE が設定されている場合などは、ネットワーク状況が回復したタイミングで画像の再リクエスト処理が行われることになります。

画像再リクエストを無効化する方法

この画像再リクエストの機能は、アプリによっては不要な場合もあり得るかと思います。

この機能を無効化するためには ConnectivityMonitorFactory を継承したクラスを作成し、何もしない ConnectivityMonitor を返すようにします。

そのためには AppGlideModule を継承したクラスで以下のように設定することで可能になります。

@GlideModule
class GlideModule : AppGlideModule() {

    override fun applyOptions(context: Context, builder: GlideBuilder) {
        ...

        builder.setConnectivityMonitorFactory { _, _ -> NullConnectivityMonitor() }
    }

    private class NullConnectivityMonitor : ConnectivityMonitor {
        override fun onStart() {
        }

        override fun onStop() {
        }

        override fun onDestroy() {
        }
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android開発でJSONからdataクラスを一発で作成する

この記事は?

JSON TO Kotlin Classを使って、
JSONのテキストを適当なKotlin data classに変換する方法を紹介します。

Pluginの導入

AndroidStudioのPreferences>PluginsからJSON TO Kotlin Classをinstall(ない場合はMarketplaceタブからDownload)

使用方法

data classを作成したいpackageで右クリックをして、Kotlin data class File from JSONを選択。

JSONテキストをペーストし、Class Nameを入力し、Generateを押す。

このような感じでdata classが作成される。

data class MovieList(
        val page: Int,
        val movies: List<Result>,
        val total_pages: Int,
        val total_results: Int
)

data class Result(
        val genre_ids: List<Int>,
        val id: Int,
        val original_language: String,
        val original_title: String,
        val overview: String,
        val popularity: Double,
        val poster_path: String,
        val release_date: String,
        val title: String,
        val video: Boolean,
        val vote_average: Int,
        val vote_count: Int
)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android開発でAPIキーを隠蔽してGitHubに上げる

この記事の目的

Android開発でWebAPIを利用する場合に、APIキーをGitのコミットログに含めずに扱う方法を紹介する。

手順

gradle.propertiesにAPIキーを記述

APIキーを記述

myApiKey=3a6...534

記述したAPIキーがgitの管理化に載らないよう、gradle.propertiesを.gitignoreに含めておく。

一度gitにgradle.propertiesを上げたことがある場合は、キャッシュを削除してからコミットする

BuildConfigの作成

app/build.gradleにbuildConfigFieldを定義、

android {
    defaultConfig {
        buildConfigField "String", "API_KEY", "\"${project.property("myApiKey")}\""
    }
}

ビルドするとBuildConfig.javaに追加されている

public final class BuildConfig {
  ...
  public static final String API_KEY = "3a6...534";
}

APIキーの取得

BuildConfig.MY_API_KEYでキーの文字列を取得

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        Log.i("MYTAG", BuildConfig.MY_API_KEY)
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Flutter】showDatePickerで誕生日を指定させよう

Flutterでは、ユーザーに「日付」を選択させたいときはshowDatePickerメソッドを使います。
UIは下の画像みたいな感じ。

image.png

これを使えば「誕生日を要求する」こともできます。
少し使い方にクセがあると感じたので、今回はshowDatePickerの基本的な使い方を紹介します。

main.dart
RaisedButton(
  child: Text('誕生日を設定する'),
  onPressed: () async {
    final date = await showDatePicker(
      context: context,

      locale: const Locale("ja"), //日本語化(アプリ自体を日本語対応させる必要があります。今回は省略させていただきます)
      initialDatePickerMode: DatePickerMode.year, //最初に年から入力させる
      initialDate: DateTime(DateTime.now().year - 10), //最初に選択させる日付(今回は10年前)

      firstDate: DateTime(DateTime.now().year - 100, DateTime.now().month, DateTime.now().day), //選択可能な、もっとも古い日付(今回は100年前の今日にしています)
      lastDate: DateTime(DateTime.now().year - 6, DateTime.now().month, DateTime.now().day), ////選択可能な、もっとも新しい日付(今回は6年前の今日にしています)
    );

    if (date != null) {
      //誕生日を取得した後の処理をここに書く
    }
  },
),

ざっとこんな感じ。(なお、今回は日本語化に関しての詳細は省かせていただきます。)

main.dart
      initialDatePickerMode: DatePickerMode.year, //最初に年から入力させる

これで「年」から入力させるようにできます。
誕生日なんかはいきなり月日からではなく、まずは年から選択させたいことが多いので、これは多用するかも。

main.dart
      initialDate: DateTime(DateTime.now().year - 10), //最初に選択させる日付(今回は10年前)

これは日付選択のポップアップが表示されたとき、一番最初に選ばれている日付を設定します。

main.dart
      firstDate: DateTime(DateTime.now().year - 100, DateTime.now().month, DateTime.now().day), //選択可能な、もっとも古い日付(今回は100年前の今日にしています)
      lastDate: DateTime(DateTime.now().year - 6, DateTime.now().month, DateTime.now().day), ////選択可能な、もっとも新しい日付(今回は6年前の今日にしています)

ここでは「日付選択の範囲」を設定しています。
さすがに0歳児に扱わせるわけにもいかず、かえって200、300歳の超絶高齢者がいるわけでもないので、とりあえず今回は「6歳~100歳」に範囲制限しています。

ちなみにDateTime型の形式は以下の通り。
image.png

もし「10年3か月4日前」と表現させたい場合は、上記に沿って考えると

DateTime(DateTime.now().year - 10, DateTime.now().month - 3, DateTime.now().day - 4)

と表現できますし、「10年4日前」と表現させたい場合は

DateTime(DateTime.now().year - 10, DateTime.now().month, DateTime.now().day - 4)

と表現できます。

main.dart
    if (date != null) {
      //誕生日を取得した後の処理をここに書く
    }

最後に、showDatePickerメソッド外のこの記述ですが、日付無選択ならnullが返ってくるので「日付を選択されたとき」のみ処理を実行させるようにしています。

上のコードをコピペすれば、とりあえずshowDatePickerを表示するRaisedButtonウィジェットを実装できると思います。

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

【Android】Fatal Exception: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState をコルーチンで回避する【Tips】

結論

lifecycleScope.launchWhenResumed {
    HogeDialogFragment.show(supportFragmentManager, null)
    とか
    supportFragmentManager.commit {
        replace(R.id.container, HogeFragment())
    }
}

解説

Can not perform this action after onSaveInstanceStateはその名の通り、ActivityのonSaveInstanceStateメソッドが実行された後に、Fragmentの操作などを行うと発生するクラッシュです。
Rxなどの非同期処理を実行後にダイアログを表示する、などの実装をすると発生します。

では、onSaveInstanceStateはいつ実行されるのかというと

If called, this method will occur after onStop() for applications targeting platforms starting with Build.VERSION_CODES.P. For applications targeting earlier platform versions this method will occur before onStop() and there are no guarantees about whether it will occur before or after onPause().

のように、なんとP以前ではいつ呼ばれるかも明確に決まっていません。

ですが、少なくともライフサイクルがonPauseに入る前、つまりonResumeの時のみ動くようにすれば回避できそうですよね?
そこで、launchWhenResumedを使います。これは、ライフサイクルがonResumeの時のみブロック処理を実行する関数になります。

Launches and runs the given block when the Lifecycle controlling this LifecycleCoroutineScope is at least in Lifecycle.State.RESUMED state.

これを使うことで、P以前、以後関係なく安心して処理を実行することができるようになります。特にFragmentの操作などは、拡張関数などを定義しておくとスッキリ書けるのでオススメです。

おわりに

こんな便利なものを用意してくれて、コルーチンありがとう。コルーチンをすこれ。

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

Charlesの便利な機能・使い方

Charles は HTTP(S) 通信の内容を閲覧したり、改変したりすることができるツールです。

Android や iOS のアプリ開発などで通信のデバッグをする際に非常に役に立つツールですので、便利な機能や使い方についてご紹介したいと思います。

注意点

この記事では Charles の導入・設定方法についての解説は省略させていただきます。

私が Mac ユーザーですので、記事内で紹介しているショートカットキーや画面キャプチャは Mac のものとなります。

また、Charles のバージョンは v4.6.1 時点のものとなります。

通信の閲覧

Structure と Sequence タブ

通信を閲覧する際に StructureSequence の2種類のタブがあります。

Structure では Host ごとにグルーピングされて表示され、Sequence では指定した順番に表示されます。

Structure

Sequence

リクエスト、レスポンス情報の表示

画面上部にある Contents タブを選択するとリクエストヘッダ、レスポンスヘッダ、レスポンスボディを閲覧することができます。

リクエストヘッダ、レスポンスヘッダ

以下のキャプチャの右側上部がリクエストヘッダの情報、右側下部がレスポンスヘッダの情報になります。

それぞれ Headers タブを指定することにより情報を見ることができます。

レスポンスボディ

以下のキャプチャでは Qiita の API を使用しており、レスポンスボディには JSON 形式の文字列が返されます。 この場合 JSON Text タブを選択することによりレスポンスボディの内容が確認できます。

通信の改変

Throttling

通信速度の設定ができます。

通信が悪い状況を確認したい場合に使えます。

通信速度の設定

メニューの Proxy > Throttle Settings... で設定画面が開きます。

ショートカットメニューだと shift + command + T です。

Throttle Settings.png

  • Throttole preset
    • 設定のパターンを選択することができます。いくつかデフォルトで設定のパターンが用意されています
  • Add Preset - 好みの設定のパターンを追加することができます
  • Enable Throttling
    • チェックを入れると設定が有効になります
  • Only for selected hosts
    • 指定したホストのみ設定を有効にすることもできます

通信速度設定の有効・無効切り替え

設定の有効・無効の切り替えは以下の方法でも可能です。

  • ショートカットキー command + T
  • メニューの Proxy > Start Throttling / Stop Throttling
  • 亀のボタンによる切り替え(以下のキャプチャ参照)

turtle.png

Breakpoint

指定した通信のリクエスト / レスポンス時にブレークポイントを設定することができます。

ブレークポイントが設定された通信のリクエストが実行される前、もしくはレスポンスを受け取る前にブレークポイント画面が表示され、リクエストやレスポンスの内容を変更することが可能です。

私はよくレスポンスにブレークポイントを設定し、Web サーバーにリクエストは届くけどレスポンスは受け取れなかった状況を確認する際に使用したりしています。

ブレークポイントの設定

メニューの Proxy > Breakpoint Settings... で設定画面が開きます。

ショートカットメニューだと shift + command + K です。

以下が設定画面になります。

Breakpoint Settings1.png

Enable breakpoints にチェックを入れるとブレークポイントの設定が有効になります。

Add から Breakpoint に設定したい通信を設定します。

Breakpoint Settings2.png

ブレークポイントの設定は RequestResponse の設定が可能です。(両方同時に設定も可能です)

Charles で閲覧済みの通信をブレークポイントに設定するのであれば、その通信を選択して右クリックし Breakpoints を選択すると簡単です。

Breakpoint Settings3.png

ブレークポイントの有効・無効切り替え

ブレークポイントの有効・無効の切り替えは以下の方法でも行えます。

  • ショートカットキー command + K
  • メニューの Proxy > Enable Breakpoints / Disable Breakpoints
  • 六角形のボタンによる切り替え(以下のキャプチャ参照)

Breakpoint Button.png

ブレークポイントの画面

ブレークポイントで指定された通信のリクエストやレスポンスが実行されるタイミングで、自動的にブレークポイントの画面が表示されます。

リクエスト時のブレークポイント

リクエスト時のブレークポイントの画面は以下のようになっています。

画面上部に Edit Request タブがあり、こちらに切り替えるとリクエストの編集が行えます。

以下のキャプチャでは URL の編集が行えるようになっています。

HTTP メソッドやパス、クエリパラメーターの変更や、クエリパラメーターの追加・削除などが行えます。

画面下部にあるタブを Headers に切り替えると、リクエストヘッダの編集も行えます。

編集が完了したら画面下部にある ExecuteAbortCancel のいずれかのボタンでその後の通信処理の動作を選択できます。

  • Execute
    • 編集で変更した内容を適用した上で通信処理を継続する
  • Abort
    • 通信を失敗させる
  • Cancel
    • 編集で変更した内容を破棄して通信処理を継続する

レスポンス時のブレークポイント

レスポンス時のブレークポイントの画面は以下のようになっています。

画面上部に Edit Response タブがあり、こちらに切り替えるとレスポンスの編集が行えます。

以下のキャプチャでは レスポンスヘッダの編集が行えるようになっています。

レスポンスボディが JSON 形式の文字列で返される場合、画面下部にあるタブを JSON Text に切り替えるとレスポンスボディの編集も行えます。

編集完了後はリクエスト時と同様に ExecuteAbortCancel のいずれかのボタンを押してその後の動作を選択します。

ブレークポイントによる編集はちょっと変更したい時には便利ですが、変更内容が多かったり通信のたびに何度も変更する必要がある場合は不便です。

その場合は後述する RewirteMap Local を使うことをオススメします。

Map Remote

Map Remote ではリクエスト先を変更することができます。

例えばアプリ開発でクライアントも Web API も一人で開発していて、Web API の変更をクライアント側で試したい場合に接続先をローカルに立ち上げたサーバに変更することなどができます。

メニューの Tools > Map Remote... で設定画面が開きます。

ショートカットメニューだと option + command + M です。

Enable Map Remote にチェックを入れると Map Remote の設定が有効になります。

Add ボタンをクリックすると設定を追加することができます。

Map From に既存の通信先を指定し、Map To で変更したい通信先を指定します。

この例だと https://qiita.com の通信をローカルに立ち上げたサーバ http://localhost:3000 に接続先を変更しています。

Map Local

Map Local は指定されたローカルファイルの中身を読み込んでレスポンスボディとして返す機能です。

レスポンスボディの内容を自由に設定することができるので、デバッグする際などにはとても重宝します。

メニューの Tools > Map Local... で設定画面が開きます。

ショートカットメニューだと option + command + L です。

Enable Map Local にチェックを入れると Map Local の設定が有効になります。

Add ボタンをクリックすると設定を追加することができます。

Map ToLocal path にレスポンスボディとして返したい内容を記述したファイルのパスを設定します。

閲覧済みの通信を Map Local で設定するのであれば、通信を選択して右クリックして Map Local を選択すると簡単です。

No Caching

リクエストヘッダ、レスポンスヘッダのキャッシュに関する設定を変更して、キャッシュを無効にしてくれます。

メニューの Tools > No Caching... で設定画面が開きます。

ショートカットメニューだと option + command + N です。

Enable No Caching にチェックを入れることで、キャッシュが無効になるように設定されます。

Only for selected locations にチェックを入れることで、Location に設定された通信先のみが無効になるように設定することも可能です。

Block List

Block List では指定された通信をブロック(失敗)させます。

メニューの Tools > Block List... で設定画面が開きます。

ショートカットメニューだと option + command + B です。

Enable Block List にチェックを入れると、Location に指定されている通信がブロックされます。

Blocking action で失敗させる際の挙動を設定でき、以下の2つのパターンがあります。

  • Drop connection
  • Return 403 Response

Add ボタンを押すと、Block List への追加設定が行えます。

以下のキャプチャの下部に説明がありますが、空のフィールドは全ての値にマッチするようになっています。

また、*? によるワイルドカードの指定が可能です。

Allow List

Allow List は Block List とは逆に指定された通信だけを許可し、それ以外の通信はブロック(失敗)させます。

メニューの Tools > Allow List... で設定画面が開きます。

ショートカットメニューだと option + command + W です。

設定方法自体は Block List と同じなので、設定方法の説明は割愛いたします。

Rewrite

Rewrite は指定された通信のリクエスト、レスポンスの内容を変更するための機能で、以下のことが行えます。

  • リクエストヘッダ、レスポンスヘッダへの値の追加、削除、変更
  • ホスト、パスの変更
  • クエリパラメータの追加、削除、変更
  • レスポンスのステータスコードの変更
  • ボディの変更

Rewrite はメニューの Tools > Rewirte で設定画面が開きます。

ショートカットメニューだと option + command + R です。

画面左側には設定した内容の一覧が表示されます。

左側で設定した内容の一覧を選択すると、画面右上に Location の一覧(どの通信を Rewrite 対象にするか)、右下には Rewrite する際のルールが表示されます。

Location の設定は Block List と同じなので割愛します。

Rewrite のルールを設定する際の画面は以下のようになっています。

Type で変更したい内容を選択でき、以下の項目が選択可能です。

MatchReplace の項目にそれぞれ値を設定することで、変更する内容を設定することができます。

先ほどのキャプチャの例だと、per_page のクエリパラメータの値を 10 に設定するようにしています。

正規表現を使用することもできるので、値の変更を行う際にはある程度柔軟に設定することも可能になります。

その他機能

Copy cURL Request

curl コマンドをクリップボードにコピーしてくれます。

例えば特定の Web API に何か問題が見つかってチームメンバーに共有する必要があった場合に便利で、 curl コマンドを共有することによって解決が容易になることが多いので、ぜひとも使ってみてください。

  • 特定の通信を選択して右クリックする
  • Copy cURL Request を選択する(キャプチャ参照)

実行した結果、以下のようにクリップボードにコピーされます。

curl -H 'Host: qiita.com' -H 'user-agent: okhttp/4.9.1' -H 'if-none-match: W/"6687ae9470c7ca50e98cf521b5ec1291"' --compressed 'https://qiita.com/api/v2/items?page=1&per_page=20'

Copy Response

レスポンスボディをクリップボードにコピーしてくれます。

前述の Map Local でローカルに配置するファイルを作成する際などに使えます。

  • 特定の通信を選択して右クリックする
  • Copy Response を選択する(キャプチャ参照)

Repeat / Repeat Adcanced

Repeat では指定した通信を再度リクエストします。

Repeat Advanced ではリクエストをリピートする回数やその際の並列数、リクエスト間隔を指定することができます。

  • 特定の通信を選択して右クリックする
  • Repeat / Repeat Advanced... を選択する(キャプチャ参照)

まとめ

以下に今回紹介した機能をまとめてみました。

機能名 概要
Throttling 通信速度の設定
Breakpoint 指定した通信にブレークポイントを設定し、リクエスト・レスポンスの内容を変更して実行したり、通信自体を中断させることができる
Map Remote 接続先の変更
Map Local 指定されたローカルファイルの内容を読み込んで、それをレスポンスボディとして返す
No Caching キャッシュを無効にする
Block List 指定した通信をブロックする
Allow List 指定した通信のみを許可し、それ以外をブロックする
Rewrite リクエスト・レスポンスの内容を変更する
Copy cURL Request curl コマンドをクリップボードにコピーする
Copy Response レスポンスボディの内容をクリップボードにコピーする
Repeat / Repeat Advanced 通信の再リクエストを行う

最後に

以上、いくつか Charles の機能を紹介させていただきました。

Charles は今回紹介した機能以外にも便利な機能があるので、ドキュメントなどを参考にしてみてください。

Charles を使う際にこの記事が参考になっていただければ嬉しいです。

参考

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

デザイナさんと一緒に検討すること・検討しておいて欲しいこと

 アプリ開発をしていて設計・実装者の立場から、デザイナ・UIUX担当との連携の初期段階で共有しておきたいことをリストアップしました。

 要望をデザインに落とし込む手法とかとかは専門外なので、ここでは設計・実装者の立場から、デザイナ・UIUX担当との連携の中で漏らしてしまいそうな検討事項をまとめてます。

1. 一緒に検討したい仕様

まず、設計・実装上の理由から「こんな場面があるのでUIUX・デザインを考えて欲しい」という項目。

  1. アクセス許可の取得箇所と許可してもらうための工夫

    • 必要とするアクセス許可とその理由をデザイナと共有して、画面遷移のどこに挟むのがいいのか、なるべく許可してもらうための説明方法(ダイアログだけでいいのか、画面案内にしたがって許可を促すのか等)を検討。
  2. 情報がない場合のデザイン

    • サーバからデータ取得中で情報がない場合の見せ方
      初回起動で情報がない、キャッシュが古いのでキャッシュ表示はできない、という場合のデザイン
    • 表示件数が0件の場合の見せ方
    • サーバエラーや通信エラーで表示するものがない場合の見せ方
  3. サーバアクセス中や時間のかかる処理中のインジケーター

    • 画面操作をブロックしてはダメな場所、良い場所(ブロックせざるを得ない場所)を振り分ける
    • 操作ブロックしてはダメな場所でのインジケーターの見せ方(画面の一部をぐるぐる表示、画面の一部にプログレスバー配置等)
    • 操作ブロックする場合、そのキャンセル方法の検討
      • キャンセルボタンを配置
      • ヘッダ等の戻るボタンタップでキャンセル
      • 画面のどこかをタップするとキャンセルダイアログ表示、等
    • 進捗付きのインジケーターにするか・否か
  4. アプリ起動時に表示する画面

    • アプリ強制終了時の画面を表示するのか(操作の継続性を優先)
    • 一定時間が経っていたら最新情報が集まる画面等、なるべく見せたい画面を表示するのか
    • 常に決まった画面を出すのか
  5. 表示する情報の更新方法

    • 表示する情報の賞味期限により、更新の仕方が変わるので情報を分類。
      • ログ情報・・・過去の出来事で変わらない情報。例)利用履歴、購入実績
      • 即時情報・・・更新されたら即座に伝える必要がある情報。例)残額、住所等の個人情報
      • 期限付き情報・・・一定頻度で更新が必要な情報。 例)セール情報、レコメンド情報
    • 分類した情報のシチュエーション別の更新要否判断
      • リロードボタン/プルリフレッシュ等、利用者操作による更新要否
      • アプリ起動時の情報更新要否
      • バックグラウンドでの情報更新要否
      • アプリフォア時の情報更新要否
      • 一定時間での自動更新要否
    • 情報のキャッシュ要否
      分類した情報別にキャッシュの有無、キャッシュ期間を決める
  6. オフラインでできること、できないこと

    • 画面毎にオフラインでの制約事項の明確化と、その際のUIUX・デザインを決める
  7. 検索機能の具体的な動作

    • インクリメンタルサーチの必要性判断
  8. アプリストアのレビュー訴求

    • ★評価アラートを表示する条件とタイミングを決める
  9. DynamicType対応要否
    工数がかかるし、後で対応するのも大変なので関係者納得の理由で判断

  10. アイコンバッジに表示する件数の定義

  11. Appスイッチャー/タスクスイッチャーの状態で表示する画像

    • セキュリティ等の対策のため、OSのAppスイッチャーにスクリーンショットを表示したくない場合、何を表示しておくか
  12. その他

    • オープンソースライセンス表記どこにするか?
    • VoiceOver/TalkBack対応要否

2. デザイナさん主体で決めてもらいたい仕様

ここはデザイナ・UIUX担当さんに忘れずに考えて欲しい項目。

  1. 条件を満たさない場合のボタンタップ可否ルール
    • 非活性にして押させないのか、押させて動作しない理由の説明表示をするのか。
  2. 長いテキストの表示ルール
    • 右端で折り返して全部表示するのか、文字を縮小してでも表示するのか。
    • 行数の上限を設けるのか。
    • 行数の上限を設ける場合、「…」はどこに入れるのか、いれないのか。
    • 数値の場合、桁不足をどう見せるか。(NNN+とか)
  3. 日時のフォーマットとそれを使う場所のルール
    • 202X年01月01日、202X年1月1日。202X/1/1
    • 00時00分、00:00
    • 12時間表記、24時間表記
    • あとX日、あとX時間
    • X日前、X時間前
  4. タップ以外での画面遷移
    • スワイプで画面遷移できる画面とできない画面の明確化
    • エッジスワイプで戻れる画面の明確化
  5. 表示要素のスケール
    • 大きな画面では、大きく見せたいのか、たくさんの情報を見せたいのか。
      大きく見せたい場合、各種アイコン・フォントのサイズは追従させるのか、させないのか。
      追従させるなら、エンジニアと一緒に追従させる部分を決める(追従させる範囲はなるべく限定したい)
    • 小さい画面の場合にどう見せるのか。
      構成要素を減らすのか、アイコン+フォントの組み合わせならアイコンだけにするとか。
  6. 表示する画像のアスペクト比がバラバラな場合の表示
    • 異なるアスペクト比の画像をどう敷き詰めるのか(グリッド状にするのか、隙間なく敷き詰めるのか)、Aspect Fit/Aspect Fillはどうするか。
  7. ダークモード/ダークテーマの対応要否
    • 対応する場合、配色のルール
  8. 対応するデバイスの向きとデザイン
    • スマホの縦横の専用デザインの有無
    • タブレットの縦横の専用デザインの有無
  9. マルチウィンドウ対応要否(SlideOver、SplitView)
  10. アラート
    • はい・いいえ、OK・キャンセル・閉じる or OK の文言の適用ルール
    • はい・いいえ の配置ルール(破壊的/非破壊的の考慮要否)
  11. その他
    • 既存アプリと同様のアクションをさせたいなら『XアプリのY部分と同じ』のようなデザイン指示をお願いしておく。

3. 進め方について合意しておきたいこと。

スムーズに作業を進めるためにデザイナさん(&プロジェクト全体)で合意しておく項目。

  1. デザイン環境
    • デザインツール
      開発するアプリの特徴にもよるが、なるべく意思疎通しやすいように動くもので確認したい。デザイナには高度な紙芝居ができるツール(AdobeXDとか)を使って欲しい(プロトタイプとして関係者に確認してもらいやすい)。
    • ツールの利用にライセンスが必要なら必要なラインセンス数の確認。
    • デザインデータのバージョン管理方法
      チケット・イシューと関係づけてどう管理するのか、変更箇所をどう記録するか、ツールが備えている機能と合わせて決める。
    • テキスト/メッセージの共有方法
      ダイアログに表示するメッセージやテキストを一覧化し開発者とデザイナで共有する。トンマナ・文言見直しに共有が必要。多言語対応の場合は全てのテキストを共有。
    • デザインツールで使っている色空間(sRGB、AdobeRGB、Display P3)
      実装時に色空間を合わせておかないと微妙にデザイン色と変わってしまうので注意。
  2. 関係者との合意タイミング
    • 表示要素・ワイヤーの決定タイミング。
      これらが決まった後にサーバ側に用意してもらうAPI要件をまとめたりするため。
    • スマホ版/タブレット版のどちらを優先するか・同時にデザインするのかの順番、及び、先行する側を後続側で取り込むタイミング
    • トンマナ・文言の見直しタイミング
    • UIUX・デザイン承認者との合意タイミング、再レビューのタイミング

最後に

 デザイナさんと絡んだ経験は多くないので他にも検討すべきことは多々あると思いますが、このリストが少しでも役立てば幸いです。あと、こんな検討も必要だよね、というものがあれば指摘もらえると助かります。

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

Android App Bundle で戸惑ったこと (Unity)

はじめに

少々時間を無駄にしたので、ここに書き留めます。

環境

  • Unity 2018.4.x

得た知見

aabのBuild&Runでは、エラーせずとも実行されない場合がある

  • aabをBuild&Runした場合、端末への転送に失敗してもエラーにならず、インストール済みのバージョンが起動されます。
    • 転送に失敗する場合は、署名が違うとか、バージョンダウンだとか、色々あります。
  • なお、既にアプリが起動中だった場合は再起動されません。
    • 従って、ビルド開始時にあらかじめ旧版を起動しておけば、再起動するかどうかで成否を確認可能です。

aabとapkでは挙動が異なる場合がある

  • 同じコードをビルドしても、aabとapkでは挙動が異なる場合があります。
    • 具体的には、アセットの読み込み時に、apkには存在しない遅延が、aabでは生じることがあります。
    • apkは最初に全てロードしますが、起動を早めるために、aabでは必要になってからロードするのでしょうか。

さいごに

  • 転送に失敗していることに気付かず、特定のソースだけがコンパイルから漏れているのかと考えて、試行錯誤してしまいました。
  • 挙動が異なるのは、ソースが巻き戻っているのではないかと疑ってしまいました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む