Android生成Java AIDL

AIDL:Android Interface Definition Language

AIDL是为了实现进程间通信而设计的Android接口语言

Android进程间通信有多种方式,Binder机制是其中最常见的一种

AIDL的本质就是基于对Binder的运用从而实现进程间通信

这篇博文从实战出发,用一个尽可能精简的Demo,实现两个App(也是两个进程:服务端mainapp、客户端otherapp)通过AIDL的跨进程通信

一.创建两个App

按照下列步骤先创建两个App:

(1).新建一个开发文件夹(本Demo中命名为aidl-test)

(2).使用AndroidStudio在aidl-test文件夹下创建第一个Empty Activity的空App:mainapp

为了后续方便起见

创建完成后,把Studio默认创建的MainActivity.java名字改一下,改成MainAppActivty.java

(3).创建第二个Empty Activity的空App:otherapp

两个空App创建完成了:

二.在mainapp中创建一个Service

上一节中新建了两个空App:mainappotherapp

现在就先在mainapp中实现一个service

使用方便又快捷的studio创建MainAppService.java

可以看到新建的MainAppService会自动实现一个onBind(Intent intent)方法,这个方法后续我会在其中进行代码实现,它需要在其他进程连接到Service时,返回一个继承了android.os.Binder的对象

先在 MainAppActivity 和 MainAppService 中添加一些必要的生命周期函数代码

再在 MainAppService 中添加:

onBind(Intent intent):被客户端绑定时执行

onUnbind(Intent intent):被客户端解绑时执行

java 复制代码
@com/android/mainapp/MainAppActivity.java


package com.android.mainapp;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
 
public class MainAppActivity extends AppCompatActivity {
    private String TAG = "AIDL-MainAppActivity";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.v(TAG, "onCreate()");
        setContentView(R.layout.activity_main);
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.v(TAG, "onDestroy()");
    }
}
java 复制代码
@com/android/mainapp/MainAppService.java 


package com.android.mainapp;
 
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
 
public class MainAppService extends Service {
    private String TAG = "AIDL-MainAppService";
 
    public MainAppService() {
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
    }
 
    @Override
    public void onStart(Intent intent, int startId) {
        Log.v(TAG, "onStart()");
    }
 
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
 
    @Override
    public boolean onUnbind(Intent intent) {
        Log.v(TAG, "onUnbind()");
        return true;
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.v(TAG, "onDestroy()");
    }
}

三.mainapp中实现AIDL

依然使用方便又快捷的AndroidStudio在mainapp的main目录下创建一个名为IAppAidlInterface.aidl的AIDL文件

AndroidStudio创建 IAppAidlInterface.aidl 会自动实现一个默认的可用于进程间传参通信的void basicTypes(...)函数,其参数是java的几种基础数据类型

除此之外,AIDL还支持多种其他数据类型:byte、short、char、charSequence、 List、 Map等

除了AndroidStudio自动创建的basicTypes(...)函数,我在AIDL文件里面再新增一个setData(..)函数,用于后面跨进程的数据传输,虽然使用AndroidStudio自动生成的basicTypes(...)也是可以的,但是自己创建一个函数会更具有代表性.

在本篇博文最开始就阐述过,AIDL的本质是对Binder的运用,从而实现进程间通信

那么现在Binder在哪呢?

IAppAidlInterface.aidl文件创建之后,build一下工程,AndroidStudio会在build目录下创建一个aidl_source_output_dir文件夹,同时在这个文件夹下创建与IAppAidlInterface.aidl包名、文件名相同的文件夹目录和java文件

IAppAidlInterface.java文件中通过继承android.os.Binder创建一个抽象的代理类stub,这个stub抽象代理类主要做如下几件事:

通过stub自身实例实现进程内部的通信

通过实现一个内部代理类Porxy用于跨进程通信

重写Binder中的onTransact()函数,实现AIDL接口文件中声明的函数进行数据传输

传输的数据必须是序列化的android.os.Parcel类型数据

当然,如果项目需要对AIDL的Binder实现过程进行自定义封装,方便项目中对进程间通信机制进行定制化,那么,完全可以不采用AndroidStudio自动生成的IAppAidlInterface.java,只须要按照自己的需要实现IAppAidlInterface.java中对Binder进程间通信的实现过程就行了

因为归根结底,AIDL实现进程间通信的基础就是Binder机制,只要使用Binder实现AIDL进程间通信的目的就可以了

IAppAidlInterface.java就先讨论到这里,这篇博文主要是对AIDL的使用进行研究,其对Binder机制的实现与封装不在此做深入探讨

后续会专门开一篇博文讲解AIDL对Binder机制的内部实现,以及用户如何自定义封装

四.otherapp中也实现AIDL

上一节中,作为服务端的mainapp里创建了一个AIDL文件,客户端的otherapp中也需要实现一份相同的AIDL,要不然客户端就无法引用到了对应的函数和stub等了

很简单,把mainapp中的AIDL文件整个包名目录直接拷贝到otherapp中即可,然后再build一下工程

接下来,需要添加一些代码,实现客户端otherapp与服务端mainapp的相连

五.mainapp中添加代码

前文中已经展示过,MainAppService会随着MainAppActivity的onCreate()和onDestroy()生命周期startService()和stopService()

mainapp中要添加的代码在MainAppService.java中,需要在MainAppService中做下面一些事情:

  • 使用匿名内部类实现IAppAidlInterface.Stub抽象类,用于实现IAppAidlInterface.aidl中的接口函数和onBinder()时返回匿名内部类实例
  • **onBinder()**中启动一个线程,每1秒轮循接收客户端发送过来的数据

这里提到个题外的知识点,Service中除了onBinder()函数外还有个onRebind()函数

如果同一客户端每次unBindService()之后再bindService()并且发送的Intent也一样,那么onBind()就只会在服务端第一次被这个客户端连接时才执行,后续重连时都不会再执行了。

而onRebind()在服务端第一次被连接时不会被执行,但是之后每次重连都会执行,不论Intent是否一样。

如果想要onBind()在同一客户端连接时都能执行,客户端在每次bindService()时,改变发送Intent的type或其他成员变量就行了

定义一个string变量用于接收客户端传过来的字符串,定义一个boolean变量用于控制线程

AndroidMainfest.xml中为MainAppService添加Service标签

六.otherapp中添加代码

otherapp里面主要需要做如下几件事:

  • 新建一个Intent ,用于连接服务端mainapp
    IntentComponent设置为Sevice的包名和类名
  • 新建两个button,用于控制bindService() 绑定和unbindService() 解绑
  • 重写Service连接和断开的两个基础函数onServiceConnected()onServiceDisconnected()
  • AndroidMainfest.xml中添加查询包名权限,以便otherapp可以查询到mainapp,或者直接指定查询mainapp的包名

七.运行、验证

到此,一个最基础的使用AIDL实现两个App(服务端mainapp、客户端otherapp)之间通信的demo代码就完成了,下面我们来验证一下。

编译、安装apk:

项目build Apk后会生成两个apk,两个都install上

运行验证:

注:本demo中代码的所有日志TAG都加上了AIDL前缀,方便日志打印验证

先启动一下mainapp,MainAppService不会被启动但是会被注册到系统,因为在mainapp的AndroidMainfest.xml中对MainAppService进行了Service标签添加。

退出mainapp后,再打开otherapp:

现在执行几次"Bind Service"和"Unbind Service",就会看到如下日志打印:

好,通过这个精简Demo,初步实现了两个App通过AIDL的跨进程通信

八.源代码

接下来逐一展示实现的源码

mainapp源码:

D:\Codes\aidl-test\app\src\main\aidl\com\android\mainapp\IAppAidlInterface.aidl

java 复制代码
// IAppAidlInterface.aidl
package com.android.mainapp;
 
// Declare any non-default types here with import statements
 
interface IAppAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
 
    void setStringData(String strData);
}

D:\Codes\aidl-test\app\src\main\java\com\android\mainapp\MainAppActivity.java

java 复制代码
package com.android.mainapp;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
 
public class MainAppActivity extends AppCompatActivity {
    private String TAG = "AIDL-MainAppActivity";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.v(TAG, "onCreate()");
        setContentView(R.layout.activity_main);
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.v(TAG, "onDestroy()");
    }
}

D:\Codes\aidl-test\app\src\main\java\com\android\mainapp\MainAppService.java

java 复制代码
package com.android.mainapp;
 
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
 
public class MainAppService extends Service {
    private String TAG = "AIDL-MainAppService";
 
    private String mStrData;
    private boolean mSetServiceRunning = true;
 
    public MainAppService() {
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
        Log.v(TAG, "onStart()");
    }
 
    @Override
    public void onStart(Intent intent, int startId) {
        Log.v(TAG, "onStart()");
    }
 
    IAppAidlInterface.Stub mStub = new IAppAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
 
        }
 
        @Override
        public void setStringData(String strData) {
            mStrData = strData;
        }
    };
 
    @Override
    public IBinder onBind(Intent intent) {
        Log.v(TAG, "onBind()");
        mSetServiceRunning = true;
 
        new Thread() {
            @Override
            public void run() {
                super.run();
                while (mSetServiceRunning) {
                    try {
                        Thread.sleep(1000);
                        Log.v(TAG, "mStrData:"+mStrData);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
 
        return mStub;
    }
 
    @Override
    public boolean onUnbind(Intent intent) {
        Log.v(TAG, "onUnbind()");
        mSetServiceRunning = false;
        return true;
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.v(TAG, "onDestroy()");
    }
}

D:\Codes\aidl-test\app\src\main\AndroidManifest.xml

java 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
 
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Mainapp"
        tools:targetApi="31">
        <service
            android:name=".MainAppService"
            android:enabled="true"
            android:exported="true">
        </service>
 
        <activity
            android:name=".MainAppActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
 
            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
    </application>
 
</manifest>

otherapp源码:

D:\Codes\aidl-test\otherapp\src\main\aidl\com\android\mainapp\IAppAidlInterface.aidl

java 复制代码
// IAppAidlInterface.aidl
package com.android.mainapp;
 
// Declare any non-default types here with import statements
 
interface IAppAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
 
    void setStringData(String strData);
}

D:\Codes\aidl-test\otherapp\src\main\java\com\android\otherapp\OtherAppMainActivity.java

java 复制代码
package com.android.otherapp;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
 
import com.android.mainapp.IAppAidlInterface;
 
public class OtherAppMainActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {
    private String TAG = "AIDL-OtherAppActivity";
 
    private int mICount = 0;
    private Intent mServiceIntent;
    private IAppAidlInterface mBinder;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_other_app_main);
 
        mServiceIntent = new Intent();
        mServiceIntent.setComponent(new ComponentName("com.android.mainapp", "com.android.mainapp.MainAppService"));
 
        findViewById(R.id.btnBindMainAppService).setOnClickListener(this);
        findViewById(R.id.btnUnBindMainAppService).setOnClickListener(this);
    }
 
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btnBindMainAppService: {
                Log.v(TAG, "onClick():btnBindMainAppService");
                bindService(mServiceIntent, this, Context.BIND_AUTO_CREATE);
            }
            break;
 
            case R.id.btnUnBindMainAppService: {
                Log.v(TAG, "onClick():btnUnBindMainAppService");
                unbindService(this);
                mBinder = null;
            }
            break;
        }
    }
 
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        if (mBinder == null) {
            mBinder = IAppAidlInterface.Stub.asInterface(iBinder);
            mICount++;
            Log.v(TAG, "onServiceConnected() 第 " + mICount + " 次");
            try {
                String strData = "第" + mICount + "次连接Service成功!";
                mBinder.setStringData(strData);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
 
    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        Log.v(TAG, "onServiceDisconnected");
    }
}

D:\Codes\aidl-test\otherapp\src\main\res\layout\activity_other_app_main.xml

java 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".OtherAppActivity">
 
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="OtherApp"/>
 
    <Button
        android:id="@+id/btnBindMainAppService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="Bind  Service" />
 
    <Button
        android:id="@+id/btnUnBindMainAppService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="UnBind Service" />
 
</LinearLayout>

D:\Codes\aidl-test\otherapp\src\main\AndroidManifest.xml

java 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
 
    <!--<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>-->
    <queries>
        <package android:name="com.android.mainapp"/>
    </queries>
 
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Mainapp">
        <activity
            android:name=".OtherAppMainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
 
            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
    </application>
 
</manifest>
相关推荐
还鮟2 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡4 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi004 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
zhangphil5 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你6 小时前
Android View的绘制原理详解
android
移动开发者1号8 小时前
使用 Android App Bundle 极致压缩应用体积
android·kotlin
移动开发者1号9 小时前
构建高可用线上性能监控体系:从原理到实战
android·kotlin
ii_best13 小时前
按键精灵支持安卓14、15系统,兼容64位环境开发辅助工具
android
美狐美颜sdk14 小时前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
恋猫de小郭18 小时前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin