Android - 他のアプリのServiceにバインド

By JS | Last updated: December 20, 2020

AIDLを利用して、Remote Serviceの実装でAIDLでRemote Serviceを実装しました。この例では、同じアプリで定義されてサービスにバインドする例でした。

もし、他のアプリでは、このサービスにバインドするには、どうでしょうか

他のアプリのサービスにバインドするのは、自分のアプリのサービスにバインドすることとほぼ同じです。 しかし、次のような内容を追加で実装する必要があります。

  • サービスを提供するアプリで誰でもサービスにバインドすることができるように宣言(exported = "true"
  • サービスにバインドするアプリがIRemoteService、IRemoteServiceCallbackを参照できるようにAIDL成果をライブラリに第

この記事で使用されているアプリは、RemoteServiceアプリとClientアプリです。 RemoteServiceサービスが実装されており、AIDLを利用して、Remote Serviceの実装で紹介されたサンプルアプリです。

RemoteServiceアプリのプロジェクトにAIDLライブラリとClientアプリを追加することです。

この記事で作成したプロジェクトは、GitHub - RemoteService and Client App(Kotlin)で確認することができます。

ClientアプリでIRemoteService、IRemoteServiceCallback参照

Clientは、ServiceConnectionを通じてサービスのBinderオブジェクトを取得することができますが、このバインダーオブジェクトをIRemoteService、IRemoteServiceCallbackに変換するには、アプリがこのクラスを参照してくださいする必要があります。

RemoteServiceで実装されたバインダーインターフェイスをClientで参照するには、次のような方法があります。 1)RemoteServiceとClientアプリで同じコードのAIDLファイルをそれぞれ作成し、コンパイル 2)AIDLインターフェイスのJarライブラリを作成し、それをRemoteServiceとClientアプリで参照

ここで2の方法で実装することを紹介します。

ClientアプリとAIDLライブラリモジュールの追加

これからAIDLを利用して、Remote Serviceの実装で紹介されたRemoteServiceプロジェクトにClientアプリとAIDLライブラリを追加します。

まず、このプロジェクトをダウンロードして、Androidのスタジオで実行してください。

[File]->[New]->[New Module]をクリックして、次のようにClientアプリを追加します。 client app client app

AIDLライブラリは Android Libraryに追加します。 aidl library package nameはRemoteServiceと同じ com.example.remoteserviceに設定ください。 aidl library

RemoteServiceアプリのServiceをPublicにする

基本的にAndroidManifestにサービスを定義すると、自分のアプリでのみ実行することができます。

他のアプリで実行できるように作成するにはService TAGに exported="true"を追加する必要があります。

com.example.remoteserviceアプリのManifestに次のようにserviceを定義します。 また、他のアプリでは、このサービスを検索しやすいようにしようとし MY_SERVICEというActionにIntentFilterを登録しました。

<service android:name=".RemoteService" android:process=":remote"
         android:exported="true">
    <intent-filter>
        <action android:name="com.example.remoteservice.MY_SERVICE" />
    </intent-filter>
</service>

今Clientアプリで MY_SERVICE Actionで、このサービスを見つけることができ、実行させることもできます。

AIDL Module実装

aidllibモジュールのmain/aidl/com/example/remoteserviceパスにRemoteServiceで実装したAIDLファイルの2つを移動させます。

RemoteServiceもaidllibを参照するのでAIDLファイルは削除できます。

aidllibモジュールを BuildをするIRemoteServiceとIRemoteServiceCallbackクラスが生成され、他のアプリから参照することができます。

RemoteServiceとClientでaidllibライブラリリファレンス

RemoteServiceとClientアプリでaidllibを依存的に追加する必要がクラスを参照することができます。

[File] -> [Project Structure]を押すと、次のような画面が表示されます。 RemoteServiceとClientアプリの Module dependencyにaidllibを追加すると、アプリでは、ライブラリのコードを参照することができます。 add dependency

add dependency

ClientでRemote Serviceにバインド

ClientアプリのMainActivtyはRemoteServiceのMainActivityと名前が同じなので、MainActivity2に変更しました。

次はMainActivity2でRemoteServiceアプリのServiceにバインドするコードです。

class MainActivity2 : AppCompatActivity() {

    companion object {
        const val TAG = "MainActivity2"
    }

    private var bound = false;
    private var iRemoteService: IRemoteService? = null

    private var callback = object : IRemoteServiceCallback.Stub() {
        override fun onItemAdded(name: String?) {
            Log.d(TAG, "onItemAdded: $name")
            bound = true
        }

        override fun onItemRemoved(name: String?) {
            Log.d(TAG, "onItemRemoved: $name")
            bound = false
        }
    }

    private val connection = object : ServiceConnection {
        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            Log.d(TAG, "onServiceConnected: $className")
            iRemoteService = IRemoteService.Stub.asInterface(service)
            iRemoteService!!.addCallback(callback)
        }

        override fun onServiceDisconnected(className: ComponentName) {
            Log.d(TAG, "onServiceDisconnected: $className")
            iRemoteService = null
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onResume() {
        super.onResume()
        if (!bound) {
            val intent = Intent("com.example.remoteservice.MY_SERVICE")
            intent.setPackage("com.example.remoteservice")
            bindService(intent, connection, Context.BIND_AUTO_CREATE);
        }
    }

    override fun onStop() {
        super.onStop()
        if (bound) {
            iRemoteService!!.removeCallback(callback)
            unbindService(connection)
        }
    }
}

ほとんどのコードは、AIDLを利用して、Remote Serviceの実装で紹介した MainActivityのコードと同じです。

他の部分は、インテントオブジェクトを作成するときに MY_SERVICE ActionとsetPackage()com.example.remoteserviceパッケージを設定したものです。 RemoteServiceアプリのサービスは MY_SERVICE Actionを持っているインテントフィルタとして登録されているので、下記のようなインテントでのサービスを見つけることができます。

val intent = Intent("com.example.remoteservice.MY_SERVICE")
intent.setPackage("com.example.remoteservice")
bindService(intent, connection, Context.BIND_AUTO_CREATE);

QUERYパーミッション設定

Android 11でPackage visibilityで、Target SDK APIが30以上のアプリは、基本的に、他のアプリの情報を取得することができません。 他のアプリを発見しているAndroidManifestに <Query>Syntaxでパッケージ名を設定するか、次のようにQUERY_ALL_PACKAGESパーミッションを宣言する必要があります。

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />

ClientアプリのTarget APIが30以上に設定されている場合、上記のパーミッションを必ず宣言する必要があります。 API 30未満アプリは、Package visibilityが適用されないため、パーミッションを追加しなくても良いです。

実行

Clientアプリを実行してみるとRemote Serviceにバインドされ、以下のようにログが出力されます。

12-20 14:05:19.987  5754  5754 D MainActivity2: onServiceConnected: ComponentInfo{com.example.remoteservice/com.example.remoteservice.RemoteService}
12-20 14:05:19.989 20138 20156 D RemoteService: Add callback : com.example.remoteservice.IRemoteServiceCallback$Stub$Proxy@882781d
12-20 14:05:24.977  5754  5772 D MainActivity2: onItemAdded: new item
12-20 14:05:24.977  5754  5772 D MainActivity2: onItemRemoved: old item
12-20 14:05:29.980  5754  5772 D MainActivity2: onItemAdded: new item
12-20 14:05:29.984  5754  5772 D MainActivity2: onItemRemoved: old item

ログから見えるPIDでプロセスを確認すると、ClientとRemoteServiceアプリであることを再確認することができます。

mjs@mjs:~/codechacha$ adb shell ps -ef | grep com.example
u0_a156        5754    276 1 14:05:18 ?     00:00:00 com.example.client
u0_a153       20112    276 0 10:54:57 ?     00:00:05 com.example.remoteservice
u0_a153       20138    276 0 10:54:57 ?     00:00:36 com.example.remoteservice:remote

まとめ

ClientアプリでRemoteServiceに実装されたServiceにバインドするプロセスを説明しました。サービスの実装とは別に、二つのアプリでIRemoteService、IRemoteServiceCallbackクラスを使用するため、ライブラリモジュールで作成して参照するようにしました。

もしプロジェクトが他の二つのアプリでIRemoteServiceなどのバインダーインターフェースにアクセスが必要な場合AIDLの成果物をJarファイルにして、それぞれのアプリに依存的に追加されるようにする必要があります。

Related Posts

codechachaCopyright ©2019 codechacha