服务意图必须明确:意图


72

我现在有一个应用程序,通过广播接收器(MyStartupIntentReceiver)调用服务。为了调用服务,广播接收器中的代码是:

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

问题是,在Android 5.0 Lollipop中,出现以下错误(在早期版本的Android中,一切正常):

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

为了将服务声明为显式且正常启动,我必须更改什么?在其他类似线程中尝试了一些答案,但尽管我摆脱了该消息,但该服务无法启动。


这是您应用中的一项服务吗?
tyczj 2015年

是的,包com.duk3r.eortologio2是我的应用程序。
duk3r

3
<intent-filter>当您希望第三方应用程序与该组件通信时,仅在该组件上具有。您似乎陷入了假设自己需要<intent-filter>所有东西的陷阱–实际上,您很少需要<intent-filter>。显式的Intent是当您指定要Intent本身进行交谈的组件时,通常使用将JavaClass对象作为第二个参数的构造函数。那,而不是隐式Intents和<intent-filter>s,应该用于应用程序本地组件。
CommonsWare,2015年

Answers:


124

您对应用中的服务,活动等的任何意图均应始终遵循此格式

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

要么

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

隐式意图(当前在代码中具有的意图)被视为安全风险


1
如果需要我该bindService怎么办?
IgorGanapolsky '16

3
@IgorGanapolsky,那么您必须手动设置使其显式显示的软件包
tyczj 2016年

如果要动态获取软件包,请使用BuildConfig.APPLICATION_ID
Gibolt

31

布置你的packageName作品。

intent.setPackage(this.getPackageName());

5

将隐式意图转换为显式意图,然后启动服务。

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

0

尝试这个。这个对我有用。这里MonitoringService是我的服务类。我有两个动作,指示停止或启动服务。我从广播接收器发送该值 取决于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);
    }
}

注意:我添加以下代码来触发我的广播接收器:ManageLocationListenerReceiver

<receiver
     android:name=".ManageLocationListenerReceiver"
     android:enabled="true"
     android:exported="true">
     <intent-filter>
         <action android:name="android.intent.action.AIRPLANE_MODE" />
     </intent-filter>
</receiver>

-1

我改进了Shahidul的答案,以删除上下文依赖项:

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

在SuperApplication类中:

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

在您的清单中:

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

然后,只需调用:

ServiceUtils.startService("com.myservice");

调用全局单实例类来获取上下文不是一个好主意。相反,请公开此依赖性。
Better Shao

如果ServiceUtils在活动之外,如何调用应用程序上下文?你能提供一些例子吗?
恩戈洛·波洛托

1
ServiceUtils是一个实用程序,因此不必依赖诸如SuperApplication之类的具体Application类。startService方法的调用者有责任提供上下文。为了方便起见,有可能将应用程序注入以便在应用程序中的每个位置使用。但是,您可能已经发现,让该方法被称为“每一个地方”不是一个好主意。我们最好创建仅具有单个(一组)职责的类,并避免不必要的依赖。这样,它可以进行单元测试并且代码干净。
Better Shao

谢谢您的回答。对于其他人,我也不会删除该答案。
安吉洛Polotto
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.