android杂记干货区Android技术知识

AIDL跨进程通信的使用

2016-11-21  本文已影响200人  宝塔山上的猫

项目链接:https://github.com/liaozhoubei/AIDLService

AIDL:Android Interface Definition Language,即Android接口定义语言,这是一种用于Android的跨进程通信(IPC)的工具。
为什么有AIDL呢?这是因为在android中一个进程不允许直接访问另一个进程的内存。
进程是一个应用在手机上执行的过程,这是一个动态的概念,一个app的主进程可以从这个app打开算起,然后直接退出app,这时就算是一个进程的结束。
在android中允许有多进程,因此多进程通信可以在自己的应用中进行通信,也可以在不同应用中通信,而这便是AIDL的使用方向。
但是AIDL有个缺点,那就是比较耗资源,如非必要,就不适用AIDL的为好

AIDL的使用方法

我们知道了AIDL是用来干什么的,那么我们就开始学习如何使用AIDL吧!

我们先建立一个AIDLService项目,这个项目用来模拟远程服务端口的,即我们想通过AIDL调用这个项目中的方法,这就是跨进程通信。

由于只是一个小Demo,所以并没有做太复杂的东西,我们只想通过AIDLService这个进程进行一次基本数据类型的传递,即进行简单的计算,然后将计算结果传回去客户端中。
其次,我们还想通过AIDLService将一个Object对象传给客户端。

那么我们就开始吧。

AIDLService服务端: 创建一个AIDL文件

首先必须要有一个AIDL接口文件,即后缀名为aidl的文件,在这里我们是IService.aidl,代码如下

interface IService {
    // 传入两个正整数,返回他们的和
    int add(int num1, int num2);
    // 传入一个Person对象,返回person集合
    List<Person> addPerson(in Person p);
}

看上去是不是很熟悉,没错,这其实就是一个interface接口而已,区别在于它并没有public或者private等修饰符。
其中addPerson(in Person p)中我们看到有个in Person p,这个in是什么呢?简单的说表示这个Person是从外面传进来的(可以自行搜索其中的意思)。

除此之外还需要创建一个Person.aidl,传递序列号之后的Person对象,代码很简单,如下:

parcelable Person;

就这么一行代码。

AIDLService服务端: 创建一个RemoteService远程服务

RemoteService远程服务中将实现IService.aidl中的两个方法,让后通过IBinder传出(IBinder可以让外界调用服务中的方法)。具体代码如下:

public class RemoteService extends Service {
    public final String TAG = this.getClass().getSimpleName();
    private ArrayList<Person> persons;

    public RemoteService() {
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        persons = new ArrayList<>();
        return iBinder;
    }



    private IBinder iBinder = new IService.Stub() {
        @Override
        public int add(int num1, int num2) throws RemoteException {
            return num1 + num2;
        }

        @Override
        public List<Person> addPerson(Person p) throws RemoteException {
            persons.add(p);
            return persons;
        }
    };
}

当然,我们不要忘记建立Person.java,而且要对这个类实现序列号,因为Object必须实现序列号才能同传输,代码如下:

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) {
        // 从序列号中取出数据,要按照writeToParcel()写入的顺序一样取出
        this.name = in.readString();
        this.age = in.readInt();
    }



    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * 将数据写入序列化中
     * @param dest
     * @param flags
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // 写入时的顺序决定取出数据时的顺序
        dest.writeString(name);
        dest.writeInt(age);
    }


    /**
     * 必须实现的方法,写法基本固定
     */
    public static final Creator<Person> CREATOR = new Creator<Person>() {
        /**
         * 从序列化中取出一个个的字段
         * @param in
         * @return
         */
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };
}

OK,这样我们的AIDLService服务端就已经完成了,剩下的就是使用客户端调用了。

AIDLClient客户端

客户端用来调用AIDLService中的两个方法,那么如何调用呢?
首先必须要创建AIDLClient客户端。

然后我们直接把AIDLService当中的Person.aidl和IService.aidl两个文件拷贝到,需要注意的是这里两个文件所在的包名必须与在AIDLService时的包名保持一致。
还需要把Person.java拷贝过来,包名也需要保持一致。

AIDLClient客户端需要一个简单的界面,activity_main.xml代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.aidlclient.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_gravity="center_horizontal"
        >

        <EditText
            android:id="@+id/num1"
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="+" />

        <EditText
            android:id="@+id/num2"
            android:layout_width="50dp"
            android:layout_height="wrap_content" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="=" />

        <EditText
            android:id="@+id/result"
            android:layout_width="50dp"
            android:layout_height="wrap_content" />

    </LinearLayout>

    <Button
        android:id="@+id/btn_addNumber"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="调用"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_gravity="center_horizontal"
        >

        <EditText
            android:id="@+id/name"
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="+" />

        <EditText
            android:id="@+id/age"
            android:layout_width="50dp"
            android:layout_height="wrap_content" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="=" />

        <EditText
            android:id="@+id/getPerson"
            android:layout_width="50dp"
            android:layout_height="wrap_content" />

    </LinearLayout>

    <Button
        android:id="@+id/btn_addPerson"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="调用Person"/>

</LinearLayout>

非常简单,这里就不做解释了。

AIDLClient客户端:调用远程服务

MainActivity中的布局写好了,那么我们就开始进行远程吧。方法其实就使用本地服务基本一样。代码如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText num1;
    private EditText num2;
    private EditText result;
    private EditText name;
    private EditText age;
    private EditText getPerson;
    private Button btn_1;
    private Button btn_2;

    IService iService;

    /**
     * 创建ServiceConnection与服务进行链接
     */
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iService = IService.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iService = null;
        }
    };

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

    }

    private void initView() {
        num1 = (EditText) findViewById(R.id.num1);
        num2 = (EditText) findViewById(R.id.num2);
        result = (EditText) findViewById(R.id.result);

        btn_1 = (Button) findViewById(R.id.btn_addNumber);
        btn_2 = (Button) findViewById(R.id.btn_addPerson);

        name = (EditText) findViewById(R.id.name);
        age = (EditText) findViewById(R.id.age);
        getPerson = (EditText) findViewById(R.id.getPerson);

        btn_1.setOnClickListener(this);
        btn_2.setOnClickListener(this);
    }

    /**
     * 与远程服务进行绑定
     */
    private void bindService() {
        Intent intent = new Intent();
        // 传入AIDL的包名,以及调用远程服务的全类名
        intent.setComponent(new ComponentName("com.example.aidlservice", "com.example.aidlservice.RemoteService"));
        bindService(intent, conn, Context.BIND_AUTO_CREATE);

    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_addNumber:
                int num1 = Integer.parseInt(this.num1.getText().toString());
                int num2 = Integer.parseInt(this.num2.getText().toString());


                try {
                    int num = iService.add(num1, num2);
                    result.setText(num + "");
                    Toast.makeText(getApplicationContext(), num + "来到了", Toast.LENGTH_LONG).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;

            case R.id.btn_addPerson:
                String p1 = name.getText().toString();
                int num3 = Integer.parseInt(age.getText().toString());
                Person person = new Person(p1, num3);
                try {
                    ArrayList<Person> persons = (ArrayList<Person>) iService.addPerson(person);
                    Toast.makeText(getApplicationContext(), persons.toString(), Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
        }
    }
}

在这里,我们看到基本与调用本地的服务的方法没多大区别,只是在使用bindService()方法时,我们要设置包名,即设置我们要访问的服务的信息,

intent.setComponent(new ComponentName("com.example.aidlservice", "com.example.aidlservice.RemoteService"));

总结

AIDL的使用并不难,而且在调用的时候也就象使用普通的服务一样,区别只在双方都必须有aidl文件,其中表明能够被调用的方法而已。
下面总结一下aidl多进程通信的使用步骤

上一篇下一篇

猜你喜欢

热点阅读