新浦京81707con > 软件下载 > 新京葡娱乐场官网:IPC之AIDL看这一篇还不够

原标题:新京葡娱乐场官网:IPC之AIDL看这一篇还不够

浏览次数:198 时间:2020-03-13

总结

Android Studio Service AIDL 安详严整就到此处,今后应有能够采用AIDL达成想要的功用了,完结简答的AIDL很粗大略,主即便在自定义类型的时候,有多少个坑注意就好。

源码地址:

有何难点应接留言。

设若钟爱请关切本人,赞作者,来抚平作者虚荣的心

全科龙婷▼升职加薪

新京葡娱乐场官网 1image

3.2.1 Server端代码编写

public class AidlService extends Service {

    public static final String TAG = "MyService";

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate() executed");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand() executed");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy() executed");
    }

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

    /**
     * 本地service用法 ;
     */
    /***/
    private List<ServiceData> mData = new ArrayList<>();
    /**
     * 远程service需要使用AIDL来通讯,其实也是基于Binder,只是Google规定了写法
     */
    MyAIDLService.Stub remoteBinder = new MyAIDLService.Stub() {

        @Override
        public void addData(ServiceData data) throws RemoteException {
            mData.add(data);
        }

        @Override
        public List<ServiceData> getDataList() throws RemoteException {
            return mData;
        }
    };

}

Server的service必要在近日项目中的AndroidManifest.xml文件中扬言一下:

        <service android:name=".service.binder.BinderService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.danxx.binderService" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>

亟需提供intent-filter,这样有助于远程的Client端连接大家的劳务。

商城出品早前IM那块存在非常多题目,音信到达率低,加上左券上稍加难点,丢音信频繁,所以须求重构IM,AIDL不能够一挥而就上述难点。好呢!这AIDL能够缓和什么难题?什么是AIDL?

一、AIDL是什么?

例如你想深刻学习基于Binder通信的学识,请看自个儿前边的篇章:

Android IPC之代理格局
Android IPC之Binder机制剖析

AIDL(Android Interface Define LanguageState of Qatar是IPC进度间通信情势的一种.用于生成能够在Android设备上多少个进度之间开展进程间通讯(interprocess communication, IPC卡塔尔国的代码。
新京葡娱乐场官网,AIDL规定了三个进度之间怎么行使Binder来通讯,AIDL文件便是接口和数据注解。Android Studio利用大家写好的AIDL文件生成Java文件,真正在打包成apk文件时会把aidl文件生成的java代码打包进去,不会打包AIDL文件,那么很好驾驭,Android Studio利用大家写好的AIDL文件生成基于Binder进程通讯java代码。binder进度通讯是C/S和代办方式完毕的。ADIL文件生成的java文件中得以看出来。
一句话总括:

Android Studio利用大家写好的AIDL文件生成基于Binder进度通讯java代码。生成的java代码会被打包到apk文件,AIDL文件只是一个模具而已。

基于Binder的AIDL数据通讯流程图:

新京葡娱乐场官网 2

此处写图片描述

6.编制服务端完毕connect方法
package name.quanke.aidldemo;import android.os.IBinder;import android.os.RemoteException;import android.util.Log;/** * * Created by quanke on 16/7/23. */public class LibHandler extends IHandler.Stub{ @Override public void connect() throws RemoteException { Log.d("","connect; } @Override public IBinder asBinder() { return null; }}

上述达成了简短的接连几日,接下去大家落到实处传递自定义类型

AIDL暗许帮忙的档案的次序包蕴Java基本类型(int、long、boolean等),和(String、List、Map、CharSequence),要是要传递自定义的连串须要得以落成android.os.Parcelable接口。本身写了一个实体类public class Message implements Parcelable。

package name.quanke.aidldemo.model;import android.os.Parcel;import android.os.Parcelable;/** * * Created by quanke on 16/7/23. */public class Message implements Parcelable { private long id; private String content; public long getId() { return id; } public void setId { this.id = id; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } @Override public String toString() { return "Message{"   ", content='"   content   '''   '}'; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong; dest.writeString(this.content); } public Message() { } protected Message(Parcel in) { this.id = in.readLong(); this.content = in.readString(); } public static final Creator<Message> CREATOR = new Creator<Message>() { @Override public Message createFromParcel(Parcel source) { return new Message; } @Override public Message[] newArray { return new Message[size]; } };}

修改IHandler

// IHandler.aidlpackage name.quanke.aidldemo;// Declare any non-default types here with import statementsimport name.quanke.aidldemo.model.Message;interface IHandler { void connect(); void sendMessage(Message message);}

编写翻译项目,报错

/Users/quanke/Dev/android/src/AIDLDemo/app/src/main/aidl/name/quanke/aidldemo/IHandler.aidlError: couldn't find import for class name.quanke.aidldemo.model.MessageError:Execution failed for task ':app:compileDebugAidl'.> java.lang.RuntimeException: com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command '/Users/quanke/Dev/android/tools/android-sdks/build-tools/23.0.3/aidl'' finished with non-zero exit value 1Information:BUILD FAILED

因为自定义类型不唯有要定义实现android.os.Parcelable接口的类,还得为该完毕类定义三个aidl文件,如下:

新京葡娱乐场官网 3自定义类aidl文件

// IHandler.aidlpackage name.quanke.aidldemo.model;// Declare any non-default types here with import statementsimport name.quanke.aidldemo.model.Message;parcelable Message ;

切记 自定类型aidl文件名字、路线须要和自定义类名字、路线保持一致,

编写翻译项目,仍然报错

parameter 1: 'Message message' can be an out parameter, so you must declare it as in, out or inout.

AIDL不是 Java。它是真的很周围,但它不是 Java。

Java 参数未有动向的概念,AIDL 参数有方向,参数能够从顾客端传到服务端,再再次回到来。

如果sendMessage方法的message参数是纯粹的输入参数--那表示是从顾客端到服务器的多寡,你必要在AIDL申明:

void sendMessage(in Message message);

如果sendMessage方法的message参数是自始至终的出口-那意味它的数码是通过从服务器到客户端,使用:

void sendMessage(out Message message);

如果sendMessage方法的message参数是输入也是出口-顾客端的值在劳务只怕会改过,使用:

void sendMessage(inout Message message);

3.2编写AIDL文件

重要的aidl文件,会依照此aidl文件生成基于binder夸进程通讯的java代码:

// MyAIDLService.aidl
//生产的MyAIDLService.java类会使用这个包名
package com.server.service.aidl;
//导入数据javabean类,注意包名不要错
import com.server.service.data.ServiceData;
// Declare any non-default types here with import statements
interface MyAIDLService {
    /**
     * 除了基本数据类型,其他类型的参数都需要标上方向类型:in(输入), out(输出), inout(输入输出)
     */
    void addData(in ServiceData data);

    List<ServiceData> getDataList();
}

根本里面使用到的java数据类,这么些累必需贯彻Parcelable接口,首先看一下用aidl文件宣称一下以此java类:

// 实体类包名,要跟实际的ServiceData.java包名一致
package com.server.service.data;

//实现了Parcelable序列化接口的实体类
parcelable ServiceData;

实现Parcelable接口的java数据类:

package com.server.service.data;

import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;

/**
 * Created by dawish on 2017/8/22.
 */

public class ServiceData implements Parcelable {
    public String id;
    public String name;
    public String price;
    public String type;
    public ServiceData( ) {

    }
    /**
     * 读数据恢复
     * 系统自动添加,给createFromParcel里面用
     * @param in
     */
    protected ServiceData(Parcel in) {
        id = in.readString();
        name = in.readString();
        price = in.readString();
        type = in.readString();
    }
    public String getName() {
        return name;
    }

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

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    /**
     *  IDE自动生成
     *  读取接口,目的是要从Parcel中构造一个实现了Parcelable的类的实例处理。
     *  因为实现类在这里还是不可知的,所以需要用到模板的方式,继承类名通过模板
     *  参数传入。
     * 为了能够实现模板参数的传入,这里定义Creator嵌入接口,内含两个接口函数
     * 分别返回单个和多个继承类实例。
     */
    public static final Creator<ServiceData> CREATOR = new Creator<ServiceData>() {
        @Override
        public ServiceData createFromParcel(Parcel in) {
            return new ServiceData(in);
        }

        @Override
        public ServiceData[] newArray(int size) {
            Log.i("danxx", "newArray size--->" size);
            return new ServiceData[size];
        }
    };

    /**
     * 内容描述接口,基本不用管,IDE自动生成
     */
    @Override
    public int describeContents() {
        return 0;
    }
    /**
     * 写入接口函数,打包,IDE自动生成
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(id);
        dest.writeString(name);
        dest.writeString(price);
        dest.writeString(type);
    }
}

相关的分解早就在代码中了,小编那边就不说明了。
地方的四个aidl文件和二个java数据类是Server端和Client端共有的 ,上边写好的aidl文件和接收的java数据类都复制一份在Server端和Client端,这个是进程通讯的链接器。
接下去正是便携Server端的Service和Client的劳动连接和央求代码了:

1.创建AIDL文件夹

新京葡娱乐场官网 4创建AIDL文件夹

四、AIDL生成的Java代码剖判

代码中的多少个章程:
1、DESCRIPTION
Binderd的有一无二标记,日常用当下的类名表示。
2、asInterface(android.os.IBinder obj)
用于将服务端的Binder对象转变为顾客端要求的AIDL接口类型的指标,转变区分进度,客商端服务端坐落于同一进度,再次来到服务端的 //Stub对象自己;不然重返的是系统的包装后的Stub.proxy对象。
3、asBInder
返回Binder对象
4、onTransact
此方法运营在服务端中的Binder线程池中,当客户端发起跨进程恳求时,远程央浼会透过系统底层封装后交由此方法处理。
5、Proxy#add
此 方法运转在客商端,当客商端远程调用此情势时,它的在那之中落实是这样的:首先成立该办法所急需的输入型Parcel对象_data、输出型Parcel对象 _reple和重回值对象_result,然后将该措施的参数新闻写入_data中;接着调用transact方法来发RPC央浼,同一时间当前线程挂起;然 后服务端的onTransact方法会被调用,直到RPC进度重返后,当前线程继续履行,并从_reply中收取RPC进度再次回到的结果,写入 _result中。
AIDL文件生成java文件详细分解:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: F:\GitHubCode\AndReservoir\Server\src\main\aidl\com\server\service\aidl\MyAIDLService.aidl
 */
package com.server.service.aidl;
// Declare any non-default types here with import statements
//为了让大家看的更清楚 我把生成的binder代码 给拷贝到另外一个工程下面了,并且用ide 给他format
public interface MyAIDLService extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     * 并且这个接口里 有一个静态的抽象类Stub(注意这个名字是固定的 永远都是Stub 不会是其他)
     * 并且这个Stub是Binder的子类,并且实现了 MyAIDLService 这个接口
     * 这是Server服务端真是对象,具备真正的功能。
     */
    public static abstract class Stub extends android.os.Binder implements com.server.service.aidl.MyAIDLService {
        /**
         * 这个东西就是唯一的binder标示 可以看到就是IPersonManager的全路径名
         * SM是DNS Binder驱动是路由器,这个就是类似一个ip地址。
         */
        private static final java.lang.String DESCRIPTOR = "com.server.service.aidl.MyAIDLService";

        /**
         * Construct the stub at attach it to the interface.
         * 这个就是Stub的构造方法,供服务端调用,创建一个Binder类
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.server.service.aidl.MyAIDLService interface,
         * generating a proxy if needed.
         *这个方法 其实就做了一件事,如果是同一个进程,那么就返回Stub对象本身
         * 如果不是同一个进程,就返回Stub.Proxy这个代理对象了
         */
        public static com.server.service.aidl.MyAIDLService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            //如果是同1个进程,也就是说进程内通信的话 我们就返回括号内里的对象
            if (((iin != null) && (iin instanceof com.server.service.aidl.MyAIDLService))) {
                return ((com.server.service.aidl.MyAIDLService) iin);
            }
            //如果不是同一进程,是2个进程之间相互通信,那我们就得返回这个Stub.Proxy 看上去叫Stub 代理的对象了
            return new com.server.service.aidl.MyAIDLService.Stub.Proxy(obj);
        }

        //返回当前对象 服务端调用
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        //只有在多进程通信的时候 才会调用这个方法 ,同一个进程是不会调用的。

        //首先 我们要明白 这个方法 一般情况下 都是返回true的,也只有返回true的时候才有意义,如果返回false了 就代表这个方法执行失败,
        //所以我们通常是用这个方法来做权限认证的,其实也很好理解,既然是多进程通信,那么我们服务端的进程当然不希望谁都能过来调用
        //所以权限认证是必须的,关于权限认证的代码 以后我再讲 先略过。

        //除此之外 ,onTransact 这个方法 就是运行在Binder线程池中的,一般就是客户端发起请求,然后android底层代码把这个客户端发起的
        //请求 封装成3个参数 来调用这个onTransact方法,第一个参数code 就代表客户端想要调用服务端 方法的 标志位。
        //其实也很好理解 服务端可能有n个方法 每个方法 都有一个对应的int值来代表,这个code就是这个int值,用来标示客户端想调用的服务端的方法
        //data就是方法参数,reply就是方法返回值。都很好理解

        //其实隐藏了很重要的一点,这个方法既然是运行在binder线程池中的,所以在这个方法里面调用的服务器方法也是运行在Binder线程池中的,
        //所以我们要记得 如果你的服务端程序 有可能和多个客户端相联的话,你方法里使用的那些参数 必须要是支持异步的,否则的话
        //值就会错乱了!这点一定要记住!结论就是Binder方法 一定要是同步方法!!!!!!

        /**
         *@param code  唯一标识,客户端传递标识执行服务端代码
         *@param data  客户端传递过来的参数
         *@param reply 服务器返回回去的值
         *@param flags 是否有返回值 0:有 1:没有
         *@return
         *@throws RemoteException 异常
         */
        @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_addData: { //客服端请求区分1
                    data.enforceInterface(DESCRIPTOR);
                    com.server.service.data.ServiceData _arg0;
                    if ((0 != data.readInt())) {
                        //读取客户端传递过来的参数
                        _arg0 = com.server.service.data.ServiceData.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addData(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getDataList: { //客服端请求区分2
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.server.service.data.ServiceData> _result = this.getDataList();
                    reply.writeNoException();
                    //想客户端写入参数
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            //利用Binder驱动发送数据
            return super.onTransact(code, data, reply, flags);
        }
        //注意这里的Proxy 这个类名也是不变的,从前文我们知道 只有在多进程通信的情况下  才会返回这个代理的对象
        //这个代理并不是Binder的子类,只是实现了MyAIDLService接口的一个影子,客户端通过操作这个影子来实现对服务端的通信
        //这个代理端只是告诉客户端 服务端拥有什么样的功能可以提供。正真完整通信实现数据交换的还是服务端的Binder子类
        private static class Proxy implements com.server.service.aidl.MyAIDLService {
            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;
            }

            /**
             * 除了基本数据类型,其他类型的参数都需要标上方向类型:in(输入), out(输出), inout(输入输出)
             * 客户端调用
             */
            @Override
            public void addData(com.server.service.data.ServiceData data) 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 ((data != null)) {
                        _data.writeInt(1);
                        //写入请求参数
                        data.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    //想服务端发送请求
                    mRemote.transact(Stub.TRANSACTION_addData, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            /**
             * 客户端调用
             * @return
             * @throws android.os.RemoteException
             */
            @Override
            public java.util.List<com.server.service.data.ServiceData> getDataList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.server.service.data.ServiceData> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getDataList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.server.service.data.ServiceData.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
        //客服端请求区分1
        static final int TRANSACTION_addData = (android.os.IBinder.FIRST_CALL_TRANSACTION   0);
        //客服端请求区分2
        static final int TRANSACTION_getDataList = (android.os.IBinder.FIRST_CALL_TRANSACTION   1);
    }

    /**
     * 除了基本数据类型,其他类型的参数都需要标上方向类型:in(输入), out(输出), inout(输入输出)
     */
    public void addData(com.server.service.data.ServiceData data) throws android.os.RemoteException;

    public java.util.List<com.server.service.data.ServiceData> getDataList() throws android.os.RemoteException;
}
2.创建AIDL文件

新京葡娱乐场官网 5创建AIDL文件

3.1.2 aidl文件之中package 包名和import导包都要跟java实体类包名一致

不相似也是会报错的,导包不对直接不恐怕识别类

新京葡娱乐场官网 6

此处写图片描述

3.编写AIDL文件
// IHandler.aidlpackage name.quanke.aidldemo;// Declare any non-default types here with import statementsinterface IHandler { void connect();}

2.1 Android Binder支持传送数据类型

进度通讯便是换来数据,AIDL通讯传送帮衬的档期的顺序其实正是Binder援救的,AIDL并非扶持全体的数据类型,java中有过超级多据类型,java基本的数据类型,集合数据类型,还应该有大家顾客本身定义的数据类型。那么AIDL扶持什么列?

共 4 种:

  1. Java 的基本数据类型
  2. List 和 Map
    要素必需是 AIDL 支持的数据类型
    Server 端具体的类里则必需是 ArrayList 恐怕 HashMap
  3. 别的 AIDL 生成的接口
  4. 实现 Parcelable 的实体
  • 转发请评释出处,谢谢

二、AIDL通讯扶持的数量传送类型

本文由新浦京81707con发布于软件下载,转载请注明出处:新京葡娱乐场官网:IPC之AIDL看这一篇还不够

关键词: 新浦京81707con Android 详解 Studio

上一篇:百度地图点击自定义标记不显示泡泡但能做其他

下一篇:没有了