问题
Instead of going the regular getPackageManager().getLaunchIntentForPackage("com.example.app")
way, I want to create the launch intent by myself.
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setPackage("com.example.app");
startActivity(intent);
Why does Android not find the Activity, if the com.example.app
is installed, enabled and has a correct manifest? (It works perfectly with getLaunchIntentForPackage
.)
回答1:
I understand that you are trying to start the Launcher activity of a known application with known package name (com.example.app
). I assume you have information about the application. Thus, you can start it via explicit intent like so:
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.app", "com.example.app.MainActivity"));
if(intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
EDIT: Doing an analysis of the two intent objects (intent1
== your own intent VS intent2
== intent created from getLaunchIntentForPackage()
), the difference is
intent1:
{ act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] pkg=com.example.app }
intent2:
{ act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.example.app cmp=com.example.app/.MainActivity }
I will have to believe that what you have done to create your own intent object is not enough for the explicit intent to work. You will have to provide Android more information about your intent such as being specific with the component name (as shown in my answer above).
回答2:
'To receive implicit intents, you must include the CATEGORY_DEFAULT category in the intent filter.' - Does your receiving app have this?
Example:
<activity android:name="ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
Excerpt from: https://developer.android.com/guide/components/intents-filters#Receiving
You can also check to make sure there is an activity that can receive your broadcast:
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;
Excerpt from: https://developer.android.com/training/basics/intents/sending#java
回答3:
This is the function where android.content.Intent#CATEGORY_DEFAULT is added to all startActivity code.
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) {
try {
return AppGlobals.getPackageManager().resolveIntent(intent, resolvedType,
PackageManager.MATCH_DEFAULT_ONLY | flags
| ActivityManagerService.STOCK_PM_FLAGS, userId);
} catch (RemoteException e) {
}
return null;
}
/**
* Resolution and querying flag: if set, only filters that support the
* {@link android.content.Intent#CATEGORY_DEFAULT} will be considered for
* matching. This is a synonym for including the CATEGORY_DEFAULT in your
* supplied Intent.
*/
public static final int MATCH_DEFAULT_ONLY = 0x00010000;
This is code from where everything starts http://androidxref.com/7.1.2_r36/xref/frameworks/base/core/java/android/app/ContextImpl.java#766
回答4:
startActivity
treats all intents as if they declared CATEGORY_DEFAULT
Even if you don't have
intent.addCategory(Intent.CATEGORY_DEFAULT);
in your code.Even if you add
intent.removeCategory(Intent.CATEGORY_DEFAULT);
.Even if your intent is explicit*:
intent.setPackage("com.example.app");
.
* Supplies "either the target app's package name or a fully-qualified component class name".
...except if it doesn't
The system will not look for CATEGORY_DEFAULT
if you set the class name of the target activity:
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setClassName("com.example.app", "com.example.app.NameOfTheActivityToBeStarted");
startActivity(intent);
Source of header: the blue note on the <category> element's page.
Source of the definition of an explicit intent: developer.android.com.
回答5:
You asked to see the code executed after startActivity
and here it is.
In your app:Activity.startActivity(Intent)
callsActivity.startActivity(Intent, Bundle)
, which callsActivity.startActivityForResult(Intent, int)
, which callsFragmentActivity.startActivityForResult(Intent, int)
, which callsActivity.startActivityForResult(Intent, int)
, which callsActivity.startActivityForResult(Intent, int, Bundle)
, which callsInstrumentation.execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)
, which calls
IActivityManager.startActivity(IApplicationThread, String, Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle)
The call on the last line is a remote process call, meaning that in your app process a the method is called on a proxy IActivityManager
instance which forwards it to another process, in this case a system process.
Up to this point, no Intent filtering has taken place.
In Android's system process IActivityManager
resolved to ActivityManagerService
and:
ActivityManagerService.startivity(IApplicationThread, String, Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle)
callsActivityManagerService.startActivityAsUser(IApplicationThread, String, Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle, int)
, which callsActivityStackSupervisor.startActivityMayWait(IApplicationThread, int, String, Intent, String, IVoiceInteractionSession, IVoiceInteractor, IBinder, String, int, int, ProfilerInfo, WaitResult, Configuration, Bundle, boolean, int, IActivityContainer, TaskRecord)
, which callsActivityStackSupervisor.resolveActivity(Intent, String, int, ProfilerInfo, int)
, which callsIPackageManager.resolveIntent(Intent, String, int, int)
This is the where MATCH_DEFAULT_ONLY is added, as nkalra0123 said.
Also, this is another remote method invocation. IPackageManager
gets resolved to PackageManagerService
, and from there it goes like this:
PackageManagerService.resolveIntent(Intent, String, int, int)
callsPackageManagerService.queryIntentActivities(Intent, String, int, int)
, which attempts to get all the Activities for the Intent package. This gets the Activities from your package and then callsPackageService.ActivityIntentResolver.queryIntentForPackage(Intent, String, int, ArrayList<PackageParser.Activity>, int)
, which gets the IntentFilters in your package and then callsPackageService.ActivityIntentResolver.queryIntentFromList(Intent, String, boolean , ArrayList<F[]>, int)
, which callsIntentResolver.buildResolveList(...)
, which runs all the IntentFilters it found against the data in your Intent, taking into account whether or not we need CATEGORY_DEFAULT
, and adding the matching IntentFilters to a list accordingly.
All these call method calls then return and eventually some object somewhere will figure out there were no matching IntentFilters. I omit that here because this is the relevant part of the answer.
回答6:
You need to create a component name for the needed app such as:
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setComponent(ComponentName.createRelative("com.whatsapp",".Main"));
intent.setPackage("com.whatsapp");
The component name represents the activity that you need to open, full package name and the second parameter is the class name for that package.
回答7:
Let me add some additional info in this. as described here, Without a component name, the intent is implicit.
Component name is optional, but it's the critical piece of information that makes an intent explicit, meaning that the intent should be delivered only to the app component defined by the component name. Without a component name, the intent is implicit and the system decides which component should receive the intent based on the other intent information (such as the action, data, and category—described below). If you need to start a specific component in your app, you should specify the component name.
The DEFAULT
category is required for the Context.startActivity method to resolve your activity when its component name is not explicitly specified. Check first example in this link.
Hope this will help.
来源:https://stackoverflow.com/questions/54492708/why-is-no-activity-found-to-handle-intent