LoginSignup
8
8

More than 5 years have passed since last update.

別Package別Process別APKのServiceにAIDLからBindするサンプル (Android L以降対応版)

Last updated at Posted at 2017-02-15

Android 5.0 Lollipopでの変更点

AIDLを使ってAndroid Serviceにbind/unbindする方法は初期のAndroidから使えますが,Android 5.0 Lollipopから必要条件が追加されていて,以前からあるサンプルコードではIllegalArgumentException発生で正常に動作しなくなっています.

↓ Android L以上でIllegalArgumentException発生させる部分のCode

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

のように,service.getComponent()かservice.getPackage()のどちらか一方が非NULLでなければExplicit Intentとは判定されなくなっているようです.

これを踏まえて,AIDLからServiceにBindするサンプルを書いてみます.

Server側

AIDLから接続される側のAPK.

Dir構成 (Gradle Build世代)

AidlServer/
  - app/
    - src/
      - main/
        - aidl/
          - test/aidl/server/IAidlServerService.aidl
        - java/
          - test/aidl/server/AidlServerService.java
        - res/
          - (省略)
        - AndroidManifest.xml

GitHubにSample CodeをUpしました.
https://github.com/fezrestia/sample-bind-aidl-service/tree/master/AidlServer

Android Manifest

公開するServiceの定義.Package + Intent FilterでExplicit Intentにする.

AndroidManifest.xml
<manifest
    package="test.aidl.server"
    xmlns:android="http://schemas.android.com/apk/res/android"
>
    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
    >
        <service
            android:name=".AidlServerService"
            android:exported="true"
        >
            <intent-filter>
                <action android:name="test.aidl.server.ACTION_BIND" />
            </intent-filter>
        </service>
    </application>
</manifest>

Sample Service

『HELLO WORLD !』という文字列を返すだけのAPIをもつサンプル.

AidlServerService.java
package test.aidl.server;

// import省略

public class AidlServerService extends Service {

    private IAidlServerService.Stub mStub = new IAidlServerService.Stub() {
        @Override
        public String getMessage() throws RemoteException {
            return "HELLO WORLD !";
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return mStub;
    }

    // その他@Override省略
}

AIDL

同じものをClient側にも持たせる.Build時の名前解決にも必要.

IAidlServerService.aidl
package test.aidl.server;

interface IAidlServerService {
    String getMessage();
}

ちなみに,.aidlのファイルはBuild時にParseされ,Stubという子クラスを持ったCodeが自動生成されてBuildに使われます.

Client側

AIDLに接続する側のAPK.

Dir構成 (Gradle Build世代)

AidlClient/
  - app/
    - src/
      - main/
        - aidl/
          - test/aidl/server/IAidlServerService.aidl (Server側と同じpackage/file)
        - java/
          - test/aidl/client/AidlClientActivity.java
        - res/
          - (省略)
        - AndroidManifest.xml

GitHubにSample CodeをUpしました.
https://github.com/fezrestia/sample-bind-aidl-service/tree/master/AidlClient

Android Manifest

Client側のManifestはServiceにbindするActivityのみ.Activityである必要は無いです.

AndroidManifest.xml
<manifest
    package="test.aidl.client"
    xmlns:android="http://schemas.android.com/apk/res/android"
>
    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
    >
        <activity
            android:name=".AidlClientActivity"
        >
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>

Sample Activity

onResume/onPauseでbind/unbindService,ButtonのonClick()でAPI Callする設定.

AidlClientActivity.java
package test.aidl.client;

// AIDLのimport
import test.aidl.server.IAidlServerService;

// その他import省略

public class AidlClientActivity extends Activity {

    // onCreateなどでButton View取得 + setOnClickListener()などしておく.

    @Override
    public void onResume() {
        super.onResume();

        // Bind.
        //
        // NOTICE:
        //     Android 5.0 Lolipop以降でExplicit Intent判定に必須のため,setPackage()でpackage名を明示.
        //
        Intent service = new Intent("test.aidl.server.ACTION_BIND");
        service.setPackage("test.aidl.server"); // Android 5.0 Lolipop以降で必須の設定
        bindService(service, mAidlConnection, BIND_AUTO_CREATE);
    }

    @Override
    public void onPause() {
        // Unbind.
        if (mService != null) {
            unbindService(mAidlConnection);
        }

        super.onPause();
    }

    private IAidlServerService mService = null;
    private AidlConnection mAidlConnection = new AidlConnection();

    // Service接続状態のCallback.
    private class AidlConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = IAidlServerService.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mService = null;
        }
    }

    // Button ClickでAPI Callするサンプル.
    private class TriggerOnClickListener implements View.OnClickListener {
        @Override
        public void onClick(View view) {
            try {
                ret = mService.getMessage();
            } catch (RemoteException e) {
                e.printStackTrace();
            }

            Log.e("TraceLog", "#### Received : " + ret);
        }
    }
}

AIDL

Server側のAIDLをそのままコピーしてClientのProjectにもたせておく.

IAidlServerService.aidl
package test.aidl.server;

interface IAidlServerService {
    String getMessage();
}

こちらもServer側と同じくCodeの自動生成 + Build時名前解決に使われる.

まとめ

AndroidはOS Updateでpublic APIの暗黙仕様がコロコロ変わって過去資産がいきなり使えなくなったりして震えますね.

Code一式をGitHubとかに上げたら更新します.
→追記しました.

///---

8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8