ドキュメントが曖昧で間違えやすい箇所のメモ。
例:
<manifest>の"package"属性 | foo.bar |
ActivityクラスのFQCN(Fully Qualified Class Name) | foo.bar.activities.ActivityX |
この時、<activity>の"android:name"属性でActivityXを指定したい時の正誤パターン。
正: <activity android:name=".activities.ActivityX"></activity> 誤: <activity android:name="foo.bar.activities.ActivityX"></activity> <activity android:name="activities.ActivityX"></activity> <activity android:name=".ActivityX"></activity> <activity android:name="ActivityX"></activity>
説明:
"android:name"属性は、"."から始める場合はpackage部分を省略した表記として解釈される。
従ってpackageが"foo.bar"なら、"foo.bar.activities.ActivityX"からpackageを除去した".activities.ActivityX"を指定する。
それ以外は全て間違って解釈される。
Activityのクラスがpackageの直下にあるのなら、".クラス名"にすればよい。
例:
"foo.bar.Main" → <activity android:name=".Main">
間違えやすい背景:
Androidの公式ドキュメントに出てくる"package名"の殆どは AndroidManifest.xml の"<manifest>"の"package"属性で指定した値を指している。
Javaのクラス名で扱う"package"で考えると、ズレが生じて間違える元になる。
例えばActivityに指定したいクラスが"foo.bar.baz.Main"とすれば、Javaの世界での"package"は"foo.bar.baz"となる。しかし<manifest>のpackage属性で"foo.bar"が指定されていれば、Androidアプリの世界での"package名"は"foo.bar"となる。
公式ドキュメント上での説明:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="foo.bar" android:versionCode="1" android:versionName="1.0">
この場合、"<permission>"の"android:name"属性もpackageで始める。
正: <permission ... android:name="foo.bar.PERM_X" ...> 誤: <permission ... android:name="PERM_X" ...>
説明:
公式ドキュメントには
The name must be unique, so itshoulduse Java-style scoping
とあり、MUSTとは書かれていない。このため、タイピング数を少なくしようとpackage名を省略すると、SecurityExceptionの発生に頭を悩ませることになる。
将来的に実装がどう変わるのか不明なので、本記事で"MUST"と断言することは出来ない。ただし、実際にトラブルが発生したことは確かなので、package名で始めることを推奨しておく。
URIではなく、直接パッケージ名とクラス名を指定してIntentを使いたい場合:
Intent i = new Intent(); i.setClassName(String packageName, String className)
例:package="foo.bar" アプリの、FQCN="foo.bar.activities.Main"を指定する。
正: i.setClassName("foo.bar", "foo.bar.activities.Main"); 誤: i.setClassName("foo.bar", ".activities.Main"); i.setClassName("foo.bar.activities", ".Main"); i.setClassName("foo.bar.activities", "Main");
説明:
IntentのsetClassName(String packageName, String className)は
public ComponentName (String pkg, String cls)
のショートカットとなっている。ここで"packageName", "pkg"は当然<manifest>の"package"属性を指定する。ここをJavaクラスのパッケージと勘違いしたのが、
i.setClassName("foo.bar.activities", ".Main"); i.setClassName("foo.bar.activities", "Main");
と指定したパターン。
問題は"className", "cls"の部分で、公式ドキュメントにも
cls The name of the class inside of pkg that implements the component. Can not be null.
としか書かれておらず、FQCNを指定すればよいのかが曖昧なままになっている。
ここで<activity>の"android:name"属性と同様と勘違いしてしまうパターンが次のケース。
i.setClassName("foo.bar", ".activities.Main");
結論から言うとFQCNを指定する必要がある。
ヒントとなるのが
public ComponentName (Context pkg, Class<?> cls)
というコンストラクタの存在であり、"Class"のgetName()メソッドはFQCN表記を返す。このことから、Stringで指定する場合もFQCNとなることが予想できる。