AIDL 实现跨进程通信(android Studio)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/shizhonghuo19870328/article/details/53192870

AIDL是android Interface definition language的缩写, 也就是android  接口定义语言。通过 AIDL,android 在java 层很容易实现进程间的通信。

以下介绍一下利用AIDL 实现进程间通信的流程。

一  建立自定义类 person,

public class person implements Parcelable{
    String name;
    int age;
    public person(Parcel parcel){
        name=parcel.readString();
        age=parcel.readInt();
    }
    public person(String name, int age){
        this.name=name;
        this.age=age;
    }
    public int describeContents(){
        return 0;
    }

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

    public static Creator<person> CREATOR=new Creator<person>(){

        public person createFromParcel(Parcel source){
            return new person(source);
        }

        public person[] newArray(int size){
            return new person[size];
        }

    };
}
AIDL 支持的数据类型如下:

1) 基本数据类型

2) String 和charsequence

3) ArrayList和HashMap, 并且其中包含的元素也必须被AIDL 支持

4) 实现Parcelable 接口的对象。

5)  其他AIDL 接口。

这里我要传递的是我自定义的类型, 所以必须自定义类必须实现Parcelable接口。

实现Parcelable 接口必须定义对象生成器CREATOR(注意只能命名为CREATOR),和实现以下方法:

public int describeContents()

public void writeToParcel(Parcel dest, int flags)。


二 创建AIDL接口

以下要创建了以下AIDL 接口,并定义了进程间通信的方法(在java 目录下,右键,选择new AIDL 文件)。

// IPersonList.aidl
package example.jni.com.aidl;
import  example.jni.com.aidl.person;

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

interface IPersonList {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void addPerson(in person p);
    List<person> getPersonList();
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}


但是, 请注意,由于进程间通信传递的是person 对象, 所以必须还要定义person 的AIDL 文件,并import person AIDL 文件。

package example.jni.com.aidl;

// Declare any non-default types here with import statements
parcelable person;
/*
interface IPerson {

    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}
*/
注意, 当新建person.aidl 文件时, 由于和person.java 名字相同造成冲突。 可以先随便命名, 然后右键修改。

另外, 在person.aidl 文件中, parcelable 首字母不可以大写。

这时 Make project, 会在app\build\generated\source\aidl\debug\example\jni\com\aidl\ 路径下自动生成一个IPersonList.java 的文件。


三 建立service.

package example.jni.com.aidl;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.NonNull;
import android.util.Log;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class MyService extends Service {

    private ArrayList<person> list= new ArrayList<>();

    public String TAG="Myservice";
    public  IBinder mBind=new IPersonList.Stub(){      // service must  declare a IBind, it will return to other activity.
        public void addPerson(person p){
            list.add(p);
            Log.d(TAG,"The person's name is "+p.name);
        }
        public List<person> getPersonList(){
            Log.d(TAG,"The list len is "+list.size());
            return list;
        }
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
                        double aDouble, String aString){

        }

    };
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
         return mBind;
        // TODO: Return the communication channel to the service.
        //throw new UnsupportedOperationException("Not yet implemented");
    }
}

在service 端定义一个IBinder对象实现IPersonList 定义的方法, 并将IBinder 对象与service绑定。IBinder 对象用于进程间通信。

在这里, 由于是将service 和client 运行在一个apk,所以要给service 重新定义一个进程。

所以要在AndroidManifest.xml 文件中为service添加以下语句:

android:process=":remote"

由于我定义的包名是:example.jni.com.aidl。 所以service运行在 example.jni.com.aidl:remote 进程中。


四 建立client

public class MainActivity extends AppCompatActivity {

    String TAG="mainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent=new Intent(this,MyService.class);
        bindService(intent,conn, Context.BIND_AUTO_CREATE);
    }
    public ServiceConnection conn=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IPersonList iPersonList=IPersonList.Stub.asInterface(service);
            try{
                iPersonList.addPerson(new person("YangLi",30));
                iPersonList.addPerson(new person("HanYue",25));
                List<person> list =iPersonList.getPersonList();
                for(int i=0;i<list.size();i++){
                    Log.d(TAG," The person name is "+list.get(i).name);
                }
            }catch (RemoteException e){
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

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

    }
}


client 要想调用service 的方法, 要从service 获得IBinder 对象, 并将其实例化为IPersonList。

 1) public  boolean bindService(Intent service, @NonNull ServiceConnection conn,@BindServiceFlags int flags);

此方法将cilent 与service 进行绑定,绑定参数为serviceConnection.

 2) 利用serviceConnection 实例化IPersonList。

 3)利用 IPersonList进行进程间通信。


以下是程序的运行结果,



最后介绍一下程序运行过程中可能出现的问题。

1. 在此程序中, 传递的是自定义的对象, 在编译生成IPerson.java 时,可能出现错误:找不到自定义的包。

    解决方法是在build.gradle 文件中添加如下代码.


    sourceSets {
        main {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            java.srcDirs = ['src/main/java', 'src/main/aidl']
            resources.srcDirs = ['src/main/java', 'src/main/aidl']
            aidl.srcDirs = ['src/main/aidl']
            res.srcDirs = ['src/main/res']
            assets.srcDirs = ['src/main/assets']
        }
    }



将aidl 路径下的文件添加到java.srcDirs 下。
2. 编译生成IPerson.java 时,出现错误:'person' can be an out type, so you must declare it as in, out or inout.
   这是因为对于非基本类型的数据, AIDL 规定需要添加指向标志(in, out, inout)。
   in 表示输入型参数(service 可以获得client 传递的数据, 但是不能对client 端的数据进行修改)
   out表示输出型参数(service 不能获得client 传递的数据,但是可以对client 端的数据进行修改)
   inout表示输入输出型参数(service 既可以获得client 传递的数据,也可以对client 端的数据进行修改)
由于此处只是向service 传递数据, 在 void addPerson(in person p) 方法参数前加“in”就可以了。








                                    
展开阅读全文

没有更多推荐了,返回首页