AIDL实现IPC代码实例

本例要实现的效果是两个app,client和server,客户端能调用server端的Service提供的方法。

本次示例分为客服端和服务器端,两端的代码目录如下:

服务器端代码目录:

 

 

客户端代码目录:

 

代码说明

--->服务器端代码:

首先,要创建一个AIDL接口,IPersonManager.aidl,里面定义了服务器端提示给客户端的方法。

// IPersonManager.aidl
package com.demo.aidlprojectserver;

import com.demo.aidlprojectserver.Person;  //要传递的自定义对象需要显示的import,即使是在一个目录

interface IPersonManager {
    List<Person> getPersons();

    void addPerson(in Person person); //除了基本数据类型,要添加 in out inout

    String greet(String name);

}

 

系统自动编译会根据这个IPersonManager.aidl生成一个IPersonManager.java类。如图所示:

这个IPersonManager.java里的内部内IPersonManager.Stub就是一个Binder 

public static abstract class Stub extends android.os.Binder

提示服务的Service里new一个这样的Binder,通过这个Binder向外提示服务。

AIDLService.java

package com.demo.aidlprojectserver;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;


import java.util.ArrayList;
import java.util.List;

public class AIDLService extends Service {

    public static final String TAG = AIDLService.class.getSimpleName();

    @Override
    public void onCreate() {
        super.onCreate();
        try {
            mManage.addPerson(new Person("张三", "男", 22));
            mManage.addPerson(new Person("李四", "男", 30));
            mManage.addPerson(new Person("婉儿", "女", 18));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private List<Person> mPersons = new ArrayList<>();

    private IPersonManager.Stub mManage = new IPersonManager.Stub() {
        @Override
        public List<Person> getPersons() throws RemoteException {
            return mPersons;
        }

        @Override
        public void addPerson(Person person) throws RemoteException {
            Log.d(TAG, "client add person-----" + person.toString());
            mPersons.add(person);
        }

        @Override
        public String greet(String name) {
            return "热烈祝贺" + name + "来我院视察";
        }
    };

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

由于客户端也要定义同样的IPersonManager.aidl(所属的包目录结构一样,里面的代码也一样),生成的IPersonManager.java类也一样,所以客户端就可以通过客户端的IPersonManger来调用服务器端的Binder提示的方法,续而调用到Service提示的操作。

 

服务器端还有两个文件没有介绍:Person.java和Person.aidl。

默认情况下,AIDL 支持下列数据类型:
Java 编程语言中的所有原语类型(如 int、long、char、boolean 等等)
String/CharSequence
List

List 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 可选择将 List 用作“通用”类(例如,List)。另一端实际接收的具体类始终是 ArrayList,但生成的方法使用的是 List 接口。

Map

Map 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 不支持通用 Map(如 Map< String,Integer> 形式的 Map)。 另一端实际接收的具体类始终是 HashMap,但生成的方法使用的是 Map 接口。 
您必须为以上未列出的每个附加类型加入一个 import 语句,即使这些类型是在与您的接口相同的软件包中定义。

通过 IPC 传递对象
有时一些简单的数据无法满足我们的需求,我们需要的一个自定义实体,这时就需要使用Parcelable,要传递的实体类必须支持 Parcelable 接口。

//Person.java
package com.demo.aidlprojectserver;

import android.os.Parcel;
import android.os.Parcelable;

public class Person implements Parcelable {

    private String name;
    private String sex;
    private int age;

    protected Person(Parcel in) {
        this.name = in.readString();  //读取的顺序要和writeToParcel的write顺序一致
        this.sex = in.readString();
        this.age = in.readInt();
    }

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

    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeString(sex);
        dest.writeInt(age);
    }

    @Override
    public String toString() {
        return name + ":" + sex + ":" + age;
    }
}

除了实现Parcelable接口外,系统还要求我们自定义的对象需要写一个aidl文件。如Person.aidl

// Person.aidl
package com.demo.aidlprojectserver;

parcelable Person;

到此,服务器端代码介绍完毕

 

--->客户端代码

和服务器端一样,我们需要编写IPersonManager.aidl(生成IPersonManager.java)文件,编写Person.java,Person.aidl文件,并且package要和服务器端一样。代码目录回看上面介绍。

最后直接看调用的代码MainActivity.java

package com.demo.aidlprojectclient;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.demo.aidlprojectserver.IPersonManager;
import com.demo.aidlprojectserver.Person;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();
    private boolean connected = false;
    private IPersonManager manager;

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

        findViewById(R.id.bt_bind).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setClassName("com.demo.aidlprojectserver", "com.demo.aidlprojectserver.AIDLService");
                connected = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
            }
        });

        findViewById(R.id.bt_add).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (connected) {
                    Person person = new Person("lili", "女", 18);
                    try {
                        manager.addPerson(person);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        findViewById(R.id.bt_greet).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (connected && manager != null) {
                    try {
                        Toast.makeText(MainActivity.this, manager.greet("曹操"), 0).show();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            manager = IPersonManager.Stub.asInterface(service);
            try {
                List<com.demo.aidlprojectserver.Person> personList = manager.getPersons();
                for (Person person : personList) {
                    Log.d(TAG, person.toString());
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

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

上面代码中,就是AIDL通过Binder机制,让客户端拿到了服务器端的Binder对象,从而用服务器端的Binder去调用服务器端的Service提示的方法。

本例子的效果就是:客户端通过调用 manager.greet("曹操"),调用到了另外一个进程(App)的方法:

    public String greet(String name) {
            return "热烈祝贺" + name + "来我院视察";
        }
    };

返回结果是:热烈祝贺曹操来我院视察

=========================================================================

 

 

至此完成了一次通过Binder(AIDL)实现跨进程通信。这和App通过跨进程通信调用系统服务是一样的道理。如下实例:

Binder运行的实例解释

首先我们看看我们的程序跨进程调用系统服务的简单示例,实现浮动窗口部分代码:

//获取WindowManager服务引用
WindowManager wm = (WindowManager) getSystemService(getApplication().WINDOW_SERVICE);
//布局参数layoutParams相关设置略...
View view = LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);
//添加view
wm.addView(view, layoutParams);

注册服务(addService): 在Android开机启动过程中,Android会初始化系统的各种Service,并将这些Service向ServiceManager注册(即让ServiceManager管理)。这一步是系统自动完成的。

获取服务(getService): 客户端想要得到具体的Service直接向ServiceManager要即可。客户端首先向ServiceManager查询得到具体的Service引用,通常是Service引用的代理对象,对数据进行一些处理操作。即第2行代码中,得到的wm是WindowManager对象的引用。

+

使用服务: 通过这个引用向具体的服务端发送请求,服务端执行完成后就返回。即第6行调用WindowManager的addView函数,将触发远程调用,调用的是运行在systemServer进程中的WindowManager的addView函数。

代码下载地址:https://github.com/tomyZhou/AidlDemo

 

更多Binder相关知识推荐如下:

Android 中的 IPC 方式  https://lrh1993.gitbooks.io/android_interview_guide/content/android/basis/ipc.html 

Android Binder机制及AIDL https://lrh1993.gitbooks.io/android_interview_guide/content/android/advance/binder.html

 从getSystemService()开始,开撸Binder通讯机制 https://www.jianshu.com/p/1050ce12bc1e

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值