【问题标题】:When to use which constructor for ComponentName in Android?何时在 Android 中为 ComponentName 使用哪个构造函数?
【发布时间】:2011-06-26 12:44:49
【问题描述】:

我对 Android 中的 ComponentName 类有点困惑。

获取组件名称对象的方法有很多种,但我不知道何时使用哪个...以及为什么!

例子:

  • 应用程序包是de.zordid.sampleapp
  • 但小部件提供程序类是de.zordid.sampleapp.widget.WidgetProvider

使用

ComponentName cn = new ComponentName("de.zordid.sampleapp.widget",
    "WidgetProvider");

我得到了这个组件信息:ComponentInfo{de.zordid.sampleapp.widget/WidgetProvider},但我无法使用它 - 组件未知! 但是 JavaDoc 说我应该提供包和该包中的类 - 这就是我所做的,不是吗??

使用

ComponentName cn = new ComponentName(context, WidgetProvider.class);

产生ComponentInfo{de.zordid.sampleapp/de.zordid.sampleapp.widget.WidgetProvider} - 效果很好!!

还有另一种获取 ComponentName 的方法 - 通过上下文和字符串。 应该在何时何地使用哪一个?

谢谢!

【问题讨论】:

    标签: android constructor components


    【解决方案1】:

    Robert Tupelo-Schneck 的回答是关于优先选择对象而不是字符串是正确的。这是我的看法。

    • 要引用您自己的组件,请使用:

      new ComponentName(getApplicationContext(), WidgetProvider.class);
      
    • 要在您自己的应用程序中引用某些动态引用的组件,请使用:

      // values/strings.xml: <string name="provider">de.zordid.sampleapp.widget.WidgetProvider</string>
      String fqcn = getResources().getString(R.string.provider);
      new ComponentName(getApplicationContext(), fqcn);
      

      当您想使用 Android 的资源限定符来决定使用哪个组件时,这很有用,您可以覆盖 values-*/strings.xml 中的默认字符串。

    • 要引用另一个应用程序的组件,请使用:

      int componentFlags = GET_ACTIVITIES | GET_PROVIDERS | GET_RECEIVERS | GET_SERVICES;
      PackageInfo otherApp = context.getPackageManager().getPackageInfo("com.other.app", componentFlags);
      ComponentInfo info = otherApp.activities[i]; // or providers/receivers/...
      new ComponentName(info.packageName, info.name);
      

    关于.Names 和&lt;manifest package="

    这里可能有些混乱,因为我认为从历史上看罗伯特的说法是正确的:

    它是应用程序的包名---该应用程序的 AndroidManifest.xml 中清单元素的包属性

    但现在没有了。自从引入了新的 Gradle 构建系统以来,some changes 就在这附近。

    如果您在build.gradle 中指定了android.defaultConfig.applicationId,这将是应用程序包名称,然后在构建应用程序时,清单中的package 属性是一个单独的东西。 ComponentName 的第一个参数现在引用 applicationId + applicationIdSuffix。棘手的是,在最终清单合并和打包之后,APK 将具有&lt;manifest package=applicationId + applicationIdSuffix,并且所有 .Names 都将扩展为 FQCN。

    学习名称解析的示例应用

    这是一个基于我的一个应用程序结构的示例结构。考虑一个名为“app”的假设应用程序中的以下类:

    • net.twisterrob.app.android.App
    • net.twisterrob.app.android.GlideSetup
    • net.twisterrob.app.android.subpackage.SearchResultsActivity
    • net.twisterrob.app.android.subpackage.Activity
    • net.twisterrob.app.android.content.AppProvider

    在应用程序的服务器端后端和/或一些共享模型类:

    • net.twisterrob.app.data.*
    • net.twisterrob.app.backend.*
    • net.twisterrob.app.web.*

    在我的 Android 助手库中:

    • net.twisterrob.android.activity.AboutActivity

    其他库:

    • android.support.v4.content.FileProvider

    这样,所有东西都在net.twisterrob.app 中命名。 android 应用只是它自己的子包中整体的一部分。

    AndroidManifest.xml(无关部分省略)

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              package="net.twisterrob.app.android">
        <!--
        `package` above defines the base package for .Names
        to simplify reading/writing the manifest.
        Notice that it's different than the `applicationId` in build.gradle
        and can be independently changed in case you want to refactor your packages.
        This way you can still publish the same app with the same name.
        -->
    
        <!-- Will be expanded to net.twisterrob.app.android.App in the manifest merging phase. -->
        <application android:name=".App">
            <!-- meta-data needs FQCNs because the merger can't know if you want to expand them or not.
                 Also notice that name and value both can contain class names, depending on what you use. -->
            <meta-data android:name="net.twisterrob.app.android.GlideSetup" android:value="GlideModule" />
            <meta-data android:name="android.app.default_searchable" android:value="net.twisterrob.app.android.subpackage.SearchResultsActivity" />
            <!-- Will be expanded to net.twisterrob.app.android.subpackage.Activity in the manifest merging phase. -->
            <activity android:name=".subpackage.Activity" />
            <!-- Needs full qualification because it's not under the package defined on manifest element. -->
            <activity android:name="net.twisterrob.android.activity.AboutActivity" />
            <!-- Will be expanded to net.twisterrob.app.android.content.AppProvider in the manifest merging phase. -->
            <provider android:name=".content.AppProvider" android:authorities="${applicationId}" />
            <!-- Needs full qualification because it's not under the package defined on manifest element. -->
            <provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.share" />
        </application>
        <!-- ${applicationId} will be replaced with what's defined in `build.gradle` -->
    </manifest>
    

    build.gradle

    android {
        defaultConfig {
            // this is what will be used when you upload it to the Play Store
            applicationId 'net.twisterrob.app'
        }
        buildTypes {
            debug {
                // The neatest trick ever!
                // Released application: net.twisterrob.app
                // IDE built debug application: net.twisterrob.app.debug
                // This will allow you to have your installed released version
                // and sideloaded debug application at the same time working independently.
                // All the ContentProvider authorities within a system must have a unique name 
                // so using ${applicationId} as authority will result in having two different content providers.
                applicationIdSuffix '.debug'
            }
        }
    }
    

    查看在所有合并打开 build\intermediates\manifests\full\debug\AndroidManifest.xml 之后您的最终清单会是什么样子。

    【讨论】:

    • ResolveInfo 数据形成ComponentName 可能很有用:只需像这样intent.component = ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name) 为您的意图设置组件名称
    【解决方案2】:

    或者你可以在 BroadcastReceiver 中这样使用:

    ComponentName smsReceiver = new ComponentName(this, SMSReceiver.class);
    

    【讨论】:

      【解决方案3】:

      带有两个Strings 的ComponentName 构造函数可用于引用另一个应用程序中的组件。但是,第一个参数不是类的包名;它是应用程序的包名——该应用程序的AndroidManifest.xmlmanifest 元素的package 属性。所以你的第一个例子应该是

      ComponentName cn = new ComponentName("de.zordid.sampleapp",
          "de.zordid.sampleapp.widget.WidgetProvider");
      

      该构造函数当然可以用于引用您自己的应用程序中的组件,但是由于您已经从自己的应用程序中获得了Context,您不妨使用它并使用其他构造函数之一。在我看来,只要可用,应该首选使用Class 的那个。如果您出于某种原因仅动态了解该类,则可以使用String 的那个;在这种情况下,它应该采用上述完全限定的类名。

      【讨论】:

      • 感谢您的帮助!你能不能解释一下Android中以点开头的类名的含义?我认为这主要是在 XML 文件中看到的……我认为它可能是“此应用程序包”以及后面的任何内容的缩写。所以在我的例子中,“.widget.WidgetProvider”指的是同一个类——对吗?
      • 再次阅读 ComponentName(String, String) 的 JavaDoc 我真的认为给定的描述至少可以说是误导......他们真的说“组件所在的包的名称. 不能为空。 - 但你是对的:它是应用程序的包名,第二个字符串是类的全名,而不是我想的简单名称!哎呀……
      • @Zordid,确实你对以点开头的类名是正确的,至少在AndroidManifest.xml 中。但是,它在 ComponentName 构造函数中不起作用。
      • 谢谢!但是,如果没有点,也会找到类。所以我问自己一个问题:区别在哪里?加点与否似乎并没有改变任何东西......? straaange
      • 我在调试模式下实现这个,我的包名现在有一个后缀。当我使用新的 ComponentName(this, ".AliasName") 时,会抛出一个错误,说 package.debug 没有类名。这是为什么呢?
      猜你喜欢
      • 1970-01-01
      • 2013-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多