隐式启动activity:
intent进行匹配时需要3个属性, action category 和data,
通常情况下首先在清单文件里注册
<activity android:name="ActivityTest">
<intent-filter>
<action android:name="com_activity_test"/>
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="xxx"/>
</intent-filter>
</activity>
action 和 category 这两个属性必须要有, data可以省略.
说一下<category android:name="android.intent.category.DEFAULT" /> 这个属性的作用:
当使用隐式启动activity时,若不加这条属性会报如下错误:
// android.content.ActivityNotFoundException: No Activity found to handle Intent { act=com_activity_test }
一般来说如果不指定category,只要匹配到对应的action就应该查找到对应的activity,但真是的原因是,android默认会给intnet加上一个CATEGORY_DEFAULT,
这样的话,如果当前注册的activity中没有"android.intent.category.DEFAULT"这个category属性,intnet匹配时就无法找到对应的activity.
查看源码,当我们使用 intent.resolveActivity(getPackageManager()) 进行检查时:
/frameworks/base/core/java/android/content/Intent.java
public ComponentName resolveActivity(PackageManager pm) {
if (mComponent != null) {
return mComponent;
}
//{@link PackageManager#resolveActivity} with the "defaultOnly" parameter true MATCH_DEFAULT_ONLY 值为 1
ResolveInfo info = pm.resolveActivity(this, PackageManager.MATCH_DEFAULT_ONLY);
if (info != null) {
return new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
}
return null;
}
/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
/** if using an implicit Intent (without an explicit ComponentName
* specified), be sure to consider whether to set the {@link #MATCH_DEFAULT_ONLY} only flag
*
* @param flags Additional option flags. The most important is
* {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
* those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
*/
public abstract ResolveInfo resolveActivity(Intent intent, int flags);
通过注释可以看出resolveActivity方法会默认添加MATCH_DEFAULT_ONLY的flags,用来限制查找那些添加了android.content.Intent#CATEGORY_DEFAULT属性的
activites,所以你注册的activity没有添加此属性是无法被找到的.
隐式启动service:
在5.0之前可采用如下方式来启动:
注册
<service android:name="ServicesTest">
<intent-filter>
<action android:name="com_services_test"/>
</intent-filter>
</service>
启动
Intent sendIntent = new Intent();
sendIntent.setAction("com_services_test");
startService(sendIntent);
在5.0之后会报如下错误:
//java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com_services_test }
通过报错log发现原因如下:
/frameworks/base/core/java/android/app/ContextImpl.java
private void validateServiceIntent(Intent service) {
if (service.getComponent() == null && service.getPackage() == null) { //判断ComponentName 是否为空
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
IllegalArgumentException ex = new IllegalArgumentException(
"Service Intent must be explicit: " + service);
throw ex;
} else {
Log.w(TAG, "Implicit intents with startService are not safe: " + service
+ " " + Debug.getCallers(2, 3));
}
}
}
在5.0之前只是会报一个警告,可以正常编译运行,但是在5.0之后,系统不允许使用隐式来启动service,直接抛出异常,其实隐式启动service是非常不安全的.
根据判断的条件只要ComponentName不为null即可,所以可通过下面方法来启动一个service:
Intent intent = new Intent();
ComponentName componentName = new ComponentName(getPackageName(),ServicesTest.class.getName());
intent.setComponent(componentName);
startService(intent);
笔记 intent隐式启动的两个注意事项
最新推荐文章于 2024-07-11 21:03:53 发布