您的位置:首页 > 大数据 > 人工智能

AIDL学习笔记

2016-03-01 17:08 555 查看
Android Interface Definition Language
AIDL(安卓接口定义语言):它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口IPC(Internet
Process Connection)内部进程通信。
英文文档在(能够翻墙的话):http://developer.android.com/develop/index.html 刷了半天终于打开了~~~
本地文档(如果你有下的话):android-sdk/docs/guide/components/aidl.html
官方使用说明:
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 concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger.
Regardless, be sure that you understand Bound Services before implementing an AIDL.

也就是说不要动不动就用AIDL,在有需求的情况下,比如跨进程使用(IPC)考虑使用。
-------------------------------------------------------------------好啦,闲言碎语不用讲,我们开始吧-------------------------------------------------------------
官方是这么描述怎么使用的:
To create a bounded service using AIDL, follow these steps:

1.Create the .aidl file

This file defines the programming interface with method signatures.

2.Implement the interface

The Android SDK tools generate an interface in the Java programming language, based on your .aidl file. This interface has an 

inner abstract class named Stub that extends Binder and implements methods from your AIDL interface. You must extend the Stub 

class and implement the methods.

3.Expose the interface to clients

Implement a Service and override onBind() to return your implementation of the Stub class.

Caution: Any changes that you make to your AIDL interface after your first release must remain backward compatible in order to

 avoid breaking other applications that use your service. That is, because your .aidl file must be copied to other applications

  in order for them to access your service's interface, you must maintain support for the original interface.
那好我们就一步一步来:>>>>>>>>>>>>

1.AIDL文件的创建

  命令行方式:

我们在sdk中"build-tools/19.0.0(你的系统工具包)/aidl.exe"会有一个aidl.exe文件,它可以将“.aidl”文件编译成.java文件,以下两个工具的编译原理也是如此,在建立文件的同时其实和创建类是一样的。保证文件名和接口名一致。(PS:使用之前请将aidl.exe在环境变量里配置,或者进到根目录使用)。
比如说我创建了一个“IFristAidl.aidl”文件,其代码如下:
package com.lzy.aidl; //包名

interface IFristAidl { // AIDL 接口名
void fun(int a, int b); // 定义接口规则
}

在批处理根目录中可以通过命令行:“aidl IFristAidl.aidl”创建出对应的java文件,这里面要注意“IFristAidl.aidl”文件一定要在“com.lzy.aidl”与package
name一致的文件夹下。



然后在配置好环境(和配置JDK一样)的状态下,在当前目录下打开命令提示框,输入"aidl IFristAidl.aidl"命令,就会出现如下图:



我们打开生成的java文件
/*
* This file is auto-generated.  DO NOT MODIFY.
* Original file: IFristAidl.aidl
*/
package com.lzy.aidl;
//包名

public interface IFristAidl extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.lzy.aidl.IFristAidl
{
private static final java.lang.String DESCRIPTOR = "com.lzy.aidl.IFristAidl";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.lzy.aidl.IFristAidl interface,
* generating a proxy if needed.
*/
public static com.lzy.aidl.IFristAidl asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.lzy.aidl.IFristAidl))) {
return ((com.lzy.aidl.IFristAidl)iin);
}
return new com.lzy.aidl.IFristAidl.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_fun:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
this.fun(_arg0, _arg1);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.lzy.aidl.IFristAidl
{
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;
}
// AIDL 接口名

@Override public void fun(int a, int b) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_fun, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_fun = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
// AIDL 接口名

public void fun(int a, int b) throws android.os.RemoteException;
}

此源码我在接下来会逐一分析。

  Eclipse(ADT)创建:

Eclipse由于有自动编译功能,生成aidl文件更方便,使用起来也很方便。

使用方式:
① 在src/"you will create 'aidl'file",创建“aidl”文件,EC会自动编译,并在“gen”目录下生成对应的java文件,这个类似于Android里面“R”文件创建。



② 按照规则要求编写“aidl”文件:



③ 最终发现和上一方法使用命令行形式的创建是一致的:



  Android Studios 创建:

但是AS没有自动编译功能,所以写好aidl使用之前先编译一下,就能索引到了。优点是可以有提供生成aidl文件的向导,更容易上手,我们在接下来的demo中使用AS讲解。
 ① 我们打开AS创建一个空的项目,按照如下引导创建AIDL文件



我们会发现AS新建的aidl文件会自动生成一个接口,里面包含了AIDL所识别的常用的数据类型



② 我们可以按照上面两种实现方式修改该aidl文件
下面介绍一下aidl支持的数据类型------------------------------------------------

支持的数据类型:
*      ① 基本数据类型: int, long, boolean, float, double
*          (之所以没有short类型,由于其后台实现需要将int大数据压缩为小数据,
*          等解析展现的时候又要将小数据扩展为大数据,这个过程是比较耗内存的)
*      ② String, CharSequence;
*      另外还有Map, List(PS:这些类不需要 import,是内嵌的。其次注意
*      在使用 List 和 Map 此二者容器类时,需注意其元素必须得是 AIDL 支持的数据类型,
*      List 可支持泛型,但是 Map 不支持,同时另外一端负责接收的具体的类里则必须是
*      ArrayList 和 HashMap。)
*      ③ 实现Parcelable接口的对象
*      要传递自定义类型,首先要让自定义类型支持parcelable协议,实现步骤如下:
*       1>自定义类型必须实现Parcelable接口,并且实现Parcelable接口的
*        public void writeToParcel(Parcel dest, int flags)方法 。
*
*       2>自定义类型中必须含有一个名称为CREATOR的静态成员,该成员对象要求实现
*        Parcelable.Creator接口及其方法。
*
*       3> 创建一个aidl文件声明你的自定义类型。
*        Parcelable接口的作用:实现了Parcelable接口的实例可以将自身的状态信息
*       (状态信息通常指的是各成员变量的值)写入Parcel,也可以从Parcel中恢复其状态。
*        Parcel用来完成数据的序列化传递。
*      ④ 其他AIDL生成的接口。


比如说我们也写了一个add接口如下:



③ 我们像使用EC似的使用该接口时发现没有索引,那是因为AS没有自动编译功能,我们需要一下操作,就会发现和上两种方式得到的结果一模一样:



至此,三种创建方式就介绍完了,下面是如何使用,我们就以一个简单的例子来说明。AIDL相当于一个服务,它实现了一个处理好的功能,为了不重复实现该功能,另外一个项目也想使用该功能的话就只需要实现这个AIDL,此时这个项目就相当于客户端Client,而具体实现AIDL的就相当于服务端了Server。

2.AIDL文件的使用

我们根据英文文档来看:

Create the .aidl file







预编译一下,IAddAidl.java文件就会出现


Implement the interface

创建一个java文件用于作为server实现该上述接口:



代码如下:
package com.lzy.aidlserver;

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

/**
* Created by luzhenyu on 2016/3/2.
*/
public class AddServer extends Service{
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mIBinder;
}

/**
* 实现AIDL中的接口
* */
private final IBinder mIBinder = new IAddAidl.Stub() {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
};
}
那么,该AddServer就相当于一个Server,在服务端的mainfest.xml中记得注册一下这个AddServer,按照Android里面使用server的方式使用它即可。

至此,服务端的实现就到此为止了。

Expose the interface to clients

同理,我们再建一个Model,叫做AidlClient,我们先把server的aidl文件拷贝到client(拷贝整个文件夹能够保证包名和aidl的包名一致然后只留下aidl文件就行了),一定要保证里面的内容一样!!!



然后Clien项目中文件夹就变成这样的了:



接下来  我么预编译一下先,然后我们就在MainActivity中使用它。
package com.hy.aidlclient;

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.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.lzy.aidlserver.IAddAidl;

public class MainActivity extends AppCompatActivity {

private EditText numEditText1, numEditText2;
private Button calBtn;
private TextView resShowTextView;

private IAddAidl mIAddAidl;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R*layout.activity_main);

numEditText1 = (EditText) findViewById(R.id.num_1);
numEditText2 = (EditText) findViewById(R.id.num_2);

calBtn = (Button) findViewById(R.id.calculate_Btn);
resShowTextView = (TextView) findViewById(R.id.res_show);

bindServer();

calBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mIAddAidl != null) {
if (TextUtils.isEmpty(numEditText1.getText().toString())
||TextUtils.isEmpty(numEditText2.getText().toString())){
Toast.makeText(MainActivity.this, "输入为空", Toast.LENGTH_LONG).show();
numEditText1.setText("");
numEditText2.setText("");
resShowTextView.setText("");
} else {
int a = Integer.parseInt(numEditText1.getText().toString());
int b = Integer.parseInt(numEditText2.getText().toString());
try {
resShowTextView.setText(String.valueOf(mIAddAidl.add(a, b)));
} catch (RemoteException e) {
e.printStackTrace();
}
}
}else {
Toast.makeText(MainActivity.this, "远程连接失败!!!", Toast.LENGTH_LONG).show();
}

}

});
}

private void bindServer() {
Intent mIntent = new Intent();
/**
* 这里要特别注意包名,组件名,服务名一定要是绝对位置
* */
mIntent.setComponent(new ComponentName("com.lzy.aidlserver", "com.lzy.aidlserver.AddServer"));
bindService(mIntent, conn, Context.BIND_AUTO_CREATE);
}

private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
/**
* 这里面的IBinder service 回调回来的就是远程AidlServer里面的IBinder(其实际是一个代理,并不是IBinder本身)
* @link IddAidl
*
* public static com.lzy.aidlserver.IAddAidl asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

if (((iin!=null)&&(iin instanceof com.lzy.aidlserver.IAddAidl))) {
return ((com.lzy.aidlserver.IAddAidl)iin);
}
return new com.lzy.aidlserver.IAddAidl.Stub.Proxy(obj);
}
* */
mIAddAidl = IAddAidl.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {
/**
* 释放资源
* */
if (mIAddAidl != null)
mIAddAidl = null;
}
};

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


布局是这样的,xml我就不写了,很简单。



最后要注意一点:

服务的话需要向系统指明“我是一个服务”,所以要在XML中注册<server></server>,服务端mainfest如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lzy.aidlserver" >

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<!--指定服务
在Android的帮助文档中我们可以了解到,一般情况下一个服务没有自己独立的进程,它一般是作为一个线程
运行于它所在的应用的进程中。但是也有例外,Android声明文件中的android:process属性却可以为任意组
件包括应用指定进程,换句话说,通过在声明文件中设置android:process属性,我们可以让组件(例如Activity,
Service等)和应用(Application)创建并运行于我们指定的进程中。 如果我们需要让一个服务在一个远端进程
中运行(而不是在它所在的apk的进程中运行),我们可以在声明文件的这个服务标签中通过android:process
属性为其指定一个进程。

注意:这里选择”remote”这个名字是随意主观的,你能用其他名字来让这个服务在另外的进程中运行。
冒号’:’这个前缀将把这个名字附加到你的包所运行的标准进程名字的后面作为新的进程名称。也就是说如果指定了
android:process,这个服务就会开启一个进程“com.example.demo:remote”,否则默认就使用当前APP的包名
“com.lzy.aidlserver”的进程。

另外注意:
如果被设置的进程名是以一个冒号开头的,则这个新的进程对于这个应用来说是私有的,当它被需要或者这个服务
需要在新进程中运行的时候,这个新进程将会被创建。如果这个进程的名字是以小写字符开头的,则这个服务将
运行在一个以这个名字命名的全局的进程中,当然前提是它有相应的权限。这将允许在不同应用中的各种组件可以
共享一个进程,从而减少资源的占用。-->

<service android:name="com.lzy.aidlserver.AddServer"
android:process=":remote"
android:exported="true">

<!--指定action也可以-->
<intent-filter>
<action android:name="com.lzy.aidlserver.IAddAidl"></action>
</intent-filter>
</service>
</application>

</manifest>


这里面有个知识点:

指定服务
在Android的帮助文档中我们可以了解到,一般情况下一个服务没有自己独立的进程,它一般是作为一个线程
运行于它所在的应用的进程中。但是也有例外,Android声明文件中的android:process属性却可以为任意组
件包括应用指定进程,换句话说,通过在声明文件中设置android:process属性,我们可以让组件(例如Activity,
Service等)和应用(Application)创建并运行于我们指定的进程中。 如果我们需要让一个服务在一个远端进程
中运行(而不是在它所在的apk的进程中运行),我们可以在声明文件的这个服务标签中通过android:process
属性为其指定一个进程。

注意:这里选择”remote”这个名字是随意主观的,你能用其他名字来让这个服务在另外的进程中运行。
冒号’:’这个前缀将把这个名字附加到你的包所运行的标准进程名字的后面作为新的进程名称。也就是说如果指定了
android:process,这个服务就会开启一个进程“com.example.demo:remote”,否则默认就使用当前APP的包名
“com.lzy.aidlserver”的进程。

另外注意:
如果被设置的进程名是以一个冒号开头的,则这个新的进程对于这个应用来说是私有的,当它被需要或者这个服务
需要在新进程中运行的时候,这个新进程将会被创建。如果这个进程的名字是以小写字符开头的,则这个服务将
运行在一个以这个名字命名的全局的进程中,当然前提是它有相应的权限。这将允许在不同应用中的各种组件可以
共享一个进程,从而减少资源的占用。


在AS的Android Monitor中的表现是:



接下来我们先安装Server端后安装Client端,点击计算,就能实现远程计算了。



3.AIDL文件的分析

我先格式化一下IAddAidl.java文件,方便看

首先呢,客户端获取IBinder是通过ServerConnect回调中的方法得到的

mIAddAidl = IAddAidl.Stub.asInterface(service);


看源码知道,mIAddAidl实际就是IAddAidl代理出来的一个类

//也就是说第一次创建的并不是IAddAidl真正的接口,而是它的一个代理
return new com.lzy.aidlserver.IAddAidl.Stub.Proxy(obj);


然后调用.add(1, 2);方法

mIAddAidl.add(a, b)


那么此时其实是代理中的add方法,发现原来add的具体实现不是stub而是proxy,他最终调用

// 下降到stub的transact方法,其实就是发送到OS底层。交给了服务端去处理
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);

进而往下走

@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_add: {
data.enforceInterface(DESCRIPTOR);//找到了服务器
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);//服务端实现方法
reply.writeNoException();
reply.writeInt(_result);//返回给客户端
return true;
}
}
return super.onTransact(code, data, reply, flags);
}


操作系统根据

mIntent.setComponent(new ComponentName("com.lzy.aidlserver", "com.lzy.aidlserver.AddServer"));


找到服务器,怎么找到的?????还记得IAddAidl建立的时候有个类似flags的描述不

private static final java.lang.String DESCRIPTOR = "com.lzy.aidlserver.IAddAidl";

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


最终

_result = _reply.readInt();// 读取返回结果


客户端得到了结果了。。。。

附格式化后的java文件:

package com.hy.aidlserver;

public interface IAddAidl extends android.os.IInterface {
/** Local-side IPC implementation stub class.
自己实现自己 -- 存根
*/
public static abstract class Stub extends android.os.Binder implements
com.lzy.aidlserver.IAddAidl {
private static final java.lang.String DESCRIPTOR = "com.lzy.aidlserver.IAddAidl";

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

/**
* Cast an IBinder object into an com.lzy.aidlserver.IAddAidl interface,
* generating a proxy if needed.
*/

public static com.lzy.aidlserver.IAddAidl asInterface(
android.os.IBinder obj) {
//obj就是客户端连接成功后的回调
//也就是
//mIntent.setComponent(new ComponentName("com.lzy.aidlserver", "com.lzy.aidlserver.AddServer"));
//bindService(mIntent, conn, Context.BIND_AUTO_CREATE);
//这个绑定过程是如下实现的---> mIBinder
// public class AddServer extends Service{
// @Nullable
// @Override
// public IBinder onBind(Intent intent) {
// return mIBinder;
// }

// /**
// * 实现AIDL中的接口
// * */
// private final IAddAidl.Stub mIBinder = new IAddAidl.Stub() {
// @Override
// public int add(int a, int b) throws RemoteException {
// return a + b;
// }
// };
// }

if ((obj == null)) {
return null;
}
//先看本地有没有该接口(第一次使用肯定没有)
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.lzy.aidlserver.IAddAidl))) {
return ((com.lzy.aidlserver.IAddAidl) iin);
}
//也就是说第一次创建的并不是IAddAidl真正的接口,而是它的一个代理 return new com.lzy.aidlserver.IAddAidl.Stub.Proxy(obj);
}

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

@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_add: { data.enforceInterface(DESCRIPTOR);//找到了服务器 int _arg0; _arg0 = data.readInt(); int _arg1; _arg1 = data.readInt(); int _result = this.add(_arg0, _arg1);//服务端实现方法 reply.writeNoException(); reply.writeInt(_result);//返回给客户端 return true; } } return super.onTransact(code, data, reply, flags); }

//这个代理同样也实现了AIDL的接口,它是Stub的内部类
|/span> private static class Proxy implements com.lzy.aidlserver.IAddAidl {
private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {
// mIBinder
mRemote = remote;
}

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

public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}

/////////////////////////////////////////////////////////////////////原来真正的add实现是在代理中真正实现的
/**
* 定义求和运算的接口
*
* @param a
* - int
* @param b
* - int return result - int
*/
@Override
public int add(int a, int b) throws android.os.RemoteException {
// 序列化
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
//初始化与赋值
_data.writeInterfaceToken(DESCRIPTOR);// 先写了一个Token
_data.writeInt(a);
_data.writeInt(b);
// 下降到stub的transact方法,其实就是发送到OS底层。交给了服务端去处理 mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();// 读取返回结果
} finally {
_reply.recycle();
_data.recycle();
}
return _result;// 返回结果
}
}

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

/**
* 定义求和运算的接口
*
* @param a
* - int
* @param b
* - int return result - int
*/
public int add(int a, int b) throws android.os.RemoteException;
}


整个过程就是这样。

4.AIDL进阶使用

Passing Objects over IPC

官方是这么说的:

If you have a class that you would like to send from one process to another through an IPC interface,
you can do that. However, you must ensure that the code for your class is available to the other side
of the IPC channel and your class must support the Parcelable interface. Supporting the Parcelable
interface is important because it allows the Android system to decompose objects into primitives that
can be marshalled across processes.


就是你要实现Parcelable接口,官方给出使用步骤如下:

To create a class that supports the Parcelable protocol, you must do the following:

1.  Make your class implement the Parcelable interface.
2.  Implement writeToParcel, which takes the current state of the object and writes it
to a Parcel.
3.  Add a static field called CREATOR to your class which is an object implementing the
Parcelable.Creator interface.
4.  Finally, create an .aidl file that declares your parcelable class (as shown for the
Rect.aidl file, below).

If you are using a custom build process, do not add the .aidl file to your
build. Similar to a header file in the C language,  this .aidl file isn't compiled.

AIDL uses these methods and fields in the code it generates to marshall and unmarshall
your objects.this .aidl file isn't compiled.
AIDL uses these methods and fields in the code it generates to marshall and unmarshall your objects.

那我们按照步骤来做好了

①. 创建一个Person类并实现Parcelable接口

在Server端创建另外一个aidl文件IPersonAidl.aidl。它的作用就是添加Person类并返回该对象的集合。但是AIDL并不认识Person类,so,我先在java代码中创建了一个Person类,我们知道必须要将这个Person实现Parceable接口:
package com.lzy.aidlserver;

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

/**
* Created by luzhenyu on 2016/3/3.
*/
public class Person implements Parcelable{
private String name;
private int age;

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

private void readFromParcel(Parcel in) {
// 注意顺序
name = in.readString();
age = in.readInt();
}

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

//该方法必须是这样  ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

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

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

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

@Override
public void writeToParcel(Parcel dest, int flags) {
// 注意顺序
dest.writeString(name);
dest.writeInt(age);
}

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

@Override
public String toString() {
return "我是" +  name +" ,今年" + age + "岁了!!!";
}
}


这里面有几个部分必须要注意

//该方法必须是这样  ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

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

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


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

private void readFromParcel(Parcel in) {
// 注意顺序
name = in.readString();
age = in.readInt();
}

@Override
public void writeToParcel(Parcel dest, int flags) {
// 注意顺序
dest.writeString(name);
dest.writeInt(age);
}


②. 创建AIDL的过程

然后试着在接口中定义了

List<Person> addPerson(Person person);


方法,很明显编译报错,不知道Person是什么,那好,我手动导包

import com.lzy.aidlserver.Person;


还是报错??????!!!!
哦哦,原来AIDL并不认识Person类型,需要让AIDL知道Person是一个实现Parcelable接口的对象。所以还是先在建立一个Person的AIDL

// Person.aidl
package com.lzy.aidlserver;

// Declare any non-default types here with import statements
// 声明Person为Parcelable类型的数据
parcelable Person;

这样就行啦吧,^_^

----------------------------------------   我去还是报错 !!!!!!!!

E:\workspace_as\AIDLDemo\aidlserver\src\main\aidl\com\lzy\aidlserver\IPersonAidl.aidl:8 parameter 1:
'Person person' can be an out parameter, so you must declare it as in, out or inout.
Error:Execution failed for task ':aidlserver:compileDebugAidl'.
> com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process
'command 'D:\AndroidStudios\sdk\build-tools\23.0.1\aidl.exe'' finished with non-zero exit value 1


看这意思是我要指明一个Person是输入还是输出,原来如此。

最后就变成这样的了,编译通过



其中IPersonAidl.aidl如下:
// IPersonAidl.aidl
package com.lzy.aidlserver;

// Declare any non-default types here with import statements
import com.lzy.aidlserver.Person;//要手动倒入而且还要创建Person的AIDL去描述Person  List我们说过了,可以不用导包

interface IPersonAidl {
List<Person> addPerson(in Person person);//要指明是输入还是输出 in/out
}


Person.aidl如下:
// Person.aidl
package com.lzy.aidlserver;

// Declare any non-default types here with import statements
// 声明Person为Parcelable类型的数据
parcelable Person;


PersonServer如下:
package com.lzy.aidlserver;

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;

/**
* Created by Administrator on 2016/3/3.
*/
public class PersonServer extends Service {

private ArrayList<Person> list;// List对应的使用时ArrayList
@Nullable
@Override
public IBinder onBind(Intent intent) {
list = new ArrayList<Person>();
return mBinder;
}

private IPersonAidl.Stub mBinder = new IPersonAidl.Stub() {
@Override
public List<Person> addPerson(Person person) throws RemoteException {
list.add(person);
return list;
}
};
}

别忘了在XML中注册server
<service android:name="com.lzy.aidlserver.PersonServer"
android:process=":remote2"
android:exported="true">

<!--指定action也可以-->
<intent-filter>
<action android:name="com.lzy.aidlserver.IPersonAidl"></action>
</intent-filter>
</service>

至此,服务端搞定
------------------------------------------------------------  我是分割线 --------------------------------------------------------

③. 客户端实现

我们来实现客户端

首先将新建的两个aidl拷贝到对应的位置,注意包名,此时编译会报错,因为还缺一个Person类,我们又把Person直接复制到java文件夹下,编译发现会报错,说找不到Person,怎么会呢????原来,我们要将Person在服务端什么包名,就要在客户端也是什么包名,所以,在java文件里面建立一个和服务端Person一致的包名并将Person.java拷贝到里面,就是这个样子:



编译-----------------------------------------------------------------------------------------通过

那好,客户端接入AIDL成功,下面是使用了
MainActivity最后变成了
package com.hy.aidlclient;

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.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.lzy.aidlserver.IAddAidl;
import com.lzy.aidlserver.IPersonAidl;
import com.lzy.aidlserver.Person;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

private EditText numEditText1, numEditText2;
private Button calBtn, addPerson;
private TextView resShowTextView, showPersonTextView;

private IAddAidl mIAddAidl;
private IPersonAidl mIPersonAidl;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

numEditText1 = (EditText) findViewById(R.id.num_1);
numEditText2 = (EditText) findViewById(R.id.num_2);

calBtn = (Button) findViewById(R.id.calculate_Btn);
addPerson = (Button) findViewById(R.id.addPersonBtn);
showPersonTextView = (TextView) findViewById(R.id.show_personTv);
resShowTextView = (TextView) findViewById(R.id.res_show);

bindServer();
bindPersonServer();

calBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mIAddAidl != null) {
if (TextUtils.isEmpty(numEditText1.getText().toString())
|| TextUtils.isEmpty(numEditText2.getText().toString())) {
Toast.makeText(MainActivity.this, "输入为空", Toast.LENGTH_LONG).show();
numEditText1.setText("");
numEditText2.setText("");
resShowTextView.setText("");
} else {
int a = Integer.parseInt(numEditText1.getText().toString());
int b = Integer.parseInt(numEditText2.getText().toString());
try {
resShowTextView.setText(String.valueOf(mIAddAidl.add(a, b)));
} catch (RemoteException e) {
e.printStackTrace();
}
}
} else {
Toast.makeText(MainActivity.this, "远程连接失败!!!", Toast.LENGTH_LONG).show();
}

}

});

addPerson.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
addPerson();
} catch (RemoteException e) {
e.printStackTrace();
}
}

private void addPerson() throws RemoteException {
Person person = new Person("hy", 23);
ArrayList<Person> list = (ArrayList<Person>) mIPersonAidl.addPerson(person);
showPersonTextView.setText(list.toString());
}
});
}

private void bindServer() {
Intent mIntent = new Intent();
/**
* 这里要特别注意包名,组件名,服务名一定要是绝对位置
* */
mIntent.setComponent(new ComponentName("com.lzy.aidlserver", "com.lzy.aidlserver.AddServer"));
bindService(mIntent, conn, Context.BIND_AUTO_CREATE);
}

private void bindPersonServer() {
Intent mIntent = new Intent();
mIntent.setAction("com.lzy.aidlserver.IPersonAidl");
bindService(mIntent, conn2, Context.BIND_AUTO_CREATE);
}

private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
/**
* 这里面的IBinder service 回调回来的就是远程AidlServer里面的IBinder(其实际是一个代理,并不是IBinder本身)
* @link IddAidl
*
* public static com.lzy.aidlserver.IAddAidl asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

if (((iin!=null)&&(iin instanceof com.lzy.aidlserver.IAddAidl))) {
return ((com.lzy.aidlserver.IAddAidl)iin);
}
return new com.lzy.aidlserver.IAddAidl.Stub.Proxy(obj);
}
* */
mIAddAidl = IAddAidl.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {
/**
* 释放资源
* */
if (mIAddAidl != null)
mIAddAidl = null;
}
};

private ServiceConnection conn2 = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIPersonAidl = IPersonAidl.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {
if (mIPersonAidl != null)
mIPersonAidl = null;
}
};

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


源码下载:http://download.csdn.net/detail/luzhenyuxfcy/9192529

错误之处还请在回复区指正,共同学习共同进步
——lovey hy

【欢迎上码】
【微信公众号搜索 h2o2s2】

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  AIDL 跨进程通信