When calling startActivity
, one can try to catch an ActivityNotFoundException
to know whether the activity exists.
When calling startService
, however, there is no ServiceNotFoundException
. How can I detect if the service exists?
Why do I want to do this
As I have learnt, a call to startService
will be processed asynchronously, hence I would like to know whether I should expect a response (e.g. a callback, or a broadcast) from the service or not.
What have I done so far
I searched a bit and find a related question here. It seems that internally there is a ClassNotFoundException
raised.
Is it possible to catch this exception somewhere? The Class.forName()
method doesn't seem right ... or is it?
If it is your own service, you know whether your service exists. This issue only comes into play if you are trying to work with some third-party service. In that case, use PackageManager
and queryIntentServices()
to see if there is something that matches your Intent
.
For example, in this sample app, I use queryIntentServices()
to:
Confirm that there is only one match for my Intent
Convert it from an implicit Intent
to an explicit Intent
Validate the signing key of the service, so that I know that it is not some service that is masquerading as the one I want to work with (e.g., repackaged app with malware)
Mostly, that is handled in onCreate()
of the client fragment that will bind to the service:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
appContext=(Application)getActivity().getApplicationContext();
Intent implicit=new Intent(IDownload.class.getName());
List<ResolveInfo> matches=getActivity().getPackageManager()
.queryIntentServices(implicit, 0);
if (matches.size() == 0) {
Toast.makeText(getActivity(), "Cannot find a matching service!",
Toast.LENGTH_LONG).show();
}
else if (matches.size() > 1) {
Toast.makeText(getActivity(), "Found multiple matching services!",
Toast.LENGTH_LONG).show();
}
else {
ServiceInfo svcInfo=matches.get(0).serviceInfo;
try {
String otherHash=SignatureUtils.getSignatureHash(getActivity(),
svcInfo.applicationInfo.packageName);
String expected=getActivity().getString(R.string.expected_sig_hash);
if (expected.equals(otherHash)) {
Intent explicit=new Intent(implicit);
ComponentName cn=new ComponentName(svcInfo.applicationInfo.packageName,
svcInfo.name);
explicit.setComponent(cn);
appContext.bindService(explicit, this, Context.BIND_AUTO_CREATE);
}
else {
Toast.makeText(getActivity(), "Unexpected signature found!",
Toast.LENGTH_LONG).show();
}
}
catch (Exception e) {
Log.e(getClass().getSimpleName(), "Exception trying to get signature hash", e);
}
}
}