コンポーネント
Androidアプリケーションのアーキテクチャは、コンポーネント(アクティビティ、サービスなど)とそれらの関係のセットとして表すことができます。 Android APIは、アプリケーションコンポーネント間の通信のいくつかの標準メソッドを提供します。コンテキストを介したアクティビティ/サービスの開始、インテントメッセージの送信、ServiceConnectionまたはContentProviderの使用などです。 このすべてについては、Androidでのデータ転送に関するチュートリアルで読むことができます。 ただし、1つのニュアンスがありますが、これは原則として静かです。 コンポーネントの可用性と、他のアプリケーションのためにコンポーネント間で転送されるデータについてです。
アクティビティ
アクティビティの1つを開始するには、おそらく特別に準備されたインテントを使用します。
Intent myIntent = new Intent(CurrentActivity.this, NextActivity.class); startActivity(myIntent);
インテントのコンストラクターに渡したContextとClassを使用して、Androidはアプリケーションパッケージと目的のアクティビティのクラスを決定し、それを起動します。
パッケージ名とクラス名を知って、他の人のアクティビティを開始することもできます。
Intent intent = new Intent(); /* */ intent.setClassName("com.android.contacts", "com.android.contacts.activities.PeopleActivity"); startActivity(intent);
結果は、起動されたアクティビティです。
これは、すべてのアクティベーションがサードパーティアプリケーションによって起動できることを意味しますか? いや 前の例のclassnameを次のように置き換えた場合
intent.setClassName("com.android.contacts", "com.android.contacts.preference.ContactsPreferenceActivity");
その後、java.lang.SecurityExceptionを取得します。
実際、アプリケーション開発者は、マニフェスト内の各アクティビティに対してandroid:exportタグをそれぞれtrueまたはfalseに指定して、利用可能なアクティビティと利用できないアクティビティを決定します。 このタグのデフォルト値はfalse、つまり このタグを持たないAndroidManifest.xmlのすべてのアクティビティは、アプリケーション内でのみ使用できます。
ただし、連絡先アプリケーションのマニフェスト(たとえば、 こちら )を見ると、PeopleActivityにandroidタグがないことがわかります:export = "true"、なぜそれを起動できたのですか? 答えは公式ドキュメントにあります:アクティビティに何らかのインテントフィルターが含まれている場合、android:exportのデフォルト値はtrueになります。
アプリケーション開発者は、任意のアクティビティにインテントフィルターが表示されるまで、アプリケーション外でのアクティビティの可視性を心配する必要はありません。 インテントフィルターを宣言したら、このアクティビティを他の誰かによってトリガーする準備ができているかどうかを自問してください。 そうでない場合は、必ず<android:export = "false">を指定してください。
サービス
サービスの可視性は、アクティビティの可視性と同じ方法で定義されます。1つの「but」ではない場合、サービスに個別のアイテムを提供しません。 サービスの場合、アクティビティの場合のように、その可視性は計画外の開始よりもはるかに先に進む可能性があります。 サービスがバインディングの可能性を提供する場合、他の人がこの機会を利用して、ServiceConnectionを介してサービスへのリンクを取得できます。 標準の音楽再生サービスに接続する例を見てみましょう。
注:次の例は、かなり古いバージョンのプレーヤーにのみ関連しています。 最新バージョンでは、開発者はエクスポートされたフラグを整理し、それをfalseに設定しました:)
Intent intent = new Intent(); /* - */ intent.setClassName("com.android.music", "com.android.music.MediaPlaybackService"); ServiceConnection conn = new MediaPlayerServiceConnection(); /* */ bindService(intent , conn, 1); class MediaPlayerServiceConnection implements ServiceConnection { public void onServiceConnected(ComponentName name, IBinder boundService) { Log.i("service connection", "Connected! Name: " + name.getClassName()); } public void onServiceDisconnected(ComponentName name) { Log.i("MediaPlayerServiceConnection", "Disconnected!"); } }
バインド後、boundService変数はサービスへの参照を保存します。 次に、JavaリフレクションまたはAndroidインターフェイス定義言語(AIDL)を使用して、サービスメソッドを直接呼び出すことができます。その結果、たとえば、標準プレーヤーが既に何かを再生している場合、再生を停止できます。
この場合の推奨事項は同じです。公開する予定がない場合、インテントフィルターを持つすべてのサービスに対して<android:exported = "false">を設定します。
ContentProvider
ContentProviderの主な目的は、他のアプリケーションにデータを提供することです。 このため、このアプリケーションコンポーネントの<android:exported>タグのデフォルト値はtrueです。 しかし、主な目的はそれだけではありません。 ContentProviderがよく使用されます:
これらのすべての場合において、ほとんどの場合、アプリケーションの外部にデータを提供する必要はありません。 これが当てはまる場合は、プロバイダー<android:export = "false">に必ず伝えてください。
放送
ブロードキャストメッセージを使用した通信は、3つの段階に分けることができます。
- 意図の作成
- コンテキストを介したインテントの送信#sendBroadcast(...)
- すべての登録済みBroadcastReceiverによるインテントの取得。
登録時の各受信者(受信者)は特定のIntentFilterを示し、メッセージはこのフィルターに従ってこの受信者に配信されます。 ご想像のとおり、メッセージ受信者はフィルターを満たし、アプリケーションの外側にいる可能性があります。 目的のパッケージを指定することにより、送信されるメッセージのプライバシーを実現できます。
intent.setPackage(“com.android.example.mypackage”)
または、 LocalBroadcastManager (サポートライブラリで利用可能)を使用します。これにより、メッセージがアプリケーション(実際にはプロセス)の外に飛び出すことも防止されます。
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
さらに、LocalBroadcastManagerを使用すると、受信者を登録できるため、そのような受信者が受信したインテントもローカルであり、外部から到着することはありません。
資源
つまり、アプリケーションで使用されるすべてのリソースは、アプリケーションの外部で読み取り可能です。 それらの一部を取得するには、ターゲットアプリケーションのパッケージ名のみを知るだけで十分です。
PackageManager pm = getPackageManager(); /* Youtube */ Drawable icon = pm.getApplicationIcon("com.google.android.youtube"); /* Youtube */ Drawable logo = pm.getApplicationLogo("com.google.android.youtube"); ApplicationInfo applicationInfo = pm.getApplicationInfo("com.google.android.youtube", 0); /* Youtube */ CharSequence applicationLabel = pm.getApplicationLabel(applicationInfo);
アプリケーション内のアクティベーション情報がわかっている場合は、それぞれについて同様の操作を個別に行うことができます。
特定のリソースの名前を学習したら、それを取得できます。
Resources r = getPackageManager().getResourcesForApplication("com.google.android.youtube"); /* - Youtube * youtube_widget_preview - * drawable - , * com.google.android.youtube - package name */ int id = r.getIdentifier("youtube_widget_preview", "drawable", "com.google.android.youtube"); Drawable drawable = r.getDrawable(id); ImageView iw = new ImageView(context); iw.setImageDrawable(drawable); rootView.addView(iw);
このようにして、YouTubeウィジェットのプレビュー画像を独自のアプリケーション内に表示することができました。
リソースの主な欠点は、リソースを非表示にする方法がないことです。 リソースにプライベートデータを保存するのは悪い考えですが、そのようなニーズがある場合は、暗号化された形式で保存することをお勧めします。
記事の最後に、Androidのファイルシステムについていくつか説明します。 この質問はすでに十分にカバーされており、多くの人々は、Androidでは各アプリケーションが独自のディレクトリ/データ/データ/「アプリパッケージ名」を持ち、そのアクセスはアプリケーション自体(または1つのsharedUserIdを持つアプリケーションのグループ)のみが利用できることを知っています。 このディレクトリには、SharedPreferences設定ファイル、アプリケーションデータベースファイル、キャッシュなどが含まれますが、このリポジトリのセキュリティには依存しません。 このディレクトリ内のファイルは、次の時点まで利用できません。
- フラグContext.MODE_PRIVATEでファイルを作成します
- デバイスでルートアクセスが取得されない
- アプリケーションは、バックアップマネージャによって処理されません。
これは、リソースの場合のように、プライベートストレージが内部ストレージにある場合でも、暗号化する必要があることを意味します。
トピックに関する有用なリソース:
developer.android.comのセキュリティのヒント記事
Androidプラットフォームブックのジェフシックスアプリケーションセキュリティ