前へアンドロイド、明示的、暗黙的インテント(Explicit、Implicit Intent)でインテントの構成要素と、ACTIONとCATEGORYのリジョルビングについて調べてみました。 今回はDATAとMIMEタイプのリジョルビングについて説明します。
AndroidManifest.xmlのインテントフィルタは、次のように設定しました。私と同じコードで確認してみてシリョミョンフィルタも同様に設定してください。
<activity android:name=".SubActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
</intent-filter>
</activity>
<activity android:name=".SubActivity2">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="foo.com" />
</intent-filter>
</activity>
<activity android:name=".SubActivity3">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="foo.com" />
<data android:path="/apps" />
</intent-filter>
</activity>
Data
データ(Data)の構造は、次のとおりです。 scheme、host、port、pathで区別することができます。
<scheme>://<host>:<port>/<path>
ex)
https://foo.com:200/folder/subfolder/etc
まず、URI http://
をデータとして設定し、クエリをみてください。このデータには、schemeだけません。
val intent = Intent(Intent.ACTION_VIEW)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addCategory(Intent.CATEGORY_BROWSABLE)
val uri = Uri.parse("http://")
intent.setData(uri)
Log.d(TAG, "uri: ${uri.toString()}")
var results = packageManager.queryIntentActivities(intent, 0)
results?.forEach { resolveInfo ->
Log.d(TAG, "info: $resolveInfo")
}
ログを見ると、結果に自分のアプリと他のアプリのアクティビティがすべて含まれて返されました。
MainActivity: uri: http://
MainActivity: info: ResolveInfo{d3c504 com.google.android.setupwizard/.util.WebDialogActivity p=5 m=0x208000}
MainActivity: info: ResolveInfo{54a4aed com.android.chrome/com.google.android.apps.chrome.IntentDispatcher m=0x208000}
MainActivity: info: ResolveInfo{3c44d22 org.chromium.webview_shell/.WebViewBrowserActivity m=0x208000}
MainActivity: info: ResolveInfo{46163b3 com.codechacha.intent/.SubActivity m=0x208000}
MainActivity: info ResolveInfo{94a2d70 com.google.android.setupwizard/.util.WebDialogActivity p=5 m=0x208000}
今では、私のアプリのインテントフィルタのみ確認したいと思います。 setPackage
私のアプリのパッケージ名を入れてくれれば、私のアプリのアクティビティにのみ暗黙的インテントにクエリをします。
次のコードに戻って、クエリの結果を確認してみてください。
val intent = Intent(Intent.ACTION_VIEW)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addCategory(Intent.CATEGORY_BROWSABLE)
val uri = Uri.parse("http://")
intent.setData(uri)
intent.setPackage(packageName)
Log.d(TAG, "uri: ${uri.toString()}")
var results = packageManager.queryIntentActivities(intent, 0)
results?.forEach { resolveInfo ->
Log.d(TAG, "info: $resolveInfo")
}
ログを見ると、クエリの結果に自分のアプリのアクティビティのみいることを確認することができます。
MainActivity: uri: http://
MainActivity: info: ResolveInfo{d3c504 com.codechacha.intent/.SubActivity m=0x208000}
結果を見ると、3つのアクティビティのうちの最初のSubActivityのみが含まれました。クエリの結果に含まになると、インテントフィルタを通過して、アクティビティが処理可能であることを意味します。 含まれていなければ、インテントフィルタが処理できないインテントです。
今後の結果にアクティビティが含まれていることをインテントと一致したと表現して、含まれていないことを一致していなかったと表現します。
その後、上記のインテントは、なぜ最初のアクティビティだけ一致したでしょうか?フィルタを比較してみると、SubActivityはschemeがhttpだけあってSubActivity2はhttpとhttpsの両方があります。
インテントフィルタに定義されたschemeを見ると、データが https://
ならSubActivityは不一致、SubActivity2は一致していることを予想することができます。しかし、なぜデータが http://
のにSubActivity2は矛盾したでしょうか?
<activity android:name=".SubActivity">
<intent-filter>
...
<data android:scheme="http" />
</intent-filter>
</activity>
<activity android:name=".SubActivity2">
<intent-filter>
...
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="foo.com" />
</intent-filter>
</activity>
答えはSubActivity2に host
が定義されているからです。
フィルタにhostを設定しない場合、すべてのhostを処理することができるということを意味します。一方、hostを設定すると、その値だけを処理することができるということを意味します。
SubActivityはhostがないため、すべてのホストを処理することができ、SubActivity2は foo.com
のみを処理することができます。
したがって、データ http://
のhostは foo.com
ではないのでSubActivity2は不一致ました。
以下は、次のコードでクエリをみてください。データが https://foo.com
に変更され、残りは同じです。
val intent = Intent(Intent.ACTION_VIEW)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addCategory(Intent.CATEGORY_BROWSABLE)
val uri = Uri.parse("http://foo.com")
intent.setData(uri)
intent.setPackage(packageName)
Log.d(TAG, "uri: ${uri.toString()}")
var results = packageManager.queryIntentActivities(intent, 0)
results?.forEach { resolveInfo ->
Log.d(TAG, "info: $resolveInfo")
}
SubActivity2だけ一致し、残りは一致していないですね。
MainActivity: uri: https://foo.com
MainActivity: info: ResolveInfo{d3c504 com.codechacha.intent/.SubActivity2 m=0x308000}
先ほど上で述べたように、データのschemeを https
に変更し、SubActivityのインテントフィルタはhttps
をhostに設定していなくて矛盾しました。
scheme
のみを表示するとき、SubActivity3も一致する必要があるようですが、除外されました。その理由は、 path
です。
pathもhostと同様にインテントフィルタで設定しない場合、すべてのパスを処理することができるという意味であり、設定すると、そのパスのみを処理することができるということを意味します。
したがって、SubActivity3が不一致した理由は、pathを /apps
に設定したからです。
<activity android:name=".SubActivity2">
<intent-filter>
...
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="foo.com" />
</intent-filter>
</activity>
<activity android:name=".SubActivity3">
<intent-filter>
...
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="foo.com" />
<data android:path="/apps" />
</intent-filter>
</activity>
その後、3つのフィルターがすべて一致するようにクエリをしてみましょう。
一般的に重なるURIは http://foo.com
です。次のコードでクエリをみてください。
val intent = Intent(Intent.ACTION_VIEW)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addCategory(Intent.CATEGORY_BROWSABLE)
val uri = Uri.parse("http://foo.com/apps")
intent.setData(uri)
intent.setPackage(packageName)
Log.d(TAG, "uri: ${uri.toString()}")
var results = packageManager.queryIntentActivities(intent, 0)
results?.forEach { resolveInfo ->
Log.d(TAG, "info: $resolveInfo")
}
ログを見ると、すべてのフィルタに一致して、結果に含まれていました。
MainActivity: uri: http://foo.com/apps
MainActivity: info: ResolveInfo{d3c504 com.codechacha.intent/.SubActivity3 m=0x508000}
MainActivity: info: ResolveInfo{54a4aed com.codechacha.intent/.SubActivity2 m=0x308000}
MainActivity: info: ResolveInfo{3c44d22 com.codechacha.intent/.SubActivity m=0x208000}
MIME Type
MIME Typeは、データのタイプを意味します。 MIMEタイプは、より繊細にリジョル氷河に使用される属性です。 インテントフィルタを以下のように設定しました。
<activity android:name=".SubActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:mimeType="audio/mp3"/>
</intent-filter>
</activity>
<activity android:name=".SubActivity2">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:mimeType="audio/ogg"/>
</intent-filter>
</activity>
<activity android:name=".SubActivity3">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:mimeType="audio/*"/>
</intent-filter>
</activity>
そして次のコードのように、クエリを見ました。データとMIMEタイプを同時に設定しました。
val intent = Intent(Intent.ACTION_VIEW)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addCategory(Intent.CATEGORY_BROWSABLE)
val uri = Uri.parse("http://foo.com/apps")
val mimetype = "audio/mp3"
intent.setDataAndType(uri, mimetype)
intent.setPackage(packageName)
Log.d(TAG, "uri: ${uri.toString()}")
var results = packageManager.queryIntentActivities(intent, 0)
results?.forEach { resolveInfo ->
Log.d(TAG, "info: $resolveInfo")
}
MIMEタイプが audio/mp3
ので、SubActivity2は不一致ましですね。
まず、上記の学習データ(DATA)に一致するかどうかをチェックして、MIMEタイプが一致していることをチェックした後、結果として返します。
SubActivity3は、MIMEタイプが audio/*
のに、 *
をつけるどのようであれ処理することができるということを意味します。
MainActivity: uri: http://foo.com/apps
MainActivity: info: ResolveInfo{829b71f com.codechacha.intent/.SubActivity m=0x608000}
MainActivity: info: ResolveInfo{889a6c com.codechacha.intent/.SubActivity3 m=0x608000}
*
を検証するために、次のコードのようにインテントフィルタに定義されていない audio/abc
にクエリを見ました。
val intent = Intent(Intent.ACTION_VIEW)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addCategory(Intent.CATEGORY_BROWSABLE)
val uri = Uri.parse("http://foo.com/apps")
val mimetype = "audio/abc"
intent.setDataAndType(uri, mimetype)
intent.setPackage(packageName)
Log.d(TAG, "uri: ${uri.toString()}")
var results = packageManager.queryIntentActivities(intent, 0)
results?.forEach { resolveInfo ->
Log.d(TAG, "info: $resolveInfo")
}
予想したようにSubActivity3だけが戻されましたね。
MainActivity: uri: http://foo.com/apps
MainActivity: info: ResolveInfo{8e50b55 com.codechacha.intent/.SubActivity3 m=0x608000}
今はインテントフィルタの *
について確認しました。しかし、以下のようにクエリするインテントにMIMEタイプで audio/*
を入れるとどうなるでしょう?
val intent = Intent(Intent.ACTION_VIEW)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addCategory(Intent.CATEGORY_BROWSABLE)
val uri = Uri.parse("http://foo.com/apps")
val mimetype = "audio/*"
intent.setDataAndType(uri, mimetype)
intent.setPackage(packageName)
Log.d(TAG, "uri: ${uri.toString()}")
var results = packageManager.queryIntentActivities(intent, 0)
results?.forEach { resolveInfo ->
Log.d(TAG, "info: $resolveInfo")
}
ログを見ると、以下のようにすべてのアクティビティが一致します。
インテントに *
を入れて、クエリをすると、すべてを処理することができるアクティビティを見つけることではなく、いくつかのタイプが来ても構わないという意味です。
したがって audio/
で始まるインテントフィルタはすべて一致します。
MainActivity: uri: http://foo.com/apps
MainActivity: info: ResolveInfo{d6c2aa4 com.codechacha.intent/.SubActivity m=0x608000}
MainActivity: info: ResolveInfo{ae4500d com.codechacha.intent/.SubActivity2 m=0x608000}
MainActivity: info: ResolveInfo{aa83bc2 com.codechacha.intent/.SubActivity3 m=0x608000}
先ほどMIMEタイプにインテントフィルタを設計するとき、かすんこと一つ学びました。
今、再びインテントフィルタの定義を変更してください。他のケースについて説明します。
<activity android:name=".SubActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:mimeType="audio/mp3"/>
</intent-filter>
</activity>
<activity android:name=".SubActivity2">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content" />
<data android:mimeType="audio/mp3"/>
</intent-filter>
</activity>
<activity android:name=".SubActivity3">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE" />
<data android:mimeType="audio/mp3"/>
</intent-filter>
</activity>
これまで学んだ知識は、インテントフィルタにDataとMIMEタイプが定義されており、DataとMIMEタイプを持っているインテントでクエリをしたとき どちらの属性も一致しない場合、結果に含まれていないことでした。
その後、次のコードでクエリを試みますか?インテントは、データが http://foo.com/apps
で、MIMEタイプはaudio/mp3
に定義されています。
上記のインテントフィルタを見て、結果を予想してみるとSubActivityだけ一致するようです。結果をみましょう。
val intent = Intent(Intent.ACTION_VIEW)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addCategory(Intent.CATEGORY_BROWSABLE)
val uri = Uri.parse("http://foo.com/apps")
val mimetype = "audio/mp3"
intent.setDataAndType(uri, mimetype)
intent.setPackage(packageName)
Log.d(TAG, "uri: ${uri.toString()}")
var results = packageManager.queryIntentActivities(intent, 0)
results?.forEach { resolveInfo ->
Log.d(TAG, "info: $resolveInfo")
}
はい..予想通りSubActivity万一致ましですね。他のものはschemeが content
または定義されていません。
12-08 09:31:08.457 5752 5752 D MainActivity: uri: http://foo.com/apps
12-08 09:31:08.458 5752 5752 D MainActivity: info: ResolveInfo{8e50b55 com.codechacha.intent/.SubActivity m=0x608000}
だから、他のものは同じで、データのみ content://foo.com/apps
に変更するとどうなるでしょう?
推測してみるとSubActivity2だけ一致するようです。結果をみましょう。
val intent = Intent(Intent.ACTION_VIEW)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addCategory(Intent.CATEGORY_BROWSABLE)
val uri = Uri.parse("content://foo.com/apps")
val mimetype = "audio/mp3"
intent.setDataAndType(uri, mimetype)
intent.setPackage(packageName)
Log.d(TAG, "uri: ${uri.toString()}")
var results = packageManager.queryIntentActivities(intent, 0)
results?.forEach { resolveInfo ->
Log.d(TAG, "info: $resolveInfo")
}
結果を見ると、SubActivity2とSubActivity3が一致しました。インテントフィルタにデータを設定していないSubActivity3はなぜ一致したでしょうか?
12-08 09:26:51.524 5573 5573 D MainActivity: uri: content://foo.com/apps
12-08 09:26:51.526 5573 5573 D MainActivity: info: ResolveInfo{bcd0537 com.codechacha.intent/.SubActivity2 m=0x608000}
12-08 09:26:51.526 5573 5573 D MainActivity: info: ResolveInfo{d6c2aa4 com.codechacha.intent/.SubActivity3 m=0x608000}
Deverlopersを注意深く見れば例外に対して記載されています。
インテントフィルタにMIMEタイプは、定義したが、データを定義していない場合、 scheme
がcontent
または file
であるものとみなすとされています。
したがって、定義をしていないチェムンにschemeがcontentである場合に一致しました。
その後、データを file://foo.com/apps
に変更して、クエリしてみましょう。結果をプレビュー予想してみるとSubActivity3だけ一致するようです。
val intent = Intent(Intent.ACTION_VIEW)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addCategory(Intent.CATEGORY_BROWSABLE)
val uri = Uri.parse("file://foo.com/apps")
val mimetype = "audio/mp3"
intent.setDataAndType(uri, mimetype)
intent.setPackage(packageName)
Log.d(TAG, "uri: ${uri.toString()}")
var results = packageManager.queryIntentActivities(intent, 0)
results?.forEach { resolveInfo ->
Log.d(TAG, "info: $resolveInfo")
}
ログを確認してみるSubActivity3のみ一致しました。
12-08 09:36:34.171 5918 5918 D MainActivity: uri: file://foo.com/apps
12-08 09:36:34.171 5918 5918 D MainActivity: info: ResolveInfo{ee33236 com.codechacha.intent/.SubActivity3 m=0x608000}
まとめ
前回の記事でAction、Categoryについて調べ見て、今回の記事でDataとMIMEタイプについて調べてみました。
特にDataとMIMEタイプにインテントフィルタを設計する際に省略してもされている部分があります。
例外を覚えず省略してしまうと、意図とは異なる動作する場合が生じることがあります。
省略はなく、すべての属性を定義するのがよいようです。また、データの属性を定義するか、しないの意味が異なるため、この部分にも留意する必要があります。
最後に、 *
はインテントフィルタで使用するのとインテントで使用する意味が異なるので、これを認知して使用します。
Intentと関連があるUri、Scheme、SSPに対して知りたい場合は、以下の文もお読みください。
参考
Related Posts
- Android - 振動、Vibrator、VibrationEffectの例
- Android - TabLayoutの実装方法(+ ViewPager2)
- Android - PackageManagerにPackage情報を取得する
- Android - ACTION_BOOT_COMPLETEDイベント受信
- Android - FusedLocationProviderClientに位置情報を取得する
- Android - GPS、Network位置情報を取得する(LocationManager)
- Android - Foreground Service実行
- Android - 時間、日付、変更イベント受信
- Android - currentTimeMillis()、elapsedRealtime()、uptimeMillis()
- Android-PowerManager WakeLock
- Android - ファイル入出力の例(Read、Write、内部、外部ストレージ)
- Android - Screen On / Offイベントの受信、状態確認
- Android - 他のアプリのServiceにバインド
- Android - Handler vs Executor
- Android - Darkmode有効にする方法
- Android - hasSystemFeature()、サポートされているFeature確認
- Android - アプリの権限を確認(Permission check)
- Android - インストールされてアプリリストをインポートする
- Android App Shortcuts実装
- Android - ContentProviderを実装、および例
- Android - AIDLを利用して、Remote Serviceの実装
- Android - Uri、Scheme、SSP(Scheme Specific Part)説明
- Android - アプリのインストール、削除、イベントダウンロード(BroadcastReceiverインテントを受け取る)
- Android - SharedPreferencesに簡単なデータを保存する方法
- Android - AlarmManagerにアラームを登録する方法、および例
- Android - Quick SettingsにCustom Tile追加する方法(kotlin)
- Android - Broadcast Receiver登録およびイベントの受信方法
- Android - Runtime permissionリクエスト方法と例(kotlin)
- Android - ネットワーク(WIFI)の接続状態を確認し、変更の検出
- Mockito - static、final methodをmockingする方法
- Andriod - カスタムパーミッションを定義する方法
- RobolectricにUnit Testを作成する(kotlin)
- Android Mockitoのテストコードを作成する(kotlin)
- Android - Handlerの使用方法、および例
- Android - IntentService使用方法
- Android - JobIntentService使用方法