1. 程式人生 > >Service Intent must be explicit錯誤

Service Intent must be explicit錯誤

解決方案:

使用隱式方式繫結: **1.在service中新增action:**
<service android:name="your  service"
            android:exported="true">
            <intent-filter>
                <action android:name="your action" />
            </intent-filter>
</service>

2.在繫結時intent設定action與package:

		Intent intent = new Intent();
        intent.setAction("your action");
        intent.setPackage("service app's package name");//設定service所在app的包名
        activity.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

然後就可以順利繫結aidl中的service了。

注:onServiceDisconnected是繫結後意外斷連後的回撥,一般進行重新繫結的操作。

private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //一般進行重新繫結的操作
        }
    };

 

原因:

       在Android 5.0之後google出於安全的角度禁止了顯式宣告Intent來繫結aidl中的Service.否則就會拋個異常出來.

      
      在Android 4.4的ContextImpl原始碼中,能看到如果啟動service的intent的component和package都為空並且版本大於KITKAT的時候只是報出一個警報,告訴開發者隱式宣告intent去啟動Service是不安全的.再往下看,丫的異常都寫好了只是註釋掉了,看來google早就想這麼幹了.

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

      
      果然在Android 5.0的原始碼中上面註釋的程式碼已經不註釋了,當targetSdkVersion版本大於LOLLIPOP直接異常丟擲來,要求Service intent必須顯式宣告.所以如果開發的應用指定targetSdkVersion版本是小於LOLLIPOP的話還是按以前的方式給報個警報,這也就造成了如果沒有做了完善的Android 5.0相容就貿然把targetSdkVersion升到LOLLIPOP的話很有可能就會碰到這個問題.並且這個問題是很嚴重的,想象一下,你的app自升級的Service是隱式啟動的,碰到這個問題後app就不能自升級了,這批使用者有可能就一直停留在當前版本.會產生很致命的問題.

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是不是顯式宣告的點就是component和package,只要這兩個有一個生效就不算是隱式宣告的,接下來繼續分析一下Intent的原始碼,可以看到下面三種構造方式,設定action來宣告Intent是沒有構建component的,所以顯式宣告需要用到第一和第二種構造(還有帶packagename或component的拷貝構造),或者後面設定package屬性.

public Intent(Context packageContext, Class<?> cls) {
        mComponent = new ComponentName(packageContext, cls);
    }
    public Intent(String action) {
        setAction(action);
    }
    public Intent(String action, Uri uri,
                  Context packageContext, Class<?> cls) {
        setAction(action);
        mData = uri;
        mComponent = new ComponentName(packageContext, cls);
    }
    public Intent setPackage(String packageName) {
        if (packageName != null && mSelector != null) {
            throw new IllegalArgumentException(
                    "Can't set package name when selector is already set");
        }
        mPackage = packageName;
        return this;
    }