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アプリを追加します。
AIDLライブラリは Android Library
に追加します。
package nameはRemoteServiceと同じ
com.example.remoteservice
に設定ください。
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を追加すると、アプリでは、ライブラリのコードを参照することができます。
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
- Android - 振動、Vibrator、VibrationEffectの例
- Android - TabLayoutの実装方法(+ ViewPager2)
- Android - PackageManagerにPackage情報を取得する
- Android - ACTION_BOOT_COMPLETEDイベント受信
- Android - FusedLocationProviderClientに位置情報を取得する
- Android - GPS、Network位置情報を取得する(LocationManager)
- Android - Foreground Service実行
- Android - 時間、日付、変更イベント受信
- Android - currentTimeMillis()、elapsedRealtime()、uptimeMillis()
- Android-PowerManager WakeLock
- Android - ファイル入出力の例(Read、Write、内部、外部ストレージ)
- Android - Screen On / Offイベントの受信、状態確認
- Android - 他のアプリのServiceにバインド
- Android - Handler vs Executor
- Android - Darkmode有効にする方法
- Android - hasSystemFeature()、サポートされているFeature確認
- Android - アプリの権限を確認(Permission check)
- Android - インストールされてアプリリストをインポートする
- Android App Shortcuts実装
- Android - ContentProviderを実装、および例
- Android - AIDLを利用して、Remote Serviceの実装
- Android - Uri、Scheme、SSP(Scheme Specific Part)説明
- Android - アプリのインストール、削除、イベントダウンロード(BroadcastReceiverインテントを受け取る)
- Android - SharedPreferencesに簡単なデータを保存する方法
- Android - AlarmManagerにアラームを登録する方法、および例
- Android - Quick SettingsにCustom Tile追加する方法(kotlin)
- Android - Broadcast Receiver登録およびイベントの受信方法
- Android - Runtime permissionリクエスト方法と例(kotlin)
- Android - ネットワーク(WIFI)の接続状態を確認し、変更の検出
- Mockito - static、final methodをmockingする方法
- Andriod - カスタムパーミッションを定義する方法
- RobolectricにUnit Testを作成する(kotlin)
- Android Mockitoのテストコードを作成する(kotlin)
- Android - Handlerの使用方法、および例
- Android - IntentService使用方法
- Android - JobIntentService使用方法