AIDL实现进程间通信

一、什么是AIDL

AIDL(Android Interface Definition Language),Android接口定义语言,利用它定义客户端与服务端使用进程间通信进行相互通信时都认可的接口。在Android上,一个进程通常无法访问另一个进程的内存。尽管如此,进程需要需要将其对象分解成操作系统能够识别的原语,并将对象编组成括约边界的对象,编写这一编组操作的代码是一项繁琐的工作,因此Android使用AIDL来处理。这是Google官方文档对AIDL的解释,但是看完后还不是很清楚,简单解释一下就是,通过AIDL能够实现多个进程(应用程序)之间的通信。下面我们主要通过一个例子来看一下。

二、AIDL Demo

具体步骤

1. 创建一个AIDL文件(Rebuild一下会生成一个接口)。

2. 创建一个Service,实现该AIDL生成的接口,并在onBind方法中返回一个Binder类型的对象Stub,这个Stub是AIDL生成的接口的内部类。

3. 在客户端绑定此Service,创建ServiceConnection对象,重写onServiceConnected和onServiceDisconnected,通过传入的IBinder对象实现进程间通信。

接下来我们一步一步通过代码来理解AIDL

1.      首先新建一个项目叫做AIDL,接着新建一个Module叫remote_service,用于服务端的项目部署。完成后的项目结构是这样的


2.      接着在remote_service的main文件夹下新建一个名为aidl文件夹,在这个文件夹下建一个包名,客户端的AIDL文件名必须和服务端保持一致。接着在包名下新建一个AIDL文件叫做IRemoteService。新建玩的AIDL文件是这样的


手动添加了两个方法,完成后Rebuild一下项目,就会在相同的文件夹下生成一个IRemoteService的Java文件,目录是这样的(在Package下看)


这个Java文件就是我们在服务端要实现的接口,其中有一个静态内部类Stub,这个类继承了Binder并且实现了我们创建的IRemoteService接口,所以我们可以通过Stub完成远程服务的建立。

3.      新建一个Service,实例化一个Stub对象,将这个对象作为Service的代理,在onBind方法中返回这个对象。

package com.zmt.remote_service;

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

import com.zmt.aidl.IRemoteService;
import com.zmt.aidl.Person;

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

/**
 * Created by zmt on 2017/3/31.
 */

public class RemoteService extends Service {

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

    @Override
    public IBinder onBind(Intent intent) {
        for (int i = 0; i < MainActivity.name.length; i++) {
            personList.add(new Person(MainActivity.name[i], MainActivity.age[i]));
        }
        return mBinder;
    }

    private final IRemoteService.Stub mBinder = 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("basicType int: " + anInt + " long: " + aLong + " boolean: " + aBoolean
                    + " float: " + aFloat + " double: " + aDouble + " string: " + 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 MainActivity.name[id];
        }

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

创建完Service在manifest中注册时需要添加有action属性的intent-filter,并且在绑定Service时要隐式绑定,因为从Android5.0开始只能通过隐士绑定的方式启动Service,并且必须设置action和package。

4.      客户端创建服务连接,绑定服务。客户端同样需要建立AIDL文件,直接从服务端复制过来就行。放在同样位置的目录下。

创建服务连接

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mRemoteService = IRemoteService.Stub.asInterface(service);
            try {
                int pid = mRemoteService.getPid();
                int currentPid = android.os.Process.myPid();
                System.out.println("currentPid: " + currentPid + " remotePid: " + pid);
                mRemoteService.basicTypes(1,2,true, 1.2f, 2.4, "aidl");
            } catch (RemoteException e) {
                Log.e("error", e.toString());
            }
            System.out.println("bind success! " + mRemoteService.toString());
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            System.out.println("disconnected " + mRemoteService.toString());
        }
    };
绑定服务

        /**
         * 绑定远程服务
         */
        Intent intent = new Intent();
        intent.setAction("remote_service");
        intent.setPackage("com.zmt.remote_service");
        isConnected = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
Activity的代码

package com.zmt.aidl;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
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.view.View;
import android.widget.EditText;
import android.widget.TextView;

import org.w3c.dom.Text;

public class MainActivity extends Activity {

    private IRemoteService mRemoteService;
    private EditText editText;
    private TextView textView;
    private boolean isConnected = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editText = (EditText) findViewById(R.id.editText);
        textView = (TextView) findViewById(R.id.result);
        /**
         * 绑定远程服务
         */
        Intent intent = new Intent();
        intent.setAction("remote_service");
        intent.setPackage("com.zmt.remote_service");
        isConnected = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mRemoteService = IRemoteService.Stub.asInterface(service);
            try {
                int pid = mRemoteService.getPid();
                int currentPid = android.os.Process.myPid();
                System.out.println("currentPid: " + currentPid + " remotePid: " + pid);
                mRemoteService.basicTypes(1,2,true, 1.2f, 2.4, "aidl");
            } catch (RemoteException e) {
                Log.e("error", e.toString());
            }
            System.out.println("bind success! " + mRemoteService.toString());
        }

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

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

    public void search(View view) {
        if(isConnected){
            String text = editText.getText().toString();
            if(!text.equals("")){
                try {
                    int pid = Integer.valueOf(text);
                    String name = mRemoteService.getName(pid);
                    //Person person = mRemoteService.getPerson(pid);
                    if(person != null){
                        textView.setText("基础类型: " + name + "\n" + "对象类型: " + person.getName() + " " + person.getAge());
                    }
                } catch (Exception e) {
                    Log.e("error", e.toString());
                }
            }
        } else {
            Log.e("failed", "disconnected");
        }
    }
}
运行时的日志是这样的

客户端



服务端


可以看到两个进程之间实现了通信,AIDL不仅能够实现基本类型的通信,还能够实现自定义对象的通信,这个对象必须实现Parcelable,重写writeToParcel方法和readFromParcel方法,新建一个实现了Creator接口的对象来实现反序列化,并且新建一个该对象的AIDL文件,其它方面与基本类型的传递类似

Person对象类

package com.zmt.aidl;

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

/**
 * Created by zmt on 2017/4/1.
 */

public class Person implements Parcelable {

    private String name;
    private int age;

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

    private Person(Parcel in) {
        readFromParcel(in);
    }

    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;
    }

    private void readFromParcel(Parcel in){
        name = in.readString();
        age = in.readInt();
    }

    /**
     * 反序列化
     */
    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in.readString(), in.readInt());
        }

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

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

    /**
     * 序列化
     * @param dest
     * @param flags
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
    }
}


Person的AIDL文件

// Person.aidl
package com.zmt.aidl;

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

parcelable Person;

需要注意的是在IRemoteService.aidl接口中添加对象方法时必须导入该对象的完整包名,例如这样

// IRemoteService.aidl
package com.zmt.aidl;

// Declare any non-default types here with import statements
import com.zmt.aidl.Person;

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);

    int getPid();

    String getName(int id);

    Person getPerson(int id);
}
同时还要在app和module remote_service中的build文件中添加如下代码,不然会导致AIDL自定义类型导入失败

    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的使用基本就是这样,可以发现AIDL的功能是非常强大的,能够实现进程间互相通信,并且传递的数据类型也十分广泛,支持绝大多数类型,对象类型在使用的时候需要做一些处理。总之在进程间通信时AIDL是很合适的。

Demo地址:https://github.com/xiyouZmt/AIDL




  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值