示例:Android使用AIDL实现跨进程通讯(IPC)

AIDL概述

AIDL(Android Interface Definition Language),是Android接口定义语言,这种语言定义了一个客户端和服务器通讯接口的一个标准、规范。Google官方AIDL的说明如下:

Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.

意思是说“只有当你允许来自不同应用通过你的service实现进程通讯,并且需要在你的service中处理多线程的情况下才用AIDL,如果你不需要实现不同应用间即时的进程通讯,那么,你应该创建一个接口实现Binder,或者,如果你想实现进程通讯但是不需要处理多线程,那么用一个Messenger实现你的接口,但是,无论如何,你都得先理解本地的服务在你实现AIDL之前“
通过上面的这句话我们就非常清楚了AIDL的作用就是让两个不同的应用间通过Service进行通信(进程通讯IPC),并且远程的Service可以处理多线程。简单来讲就是,两个应用,一个应用对外提供一个远程Service,其他的应用可以并发地访问这个Service,即:C/S模式。

AIDL使用示例

服务端实现

1.通过Android Studio新建一个Module,这个Module作为服务端应用。

2.建立AIDL文件。在src/main目录下新建一个aidl文件夹,然后,在此目录下新建一个IRemoteService.aidl文件。如下:

// IRemoteService.aidl
package com.lonly.example.aidlservicer;
//用到的自定义类型数据类一定要手动导入
import com.lonly.example.aidlservicer.Person;

// Declare any non-default types here with import statements

interface IRemoteService {
    /**
     * 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);
     /**
      * Request the process ID of this service, to do evil things with it
      */
     int getPid();
     /**
      * get name by id
      */
     String getName(int id);
     /**
     * get Person
     */
     Person getPerson(int id);

     /**
     * 添加人员 in:定向tag。
     * 其中,
      * in 表示数据只能由客户端流向服务端,
     * out 表示数据只能由服务端流向客户端,
     * inout 则表示数据可在服务端与客户端之间双向流通。
     */
     void addPerson(in Person person);

     List<Person> getPersons();
}


注意:此处Person类是自定义的,所以,必须加入“import com.lonly.example.aidlservicer.Person;”这语句,即使IRemoteService.aidl和Person.java放在相同的包中。如果不加,有可能在编译时报“Error:Execution failed for task ':android:compileDebugAidl...'”错误,导致编译失败。

在IRemoteService.aidl文件中,我们使用到了自定义的Person类(同样放在src/main/aidl目录下),该类必须实现序列化接口Parcelable。定义如下:

public class Person implements Parcelable{
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    protected Person(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    /**
     * 在想要进行序列号传递的实体类内部一定要声明该常量。常量名只能是CREATOR,类型也必须是
     * Parcelable.Creator<T>  T:就是当前对象类型
     */
    public static final Creator<Person> CREATOR = new Creator<Person>() {
        /***
         * 根据序列化的Parcel对象,反序列化为原本的实体对象
         * 读出顺序要和writeToParcel的写入顺序相同
         */
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in.readString(),in.readInt());
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[0];
        }
    };
    @Override
    public int describeContents() {
        return 0;
    }
    /**
     * 将对象写入到Parcel(序列化)
     * @param dest:就是对象即将写入的目的对象
     * @param flags: 有关对象序列号的方式的标识
     * 这里要注意,写入的顺序要和在createFromParcel方法中读出的顺序完全相同。例如这里先写入的为name,
     * 那么在createFromParcel就要先读name
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
    }
    /**
     * 参数是一个Parcel,用它来存储与传输数据
     * @param dest
     */
    private void readFromParcel(Parcel dest) {
        //注意,此处的读值顺序应当是和writeToParcel()方法中一致的
        this.name = dest.readString();
        this.age = dest.readInt();
    }
}

借助Android Studio的快捷提示功能,可以很方便的编写出该Person类,只是下面这个方法是我们手动添加的:

 /**
     * 参数是一个Parcel,用它来存储与传输数据
     * @param dest
     */
    private void readFromParcel(Parcel dest) {
        //注意,此处的读值顺序应当是和writeToParcel()方法中一致的
        this.name = dest.readString();
        this.age = dest.readInt();
    }
为了一直方便,我们把Person.java类也存放到与IRemoteService.aidl文件一致的src/main/aidl目录下。不过,这要修改 build.gradle 文件,在 android{} 中间加上下面的内容: 

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

另外,在定义好了Person类之后,我们还需定义一个此类对应的aidl文件,即文件名和类名一样,扩展名为.aidl。如下

// Person.aidl
package com.lonly.example.aidlservicer;

// Declare any non-default types here with import statements
parcelable Person;

注意:parcelable首字母是小写。

3.编译运行项目。编译成功后,会在build目录下自动生成一个IRemoteService.aidl所对应的IRemoteService.java文件。如下:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: E:\\ASWorkSpace\\AIDLDemo\\aidlservicer\\src\\main\\aidl\\com\\lonly\\example\\aidlservicer\\IRemoteService.aidl
 */
package com.lonly.example.aidlservicer;
// Declare any non-default types here with import statements

public interface IRemoteService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.lonly.example.aidlservicer.IRemoteService
{
private static final java.lang.String DESCRIPTOR = "com.lonly.example.aidlservicer.IRemoteService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.lonly.example.aidlservicer.IRemoteService interface,
 * generating a proxy if needed.
 */
public static com.lonly.example.aidlservicer.IRemoteService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.lonly.example.aidlservicer.IRemoteService))) {
return ((com.lonly.example.aidlservicer.IRemoteService)iin);
}
return new com.lonly.example.aidlservicer.IRemoteService.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_basicTypes:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_getPid:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.getPid();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getName:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.getName(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_getPerson:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
com.lonly.example.aidlservicer.Person _result = this.getPerson(_arg0);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_addPerson:
{
data.enforceInterface(DESCRIPTOR);
com.lonly.example.aidlservicer.Person _arg0;
if ((0!=data.readInt())) {
_arg0 = com.lonly.example.aidlservicer.Person.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addPerson(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getPersons:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.lonly.example.aidlservicer.Person> _result = this.getPersons();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.lonly.example.aidlservicer.IRemoteService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public int getPid() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
      * get name by id
      */
@Override public java.lang.String getName(int id) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(id);
mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
     * get Person
     */
@Override public com.lonly.example.aidlservicer.Person getPerson(int id) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.lonly.example.aidlservicer.Person _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(id);
mRemote.transact(Stub.TRANSACTION_getPerson, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.lonly.example.aidlservicer.Person.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
     * 添加人员 in:定向tag。
     * 其中,
      * in 表示数据只能由客户端流向服务端,
     * out 表示数据只能由服务端流向客户端,
     * inout 则表示数据可在服务端与客户端之间双向流通。
     */
@Override public void addPerson(com.lonly.example.aidlservicer.Person person) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((person!=null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.util.List<com.lonly.example.aidlservicer.Person> getPersons() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.lonly.example.aidlservicer.Person> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPersons, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.lonly.example.aidlservicer.Person.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_getPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_getPersons = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public int getPid() throws android.os.RemoteException;
/**
      * get name by id
      */
public java.lang.String getName(int id) throws android.os.RemoteException;
/**
     * get Person
     */
public com.lonly.example.aidlservicer.Person getPerson(int id) throws android.os.RemoteException;
/**
     * 添加人员 in:定向tag。
     * 其中,
      * in 表示数据只能由客户端流向服务端,
     * out 表示数据只能由服务端流向客户端,
     * inout 则表示数据可在服务端与客户端之间双向流通。
     */
public void addPerson(com.lonly.example.aidlservicer.Person person) throws android.os.RemoteException;
public java.util.List<com.lonly.example.aidlservicer.Person> getPersons() throws android.os.RemoteException;
}
打开这个AIDL生成的Java接口,我们可以发现,里面有一个内部静态抽象类Stub,这个类继承了Binder并实现了这个接口,所以,我们可以直接使用这个Stub类完成远程服务的搭建。
4.创建RemoteService类,实现该AIDL文件生成的Java接口。在onBind方法中返回实现了AIDL Java接口的那个Binder类(new一个Stub类正好),这个类将作为这个Service代理类。如下:

public class RemoteService extends Service{

    private List<Person> persons = new ArrayList<>();
    /**
     * 返回一个RemoteService代理对象IBinder给客户端使用
     * @param intent
     * @return
     */

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mRemoteService;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //初始化persons
        persons.add(new Person("吕布",20));
        persons.add(new Person("关羽",23));
        persons.add(new Person("张飞",21));
        persons.add(new Person("赵子龙",19));
    }

    /**
     * 实现接口
     */
    private IRemoteService.Stub mRemoteService = new IRemoteService.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            System.out.println("Thread: " + Thread.currentThread().getName());
            System.out.println("basicTypes aDouble: " + aDouble +" anInt: " + anInt+" aBoolean " + aBoolean+" aString " + aString);
        }

        @Override
        public int getPid() throws RemoteException {
            System.out.println("Thread: " + Thread.currentThread().getName());
            System.out.println("RemoteService getPid ");
            return android.os.Process.myPid();
        }

        @Override
        public String getName(int id) throws RemoteException {
            return persons.get(id).getName();
        }

        @Override
        public Person getPerson(int id) throws RemoteException {
            return persons.get(id);
        }

        @Override
        public void addPerson(Person person) throws RemoteException {
            persons.add(person);
        }

        @Override
        public List<Person> getPersons() throws RemoteException {
            return persons;
        }

    };
}

5.在Menifest中声明这个Service。

  <service android:name=".RemoteService">
            <intent-filter>
                <action android:name="com.lonly.example.aidl"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>

客户端实现

1.新建一个Moudle,这个Moudle作为客户端应用。
2.将服务端定义的AIDL文件和相关java类移植到客户端中。如果相关的文件都是按上述的步骤存放到src/main/aidl目录,那个直接将aidl文件夹复制过去就可以了。保证包名和服务端的一致。最后,别忘了修改 build.gradle 文件,在 android{} 中间加上下面的内容:

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

3.定义MainActivity,调用服务端的方法。但在此之前,我们首先要连接上服务端。如下:

 private ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 这里的IBinder对象service是代理对象,所以必须调用下面的方法转换成AIDL接口对象
            mRemoteService = IRemoteService.Stub.asInterface(service);
            int pid = 0;
            try {
                pid = mRemoteService.getPid();
                int currentPid = android.os.Process.myPid();
                System.out.println("currentPID: " + currentPid + "  remotePID: " + pid);
                mRemoteService.basicTypes(12, 1223, true, 12.2f, 12.3, "有梦就要去追,加油!");
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            System.out.println("bind success! " + mRemoteService.toString());
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteService = null;
            System.out.println(mRemoteService.toString() + " disconnected! ");
        }
    };

然后,通过隐式意图绑定远程服务,建立一个服务连接。如下:

// 连接绑定远程服务
        Intent intent = new Intent();
        intent.setAction("com.lonly.example.aidl");
        intent.setPackage("com.lonly.example.aidlservicer");
        isConnSuccess = bindService(intent, conn, Context.BIND_AUTO_CREATE);
注意:此处的intent.setAction("com.lonly.example.aidl");参数必须和服务端Menifrst文件Service中声明的action属性一致。

完整的客户端代码是这样的:

public class MainActivity extends AppCompatActivity {
    private IRemoteService mRemoteService;
    private TextView mTv_result;
    private TextView mAll_result;
    private EditText mEditText;
    private EditText mEditText1;
    private EditText mEditText2;
    private boolean isConnSuccess;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mEditText = (EditText) findViewById(R.id.editText);
        mEditText1 = (EditText) findViewById(R.id.editText1);
        mEditText2 = (EditText) findViewById(R.id.editText2);

        mTv_result = (TextView) findViewById(R.id.tv_result);
        mAll_result = (TextView) findViewById(R.id.all_result);

        // 连接绑定远程服务
        Intent intent = new Intent();
        intent.setAction("com.lonly.example.aidl");
        intent.setPackage("com.lonly.example.aidlservicer");
        isConnSuccess = bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    public void search(View view) {
        if (isConnSuccess) {
            // 连接成功
            String keyStr = mEditText.getText().toString();
            if (!TextUtils.isEmpty(keyStr)) {
                int id = Integer.valueOf(keyStr);
                try {
                    String name = mRemoteService.getName(id);
                    mTv_result.setText(name);
                } catch (RemoteException ex) {
                    ex.printStackTrace();
                }
            } else {
                Toast.makeText(this, "请输入查询", Toast.LENGTH_SHORT).show();
            }


        } else {
            System.out.println("连接失败!");
        }
    }

    public void searchPerson(View view) {
        if (isConnSuccess) {
            // 连接成功
            String keyStr = mEditText.getText().toString();
            if (!TextUtils.isEmpty(keyStr)) {
                int id = Integer.valueOf(keyStr);
                try {
                    Person person = mRemoteService.getPerson(id);
                    mTv_result.setText(new StringBuffer("姓名:").append(person.getName()).append("\t年龄:").append(String.valueOf(person.getAge())));
                } catch (RemoteException ex) {
                    ex.printStackTrace();
                }
            } else {
                Toast.makeText(this, "请输入查询", Toast.LENGTH_SHORT).show();
            }
        } else {
            System.out.println("连接失败!");
        }
    }
    public void add(View view) {
        if (isConnSuccess) {
            // 连接成功
            String nameStr = mEditText1.getText().toString();
            String ageStr = mEditText2.getText().toString();
            if (!TextUtils.isEmpty(nameStr) && !TextUtils.isEmpty(ageStr) ) {
                int age = Integer.valueOf(ageStr);
                try {
                    //判断是否已有此人
                    List<Person> list = mRemoteService.getPersons();
                    for (Person per:list) {
                        if(TextUtils.equals(nameStr,per.getName())){
                            Toast.makeText(this, "名册已有此人", Toast.LENGTH_SHORT).show();
                            return;
                        }
                    }
                    //添加
                    Person person = new Person(nameStr,age);
                    mRemoteService.addPerson(person);

                    //重新获取
                    List<Person> persons = mRemoteService.getPersons();

                    StringBuffer  sb = new StringBuffer ("");
                    for (Person per:persons) {
                        sb.append("姓名:")
                                .append(per.getName())
                                .append("\t年龄:")
                                .append(per.getAge())
                                .append("\n");
                    }
                    mAll_result.setText(sb.toString());
                } catch (RemoteException ex) {
                    ex.printStackTrace();
                }
            } else {
                Toast.makeText(this, "请输入姓名和年龄", Toast.LENGTH_SHORT).show();
            }

        } else {
            System.out.println("连接失败!");
        }
    }
    private ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 这里的IBinder对象service是代理对象,所以必须调用下面的方法转换成AIDL接口对象
            mRemoteService = IRemoteService.Stub.asInterface(service);
            int pid = 0;
            try {
                pid = mRemoteService.getPid();
                int currentPid = android.os.Process.myPid();
                System.out.println("currentPID: " + currentPid + "  remotePID: " + pid);
                mRemoteService.basicTypes(12, 1223, true, 12.2f, 12.3, "有梦就要去追,加油!");
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            System.out.println("bind success! " + mRemoteService.toString());
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteService = null;
            System.out.println(mRemoteService.toString() + " disconnected! ");
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

activity_main布局文件代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent"
    >

    <EditText
        android:id="@+id/editText"
        android:hint="输入查询ID,如0,1,2."
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="number"/>
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
    <Button
        android:onClick="search"
        android:layout_width="wrap_content"
        android:text="查询"
        android:layout_height="wrap_content"/>
    <Button
        android:onClick="searchPerson"
        android:layout_width="wrap_content"
        android:text="查询对象"
        android:layout_height="wrap_content"/>
</LinearLayout>
    <LinearLayout
        android:layout_width="wrap_content"
        android:orientation="horizontal"
        android:layout_marginBottom="10dp"
        android:layout_height="wrap_content">
        <TextView
            android:text="结果:"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <TextView
            android:id="@+id/tv_result"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </LinearLayout>
    <EditText
        android:id="@+id/editText1"
        android:hint="输入姓名"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text"/>
    <EditText
        android:id="@+id/editText2"
        android:hint="输入年龄"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="number"/>
    <Button
        android:onClick="add"
        android:layout_width="wrap_content"
        android:text="添加"
        android:layout_height="wrap_content"/>
    <LinearLayout
        android:layout_width="wrap_content"
        android:orientation="horizontal"
        android:layout_height="wrap_content">
        <TextView
            android:text="三国名册:"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <TextView
            android:id="@+id/all_result"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </LinearLayout>
</LinearLayout>

测试

运行服务端应用,然后在运行客户端应用,测试结果:


源码传送门:https://github.com/legallonly/AIDLDemo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值