【Android】Binder (1) Binderの紹介とAIDLでのBinder使用例

バインダーの紹介

Android の Binder は、異なるプロセスがメソッドを呼び出し、相互にデータを転送できるようにするプロセス間通信メカニズムです。Binder は主にシステムサービスとアプリケーション間の通信や IPC (Inter-Process Communication、プロセス間通信) の実装に使用されます。

Binder の中核は Binder ドライバーであり、異なるプロセス間の通信の管理を担当します。各プロセスはサービス プロバイダーとして独自の Binder オブジェクトを作成でき、他のプロセスがクライアントとして提供する Binder オブジェクトを取得することもできます。これらの Binder オブジェクトは、プロセス間通信のために Binder によって駆動される必要があります。

観点机制: Binder はプロセス間通信メカニズムです
観点驱动: Binder は仮想物理デバイスドライバーです
観点应用层: Binder は通信を開始できる Java クラスです

Android 開発では、AIDL (Android Interface Definition Language) を通じて独自の Binder インターフェイスを定義し、対応するサービス プロバイダーとクライアントを実装できます。AIDL によって生成されたコードは、異なるプロセス間の IPC 通信に使用できます。


Binder と他のプロセス通信方式の比較

Binder と従来の IPC の比較
バインダー 共有メモリ ソケット
パフォーマンス 一度コピーする必要がある コピーする必要はありません 2回コピーする必要がある
特徴 C/Sアーキテクチャをベースとした高いユーザビリティ 操作が複雑で操作性が悪い C/Sアーキテクチャを採用しており、汎用インタフェースとしては伝送効率が低く、オーバーヘッドが大きい
安全性 各アプリに UID を割り当て、実名と匿名の両方をサポートします 上位層のプロトコル アクセスに依存しているため、アクセス ポイントはオープンで安全ではありません

バインダー機構構成

バインダー (IInterface インターフェース) このクラスは、クライアントとサーバー間の通信のためのリモート サービス インターフェイスを定義します。
IBinderインターフェース このクラスは、軽量のプロセス間通信機能を提供する Binder の実装クラスです。
サービスマネージャ このクラスは、バインダー サービスを登録および検索するためのメカニズムを提供します。
バインダードライバー ドライバーは Binder のプロセス間通信の中核であり、Binder の接続、データ送信、スレッド、その他の操作を管理するために使用されます。

Binder メカニズムは、Android 開発に効率的かつ柔軟なプロセス間通信方法を提供し、アプリケーションがプロセス間でデータを共有したり、リモート サービスを呼び出したりすることを容易にします。


AIDLとバインダーの関係

AIDL (Android Interface Definition Language、Android インターフェイス定義言語) は、Android システムでプロセス間通信 (IPC) を実装するために使用されるメカニズムです。AIDL は、開発者が異なるアプリケーション間または異なるプロセス間でリモート メソッド呼び出しを実装し、プロセス間のデータ共有と対話を実現するのに役立ちます。

Binder は、Android システムに IPC を実装するためのコア ドライバーであり、AIDL で定義されたインターフェイスの基礎的なサポートを提供できます。バインダードライバーは、プロセス間の接続やデータ送信などを管理することで、AIDLに基づくプロセス間通信機能を実現します。

AIDL と Binder の関係は次のように理解できます。AIDL はプロセス間通信インターフェイスを記述するための言語を提供し、Binder は AIDL インターフェイス呼び出しを実装するための内部フレームワークです。Androidアプリケーションでは一般に、AIDLファイルにクロスプロセスアクセスのためのインターフェースを定義し、それをBinderを通じて実装することでアプリケーション間の通信を実現しています。


Binderを使用したプロセス間通信の例

服务端まず、とという 2 つの端末を作成する必要があります。客户端

サーバ

という名前のプロジェクトを作成し、MyBinderService
ここに画像の説明を挿入
AIDL ファイルを作成します。

注:aidlフォルダーは独自に作成する必要があり、フォルダーはmainフォルダーの下にあり、javaフォルダーと同じレベルにあります。

ここに画像の説明を挿入

aidlフォルダー内に Aidl ファイルを作成したときに、次のエラーが発生しました。
ここに画像の説明を挿入

このプロンプトは、AIDL テクノロジを使用するには、build.gradle ファイルで buildFeature.aidl を true に設定する必要があることを意味します。
ここに画像の説明を挿入

 buildFeatures {
    
    
    aidl = true
 }

この時点で、上のaidlフォルダーの色も変わっていることがわかります。

サーバー アプリケーションでは、クライアントにデータ取得方法を提供する AIDL インターフェイスを定義する必要があります。
ここに画像の説明を挿入
次に、インターフェイス メソッドを実装してシステムに登録する必要があります。ここではサービスを使用して次のことを実現します。
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入

完全なコード

public class MyService extends Service {
    
    

    private static final int NOTIFICATION_ID = 1001; // 前台通知 ID

    private final IBinder mBinder = new MyBinder();

    @Override
    public IBinder onBind(Intent intent) {
    
    
        Log.e("Binder响应:", "已经绑定到Binder上面了");
        return mBinder;
    }

    static class MyBinder extends IMyService.Stub {
    
    
        @Override
        public long getCurrentTimestamp() throws RemoteException {
    
    
            long time = System.currentTimeMillis();
            Log.e("Binder数据:", "服务端的时间戳:" + time);
            return time;
        }
    }

    @Override
    public void onCreate() {
    
    
        super.onCreate();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    
    
            NotificationManager manager = getSystemService(NotificationManager.class);
            NotificationChannel channel = new NotificationChannel(
                    "channel_id",
                    "前台通知渠道名称",
                    NotificationManager.IMPORTANCE_DEFAULT
            );
            manager.createNotificationChannel(channel);
        }

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "channel_id")
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setContentTitle("Binder服务端")
                .setContentText("测试Binder的前台Service");

        startForeground(NOTIFICATION_ID, builder.build());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    
    
        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                Log.e("Binder的服务", "在Service做耗时任务的话,请使用子线程,因为Service是在主线程运行的");
                Log.e("Binder的服务", "此时的线程:" + Thread.currentThread().getName());
            }
        }).start();
        return START_STICKY;
    }
}

メインページ:
ここに画像の説明を挿入
完全なコード:

public class MainActivity extends AppCompatActivity {
    
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tvStartService = findViewById(R.id.tv_startService);
        TextView tvStopService = findViewById(R.id.tv_stopService);
        tvStartService.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View view) {
    
    
                Intent intent = new Intent(MainActivity.this, MyService.class);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    
    
                    startForegroundService(intent);
                } else {
    
    
                    startService(intent);
                }
            }
        });
        tvStopService.setOnClickListener(new View.OnClickListener() {
    
    

            @Override
            public void onClick(View view) {
    
    
                Intent intent = new Intent(MainActivity.this, MyService.class);
                stopService(intent);
            }
        });
    }
}

知らせ:

AndroidManifest.xmlこの権限をファイルに追加する必要があります。

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

それ以外の場合は、エラーが報告されます。

Permission Denial: startForeground from pid=16363, uid=10134 requires android.permission.FOREGROUND_SERVICE

同時に、このファイルに完璧なMyServiceファイルを登録します

<service
     android:name=".MyService"
     android:enabled="true"
     android:exported="true">
     <intent-filter>
           <action android:name="com.shsany.mybinderservice"/>
     </intent-filter>
</service>

この時点で構成した後、Android Studio が AIDL コードを自動的に生成するように、忘れずにプロジェクトをビルドしてください。
ここに画像の説明を挿入
生成されたコード:
ここに画像の説明を挿入

クライアント

クライアント側で設定する必要がある手順と方法はサーバー側と同じであるため、ここでは説明しません。

知らせ:

AIDL ファイルはクライアント側で書き換えず、サーバー側からコピーして、両側が同じであることを確認します。

サーバ

ここに画像の説明を挿入

クライアント

ここに画像の説明を挿入

両端のファイルはパッケージ名と内部のコードが同じであるため、直接コピーすることをお勧めします。

クライアントの名前はMyBinder、ファイルは 1 つだけです

ここに画像の説明を挿入

MainActivity コード

ここに画像の説明を挿入
ここに画像の説明を挿入
完全なコード:

public class MainActivity extends AppCompatActivity {
    
    
    private IMyService mMyService;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tvGetData = findViewById(R.id.tv_getBinderData);

        Intent intent = new Intent();
        intent.setAction("com.shsany.mybinderservice");
        intent.setPackage("com.shsany.mybinderservice");
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

        tvGetData.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View view) {
    
    
                try {
    
    
                    // 在使用服务端方法时,先判断服务是否可用
                    if (mMyService != null) {
    
    
                        long currentTime = mMyService.getCurrentTimestamp();
                        Log.e("Binder数据:", "从服务端接收到的时间戳:" + currentTime);
                    } else {
    
    
                        Log.e("Binder数据:", "无法连接到服务端");
                    }
                } catch (Exception e) {
    
    
                    Log.e("Binder错误:", e.getMessage());
                }
            }
        });
    }

    private final ServiceConnection mConnection = new ServiceConnection() {
    
    
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
    
    
            //远程Service绑定
            Log.e("Binder数据:", "已经连接到服务端");
            mMyService = IMyService.Stub.asInterface(iBinder);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
    
    
            //Service被意外销毁
            Log.e("Binder数据:", "服务端被意外销毁");
            mMyService = null;
        }
    };
}
テスト

服务端
ここに画像の説明を挿入
まず、開始してから再度開始する必要があります客户端
ここに画像の説明を挿入
。サーバーに接続した後、サーバーが表示され、
ここに画像の説明を挿入
クライアントでメソッドを呼び出してgetCurrentTimestampデータを取得します。
ここに画像の説明を挿入
この時点でサーバーを見てください。これまでのところ
ここに画像の説明を挿入
、Binder クロスを使用した単純な AIDL -プロセス通信が完了しました。

おすすめ

転載: blog.csdn.net/qq_43358469/article/details/130981992