Service Intent must be explicit 解决及详解

一般情况下,当我们要隐式启动一个Service,首先我们需要配置AndroidMainfest.xml,代码如下:

        <service android:name=".MyAsdlService">
            <intent-filter>
                <action android:name="com.example.myasdlservice" />
            </intent-filter>
        </service>



并在Activity中启动service,代码如下:

    @Override
    public void onClick(View view) {
        Intent intent = new Intent();
        intent.setAction("com.example.myasdlservice");
        startService(intent);
    }



启动虚拟机调用监听方法发现软件无法运行。错误如下:Service Intent must be explicit
Android Monitor报了:Service Intent must be explicit,意思是Service Intent 应该被明确。

问题原因


根据官方文档解释,为了确保应用的安全性,启动 Service 时,请始终使用显式 Intent,且不要为服务声明 Intent 过滤器。使用隐式 Intent 启动服务存在安全隐患,因为您无法确定哪些服务将响应 Intent,且用户无法看到哪些服务已启动。从 Android 5.0(API 级别 21)开始,如果使用隐式 Intent 调用 bindService(),系统会抛出异常。(观看文档需要翻墙)


解决方式


根据官方文档Intent匹配。应用可以采用使用 Intent 匹配。PackageManager 提供了一整套 query…() 方法来返回所有能够接受特定 Intent 的组件。此外,它还提供了一系列类似的 resolve…() 方法来确定响应 Intent 的最佳组件。例如,queryIntentActivities() 将返回能够执行那些作为参数传递的 Intent 的所有 Activity 列表,而 queryIntentServices() 则可返回类似的服务列表。这两种方法均不会激活组件,而只是列出能够响应的组件。对于广播接收器,有一种类似的方法: queryBroadcastReceivers()。
所以根据PackageManager中的queryIntentServices(),可以把隐式Intent转换为ResolveInfo对象,并转换为显示Intent。

其实原理就是为隐式Intent设置包名以及类名使其转变为显示Intent。

  • 1.不知道Intent对应Service的包名、类名
  • 2.知道Intent对应Service的包名、类名


具体实现代码


1、不知道包名、类名

下述方法主要针对并事先不知道包名以及类名,常用于调用跨应用的服务

在你的Acticity增加一个方法,代码如下:

public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
        // Retrieve all services that can match the given intent
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);//根据自己所需意图返回能够响应的服务列表

        // Make sure only one match was found
        if (resolveInfo == null || resolveInfo.size() != 1) {
            return null;
        }

        // Get component info and create ComponentName
        ResolveInfo serviceInfo = resolveInfo.get(0);//得到对应的Intent,因为只有一个所以索引为0
        String packageName = serviceInfo.serviceInfo.packageName;//得到其包名
        String className = serviceInfo.serviceInfo.name;//得到其类名
        ComponentName component = new ComponentName(packageName, className);

        // Create a new intent. Use the old one for extras and such reuse
        Intent explicitIntent = new Intent(implicitIntent);//使用原Intent创建

        // Set the component to be explicit
        explicitIntent.setComponent(component);//设置component,创建明确的Intent

        return explicitIntent;
    }



调用上述方法,传入隐式Intent,得到显示Intent,通过显示Intent启动Service,代码如下:

 @Override
    public void onClick(View view) {
        Intent intent = new Intent();
        intent.setAction("com.example.myasdlservice");
        final Intent eintent = createExplicitFromImplicitIntent(this,intent);
        startService(eintent);
        }


2.知道其包名以及类名

如果事先知道其包名以及类名,可以直接设置,代码如下:

Intent intent = new Intent();  
intent.setAction("ccom.example.myasdlservice");  
//两种方式设置
//第一种,直接设置包名
intent.setPackage("这里输入包名");  
//第二种
ComponentName mComponentName = new ComponentName("包名", "类名");
intent.setComponent(mComponentName);
//启动Service
startService(intent);  



bindService(),与startService道理一样..

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值