84

I have an app some time now in which I call a service through a broadcast receiver (MyStartupIntentReceiver). The code in the broadcast receiver in order to call the service is:

public void onReceive(Context context, Intent intent) {
    Intent serviceIntent = new Intent();
    serviceIntent.setAction("com.duk3r.eortologio2.MyService");
    context.startService(serviceIntent);
}

The problem is that in Android 5.0 Lollipop I get the following error (in previous versions of Android, everything works ok):

Unable to start receiver com.duk3r.eortologio2.MyStartupIntentReceiver: java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.duk3r.eortologio2.MyService }

What do I have to change in order for the service to be declared as explicit and start normally? Tried some answers in other similar threads but although i got rid of the message, the service wouldn't start.

duk3r
  • 1,174
  • 2
  • 12
  • 16
  • is this a service in your app? – tyczj Jan 08 '15 at 14:28
  • yes the package com.duk3r.eortologio2 is my app. – duk3r Jan 08 '15 at 14:29
  • 4
    Only have an `` on a component when you want third-party apps to communicate with that component. You seem to have fallen into the trap of assuming that you need an `` for everything -- in reality, you infrequently need an ``. An explicit `Intent` is when you designate the component to talk to in the `Intent` itself, usually using the constructor that takes a Java `Class` object as the second parameter. That, rather than implicit `Intent`s and ``s, are what you should use for components local to your app. – CommonsWare Jan 08 '15 at 14:34
  • 1
    Possible duplicate of [Android L (API 21) - java.lang.IllegalArgumentException: Service Intent must be explicit](http://stackoverflow.com/questions/27183164/android-l-api-21-java-lang-illegalargumentexception-service-intent-must-be) – regisd Feb 20 '16 at 22:04

5 Answers5

140

any intent you make to a service, activity etc. in your app should always follow this format

Intent serviceIntent = new Intent(context,MyService.class);
context.startService(serviceIntent);

or

Intent bi = new Intent("com.android.vending.billing.InAppBillingService.BIND");
bi.setPackage("com.android.vending");

implicit intents (what you have in your code currently) are considered a security risk

tyczj
  • 71,600
  • 54
  • 194
  • 296
  • 1
    What if I need to `bindService`? – IgorGanapolsky Jul 14 '16 at 18:06
  • 3
    @IgorGanapolsky then you have to manually set the package which makes it explicit – tyczj Jul 14 '16 at 18:13
  • If you want to get the package dynamically, use BuildConfig.APPLICATION_ID – Gibolt Mar 05 '18 at 04:21
  • Beautiful answer. This indeed eliminated my "Service Intent must be explicit" error. But now I am getting *"Your version of Market may be out of date. You can continue to use this app but you won't be able to make purchases."* How do I solve this? – WebViewer Dec 12 '22 at 12:45
34

Set your packageName works.

intent.setPackage(this.getPackageName());
ketan
  • 19,129
  • 42
  • 60
  • 98
kai chen
  • 341
  • 3
  • 2
6

Convert implicit intent to explicit intent then fire start service.

        Intent implicitIntent = new Intent();
        implicitIntent.setAction("com.duk3r.eortologio2.MyService");
        Context context = getApplicationContext();
        Intent explicitIntent = convertImplicitIntentToExplicitIntent(implicitIntent, context);
        if(explicitIntent != null){
            context.startService(explicitIntent);
            }


    public static Intent convertImplicitIntentToExplicitIntent(Intent implicitIntent, Context context) {
            PackageManager pm = context.getPackageManager();
            List<ResolveInfo> resolveInfoList = pm.queryIntentServices(implicitIntent, 0);

            if (resolveInfoList == null || resolveInfoList.size() != 1) {
                return null;
            }
            ResolveInfo serviceInfo = resolveInfoList.get(0);
            ComponentName component = new ComponentName(serviceInfo.serviceInfo.packageName, serviceInfo.serviceInfo.name);
            Intent explicitIntent = new Intent(implicitIntent);
            explicitIntent.setComponent(component);
            return explicitIntent;
        }
Ângelo Polotto
  • 8,463
  • 2
  • 36
  • 37
Shahidul
  • 2,997
  • 1
  • 21
  • 20
0

Try this. it works for me. Here MonitoringService is my service class. I have two action, which indicate service to stop or start. I send that value from my broadcast receiver depend on AIRPLANE_MODE_CHANGED.

@Override
public void onReceive(Context context, Intent intent) {   
    String action = intent.getAction();

    if(Intent.ACTION_AIRPLANE_MODE_CHANGED.equalsIgnoreCase(action)){
         boolean isOn = intent.getBooleanExtra("state", false);
         String serviceAction = isOn? MonitoringService.StopAction : MonitoringService.StartAction;
         Intent serviceIntent = new Intent(context, MonitoringService.class);
         serviceIntent.setAction(serviceAction);
         context.startService(serviceIntent);
    }
}

NOTE: I add following code to trigger my broadcast receiver named: ManageLocationListenerReceiver.

<receiver
     android:name=".ManageLocationListenerReceiver"
     android:enabled="true"
     android:exported="true">
     <intent-filter>
         <action android:name="android.intent.action.AIRPLANE_MODE" />
     </intent-filter>
</receiver>
reza.cse08
  • 5,938
  • 48
  • 39
-1

I improved the answer of Shahidul to remove the context dependency:

public class ServiceUtils {
    public static void startService(String intentUri) {
        Intent implicitIntent = new Intent();
        implicitIntent.setAction(intentUri);
        Context context = SuperApplication.getContext();
        Intent explicitIntent = convertImplicitIntentToExplicitIntent(implicitIntent, context);
        if(explicitIntent != null){
            context.startService(explicitIntent);
        }
    }

    private static Intent convertImplicitIntentToExplicitIntent(Intent implicitIntent, Context context) {
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> resolveInfoList = pm.queryIntentServices(implicitIntent, 0);

        if (resolveInfoList == null || resolveInfoList.size() != 1) {
            return null;
        }
        ResolveInfo serviceInfo = resolveInfoList.get(0);
        ComponentName component = new ComponentName(serviceInfo.serviceInfo.packageName, serviceInfo.serviceInfo.name);
        Intent explicitIntent = new Intent(implicitIntent);
        explicitIntent.setComponent(component);
        return explicitIntent;
    }
}

Inside the SuperApplication class:

public class SuperApplication extends Application {
    private static MyApp instance;

    public static SuperApplication getInstance() {
        return instance;
    }

    public static Context getContext(){
        return instance;
        // or return instance.getApplicationContext();
    }

    @Override
    public void onCreate() {
        instance = this;
        super.onCreate();
    }
}

In your manifest:

<application
    android:name="com.example.app.SuperApplication "
    android:icon="@drawable/icon"
    android:label="@string/app_name"
    .......
    <activity
        ......

And then, just call:

ServiceUtils.startService("com.myservice");
Ângelo Polotto
  • 8,463
  • 2
  • 36
  • 37
  • It's not a good idea to call a global single instance class to get context. Instead, expose this dependency please. – Better Shao Aug 30 '18 at 01:35
  • How you can call the application context if the ServiceUtils is outside of the activity? Can you provide some example? – Ângelo Polotto Aug 30 '18 at 14:24
  • 1
    ServiceUtils is a utility, thus, it's not necessary to depend on a concret Application class such as SuperApplication. It's the responsibility of the caller of startService method to provide a context. For convenience, it's possible to inject the Application to be used every where in the application. But as you may already find, it's not a good idea to allow the method to be called "every where". We'd better create classes with only single (set of) responsibility and avoid unnecessary dependency. This way, it's unit-testable and the code is clean. – Better Shao Sep 06 '18 at 05:31
  • Thank you for you answer. I will not remove that answer to the others learn more about this too. – Ângelo Polotto Sep 06 '18 at 15:09