シームレス化のための設計

アプリが高速でレスポンシブであっても、設計上の決定事項によっては、ユーザーに問題が発生する可能性があります。その理由は、他のアプリやダイアログとの予期せぬ操作、意図しないデータの損失、意図しないブロックなどがあります。このような問題を回避するには、アプリが実行されるコンテキストと、アプリに影響を与える可能性があるシステム操作を把握することが重要です。つまり、システムや他のアプリとシームレスに連携するアプリを開発する必要があります。

シームレス性の一般的な問題は、アプリのバックグラウンド プロセス(サービスやブロードキャスト レシーバなど)が、なんらかのイベントに応答してダイアログをポップアップすることです。特にエミュレータでアプリを単独でビルドしてテストしている場合は、無害な動作に見えるかもしれません。ただし、アプリを実際のデバイスで実行する場合、バックグラウンド プロセスがダイアログを表示する時点で、アプリにユーザー フォーカスがないことがあります。そのため、アプリがアクティブなアプリの背後にダイアログを表示する場合や、現在のアプリからフォーカスを取得して、ユーザーが行っている操作(たとえば、電話の発信など)の前にダイアログを表示する場合があります。この動作は、アプリやユーザーにとっては機能しません。

この問題を回避するには、アプリでユーザーに通知するための適切なシステム機能(Notification クラス)を使用する必要があります。アプリは通知を使用することで、フォーカスしてユーザーの操作を妨げるのではなく、ステータスバーにアイコンを表示することで、イベントが発生したことをユーザーに通知できます。

シームレス性の問題の別の例としては、onPause() などのライフサイクル メソッドが正しく実装されていないためにアクティビティが誤って状態やユーザーデータが失われることがあります。または、アプリが他のアプリで使用することを想定したデータを公開している場合は、たとえば、誰でも読み取り可能な未加工のファイルやデータベースを通じて公開するのではなく、ContentProvider を介して公開する必要があります。

これらの例に共通するのは、システムや他のアプリとうまく連携するという点です。Android システムは、ブラックボックス コードのチャンクではなく、疎結合コンポーネントの連携のようにアプリを扱うように設計されています。これにより、デベロッパーはシステム全体を、これらのコンポーネントのさらに大規模な連携として表示できます。他のアプリとクリーンかつシームレスに統合できるため、独自のコードを設計する際は、その点に配慮する必要があります。

このドキュメントでは、シームレス化に関する一般的な問題とその回避方法について説明します。

データを削除しない

Android がモバイル プラットフォームであることを常に頭に入れておいてください。当然のことのように見えますが、別のアクティビティ(「電話着信」アプリなど)がいつでも自分のアクティビティの上にポップアップ表示される可能性があることを覚えておくことが重要です。これにより、onSaveInstanceState() メソッドと onPause() メソッドが起動され、アプリが強制終了される可能性があります。

他のアクティビティが表示されたときにユーザーがアプリのデータを編集していた場合、アプリが強制終了されると、アプリはそのデータを失う可能性があります。もちろん、編集中のデータを先に保存した場合は除きます。「Android Way」はまさにそのための手段です。入力を受け付けたり編集したりする Android アプリは、onSaveInstanceState() メソッドをオーバーライドし、なんらかの適切な方法で状態を保存する必要があります。ユーザーがアプリに再度アクセスしたときに、データを取得できる必要があります。

この動作をうまく活用している典型的な例として、メールアプリが挙げられます。別のアクティビティの開始時にユーザーがメールを作成していた場合、アプリは処理中のメールを下書きとして保存する必要があります。

未加工のデータを公開しない

もし下着を着て通りを歩かなければ、データも必要ありません。特定の種類のアプリを公開することは可能ですが、通常はこれは最適な方法ではありません。元データを公開するには、他のアプリがデータ形式を認識する必要があります。その形式を変更すると、同様に更新されていない他のアプリはすべて中断します。

「Android Way」とは、クリーンでよく練られたメンテナンス可能な API を介して、他のアプリにデータを公開する ContentProvider を作成することです。ContentProvider を使用するのは、Java 言語インターフェースを挿入して、密結合の 2 つのコードを分割、コンポーネント化する場合に似ています。つまり、ContentProvider によって公開されるインターフェースを変更することなく、データの内部形式を変更でき、他のアプリに影響を与えることもありません。

ユーザーの操作を中断させない

ユーザーがアプリ(通話中に電話アプリなど)を実行している場合、意図的に実行しているのはかなり確実です。そのため、現在のアクティビティからのユーザー入力に直接応答する場合を除き、アクティビティを生成しないようにしてください。

つまり、バックグラウンドで実行されている BroadcastReceiver またはサービスから startActivity() を呼び出さないでください。このように設定すると、現在実行中のアプリはすべて中断され、ユーザーがイライラします。さらに悪いことに、アクティビティが「キーストローク バンディット」になって、ユーザーが前のアクティビティに提供した入力の一部を受け取る可能性があります。アプリの動作によっては、これは好ましくないニュースになる可能性があります。

バックグラウンドからアクティビティ UI を直接生成するのではなく、NotificationManager を使用して通知を設定する必要があります。これらはステータスバーに表示されます。ユーザーは好きなときにこれらをクリックして、アプリケーションで表示する内容を確認できます。

(自分のアクティビティがすでにフォアグラウンドにある場合、ユーザーは入力に応じて次のアクティビティが表示されることを期待します)。

処理が多い場合はスレッドで実行する

アプリケーションで高価な計算や長時間実行の計算を実行する必要がある場合は、通常はスレッドに移動する必要があります。これにより、「アプリケーション応答なし」という恐ろしいダイアログがユーザーに表示されることを防ぎます。最終的な結果はアプリが壊れることになります。

デフォルトでは、アクティビティとそのすべてのビュー内のすべてのコードが同じスレッドで実行されます。これは、UI イベントを処理するのと同じスレッドです。たとえば、ユーザーがキーを押すと、キーダウン イベントがアクティビティのメインスレッドのキューに追加されます。イベント ハンドラ システムは、そのイベントを迅速にキューから出して処理する必要があります。そうでなければ、数秒後にアプリケーションがハングしていると判断し、ユーザーに対してアプリケーションの強制終了を提示します。

長時間実行コードがある場合、アクティビティ内でインラインで実行すると、イベント ハンドラ スレッドで実行されます。したがって、イベント ハンドラが実質的にブロックされます。これにより入力処理が遅延し、ANR ダイアログが表示されます。これを回避するには、計算をスレッドに移動します。その方法については、こちらの応答性の設計ドキュメントで説明しています。

1 つのアクティビティ画面に負荷をかけすぎない

使用する価値のあるアプリには、おそらく複数の異なる画面があります。UI の画面を設計する際は、必ず複数の Activity オブジェクトのインスタンスを使用してください。

開発のバックグラウンドによっては、アクティビティをアプリのエントリ ポイントとするという点で、アクティビティを Java アプレットのようなものと考えることができます。ただし、これはあまり正確ではありません。アプレットのサブクラスが Java アプレットの単一エントリ ポイントである場合、Activity はアプリへの複数のエントリ ポイントの 1 つとみなす必要があります。「main」アクティビティと他のアクティビティとの唯一の違いは、「main」アクティビティのみが、AndroidManifest..xml ファイルで「android.intent.action.MAIN」アクションに興味を示した唯一のアクティビティである点です。

そのため、アプリを設計する際は、アプリをアクティビティ オブジェクトの連携と考えてください。これにより、長期的に見てコードのメンテナンスが容易になります。また、優れた副作用として、Android のアプリ履歴や「バックスタック」モデルともうまく調和します。

システムのテーマをベースにする

ユーザー インターフェースをデザインする場合は、違和感なく溶け込ませることが重要です。ユーザーは、思っているユーザー インターフェースと対照的なアプリに対して不快感を与える。UI を設計する際は、できる限り独自のものを展開しないようにしてください。代わりにテーマを使用します。テーマの必要な部分はオーバーライドまたは拡張できますが、少なくとも他のすべてのアプリと同じ UI ベースから始める必要があります。詳細については、スタイルとテーマをご覧ください。

複数の画面解像度に対応する UI を設計する

サポートされる画面解像度は Android 搭載デバイスによって異なります。また、横表示に切り替えるなど、その場で解像度を変更できるアプリもあります。レイアウトとドローアブルには、さまざまなデバイス画面で適切に表示できるだけの柔軟性を持たせることが重要です。

幸い、これを簡単に行うことができます。簡単に言うと、主要な解像度に合わせてアートワークのさまざまなバージョン(使用する場合)を用意し、さまざまなサイズに対応するようにレイアウトを設計する必要があります。(たとえば、ハードコードされた位置は使用せず、代わりに相対レイアウトを使用してください)。これだけ行うと、あとはシステムが処理し、どのデバイスでもアプリが適切に表示されます。

ネットワークは低速であることを前提とする

Android デバイスにはさまざまなネットワーク接続オプションが用意されています。すべてにある程度のデータアクセスのプロビジョニングがありますが、どれも他よりも高速になるものもあります。ただし、最も低い共通点は、GSM ネットワーク用の非 3G データサービスである GPRS です。3G 対応デバイスであっても 3G 以外のネットワークでは多くの時間が費やされることになるため、低速のネットワークは今後かなり長期にわたって現実のものとなります。

そのため、ネットワーク アクセスと帯域幅を最小限に抑えるようにアプリケーションをコーディングする必要があります。ネットワークが高速であるとは仮定できないため、常に低速であることを想定しておく必要があります。ユーザーがたまたま高速のネットワークを使用していれば、それは素晴らしいことです。ユーザーのエクスペリエンスは向上するだけです。ただし、この逆のケースは回避する必要があります。使用できる場合があるものの、その時点でユーザーの現在地から判断して残りが遅くなるアプリは、人気がない可能性が高いためです。

潜在的な問題点の一つは、エミュレータを使用している場合、エミュレータはデスクトップ コンピュータのネットワーク接続を使用するため、このトラップに陥りやすいことです。これは、セル ネットワークよりもはるかに高速であることがほとんどです。そのため、低速のネットワーク速度をシミュレートするように、エミュレータの設定を変更する必要があります。この操作は、Android Studio 内で AVD Manager を使用して行うか、エミュレータの起動時にコマンドライン オプションを使用して行います。

タッチスクリーンやキーボードがあることを前提としない

Android はさまざまなデバイス フォーム ファクタをサポートしています。つまり、Android デバイスには完全な「QWERTY」キーボードを搭載するモデルもあれば、40 キーや 12 キーなどのキー構成を搭載するデバイスもあると言えるでしょう。同様に、タッチ スクリーンを搭載しているデバイスもありますが、そうでないデバイスも多数あります。

アプリを作成する際には、こうした点に注意してください。特定のキーボード レイアウトについて仮定しないでください。ただし、そのデバイスでのみ使用できるようにアプリを制限したい場合は例外です。

デバイスの電池を節約する

モバイル デバイスは、壁に常時接続されている場合、モバイル性は低くなります。モバイル デバイスはバッテリー駆動です。バッテリーを長時間充電すればするほど、すべての人、特にユーザーの満足度が上がります。電池を消費する最大の消費電力は、プロセッサと無線通信の 2 つです。そのため、できる限り処理を少なくし、ネットワークの使用頻度をできるだけ抑えるようにアプリを作成することが重要です。

アプリケーションで使用されるプロセッサ時間を最小限に抑えることは、実際に効率的なコードを作成することにあります。無線通信による電力消費を最小限に抑えるため、エラー状態を適切に処理し、必要なものだけを取得してください。たとえば、ネットワーク オペレーションが失敗したときに、常に再試行しないでください。一度失敗した場合は、ユーザーが受信していないことが原因である可能性があります。そのため、すぐに試行すると再び失敗する可能性がありますが、実行するのはバッテリーの電力を浪費するだけです。

ユーザーはかなり賢いです。プログラムが消費電力を大量に消費している場合、そのことに気付くと信頼できます。その時点で確信を持てる唯一の点は、プログラムがあまり長くインストールされないということです。