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

Posted

技术标签:

【中文标题】使用自定义意图操作仅在特定应用程序上下文中启动活动【英文标题】:Use custom intent action to launch activity only in specific application context 【发布时间】:2018-04-28 09:40:58 【问题描述】:

我需要使用意图过滤器指定我的登录活动:

    <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 这是登录活动的最佳方法,而它只在应用模块中定义。

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

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

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

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

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

【问题讨论】:

【参考方案1】:

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

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

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);

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

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

【讨论】:

Boris,你在你的库中定义了 startLoginActivity 吗?您的每个应用程序中都有不同的意图过滤器? @RonakMehta 是的 @BorisStrandjev,你能帮忙吗?我使用IntentFilter filter = new IntentFilter(Intent.ActionTimeTick);,但自定义操作的过滤器看起来如何? @AlexanderGorg 您在此处使用的构造函数使用 String 作为参数。这里你使用系统中预先配置的常量developer.android.com/reference/android/content/…等于android.intent.action.TIME_TICK但是你可以直接使用"com.example.myapp.LOGIN"【参考方案2】:

在这种情况下,在每个应用程序中,Activity 启动都会启动一个随机登录活动。

这并不完全正确。来自docs:

如果只有一个应用可以处理它,则该应用会立即打开并获得意图。如果多个活动接受意图,系统会显示一个对话框,以便用户选择要使用的应用程序。

关于你的第二个问题:

有没有办法告诉应用程序 A 中的调用只使用应用程序 A 中的意图过滤器声明?

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

没有 API,可以为您提供活动类,由其intent-filter 属性过滤。 PackageManager API 无法提供帮助。这意味着,您无法获取活动的类实例以显式启动活动。这反过来意味着,您的唯一方法是隐式启动活动,如前所述,它将通过对话框提示。

【讨论】:

对话框不方便,因为活动将在令牌过期时启动(这绝对不是用户获得选择器对话框的自然事件) 我找到了解决我的任务的方法...您可以在我的答案中查看方法【参考方案3】:

这些是我做的步骤:

    在清单中定义自定义意图过滤器 在您的 Activity(您要启动的)内部覆盖 onCreateonNewIntent 通过创建另一个启动自定义 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>

覆盖活动生命周期:

@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(); if (intent.resolveActivity(packageManager) != null) 开始活动(意图);

【讨论】:

我不完全明白你的建议 - 我如何区分两个 MainActivity 类(来自两个应用程序)【参考方案4】:

我认为更好的解决方案是在AndroidManifest&lt;application&gt; 部分中使用&lt;meta-data&gt; 属性。您的客户可以在此处指定任何值,然后您可以在您的库中读取它。因此,您的客户将指定他的 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);

【讨论】:

以上是关于使用自定义意图操作仅在特定应用程序上下文中启动活动的主要内容,如果未能解决你的问题,请参考以下文章

使用自定义类启动浏览器意图 - 找不到活动

将仅在特定应用程序中可见的操作添加到 ACTION_SEND?

自定义操作意图在与我单击的应用程序相同的上下文中打开我的应用程序

将自定义 ListView 项传递给 Android 中的其他活动

从列表视图的自定义适配器内的意图服务接收结果

使用自定义 Google Now 命令启动 Android 应用