Service Intent muss explizit sein: Intent

72

Ich habe seit einiger Zeit eine App, in der ich einen Dienst über einen Rundfunkempfänger (MyStartupIntentReceiver) anrufe. Der Code im Rundfunkempfänger zum Aufrufen des Dienstes lautet:

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

Das Problem ist, dass ich in Android 5.0 Lollipop den folgenden Fehler erhalte (in früheren Versionen von Android funktioniert alles in Ordnung):

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

Was muss ich ändern, damit der Dienst als explizit deklariert und normal gestartet wird? Versuchte einige Antworten in anderen ähnlichen Threads, aber obwohl ich die Nachricht loswurde, würde der Dienst nicht starten.

duk3r
quelle
Ist dies ein Dienst in Ihrer App?
Tyczj
ja das paket com.duk3r.eortologio2 ist meine app.
duk3r
3
Haben <intent-filter>Sie nur eine Komponente, wenn Sie möchten, dass Apps von Drittanbietern mit dieser Komponente kommunizieren. Sie scheinen in die Falle geraten zu sein, anzunehmen, dass Sie <intent-filter>für alles eine brauchen - in Wirklichkeit brauchen Sie selten eine <intent-filter>. Eine explizite Intentist, wenn Sie die Komponente festlegen, mit der in sich Intentselbst gesprochen werden soll, normalerweise unter Verwendung des Konstruktors, der ein Java- ClassObjekt als zweiten Parameter verwendet. Dies sollten Sie anstelle von impliziten Intents und <intent-filter>s für Komponenten verwenden, die für Ihre App lokal sind.
CommonsWare

Antworten:

124

Alle Absichten, die Sie in Bezug auf einen Dienst, eine Aktivität usw. in Ihrer App machen, sollten immer diesem Format folgen

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

oder

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

implizite Absichten (was Sie derzeit in Ihrem Code haben) gelten als Sicherheitsrisiko

tyczj
quelle
1
Was ist, wenn ich muss bindService?
IgorGanapolsky
3
@IgorGanapolsky dann müssen Sie das Paket manuell einstellen, das es explizit macht
tyczj
Wenn Sie das Paket dynamisch erhalten möchten, verwenden Sie BuildConfig.APPLICATION_ID
Gibolt
31

Stellen Sie Ihre packageNameWerke ein.

intent.setPackage(this.getPackageName());
Kai Chen
quelle
5

Konvertieren Sie implizite Absichten in explizite Absichten und starten Sie dann den Dienst.

        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;
        }
Shahidul
quelle
0

Versuche dies. Für mich geht das. Hier ist MonitoringService meine Serviceklasse. Ich habe zwei Aktionen, die anzeigen, dass der Dienst gestoppt oder gestartet werden soll. Ich sende diesen Wert von meinem Rundfunkempfänger abhängig von 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);
    }
}

HINWEIS: Ich füge folgenden Code hinzu, um meinen Broadcast-Empfänger mit dem Namen " ManageLocationListenerReceiver" auszulösen .

<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
quelle
-1

Ich habe die Antwort von Shahidul verbessert, um die Kontextabhängigkeit zu beseitigen:

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;
    }
}

Innerhalb der SuperApplication-Klasse:

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 Ihrem Manifest:

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

Und dann rufen Sie einfach an:

ServiceUtils.startService("com.myservice");
Engelo Polotto
quelle
Es ist keine gute Idee, eine globale Einzelinstanzklasse aufzurufen, um den Kontext abzurufen. Stellen Sie stattdessen diese Abhängigkeit bitte offen.
Besser Shao
Wie können Sie den Anwendungskontext aufrufen, wenn sich ServiceUtils außerhalb der Aktivität befindet? Können Sie ein Beispiel geben?
Angelo Polotto
1
ServiceUtils ist ein Dienstprogramm, daher ist es nicht erforderlich, von einer konkreten Anwendungsklasse wie SuperApplication abhängig zu sein. Es liegt in der Verantwortung des Aufrufers der startService-Methode, einen Kontext bereitzustellen. Der Einfachheit halber ist es möglich, die Anwendung zu injizieren, um sie überall in der Anwendung zu verwenden. Wie Sie vielleicht bereits feststellen, ist es keine gute Idee, die Methode "überall" nennen zu lassen. Wir sollten Klassen mit nur einer einzigen Verantwortung erstellen und unnötige Abhängigkeiten vermeiden. Auf diese Weise kann es auf Einheiten getestet werden und der Code ist sauber.
Besser Shao
Vielen Dank für Ihre Antwort. Ich werde diese Antwort nicht entfernen, damit die anderen auch mehr darüber erfahren.
Angelo Polotto