Android App 作成 ~アプリの基礎を学ぶ~

Androidアプリの基礎

下のページにあるチュートリアルを行っていきます。
参考サイト:Android Developperのサイトです。

そして、Androidアプリを作成するうえで理解しておきたいのが、下のようなライフサイクルと呼ばれるものです。
LifeCycle

具体的には、「アクティビティのライフサイクル」ということです。

「初めてのアプリを作成する」という項目は飛ばします。以前やったので。。。

概要をつかむ

Androidアプリを作成するための材料(コンポーネント)として以下のものがあります。

  • アクティビティ
  • サービス
  • ブロードキャスト レシーバ
  • コンテンツ プロバイダ

つまりは、上のような「グループに分けられたクラスがありますよ」ということです。

これらのクラスがそれぞれ、次のように説明されています。

アクティビティ
アクティビティは、ユーザーとやり取りするためのエントリ ポイントです。これは、1 つのユーザー インターフェースを持つ 1 つの画面で表されます。たとえば、メールアプリには、新着メールの一覧を表示するアクティビティ、メールを作成するアクティビティ、そしてメールを閲覧するアクティビティがあります。メールアプリでは、これらの複数のアクティビティが一体となって 1 つのユーザー エクスペリエンスを形成しますが、それぞれのアクティビティは他のものから独立しています。したがって、これらのアクティビティのいずれかを、別のアプリから開始することができます(メールアプリが許可している場合)。たとえば、カメラアプリからメールアプリの新規メールを作成するアクティビティを開始できます。そのようにして、ユーザーが写真をメールで共有できるようにします。アクティビティは、システムとアプリ間における次の重要なインタラクションを行えるようにします。
アクティビティをホストしているプロセスを継続的に実行するために、ユーザーの現在の操作内容(画面の表示)を追跡。
以前に使用されたプロセス(停止されたアクティビティ)のうち、ユーザーが再度アクセスする可能性があるものを検知し、それらの優先順位を上げてプロセスを維持。
アプリがプロセスを強制終了した場合に、ユーザーが以前の状態を復元したアクティビティに戻れるように支援。
アプリ間でのユーザーフローをアプリが実装する手段と、システムがそれらのフローを連携させるための手段を提供(ここでは最も一般的な例を説明します)。
アクティビティは Activity クラスのサブクラスとして実装します。Activity クラスの詳細については、デベロッパー ガイドのアクティビティをご覧ください。

サービス
サービスは、さまざまな理由によりアプリをバックグラウンドで実行し続けるための汎用エントリ ポイントです。長期間の操作やリモート プロセスを処理するためにバックグラウンドで実行されるコンポーネントです。サービスにはユーザー インターフェースがありません。たとえば、サービスはユーザーが別のアプリを使用している間にバックグラウンドで音楽を再生したり、ユーザーが別のアクティビティを操作している間にそれを妨げることなくネットワークからデータを取得したりします。アクティビティなどの他のコンポーネントが、サービスを開始して実行したり、サービスとやり取りするためにサービスにバインドしたりすることができます。アプリの管理方法についてサービスがシステムに通知するセマンティクスは 2 つあり、それぞれ異なる意味を持っています。開始されたサービスが、作業が終了するまで自身の実行を維持するようシステムに指示します。これにより、ユーザーがアプリから離れても、バックグラウンドでデータを同期したり、音楽を再生したりできます。バックグラウンドでのデータの同期や音楽の再生は、開始されたサービスとしてはそれぞれ異なるタイプと認識され、システムがそれらのサービスに対して行う処理もそれぞれ異なります。
音楽の再生はユーザーが直接意識しているものなので、アプリはフォアグラウンドになりたいことをユーザーに通知で知らせることで、システムにそれを指示します。この場合、システムはそのサービス プロセスの実行を維持するよう最善を尽くします。このプロセスが終了するとユーザーが不満を覚えるからです。
通常のバックグラウンド サービスは、その実行をユーザーが直接意識していません。したがって、システムはより柔軟にそのプロセスを管理できます。ユーザーにとってより緊急な課題を処理するために RAM が必要となった場合には、プロセスを強制終了し、後から再開できます。
他のアプリ(またはシステム)がサービスを利用することを明示しているなら、バインドされたサービスが実行されます。これは基本的に、別のプロセスに API を提供するサービスです。これにより、これらのプロセス間に依存関係があることをシステムは認識します。プロセス A がプロセス B のサービスにバインドされている場合に、システムはプロセス B とそのサービスの実行を A のために維持する必要があることを認識します。さらに、プロセス A がユーザーにとって優先度が高い場合は、プロセス B もユーザーにとって重要であるとみなされます。サービスは良くも悪くもその柔軟性から、さまざまな上位レベルのシステム コンセプトにおいて、非常に有用な構成要素となってきました。ライブ壁紙、通知リスナー、スクリーン セーバー、入力方法、ユーザー補助機能サービス、その他多くの主要なシステム機能はすべて、アプリが実行するサービスとしてビルドされ、その実行時にシステムによりバインドされます。
サービスは Service のサブクラスとして実装されます。Service クラスの詳細については、デベロッパー ガイドのサービスをご覧ください。

ブロードキャスト レシーバ
ブロードキャスト レシーバは、通常のユーザーフローを外れて、システムがアプリにイベントを配信できるようにするコンポーネントです。これにより、アプリはシステム全体のブロードキャスト アナウンスに応答できます。ブロードキャスト レシーバは明確に定義されたアプリへのエントリであるため、システムは実行中でないアプリに対してもブロードキャストを配信できます。したがってアプリが、たとえば、近づいているイベントについてユーザーに知らせる通知を投稿するためのアラームをスケジューリングすることができます。アプリの BroadcastReceiver にアラームを配信することにより、そのアラームが作動するまでアプリの実行を維持する必要がなくなります。多くのブロードキャストの発信源はシステムです — たとえば、画面がオフになったことを通知するブロードキャスト、電池が残り少ないことを通知するブロードキャスト、画像がキャプチャされたことを通知するブロードキャストなどです。アプリから発信されるブロードキャストもあります — たとえば、端末にデータがダウンロードされ使用できることを他のアプリに知らせる場合などです。ブロードキャスト レシーバがユーザー インターフェースを表示することはありませんが、ステータスバー通知を作成して、ブロードキャスト イベントの発生時にユーザーにアラートできます。一般的には、ブロードキャスト レシーバは他のコンポーネントへの単なるゲートウェイであり、最小限の作業を行うことが前提となっています。たとえば、JobScheduler を使用してイベントに基づいた何らかの作業を実行する JobService をスケジュール設定する場合などです。
ブロードキャスト レシーバは BroadcastReceiver のサブクラスとして実装され、各ブロードキャストは Intent オブジェクトとして配信されます。詳細については、BroadcastReceiver クラスをご覧ください。

コンテンツプロバイダ
コンテンツ プロバイダは、ファイル システム、SQLite データベース、ウェブ、またはアプリからアクセス可能な他の永続的なストレージの場所に保存できるアプリデータの共有されている部分を管理します。コンテンツ プロバイダを介して、他のアプリがデータをクエリしたり、修正したりできます(コンテンツ プロバイダが許可している場合)。たとえば、Android システムはユーザーの連絡先情報を管理するコンテンツ プロバイダを提供しています。したがって、適切なパーミッションさえあれば、アプリからコンテンツ プロバイダに ContactsContract.Data などをクエリして、特定の人物に関する情報を読み取ったり書き込んだりできます。このような一般的なケースのために多くの API やサポートが組み込まれているため、コンテンツ プロバイダをデータベースの抽象化として考えたくなるかもしれません。しかし、システム設計の観点では、コンテンツ プロバイダには別の目的があります。システムから見て、コンテンツ プロバイダは、URI スキームにより識別される名前付きデータ項目を公開するための、アプリへのエントリ ポイントです。したがって、アプリは自身が保有するデータを URI 名前空間にどのようにマッピングし、その URI を他のエンティティに渡してデータにアクセスできるようにするかを決めることができます。アプリの管理に関して、システムでは以下が実行できます。
URI の割り当てはアプリが実行中であるかどうかには影響されないため、その URI を所有しているアプリが終了していても URI は維持されます。システムが確認する必要があるのは、該当 URI からアプリのデータを取得する必要があるときに、所有しているアプリが実行中であることだけです。
URI は重要で詳細なセキュリティ モデルも提供します。たとえば、アプリは画像の URI をクリップボードに配置することができますが、他のアプリが自由にアクセスできないようにコンテンツ プロバイダをロックしたままにできます。別のアプリがクリップボードのその URI にアクセスしようとした場合には、システムが一時的な URI パーミッションを付与して、その URI にあるデータのみにアクセスすることを許可します。その他のデータにはアクセスできません。
コンテンツ プロバイダは、アプリだけに公開されている、他で共有されていないデータを閲覧したり書き込んだりする場合にも役立ちます。
コンテンツ プロバイダは ContentProvider のサブクラスとして実装され、他のアプリがトランザクションを実行できるようにする API の標準セットを実装する必要があります。詳細については、デベロッパー ガイドのコンテンツ プロバイダをご覧ください。

これらを使って作る

上記のコンポーネント(部品)をしようしてAndroidアプリを作成します。この基礎になる部分をちゃんと理解すればほぼ極めたといってよいと思います。※勝手な想像です。なぜなら自分もこれから学習するので(笑)

なので、この基礎部分をしっかり学習したいと思います。

「概要をつかむ」のまとめ

Androidアプリを作るための基本的な材料(クラス)として次の4つがある。

  • アクティビティ
  • サービス
  • ブロードキャスト レシーバ
  • コンテンツ プロバイダ

そして、これらの材料を組み合わせてAndroidアプリを作成するということを理解。

次は、順番に「アクティビティ」を理解します。

アクティビティについて

アクティビティについて学習します。まずは、概要にある内容を抜粋を読みます。

main() メソッドで起動するアプリをプログラミングする際の枠組みとは異なり、Android システムでは、ライフサイクルのそれぞれの段階に対応するコールバック メソッドを呼び出すことにより、Activity インスタンス内のコードが開始されます。

つまるところは、以下の文言がキーポイントになります。

一般に、1 つのアクティビティがアプリ内の 1 つの画面を実装します。

例として、次のような説明があります。

アプリのアクティビティの 1 つが「設定」画面を実装し、他のアクティビティが「写真を選択」画面を実装します。

画面=アクティビティであり、このアクティビティの操作により様々な操作(処理)を実装するというわけです。

マニフェストで画面の設定

アプリでアクティビティを使用できるようにするには、マニフェストでアクティビティとその属性を宣言する必要があります。

    <manifest ... >
      <application ... >
          <activity android:name=".ExampleActivity" />
          ...
      </application ... >
      ...
    </manifest >

必須の属性は、アクティビティのクラス名を指定する android\:name だけです。

簡単ですね。つまりわかりやすい=使いやすいということだと思います。
続けて、そのほかの設定に関しては、次の部分にあります。

ラベル、アイコン、UI テーマなどのアクティビティの特性を定義する属性を追加することもできます。

こちらのページにアクティビティの詳細がありました。

構文は以下のようになっています。

<activity android:allowEmbedded=["true" | "false"]
          android:allowTaskReparenting=["true" | "false"]
          android:alwaysRetainTaskState=["true" | "false"]
          android:autoRemoveFromRecents=["true" | "false"]
          android:banner="drawable resource"
          android:clearTaskOnLaunch=["true" | "false"]
          android:colorMode=[ "hdr" | "wideColorGamut"]
          android:configChanges=["mcc", "mnc", "locale",
                                 "touchscreen", "keyboard", "keyboardHidden",
                                 "navigation", "screenLayout", "fontScale",
                                 "uiMode", "orientation", "density",
                                 "screenSize", "smallestScreenSize"]
          android:directBootAware=["true" | "false"]
          android:documentLaunchMode=["intoExisting" | "always" |
                                  "none" | "never"]
          android:enabled=["true" | "false"]
          android:excludeFromRecents=["true" | "false"]
          android:exported=["true" | "false"]
          android:finishOnTaskLaunch=["true" | "false"]
          android:hardwareAccelerated=["true" | "false"]
          android:icon="drawable resource"
          android:immersive=["true" | "false"]
          android:label="string resource"
          android:launchMode=["standard" | "singleTop" |
                              "singleTask" | "singleInstance"]
          android:lockTaskMode=["normal" | "never" |
                              "if_whitelisted" | "always"]
          android:maxRecents="integer"
          android:maxAspectRatio="float"
          android:multiprocess=["true" | "false"]
          android:name="string"
          android:noHistory=["true" | "false"]  
          android:parentActivityName="string" 
          android:persistableMode=["persistRootOnly" | 
                                   "persistAcrossReboots" | "persistNever"]
          android:permission="string"
          android:process="string"
          android:relinquishTaskIdentity=["true" | "false"]
          android:resizeableActivity=["true" | "false"]
          android:screenOrientation=["unspecified" | "behind" |
                                     "landscape" | "portrait" |
                                     "reverseLandscape" | "reversePortrait" |
                                     "sensorLandscape" | "sensorPortrait" |
                                     "userLandscape" | "userPortrait" |
                                     "sensor" | "fullSensor" | "nosensor" |
                                     "user" | "fullUser" | "locked"]
          android:showForAllUsers=["true" | "false"]
          android:stateNotNeeded=["true" | "false"]
          android:supportsPictureInPicture=["true" | "false"]
          android:taskAffinity="string"
          android:theme="resource or theme"
          android:uiOptions=["none" | "splitActionBarWhenNarrow"]
          android:windowSoftInputMode=["stateUnspecified",
                                       "stateUnchanged", "stateHidden",
                                       "stateAlwaysHidden", "stateVisible",
                                       "stateAlwaysVisible", "adjustUnspecified",
                                       "adjustResize", "adjustPan"] >
    . . .
</activity>
属性 説明
android\:allowEmbedded アクティビティを別のアクティビティの子として埋め込み、起動できることを示します。これは特に、別のアクティビティが所有するディスプレイなどのコンテナに子が存在する場合です。たとえば、Wear のカスタム通知に使用されるアクティビティは、Wear が別のプロセスに存在するコンテキスト ストリーム内でこのアクティビティを表示できるようにするため、この宣言が必要です。この属性のデフォルト値は false です。
android\:allowTaskReparenting タスクが次に前面に移動したとき、アクティビティを開始したタスクからアフィニティを持つタスクにアクティビティを移動できるかどうか。移動できる場合は "true"、アクティビティを開始したタスクに留まる必要がある場合は "false" を指定します。この属性が設定されていない場合、対応する 要素の allowTaskReparenting 属性によって設定された値がアクティビティに適用されます。デフォルト値は "false" です。通常、アクティビティを開始すると、アクティビティを開始したタスクにアクティビティが関連付けられ、アクティビティの生存期間中はそこに留まります。この属性を使用すると、現在のタスクが表示されなくなったときに、アフィニティを持つタスクをアクティビティの親として再割り当てすることができます。一般に、アプリに関連付けられたメインタスクにアプリのアクティビティを移動する場合に使用します。たとえば、メール メッセージにウェブページへのリンクが含まれている場合、リンクをクリックすると、そのウェブページを表示できるアクティビティが起動します。そのアクティビティはブラウザアプリで定義されていますが、メールタスクの一部として起動されます。ブラウザタスクをアクティビティの親として再割り当てすると、ブラウザが次に前面に移動したときにアクティビティが表示され、メールタスクが再び前面に移動したときにはそのアクティビティが表示されなくなります。アクティビティのアフィニティは、taskAffinity 属性で定義されます。タスクのアフィニティは、タスクのルート アクティビティのアフィニティを読み取って決定されます。したがって、定義上は、ルート アクティビティは常に、同じアフィニティを持つタスク内に存在します。起動モードに "singleTask" または "singleInstance" が設定されたアクティビティはタスクのルートにのみ存在できるため、親の再割り当ては "standard" モードと "singleTop" モードに限定されます。(launchMode 属性もご覧ください。)
android\:alwaysRetainTaskState アクティビティが割り当てられているタスクの状態を常にシステムで維持するかどうか。維持する場合は "true"、特定の状況でシステムがタスクを初期状態にリセットできるようにするには "false" を指定します。デフォルト値は "false" です。この属性が重要なのは、タスクのルート アクティビティのみです。その他すべてのアクティビティについては無視されます。通常は、ユーザーがホーム画面からタスクを選択し直したような状況で、システムはタスクをクリアします(ルート アクティビティ上のスタックからすべてのアクティビティを削除します)。一般に、ユーザーが特定の時間(たとえば 30 分間)、タスクにアクセスしなかった場合にこの処理が行われます。ただし、この属性が "true" の場合、ユーザーがどのような方法でタスクにアクセスしても、常に前回のタスクの状態に戻ります。これは、たとえば、ウェブブラウザのようなアプリで便利です。これにより、ユーザーは多くの状態(開いている複数のタブなど)をそのまま維持することができます。
android\:autoRemoveFromRecents この属性を指定したアクティビティによって開始されたタスクについて、タスク内の最後のアクティビティが完了するまでオーバービュー画面に表示し続けるかどうか。true の場合、タスクが自動的にオーバービュー画面から削除されます。これは、呼び出し元による FLAG_ACTIVITY_RETAIN_IN_RECENTS の使用よりも優先されます。"true" または "false" のブール値を指定する必要があります。
android\:banner 関連するアイテムに拡張されたグラフィック バナーを提供するドローアブル リソース。 タグで使用すると、特定のアクティビティにデフォルトのバナーを配置します。 タグで使用すると、アプリのすべてのアクティビティにバナーを配置します。Android TV のホーム画面では、システムがこのバナーを使用してアプリを表示します。バナーはホーム画面のみに表示されるため、CATEGORY_LEANBACK_LAUNCHER インテントを処理するアクティビティを持つアプリでのみ指定する必要があります。この属性は、画像を含むドローアブル リソースへの参照(たとえば "@drawable/banner")として設定する必要があります。デフォルトのバナーはありません。詳細については、TV アプリのビルドを開始するページのホーム スクリーンにバナーを配置するをご覧ください
android\:clearTaskOnLaunch タスクがホーム画面から再起動されたときに、ルート アクティビティを除くすべてのアクティビティをタスクから削除するかどうか。常にタスクのルート アクティビティ以外をクリアする場合は "true"、そうでない場合は "false" を指定します。デフォルト値は "false" です。この属性は、新しいタスクを開始するアクティビティ(ルート アクティビティ)にのみ影響します。タスクのその他すべてのアクティビティでは、この属性は無視されます。値が "true" の場合は、タスクの直前の操作や、タスクから離れるときに [戻る] ボタンまたは [ホーム] ボタンを押したかどうかにかかわらず、ユーザーが再びタスクを開始するたびにルート アクティビティから開始します。値が "false" の場合、状況によってはタスクのアクティビティがクリアされることがありますが、常にクリアされるわけではありません(alwaysRetainTaskState 属性をご覧ください)。たとえば、ホーム画面からアクティビティ P を起動し、そこからアクティビティ Q に移ったとします。ユーザーが [ホーム] を押して、再びアクティビティ P に戻りました。通常、ユーザーにはアクティビティ Q が表示されます。前回、P のタスクで Q を実行していたからです。ただし、P でこのフラグが "true" に設定されていると、P をベースにするすべてのアクティビティ(この場合は Q)は、ユーザーが [ホーム] を押したときに削除され、タスクはバックグラウンドに移動します。そのため、ユーザーがタスクに戻ると、P のみが表示されます。この属性と allowTaskReparenting の両方が "true" の場合、親の再割り当てが可能なアクティビティはすべて、アフィニティを共有するタスクに移動され、残りのアクティビティは上記のとおり削除されます。
android\:colorMode 対応端末でアクティビティを広色域モードで表示するようにリクエストします。広色域モードでは、SRGB よりも広い色域でウィンドウをレンダリングして、より鮮やかな色を表示できます。端末で広色域レンダリングがサポートされていない場合、この属性を指定しても効果はありません。ワイドカラー モードでのレンダリングの詳細については、ワイドカラー コンテンツによるグラフィックの拡張をご覧ください。
android\:configChanges アクティビティで処理する設定変更のリストを指定します。実行時に設定の変更が発生すると、デフォルトではアクティビティはシャットダウンおよび再起動されますが、この属性で設定を宣言しておくと、アクティビティの再起動を防止できます。代わりに、アクティビティは実行中のままになり、アクティビティの onConfigurationChanged() メソッドが呼び出されます。

ほかにもありますが、詳細は本家のページを参照ください。

このマニフェストで、各アクティビティの設定を行います。

次回は、画面(アクティビティ)から画面(アクティビティ)を呼び出す方法を学習します。>次回は、画面(アクティビティ)から画面(アクティビティ)を呼び出す方法を学習します。

アクティビティの作成

AndroidStudioでプロジェクトを作成すると、「MainActivity」クラスが出来上がっています。
このクラスが上記のアクティビティになります。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

このアクティビティ(画面)を作成しているのは、
setContentView(R.layout.activity_main);の部分になります。
R.layout.activity_mainの部分は、下の図のように、「activity_main.xml」に定義(描かれて)されています。

「activity_main.xml」を開くと右上のほうにのようなボタンがあるので、「Design」を選択すると画面のイメージが表示されます。

ここで、テキスト「Hello World」が表示されています。この部品が「TextView」という部品です。
この部品を移動してタイトルとして使用しようと思っていますが、その前に。。。

パッケージの修正

デフォルトで作成されたパッケージは「com.example.プロジェクト名」になっていると思います。
これを自作のパッケージ名に修正したいと思いますが、ただ単に修正するとエラーになります。

これは、AndroidManifest.xmlの修正も必要なためです。
使用するパッケージ名を上のXMLに記述します。今回作成するパッケージ名は「jp.zenryoku.mokuhyotasseiap」としてます。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.zenryoku.mokuhyotasseiap">

これで、エラーが解消され、Javaファイルも下のようにエラーが消えました。

多言語対応

作成するアプリは、多言語対応で作成したいと考えております。なので、こちらのページを参考に、多言語化を行います。

まとめると、下の図のように各言語に対応するXMLファイルを作成するというところです。

resを右クリックして、フォルダ名「values」の後ろに対応する言語を付けます。

  • 日本語(values-ja)
  • 英語(values-en)

そして、それぞれのstring.xmlに各言語に対応する文字列を記述します。

XMLの参照方法

多言語の文字列を設定することができたら、次は画面のコンポーネント(部品)にその値を設定します。
必要な処理ととしては、次のものになります。

  1. コンポーネントの取得
  2. 文字列の取得
  3. 値の設定
// コンポーネントの取得
TextView titleView = findViewById(R.id.appTitle);
// 値の設定
titleView.setText(R.string.app_name);

上のような実装で行けそうです。まだ起動確認していません。。。

バーチャルデバイスのインストール

実機がない場合は仮想デバイス(バーチャルデバイス)をインストールする必要があります。
動かしたときのイメージを見るためです。
これは、画面操作で簡単に行けます。

しかしエラーが!

Execution failed for task ':app:compressDebugAssets'.

上記のようなエラーが出ました。
こちらのサイトを参考にすると、Gradleのバージョンを下げてやればいけるというところで。。。

それぞれのバージョンを以下のように設定して再実行しました。
Android Gradle Plugin: 4.2.1
Gradle: 6.7.1

次のエラー

Caused by: org.gradle.internal.metaobject.AbstractDynamicObject$CustomMessageMissingMethodException: Could not find method dependencyResolutionManagement() for arguments

これもGradleのバージョンを次のように変えてやればよい感じでした。
Android Gradle Plugin: 7.0
Gradle: 7.0

しかし、使用しているPCの容量不足のため、AndroidStudioが落ちました。。。
Android開発は、十分にHDの要領があるPCを使用しないと駄目なようです。。。

でわでわ。。。