aidl使用

前言

aidl,即android interface definition language,即安卓接口定义语言

使用 aidl 可以帮助我们发布以及调用远程服务,实现跨进程通信

将服务的 aidl 放到对应的 src 目录然后build,工程的 gen 目录会生成相应的接口类
通过 bindService(Intent service, ServiceConnection conn, int flags) 方法绑定远程服务,在bindService 中有一个 ServiceConnec 接口,我们需要覆写该类的 onServiceConnected(ComponentName, IBinder)方法,这个方法的第二个参数 IBinder 对象其实就是已经在 aidl 中定义的接口,因此我们可以将 IBinder 对象强制转换为 aidl 中的接口类

通过 IBinder 获取到的对象(也就是 aidl 文件生成的接口)其实是系统产生的代理对象,该代理对象既可以跟我们的进程通信,又可以跟远程进程通信,作为一个中间的角色实现了进程间通信

案例

本篇展示案例包含对象数据、接口回调在aidl中的使用,常规跨进程通信需求参考本篇即可完成

服务和客户apk的gradle中均需包含aidl目录下的代码,现在不需要了

sourceSets {
    main {
        java.srcDirs = ['src/main/java', 'src/main/aidl']
    }
}

aidl支持传递的数据类型

  1. byte、int、long、float、double、char、boolean,不支持short

  2. String、CharSequence

  3. List(接收端必须为ArrayList)、Map(接收端必须为HashMap)

  4. Parcelable(需import)

  5. 其他已定义的aidl接口(需import)

service app端

模拟使用跨进程通信进行发送验证码和登录两个接口操作

  • aidl文件

    1. 回调接口

      // 发送验证码接口
      package cc.catface.aidls.login;
      import cc.catface.aidls.login.SendBean;
      
      interface ILoginSendCallback {
      
          void onSuccess(in SendBean sendBean);
      
          void onFailure(String error);
      }
      

      // 登录接口
      package cc.catface.aidls.login;
      import cc.catface.aidls.login.LoginBean;
      
      interface ILoginLoginCallback {
      
          void onSuccess(in LoginBean loginBean);
      
          void onFailure(String error);
      }
      
    2. 实体类

      package cc.catface.aidls.login;
      
      import android.os.Parcel;
      import android.os.Parcelable;
      
      // 发送验证码成功返回的实体类
      public class SendBean implements Parcelable {
      
          private String code;
          private String desc;
      
          protected SendBean() { }
      
          public SendBean(String code, String desc) {
              this.code = code;
              this.desc = desc;
          }
      
          @Override
          public void writeToParcel(Parcel dest, int flags) {
              dest.writeString(code);
              dest.writeString(desc);
          }
      
          public void readFromParcel(Parcel source) {
              code = source.readString();
              desc = source.readString();
          }
      
          public SendBean(Parcel in) {
              code = in.readString();
              desc = in.readString();
          }
      
          public String getCode() {
              return code;
          }
      
          public void setCode(String code) {
              this.code = code;
          }
      
          public String getDesc() {
              return desc;
          }
      
          public void setDesc(String desc) {
              this.desc = desc;
          }
      
          public static final Creator<SendBean> CREATOR = new Creator<SendBean>() {
              @Override
              public SendBean createFromParcel(Parcel in) {
                  return new SendBean(in);
              }
      
              @Override
              public SendBean[] newArray(int size) {
                  return new SendBean[size];
              }
          };
      
          @Override
          public int describeContents() {
              return 0;
          }
      
          @Override
          public String toString() {
              return "BaseResponse{" + "code='" + code + '\'' + ", desc='" + desc + '\'' + '}';
          }
      }
      

      // 登录成功返回的实体类
      package cc.catface.aidls.login;
      
      import android.os.Parcel;
      import android.os.Parcelable;
      
      /**
       * {"biz":{"lastLoginTime":1593349277744,"userInfo":{"gender":"0","phone":"13012892925","userId":2090983969115105},"isReg":0,"logErrTimes":0,"sessionId":"202093349277732500"},"code":"000000","desc":"成功"}
       */
      public class LoginBean implements Parcelable {
      
          private String code;
          private String desc;
          private Biz biz;
      
          public LoginBean() { }
      
          @Override
          public void writeToParcel(Parcel dest, int flags) {
              dest.writeString(code);
              dest.writeString(desc);
              dest.writeParcelable(biz, flags);
          }
      
          public void readFromParcel(Parcel source) {
              code = source.readString();
              desc = source.readString();
              biz = source.readParcelable(Biz.class.getClassLoader());
          }
      
          protected LoginBean(Parcel in) {
              code = in.readString();
              desc = in.readString();
              biz = in.readParcelable(Biz.class.getClassLoader());
          }
      
          public String getCode() {
              return code;
          }
      
          public void setCode(String code) {
              this.code = code;
          }
      
          public String getDesc() {
              return desc;
          }
      
          public void setDesc(String desc) {
              this.desc = desc;
          }
      
          public Biz getBiz() {
              return biz;
          }
      
          public void setBiz(Biz biz) {
              this.biz = biz;
          }
      
          public static final Creator<LoginBean> CREATOR = new Creator<LoginBean>() {
              @Override
              public LoginBean createFromParcel(Parcel in) {
                  return new LoginBean(in);
              }
      
              @Override
              public LoginBean[] newArray(int size) {
                  return new LoginBean[size];
              }
          };
      
          @Override
          public int describeContents() {
              return 0;
          }
      
          public static class Biz implements Parcelable {
      
              private long lastLoginTime;
              private UserInfo userInfo;
              private int isReg;
              private int logErrTimes;
              private String sessionId;
      
              public Biz() { }
      
              @Override
              public void writeToParcel(Parcel dest, int flags) {
                  dest.writeLong(lastLoginTime);
                  dest.writeParcelable(userInfo, flags);
                  dest.writeInt(isReg);
                  dest.writeInt(logErrTimes);
                  dest.writeString(sessionId);
              }
      
              public void readFromParcel(Parcel source) {
                  lastLoginTime = source.readLong();
                  userInfo = source.readParcelable(UserInfo.class.getClassLoader());
                  isReg = source.readInt();
                  logErrTimes = source.readInt();
                  sessionId = source.readString();
              }
      
              protected Biz(Parcel in) {
                  lastLoginTime = in.readLong();
                  userInfo = in.readParcelable(UserInfo.class.getClassLoader());
                  isReg = in.readInt();
                  logErrTimes = in.readInt();
                  sessionId = in.readString();
              }
      
              public void setLastLoginTime(long lastLoginTime) {
                  this.lastLoginTime = lastLoginTime;
              }
      
              public long getLastLoginTime() {
                  return lastLoginTime;
              }
      
              public void setUserInfo(UserInfo userInfo) {
                  this.userInfo = userInfo;
              }
      
              public UserInfo getUserInfo() {
                  return userInfo;
              }
      
              public void setIsReg(int isReg) {
                  this.isReg = isReg;
              }
      
              public int getIsReg() {
                  return isReg;
              }
      
              public void setLogErrTimes(int logErrTimes) {
                  this.logErrTimes = logErrTimes;
              }
      
              public int getLogErrTimes() {
                  return logErrTimes;
              }
      
              public void setSessionId(String sessionId) {
                  this.sessionId = sessionId;
              }
      
              public String getSessionId() {
                  return sessionId;
              }
      
              public static final Creator<Biz> CREATOR = new Creator<Biz>() {
                  @Override
                  public Biz createFromParcel(Parcel in) {
                      return new Biz(in);
                  }
      
                  @Override
                  public Biz[] newArray(int size) {
                      return new Biz[size];
                  }
              };
      
              @Override
              public int describeContents() {
                  return 0;
              }
      
      
              public static class UserInfo implements Parcelable {
      
                  private String gender;
                  private String phone;
                  private long userId;
      
                  public UserInfo() { }
      
                  @Override
                  public void writeToParcel(Parcel dest, int flags) {
                      dest.writeString(gender);
                      dest.writeString(phone);
                      dest.writeLong(userId);
                  }
      
                  public void readFromParcel(Parcel source) {
                      gender = source.readString();
                      phone = source.readString();
                      userId = source.readLong();
                  }
      
                  protected UserInfo(Parcel in) {
                      gender = in.readString();
                      phone = in.readString();
                      userId = in.readLong();
                  }
      
                  public void setGender(String gender) {
                      this.gender = gender;
                  }
      
                  public String getGender() {
                      return gender;
                  }
      
                  public void setPhone(String phone) {
                      this.phone = phone;
                  }
      
                  public String getPhone() {
                      return phone;
                  }
      
                  public void setUserId(long userId) {
                      this.userId = userId;
                  }
      
                  public long getUserId() {
                      return userId;
                  }
      
                  public static final Creator<UserInfo> CREATOR = new Creator<UserInfo>() {
                      @Override
                      public UserInfo createFromParcel(Parcel in) {
                          return new UserInfo(in);
                      }
      
                      @Override
                      public UserInfo[] newArray(int size) {
                          return new UserInfo[size];
                      }
                  };
      
                  @Override
                  public int describeContents() {
                      return 0;
                  }
      
      
                  @Override
                  public String toString() {
                      return "UserInfo{" + "gender='" + gender + '\'' + ", phone='" + phone + '\'' + ", userId=" + userId + '}';
                  }
              }
      
              @Override
              public String toString() {
                  return "Biz{" + "lastLoginTime=" + lastLoginTime + ", userInfo=" + userInfo + ", isReg=" + isReg + ", logErrTimes=" + logErrTimes + ", sessionId='" + sessionId + '\'' + '}';
              }
          }
      
          @Override
          public String toString() {
              return "LoginBean{" + "code='" + code + '\'' + ", desc='" + desc + '\'' + ", biz=" + biz + '}';
          }
      }
      
    3. 实体类对应的aidl声明

      package cc.catface.aidls.login;
      parcelable LoginBean;
      

      package cc.catface.aidls.login;
      parcelable SendBean;
      
    4. 将要被实例化使用的代理类

      package cc.catface.aidls.login;
      import cc.catface.aidls.login.ILoginSendCallback;
      import cc.catface.aidls.login.ILoginLoginCallback;
      import cc.catface.aidls.login.SendBean;
      
      interface ILogin {
          void send(String phone, ILoginSendCallback callback);
      
          void login(String phone, String code, ILoginLoginCallback callback);
      
          boolean isLogin(boolean isLogin);
      
      
          /* 非基本数据类型的参数需要加tag
                  [in-数据由客户进程流向服务进程/out-数据由服务进程流向客户进程/inout-数据在客户和服务进程双向流通]
      
             基本类型的tag均是in */
          void testSendBeanIn(in SendBean sendBean);
      
          void testSendBeanOut(out SendBean sendBean);
      
          void testSendBeanInout(inout SendBean sendBean);
      }
      

      in:参数由客户端设置
      out:参数由服务端设置
      inout:参数由客户端和服务端设置都可

      传递的对象参数前必须声明in/out/inout其中的一种,最好按实际情况设置,不然全部设置成inout虽然也可以,但是会增加系统负担,白给

    • 服务

      package cc.catface.aidls.login;
      
      import android.app.Service;
      import android.content.Intent;
      import android.os.IBinder;
      import android.os.RemoteCallbackList;
      import android.os.RemoteException;
      import android.util.Log;
      
      import androidx.annotation.Nullable;
      
      public class LoginService extends Service {
      
          private void log(String msg) {
              Log.d("catface login service", msg);
          }
      
          private RemoteCallbackList<ILoginSendCallback> mSendCallbacks = new RemoteCallbackList<>();
          private RemoteCallbackList<ILoginLoginCallback> mLoginCallbacks = new RemoteCallbackList<>();
      
          public class Binder extends ILogin.Stub {
              @Override
              public void send(String phone, ILoginSendCallback callback) throws RemoteException {
                  mSendCallbacks.register(callback);
      
                  if (phone.trim().length() == 11) {
                      SendBean bean = new SendBean("000000", "发送成功!");
                      int count = mSendCallbacks.beginBroadcast();
                      for (int i = 0; i < count; i++) {
                          mSendCallbacks.getBroadcastItem(i).onSuccess(bean);
                      }
                  } else {
                      int count = mSendCallbacks.beginBroadcast();
                      for (int i = 0; i < count; i++) {
                          mSendCallbacks.getBroadcastItem(i).onFailure("请检查手机号是否正确![" + phone.trim() + "]");
                      }
                  }
                  mSendCallbacks.finishBroadcast();
                  mSendCallbacks.unregister(callback);
              }
      
              @Override
              public void login(String phone, String code, ILoginLoginCallback callback) throws RemoteException {
                  mLoginCallbacks.register(callback);
      
                  if (phone.trim().length() == 11 && code.trim().length() > 0) {
                      LoginBean bean = new LoginBean();
                      bean.setCode("000000");
                      bean.setDesc("登录成功!");
                      LoginBean.Biz biz = new LoginBean.Biz();
                      biz.setSessionId("id is: " + phone.trim());
                      bean.setBiz(biz);
                      int count = mLoginCallbacks.beginBroadcast();
                      for (int i = 0; i < count; i++) {
                          mLoginCallbacks.getBroadcastItem(i).onSuccess(bean);
                      }
                  } else {
                      int count = mLoginCallbacks.beginBroadcast();
                      for (int i = 0; i < count; i++) {
                          mLoginCallbacks.getBroadcastItem(i).onFailure("请检查手机号和验证码是否正确![" + phone.trim() + "]-[" + code.trim() + "]");
                      }
                  }
                  mLoginCallbacks.finishBroadcast();
                  mLoginCallbacks.unregister(callback);
              }
      
              @Override
              public boolean isLogin(boolean isLogin) throws RemoteException {
                  return isLogin;
              }
      
      
              /**
               * 测试aidl非基本型参数的tag区别[in/out/inout]
               */
              @Override
              public void testSendBeanIn(SendBean sendBean) throws RemoteException {
                  log("testSendBeanIn-->sendBean: " + sendBean.toString());
              }
      
              @Override
              public void testSendBeanOut(SendBean sendBean) throws RemoteException {
                  log("testSendBeanOut-->sendBean: " + sendBean.toString());
              }
      
              @Override
              public void testSendBeanInout(SendBean sendBean) throws RemoteException {
                  log("testSendBeanInout-->sendBean: " + sendBean.toString());
              }
          }
      
          @Nullable
          @Override
          public IBinder onBind(Intent intent) {
              return new Binder();
          }
      }
      
    • 清单文件声明服务

      <service
          android:name="cc.catface.aidls.login.LoginService"
          android:enabled="true"
          android:exported="true">
          <intent-filter>
              <action android:name="cc.catface.aidls.login.action" />
              <category android:name="android.intent.category.DEFAULT" />
          </intent-filter>
      </service>
      

      android:enabled:是否可被系统实例化,默认true

      android:exported:是否可被三方应用隐式调用,如配置了intent-filter则默认true若未配置则默认false;若置为false则即使配了intent-filter也无法被三方应用隐式调用

      android:process:值为:remote代表该服务在单独的进程中运行,且该进程名前附加当前包名如cc.catface.services:remote;若值为remote则该进程名为remote,无附加包名

client app端

  • 使用参考

    package cc.catface.app_aidl_client;
    
    import android.app.Service;
    import android.content.ComponentName;
    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.widget.EditText;
    import android.widget.Toast;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import java.util.Random;
    
    import cc.catface.aidls.login.ILogin;
    import cc.catface.aidls.login.ILoginLoginCallback;
    import cc.catface.aidls.login.ILoginSendCallback;
    import cc.catface.aidls.login.LoginBean;
    import cc.catface.aidls.login.SendBean;
    
    public class MainActivity extends AppCompatActivity {
    
        private void log(String msg) {
            Log.d("catface-aidl-client", msg);
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            findViewById(R.id.btSend).setOnClickListener(v -> {
                try {
                    mService.send(getPhone(), new ILoginSendCallback.Stub() {
                        @Override
                        public void onSuccess(SendBean sendBean) throws RemoteException {
                            log("send success-->response: " + sendBean.toString());
                        }
    
                        @Override
                        public void onFailure(String error) throws RemoteException {
                            log("send failure-->error: " + error);
                        }
                    });
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            });
            findViewById(R.id.btLogin).setOnClickListener(v -> {
                try {
                    mService.login(getPhone(), getCode(), new ILoginLoginCallback.Stub() {
                        @Override
                        public void onSuccess(LoginBean loginBean) throws RemoteException {
                            log("login success-->loginBean: " + loginBean.toString());
                        }
    
                        @Override
                        public void onFailure(String error) throws RemoteException {
                            log("login failure-->error: " + error);
                        }
                    });
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            });
            findViewById(R.id.btIsLogin).setOnClickListener(v -> {
                int i = new Random().nextInt(2);
                try {
                    Toast.makeText(this, mService.isLogin(i == 0) + "--" + i, Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            });
    
    
            findViewById(R.id.btIn).setOnClickListener(v -> {
                SendBean bean = new SendBean("111111", "test in");
                try {
                    mService.testSendBeanIn(bean);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            });
            findViewById(R.id.btOut).setOnClickListener(v -> {
                SendBean bean = new SendBean("222222", "test out");
                try {
                    mService.testSendBeanOut(bean);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            });
            findViewById(R.id.btInout).setOnClickListener(v -> {
                SendBean bean = new SendBean("333333", "test inout");
                try {
                    mService.testSendBeanInout(bean);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            });
    
            initConnAndBind();
        }
    
        protected ILogin mService;
        private ServiceConnection mServiceConnection;
    
        private void initConnAndBind() {
            mServiceConnection = new ServiceConnection() {
                @Override
                public void onServiceDisconnected(ComponentName name) {
                    log("服务断开连接" + " || 当前线程:" + Thread.currentThread().getName());
                }
    
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    mService = ILogin.Stub.asInterface(service);
                    log("服务已连接 || service is: " + mService);
                }
            };
    
            if (mService == null) {
                Intent intent = new Intent();
                intent.setClassName("cc.catface.app_aidl_service", "cc.catface.aidls.login.LoginService");
                intent.setAction("cc.catface.aidls.login.action");
                bindService(intent, mServiceConnection, Service.BIND_AUTO_CREATE);
                log("bind service login");
            }
        }
    
        private String getPhone() {
            return ((EditText) findViewById(R.id.etPhone)).getText().toString().trim();
        }
    
        private String getCode() {
            return ((EditText) findViewById(R.id.etCode)).getText().toString().trim();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (null != mServiceConnection) {
                unbindService(mServiceConnection);
            }
        }
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值