Android AIDL的简单应用

概要

AIDL(Android Interface Definition Language),从字面解释来看就是Android接口协议语言。如果是接口协议语言。那么就跟接口脱离不了关系。通过它我们可以定义进程间的通信接口。

什么情况下使用AIDL

你能看到这说明你已经知道AIDL是为了跨进成通信使用的。但是已经有了广播、ContentProvider等都可以进行跨进程通信的方式后还要有AIDL呢?官方文档中有这么一段话:

Note: Using AIDL is necessary only if you allow clients from different applications to access your
 service for IPC and want to handle multithreading in your service. If you do not need to perform 
your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.

也就是说“只有当你允许来自不同的客户端访问你的服务并且需要处理多线程问题时你才必须使用AIDL“其他情况下你都可以选择其他方法,如使用广播、ContentProvider等都可以。

AIDL的使用

AIDL支持的数据类型

  1. 基本数据类型(int、long、char、boolean、double等)

  2. String和CharSequence

  3. List:只支持ArrayList,里面的每个元素都必须能被AIDL支持

  4. Map:只支持HashMap,里面的每个元素都必须被AIDL支持,包括key和value

  5. Parcelable:所有实现了Parcelable接口的对象

  6. AIDL:所有的AIDL接口本身也可以在AIDL文件中使用

注:自定义的Parcelable对象和AIDL对象必须要显式import进来,尽管在同一个包下也必须显示的import进来

为Model实现序列化Parcelable

只有实现了序列化Parcelable接口的对象,才可以通过AIDL进行跨进成通信。关于序列化这块就不过多赘述了,不是很清楚的可以参考一下这篇文章android开发之Parcelable使用详解

/**
 * Created by Administrator on 2017/3/13.
 * description :
 */

public class Message implements Parcelable {
    private String name ;
    private String msg;

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "\n"+name+ "-说 : "+msg+"\n";
    }

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

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

    public Message() {
    }

    protected Message(Parcel in) {
        this.msg = in.readString();
        this.name = in.readString();
    }

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

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

定义AIDL文件

首先定义Message.aidl

这里说一下,当你新建Message.aidl的时候可能会出现这个错误Interface Name must be unique。对于这个错误,我们可以先新建一个MessageManager.aidl。然后在新建文件Message.aidl。如下

首先通过新建AIDL建立一个MessageManager.aidl

这里写图片描述

然后在通过新建文件建立一个Message.aidl就避免刚才的错误

这里写图片描述

编写AIDL文件

首先编写Message.aidl

这个文件主要就是声明那个对象可以通过AIDL去进行通信

package com.tz.server.aidl;
//注意parcelable前面的p一定要为小写p
parcelable Message;

其次编写MessageManager.aidl

这个文件主要就是进行接口的声明

package com.tz.server.aidl;
//这里一定要import进来,尽管在一个包下也必须import
import com.tz.server.aidl.Message;

interface MessageManager {
     //一下为声明的接口
     List<Message> getMessage();
     boolean addMessage(in Message message);
}

查看生成的MessageManager.java

当两个aidl文件编写完成后,将项目重新Rebuild一下就会在build->generated->source->aidl->debug目录下就可以找到系统自动生成的一个MessageManager.java文件

package com.tz.server.aidl;

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

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

        /**
         * Cast an IBinder object into an com.tz.server.aidl.MessageManager interface,
         * generating a proxy if needed.
         */
        public static com.tz.server.aidl.MessageManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.tz.server.aidl.MessageManager))) {
                return ((com.tz.server.aidl.MessageManager) iin);
            }
            return new com.tz.server.aidl.MessageManager.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_getMessage: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.tz.server.aidl.Message> _result = this.getMessage();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addMessage: {
                    data.enforceInterface(DESCRIPTOR);
                    com.tz.server.aidl.Message _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.tz.server.aidl.Message.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    boolean _result = this.addMessage(_arg0);
                    reply.writeNoException();
                    reply.writeInt(((_result) ? (1) : (0)));
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.tz.server.aidl.MessageManager {
            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.tz.server.aidl.Message> getMessage() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.tz.server.aidl.Message> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getMessage, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.tz.server.aidl.Message.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public boolean addMessage(com.tz.server.aidl.Message message) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                boolean _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((message != null)) {
                        _data.writeInt(1);
                        message.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addMessage, _data, _reply, 0);
                    _reply.readException();
                    _result = (0 != _reply.readInt());
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_getMessage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addMessage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public java.util.List<com.tz.server.aidl.Message> getMessage() throws android.os.RemoteException;

    public boolean addMessage(com.tz.server.aidl.Message message) throws android.os.RemoteException;
}

编写MessageService文件

在这里主要实现了MessageManager中的一个抽象类Stub。主要实现了getMessage()方法和addMessage()方法

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

import com.tz.server.aidl.Message;
import com.tz.server.aidl.MessageManager;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by Administrator on 2017/3/13.
 * description :
 */

public class MessageService extends Service {
    private CopyOnWriteArrayList<Message> msgList = new CopyOnWriteArrayList<Message>();

    public MessageService() {

    }

    Binder binder = new MessageManager.Stub() {

        @Override
        public List<Message> getMessage() throws RemoteException {
            return msgList;
        }

        @Override
        public boolean addMessage(Message message) throws RemoteException {
            return msgList.add(message);
        }
    };

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

    @Override
    public void onCreate() {
        super.onCreate();
        Message msg = new Message();
        msg.setName("Service");
        msg.setMsg("我是服务端");
        msgList.add(msg);
    }
}

Service中使用的CopyOnWriteArrayList是CopyOnWrite容器即写时复制的容器,通俗的理解是当往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

在MainActivity中bindService

如果是在5.0以上的Android系统中使用BindService时,一定要给启动Service的Intent使用setPackage方法才可以成功绑定服务

public class MainActivity extends AppCompatActivity {
    //由AIDL文件生成的Java类
    private MessageManager mMessageManager = null;

    //标志当前与服务端连接状况的布尔值,false为未连接,true为连接中
    private boolean mBound = false;

    //包含Book对象的list
    private List<Message> mMessages;

    TextView tv;

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

    private int count = 1;
    /**
     * 按钮的点击事件,点击之后调用服务端的addBookIn方法
     *
     * @param view
     */
    public void addMessage(View view) {
        //如果与服务端的连接处于未连接状态,则尝试连接
        if (!mBound) {
            attemptToBindService();
            Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show();
            return;
        }
        if (mMessageManager == null)
            return;

        Message book = new Message();
        book.setName("服务端 : ");
        book.setMsg("消息"+count);
        try {
            mMessageManager.addMessage(book);
            tv.setText(mMessages.toString());
            Log.e("hypertian", getLocalClassName() + " : "+ book.toString());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    /**
     * 尝试与服务端建立连接
     */
    private void attemptToBindService() {
        Intent intent = new Intent();
        intent.setAction("com.tz.server.service.MessageService");
        intent.setPackage("com.tz.server");
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (!mBound) {
            attemptToBindService();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mBound) {
            unbindService(mServiceConnection);
            mBound = false;
        }
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e("hypertian", getLocalClassName() + " : service connected");
            mMessageManager = MessageManager.Stub.asInterface(service);
            mBound = true;

            if (mMessageManager != null) {
                try {
                    mMessages = mMessageManager.getMessage();
                    tv.setText(mMessages.toString());
                    Log.e("hypertian", getLocalClassName() + " : "+ mMessages.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e("hypertian", getLocalClassName() + " : service disconnected");
            mBound = false;
        }
    };
}

实现效果

这里写图片描述

源码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值