Skip to content

Android(3): Service Intent must be explicit 异常解决

clarkehe edited this page Mar 29, 2016 · 1 revision

问题:

5.0 上采用隐式的方法启动服务,会报异常“Service Intent must be explicit”。

分析:

Android 5.0一出来后,其中有个特性就是Service Intent must be explitict,也就是说从Lollipop开始,service服务必须采用显示方式启动。而android源码是这样写的(源码位置:sdk/sources/android-21/android/app/ContextImpl.java):

 private void validateServiceIntent(Intent service) {   
      if (service.getComponent() == null && service.getPackage() == null) {
            if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {   
                IllegalArgumentException ex = new IllegalArgumentException(
                        "Service Intent must be explicit: " + service);     
                throw ex;
            } else {
                Log.w(TAG, "Implicit intents with startService are not safe: " + service   
                        + " " + Debug.getCallers(2, 3));  
            }    
        }       
    }      

要求使用显示Intent的原因:

官方文档也提到使用显式的 Intent 来 startService/bindService 以确保安全。
Caution: To ensure your app is secure, always use an explicit intent when starting a Service and do not declare intent filters for your services. Using an implicit intent to start a service is a security hazard because you cannot be certain what service will respond to the intent, and the user cannot see which service starts. Beginning with Android 5.0 (API level 21), the system throws an exception if you call bindService() with an implicit intent.

Note: When starting a Service, you should always specify the component name. Otherwise, you cannot be certain what service will respond to the intent, and the user cannot see which service starts.

解决方案:

1 设置Action和packageName:

Intent mIntent = new Intent(); 
mIntent.setAction("XXX.XXX.XXX");//你定义的service的action
mIntent.setPackage(getPackageName());//这里你需要设置你应用的包名   
context.startService(mIntent);

2 将隐式启动转换为显示启动:--参考地址:http://stackoverflow.com/a/26318757/1446466

public static Intent getExplicitIntent(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);
        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);
        // Set the component to be explicit
        explicitIntent.setComponent(component);
        return explicitIntent;
    }

调用代码:

Intent mIntent = new Intent();
mIntent.setAction("XXX.XXX.XXX");
Intent eintent = new Intent(getExplicitIntent(mContext,mIntent));
context.startService(eintent);
Clone this wiki locally