Когда использовать какой конструктор для ComponentName в Android?

Я немного запутался в классе ComponentName в Android.

Есть разные способы добраться до объекта имени компонента, но я не знаю, когда какой использовать... и почему!

Пример:

  • Пакет приложений 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 — по контексту и строке. Какой из них следует использовать, где и когда??

Спасибо!


person Zordid    schedule 13.02.2011    source источник


Ответы (3)


Конструктор ComponentName, принимающий два String, может использоваться для ссылки на компонент в другом приложении. Но первый аргумент — это не имя пакета класса; это имя пакета приложения --- атрибут package элемента manifest в файле AndroidManifest.xml этого приложения. Итак, ваш первый пример должен быть

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

Этот конструктор, безусловно, можно использовать для ссылки на компоненты в вашем собственном приложении, но, поскольку у вас уже есть Context из вашего собственного приложения, вы также можете использовать его и использовать один из других конструкторов. На мой взгляд, тот, у кого есть Class, должен быть предпочтительнее, когда это возможно. Вы можете использовать тот, который принимает String, если по какой-то причине вы знаете класс только динамически; в этом случае он должен принимать полное имя класса, как указано выше.

person Robert Tupelo-Schneck    schedule 13.02.2011
comment
Спасибо за вашу помощь! Не могли бы вы также объяснить значение имен классов, начинающихся с точки в Android ?? Я видел это в основном в файлах XML, я думаю... Я полагаю, что это может быть коротким для пакета этого приложения и всего, что следует за ним. Итак, в моем примере .widget.WidgetProvider относится к одному и тому же классу - это правильно?? - person Zordid; 13.02.2011
comment
Читая JavaDoc для ComponentName(String, String) снова, я действительно думаю, что данное описание, по меньшей мере, вводит в заблуждение... Они действительно говорят Имя пакета, в котором существует компонент. Не может быть нулевым. - но вы правы: это имя пакета приложения, а вторая строка - это полное имя класса, а не простое имя, как я думал! Гы... - person Zordid; 13.02.2011
comment
@Zordid, действительно, вы правы в том, что имена классов начинаются с точки, по крайней мере, в AndroidManifest.xml. Однако это не работает в конструкторе ComponentName. - person Robert Tupelo-Schneck; 14.02.2011
comment
Благодарность! Но и без точки классы найдутся. Вот я и задаю себе вопрос: а где разница? Ставить точку или нет, кажется, ничего не меняет...? странность - person Zordid; 16.02.2011
comment
Я реализую это в режиме отладки, и теперь имя моего пакета имеет суффикс. Когда я использую новое имя компонента (это, .AliasName), выдает ошибку о том, что package.debug не имеет имени класса. почему это? - person Aman Verma; 06.10.2019
comment
Я понял. Предположим, если у вас есть две сборки, выпуск и отладка. В отладке у вас есть суффикс - .debug, тогда имя вашего компонента должно быть таким - › if (debug) {new ComponentName(this, originalPackagenamewithoutsuffix + .AliasSplashScreen); } else{ новое ИмяКомпонента(это, getPackageName() + .AliasSplashScreen); } - person Aman Verma; 06.10.2019

Ответ 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 и <manifest package="

Здесь может быть некоторая путаница, потому что я думаю, что исторически утверждение Роберта было верным:

это имя пакета приложения --- атрибут пакета элемента манифеста в файле AndroidManifest.xml этого приложения.

Но не больше. С момента появления новой системы сборки Gradle были внесены некоторые изменения. где-то здесь.

Если у вас есть android.defaultConfig.applicationId, указанный в вашем build.gradle, это будет имя пакета приложения, а затем атрибут package в манифесте будет отдельной вещью при создании вашего приложения. Первый аргумент ComponentName теперь относится к applicationId + applicationIdSuffix. Сложность заключается в том, что после окончательного слияния и упаковки манифеста APK будет иметь <manifest package=applicationId + applicationIdSuffix, а все имена .Name будут расширены до FQCN.

Пример приложения для изучения разрешения имен

Вот пример структуры, основанной на структуре одного из моих приложений. Рассмотрим следующие классы в гипотетическом приложении под названием «приложение»:

  • 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.

person TWiStErRob    schedule 19.06.2016
comment
Может быть полезно сформировать ComponentName из ResolveInfo данных: просто задайте имя компонента для вашего намерения, как это intent.component = ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name) - person soshial; 16.03.2021

Или вы можете использовать это внутри BroadcastReceiver :

ComponentName smsReceiver = new ComponentName(this, SMSReceiver.class);
person Jviaches    schedule 13.02.2012