使用自定义意图操作仅在特定应用程序上下文中启动 activity

Use custom intent action to launch activity only in specific application context

我需要使用 intent 过滤器指定我的登录名 activity:

    <activity
        android:name=".view.LoginActivity"
        android:label="@string/app_name">
        <intent-filter>
            <action android:name="com.example.sdk.LOGIN" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>

我想这样做的原因是我正在开发一个我想在几个项目中使用的 SDK,这是我能弄清楚如何通知 SDK 的最佳方式是登录activity,而它仅在应用程序模块中定义。

在我做了这样的声明之后,我开始登录 activity 像这样(在库和应用程序中):

    Intent intent = new Intent();
    intent.setAction("com.example.sdk.LOGIN");
    context.startActivity(intent);

有可能会在同一台设备上同时安装两个或多个依赖同一 SDK 的应用程序。如果我理解正确,在这种情况下,在每个应用程序中,Activity 启动将启动随机登录 activity(应用程序 A 中的调用可能会启动应用程序 B 的登录)。

  1. 我对自定义 Intent 过滤器工作原理的理解是否正确?
  2. 有没有办法告诉应用程序 A 中的调用仅使用应用程序 A 中的意图过滤器声明

也欢迎提出重用登录相关逻辑而不依赖 Intent 过滤器的替代方法的建议。

In such case in each of these applications the Activity start will launch a random login activity.

这不完全正确。来自 docs:

If there's only one app that can handle it, that app opens immediately and is given the intent. If multiple activities accept the intent, the system displays a dialog, so the user can pick which app to use.

关于你的第二个问题:

Is there a way to tell the call in application A to use only intent filter declarations in application A?

我假设您无法使用显式意图启动 activity,因为您无法知道库模块中客户端的 activity 名称。

没有API,这会给你activityclass,通过intent-filter[=36]过滤=]. PackageManager API 帮不上忙。这意味着,您无法获取 activity 的 class 实例以显式启动 activity。这反过来意味着,您唯一的方法是隐式启动 activity,如前所述,这将提示对话框。

我想出了一个方法来实现我想要的。

基本上我不会在我的库中对包进行硬编码,而是使用以下方法从库开始登录 activity:

private static final String INTENT_ACTION_PATTERN = "%s.LOGIN";
public void startLoginActivity() {
    Intent intent = new Intent();
    String intentAction = String.format(INTENT_ACTION_PATTERN, mContext.getPackageName());
    intent.setAction(intentAction);
    mContext.startActivity(intent);
}

我将只使用一个约定,即我将始终定义自定义 intent 过滤器以匹配我的应用程序包:

<activity
    android:name=".view.LoginActivity"
    android:label="@string/app_name">
    <intent-filter>
        <action android:name="com.example.myapp.LOGIN" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

这些是我执行的步骤:

  1. 在您的清单中定义自定义 intent 过滤器
  2. 在您的 activity(您要启动的)中覆盖 onCreateonNewIntent
  3. 通过创建另一个启动自定义 intent 的应用程序对其进行测试

清单示例:

<activity
   android:name="com.test.MainActivity">
   <intent-filter>
       <action android:name="test_action_that_should_be_unique"></action>
       <category android:name="android.intent.category.DEFAULT"></category>
   </intent-filter>
</activity>

覆盖 activity 生命周期:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = getIntent();
        if (intent == null)
            return;
        String action = intent.getAction();
        if (action == null || !action.equals("test_action_that_should_be_unique"))
            return;

        Toast.makeText(this, "onCreate: test_action_that_should_be_unique", Toast.LENGTH_SHORT).show();
    }


 @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        if (intent == null)
            return;
        String action = intent.getAction();
        if (action == null || !action.equals("test_action_that_should_be_unique"))
            return;

        Toast.makeText(this, "onNewIntent: test_action_that_should_be_unique", Toast.LENGTH_SHORT).show();
    }

从其他应用程序测试新的自定义意图:

  Intent intent = new Intent("test_action_that_should_be_unique");
  startActivity(intent);
  • 当然检查上面的意图是否为空:

    PackageManager packageManager = getActivity().getPackageManager(); 如果 (intent.resolveActivity(packageManager) != null) { 启动活动(意图); }

我认为更好的解决方案是在 AndroidManifest<application> 部分使用 <meta-data> 属性。您的客户可以在此处指定任何值,然后您可以在您的库中读取它。因此,您的客户将指定 class 他 LoginActivity 的名称,您将使用它通过显式 Intent 启动它。

客户端应用程序中的 AndroidManifest

<application ...>
    ...
    <meta-data android:name="com.example.sdk.LOGIN_ACTIVITY" android:value=".view.LoginActivity" />
</application>

在您的库代码中的使用

(不要忘记处理特殊情况并添加异常处理)
public void startLoginActivity(Context context) {
    String packageName = context.getPackageName();

    PackageManager pm = context.getPackageManager();
    ApplicationInfo appInfo = pm.getApplicationInfo(packageName, PackgeManager.GET_META_DATA);
    Bundle data = appInfo.metaData;

    String activityName = data.getString("com.example.sdk.LOGIN_ACTIVITY");
    if (activityName == null || activityName.isEmpty()) {
        return;
    }
    ComponentName component = ComponentName.createRelative(packageName, activityName);
    Intent intent = new Intent().setComponent(component);

    context.startActivity(intent);
}