来了,用写轮眼去看AIDL

转载请标明出处:

http://blog.csdn.net/qq_27495349/article/details/53338273
本文出自:【CF凌晨的博客】

引言

hello,大家好,一周马上又要过去,不知道大家有没有想我哈!话说南京下雪了,不知道大家有没有看到雪哈,今年的雪来的有点早哈!!!

AIDL介绍

AIDL是Android Interface definition language缩小,意思是android接口定义语言,用于进程间通讯。

AIDL创建

 那么我们怎么编写AIDL呢?首先我们先打开AS,然后创建一个项目,创建好之后,我们直接创建AIDL文件,如图

这里写图片描述
这里写图片描述
直接finish就好了。创建之后,我们来看看生成的文件。
这里写图片描述
可以看到一个aidl的文件我们就创建好了。

AIDL数据传递类型

这里写图片描述
从官方的介绍来看,大体意思如下:
1.所有java的基本数据类型(short不支持)
2.String
3.CharSequence
4.List(list里面存放的必须是允许的数据类型)
5.map(同上)
6.Parcelable
那我们可以传自定义的数据么?答案是可以的。
首先我们在aidl包下面创建一个Book类,如下

package com.lingchen.testaidl;

/**
 * Author    lingchen
 * Email     838878458@qq.com
 * Time      2016/11/25
 * Function  书
 */

public class Book {
    //名字
    private String name;
    //价格
    private float price;

}
我们实现Parcelable这个接口。这里有个插件,可以快速生成    Parcelable,名字是(Android Parcelable Code Generator)。
package com.lingchen.testaidl;

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

/**
 * Author    lingchen
 * Email     838878458@qq.com
 * Time      2016/11/25
 * Function  书
 */

public class Book implements Parcelable {
    //名字
    private String name;
    //价格
    private float price;

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.name);
        dest.writeFloat(this.price);
    }

    public Book() {
    }

    protected Book(Parcel in) {
        this.name = in.readString();
        this.price = in.readFloat();
    }

    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };
}
    这就是我们最终的代码。那我们把IMyAidlInterface删掉,重新创建一个IBookAidlInterface.aidl文件,内容如下:
// IBookAidlInterface.aidl
package com.lingchen.testaidl;

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

interface IBookAidlInterface {

    List<Book> getBook(Book book);
}
        到这里我们编译下,结果如下

这里写图片描述
竟然没有找到,是不是我们没有写import?我们加上

import com.lingchen.testaidl.Book;
运行还是错误。其实aidl是根本不认识的,那怎么让他认识呢?我们创建一个Book.aidl文件,如下:
// Book.aidl
package com.lingchen.testaidl;

//申明Book
parcelable Book ;

很简单的几句话,我们再来运行:
这里写图片描述
这里我们要知道一个知识点,因为aidl底层传递数据,要分 打包与解包,这样是很消耗资源的,那如果我们指定他是输入,那么就会让效率提高,所以我们加上in:

List<Book> getBook(in Book book);

再次编译,好了,终于编译通过了。

实现AIDL,并且调用

    我们的aidl文件已经写好了,那我们现在写个service类来实现我们的aidl。接代码
package com.lingchen.testaidl;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;

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

/**
 * Author    lingchen
 * Email     838878458@qq.com
 * Time      2016/11/25
 * Function  远程服务
 */

public class MyRemoteService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return bookBinder;
    }

    private List<Book> bookList = new ArrayList<>();
    private IBinder bookBinder = new IBookAidlInterface.Stub() {

        @Override
        public List<Book> getBook(Book book) throws RemoteException {
            bookList.add(book);
            return bookList;
        }
    };
}

别忘了在manifest里面添加

         <!--添加服务 指定在remote进程-->
        <service android:name=".MyRemoteService"
            android:process=":remote"
            />

好了,我们的远程服务已经搞定了,那我们怎么开启呢?看代码

package com.lingchen.testaidl;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.widget.TextView;

import java.util.List;

public class MainActivity extends Activity {
    private TextView bookSizeTv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bookSizeTv = (TextView) findViewById(R.id.book_size);
        bindRemoteService();
    }

    private void bindRemoteService() {
        Intent intent = new Intent(this, MyRemoteService.class);
        //如果只知道他的包跟类名
        //intent.setComponent(new ComponentName("com.lingchen.testaidl", "com.lingchen.testaidl.MyRemoteService"));
        bindService(intent, serviceConnection, BIND_AUTO_CREATE);
    }


    //远程服务
    private IBookAidlInterface iBookAidl;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //拿到远程服务
            iBookAidl = IBookAidlInterface.Stub.asInterface(service);
            bookSizeTv.setText("连接成功");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            bookSizeTv.setText("连接异常");
        }
    };

    /**
     * 添加书
     */
    public void addBook(View view) {
        if (iBookAidl != null) {
            try {
                List<Book> books = iBookAidl.getBook(new Book("凌晨", 998));
                bookSizeTv.setText(String.valueOf(books.size()));
            } catch (RemoteException e) {
                bookSizeTv.setText("连接异常");
                e.printStackTrace();
            }
        }
    }
}
    好了xml我就不发了 很简单,那么。到现在为止,我们已经完成了跨进程通信。

AIDL原理解析

    我们前面也说过,通过aidl自动生成的java文件是我们的重点,我们分析的原理也要通过他来讲解,首先我们来看看它:
/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: F:\\AndroidStudioProjects\\TestAidl\\app\\src\\main\\aidl\\com\\lingchen\\testaidl\\IBookAidlInterface.aidl
 */
package com.lingchen.testaidl;

public interface IBookAidlInterface extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.lingchen.testaidl.IBookAidlInterface {
        private static final java.lang.String DESCRIPTOR = "com.lingchen.testaidl.IBookAidlInterface";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.lingchen.testaidl.IBookAidlInterface interface,
         * generating a proxy if needed.
         */
        public static com.lingchen.testaidl.IBookAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.lingchen.testaidl.IBookAidlInterface))) {
                return ((com.lingchen.testaidl.IBookAidlInterface) iin);
            }
            return new com.lingchen.testaidl.IBookAidlInterface.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @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_getBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.lingchen.testaidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.lingchen.testaidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    java.util.List<com.lingchen.testaidl.Book> _result = this.getBook(_arg0);
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.lingchen.testaidl.IBookAidlInterface {
            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;
            }

            @Override
            public java.util.List<com.lingchen.testaidl.Book> getBook(com.lingchen.testaidl.Book book) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.lingchen.testaidl.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_getBook, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.lingchen.testaidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_getBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public java.util.List<com.lingchen.testaidl.Book> getBook(com.lingchen.testaidl.Book book) throws android.os.RemoteException;
}

很多人看到这个都会产生恐惧,既然我们要想变强,那就要去看。好了,来看看下面的代码

这里写图片描述

这里我们看到了,其实大体的就是一个接口,一个内部类,一个方法,这个方法就是我们代码中定义的方法,这下我们看得清晰了吧。
这里有一个Stub静态内部类,这个名字大家一看到,就感觉很熟悉,没错

//拿到远程服务
iBookAidl = IBookAidlInterface.Stub.asInterface(service);

那我们可以肯定,这个内部类里面一定有一个方法是asInterface();我们先不着急看,先来看看我通过注释来翻译之后的代码
这里写图片描述
好了,我们发现,里面包含了一个描述符(我们可以猜想,这个可能就是标识这个服务的)果不其然,下面的构造方法就看到了一个方法attachInterface方法,那么我们点进去看看是干什么的

/**
     * Convenience method for associating a specific interface with the Binder.
     * After calling, queryLocalInterface() will be implemented for you
     * to return the given owner IInterface when the corresponding
     * descriptor is requested.
     */
    public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }

意思大体就是讲一个接口跟descriptor粘合,可以通过方法queryLocalInterface来获取到。
我们再往下面看

/**
         * 铸造一个内部对象com.lingchen.testaidl。如果需要IBookAidlInterface接口,生成一个代理.
         */
        public static com.lingchen.testaidl.IBookAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            //通过queryLocalInterface查找IBookAidlInterface
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.lingchen.testaidl.IBookAidlInterface))) {
                return ((com.lingchen.testaidl.IBookAidlInterface) iin);
            }
            //如果没找到 直接返回Proxy
            return new com.lingchen.testaidl.IBookAidlInterface.Stub.Proxy(obj);
        }
    这个方法大家肯定晓得,上面已经提到了,里面也提到了我们所说的queryLocalInterface方法。
    这里我们看到了如果没有找到,那么将直接获取Proxy(代理)。
    这个东西是什么呢?,我们先不管,我们继续往下面看asBinder这个方法我们就不说了,就是返回stub。
    下面是onTransact方法,可以从字面上看到 是交易的意思,我想这可能就是最主要的方法,毕竟我们跨进程交互本来就是一场交易。
    下面是Proxy类,也是一个静态内部类,这里我们可以看到一个getBook方法,直接上代码
 @Override
            public java.util.List<com.lingchen.testaidl.Book> getBook(com.lingchen.testaidl.Book book) throws android.os.RemoteException {
                //从池中检索一个新的包裹对象
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.lingchen.testaidl.Book> _result;
                try {
                    //写入token
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        //把数据写入包裹里
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    //底层开始发送,stub就会接收到
                    mRemote.transact(Stub.TRANSACTION_getBook, _data, _reply, 0);
                    _reply.readException();
                    //获取数据
                    _result = _reply.createTypedArrayList(com.lingchen.testaidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

这里有些同学都应该知道了,我们最终调用iBookAidl.getBook(new Book(“凌晨”, 998))的时候,其实我们就是在调用这个方法,原理看下面图

 //拿到远程服务的代理  return new com.lingchen.testaidl.IBookAidlInterface.Stub.Proxy(obj);
 iBookAidl = IBookAidlInterface.Stub.asInterface(service);
 //通过代理调用getBook方法
 List<Book> books = iBookAidl.getBook(new Book("凌晨", 998));
    是不是一下子清晰了?那么我们只要研究这个Proxy中getBook方法我们就知道了。
    上面我们已经放出了Proxy中的getBook方法,我这里在放一次
                //从池中检索一个新的包裹对象用来存放book
                android.os.Parcel _data = android.os.Parcel.obtain();
                //从池中检索一个新的包裹对象用来接受交易完成之后的数据
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.lingchen.testaidl.Book> _result;
                try {
                    //写入token
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        //把数据写入包裹里
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    //底层开始发送,stub就会接收到   0-》单向的RPC( Remote Procedure Call Protocol)——远程过程调用协议
                    mRemote.transact(Stub.TRANSACTION_getBook, _data, _reply, 0);
                    _reply.readException();
                    //获取数据
                    _result = _reply.createTypedArrayList(com.lingchen.testaidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
    我们先来看这句代码
         //把数据写入包裹里
         book.writeToParcel(_data, 0);
    这里的代码,大家应该可能会想到了,我们当初实现了Parcelable不是让实现了这个方法么?
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.name);
        dest.writeFloat(this.price);
    }
    可以看到,就是往包裹中写入数据,也就是_data包裹。
    继续看下面的方法mRemote.transact这个方法其实就通知交易可以开始了,就是回调我们到我们stub里面的transact方法,可以看到,方法里面传进了
    Stub.TRANSACTION_getBook(交易代号)
    android.os.Parcel _data 数据包裹
    android.os.Parcel _reply 接收数据的包裹
    0 单向的RPC( Remote Procedure Call Protocol)——远程过程调用协议
    这里我们就来看一下Stub中的onTransact方法
//交易
        @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_getBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.lingchen.testaidl.Book _arg0;
                    if ((0 != data.readInt())) {
                    // book中  return new Book(source);
                        _arg0 = com.lingchen.testaidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    java.util.List<com.lingchen.testaidl.Book> _result = this.getBook(_arg0);
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

看到这个方法,大家看代码其实已经晓得了,其实就是回调了我们book中CREATOR

public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };
好了,aidl的源码分析到这里结束了。

总结

    aidl是我们android 进程间通讯的语言。我们通过IBookAidlInterface.Stub.asInterface(service)拿到的是他的代理类。
    我们也知道了aidl的传输的数据类型以及自定义的数据类型,并且我们也知道了为啥要实现Parcelable方法。
    好了,这周即将结束,也希望大家能周末愉快,谢谢大家!我们下次再见!!!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值