使用自定义意图操作仅在特定应用程序上下文中启动 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 的登录)。
- 我对自定义 Intent 过滤器工作原理的理解是否正确?
- 有没有办法告诉应用程序 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>
这些是我执行的步骤:
- 在您的清单中定义自定义 intent 过滤器
- 在您的 activity(您要启动的)中覆盖 onCreate 和 onNewIntent
- 通过创建另一个启动自定义 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);
}
我需要使用 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 的登录)。
- 我对自定义 Intent 过滤器工作原理的理解是否正确?
- 有没有办法告诉应用程序 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>
这些是我执行的步骤:
- 在您的清单中定义自定义 intent 过滤器
- 在您的 activity(您要启动的)中覆盖 onCreate 和 onNewIntent
- 通过创建另一个启动自定义 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);
}