アンドロイドの明示、暗黙的インテント(Explicit、Implicit Intent)(2)

By JS | Last updated: December 06, 2018

前へアンドロイド、明示的、暗黙的インテント(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タイプは、定義したが、データを定義していない場合、 schemecontentまたは 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

codechachaCopyright ©2019 codechacha