android ipc机制(了解AIDL的工作原理及使用方法)
android ipc机制(了解AIDL的工作原理及使用方法)这里提到了两个东西他大概的意思是他允许你去定义一个自己的标准,使用的是IPC(进程间通讯)进制,跨进程通讯,有兴趣的可以去翻译一下,不过值得注意的是他的第二段标记首先,我们要知道,进程1和进程2,我们要如何让他通讯?在Android系统的虚拟机中,每一个app运行都是在一个独立的沙箱里面的,这样的话,一个应用崩溃也不会影响到其他应用,这样我们就提出了一个问题,跨进程如如何进行通讯?如何传递数据?其实两个进程是不能直接通讯的,他需要Android系统底层的帮助来进行通讯!那就是我们每个人都知道的四大组件我们首先还是先进Google的API看看
今天来讲讲AIDL,这个神秘的AIDL,也是最近在学习的,看了某课大神的讲解写下的blog,希望结合自己的看法给各位同价通俗易懂的讲解
官方文档:http://developer.android.com/guide/components/aidl.html
一.What is AIDL?(什么是AIDL)
AIDL:Android Interface Definition Language (Android接口定义语言)
首先,我们要知道,进程1和进程2,我们要如何让他通讯?
在Android系统的虚拟机中,每一个app运行都是在一个独立的沙箱里面的,这样的话,一个应用崩溃也不会影响到其他应用,这样我们就提出了一个问题,跨进程如如何进行通讯?如何传递数据?其实两个进程是不能直接通讯的,他需要Android系统底层的帮助来进行通讯!那就是我们每个人都知道的四大组件
我们首先还是先进Google的API看看
他大概的意思是他允许你去定义一个自己的标准,使用的是IPC(进程间通讯)进制,跨进程通讯,有兴趣的可以去翻译一下,不过值得注意的是他的第二段标记
这里提到了两个东西
- Binder
- Messenger
我们继续往下看话就知道
- Binder
- Messenger
翻译:如果您不需要执行并发IPC在不同的应用程序中 你就用Binder ,或者如果你想执行IPC 但不需要处理多线程 实现你的接口Messenger,无论如何 确保你了解实现AIDL之前绑定服务。
所以我们就能理清楚AIDL的概念了
AIDL //IPC 多应用 多线程
二.Defining an AIDL Interface(语法)
文档中强调。你必须定义一个.aidl的文件,那我们怎样去创建尼?
1. Create the .aidl file(创建一个.aidl文件)
官方提供的语法
// IRemoteService.aidl
package 包名(com.lgl.android);
// Declare any non-default types here with import statements
/** Example service interface 接口*/
interface IRemoteService {
/** Request the process ID of this service to do evil things with it. 方法*/
int getPid();
/** 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);
}
我们来做一个简单的演示
我们要使用的编译工具在我们的SDK/buidl-tools/android版本/aidl.bat,不过实际开发中也不需要手动编译,我们新建一个项目ForAIDL 这里我们使用的开发工具是Android Studio,其实Eclipse可能更加让人熟悉,不过用AS也是大势所趋了,而且AS的目录结构我也很喜欢
我们直接java-new - folder - AIDL folder
然后你就会发现多了一个aidl的文件夹
我们继续新建一个包名,新建一个文件文件new - AIDL - file
大致的内容
// IMyAidlInterface.aidl
package com.lgl.foraidl;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
/**
* 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);
}
AS默认是不会去重新构建Gradle 我们点击一下构建按钮
然后我们就可以在MainActivity中调用了
三.AIDL Client And Service(客户端和服务端)
现在就好玩了,我们先来理理思路,一般是这样的,我们一个软件有某个功能,也就是服务端,然后客户端通过AIDL去访问
这里可以看到服务端只是处理一些操作,我们客户端去请求,这样的话,那我创建一个AIDLService 同样的,我们需要去创建AIDL文件,命名-ServiceAidlInterface
ServiceAidlInterface
// ServiceAidlInterface.aidl
package com.lgl.aidlservice;
// Declare any non-default types here with import statements
interface ServiceAidlInterface {
/*
*计算两数之和
*/
int add(int num1 int num2);
}
现在我们处理的就不是默认的东西了,AIDL的原理就是你自定义语言接口,对的,我们也来
2. Implement the interface(实现一个AIDL)
根据我们的Google文档,第一步Create the .aidl file已经完成了,现在就来进行第二步了,我们这里需要使用到Service 看文档
这样的话我们新建一个IRemoteService继承service 我们实现计算的逻辑处理
package com.lgl.aidlservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
/**
*
* 监听服务
* Created by lgl on 16/3/13.
*/
public class IRemoteService extends Service{
/**
* 当客户端绑定到该服务的时候启动onBind
* @param intent
* @return
*/
@Override
public IBinder onBind(Intent intent) {
//绑定之后就计算
return iBinder;
}
/**
* 开始处理结果
*/
private IBinder iBinder = new ServiceAidlInterface.Stub(){
@Override
public int add(int num1 int num2) throws RemoteException {
Log.i("AIDL" "收到了请求 參數1" num1 "參數2" num2);
return num1 num2;
}
};
}
3. Expose the interface to clients(客户端的实现)
OK 写完了服务端就可以写客户端了,我们直接new一个Module-AIDLClients
我們先按剛才的逻辑,把界面写了
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="15dp">
<TextView
android:text="AIDL"
android:textSize="50sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:hint="请输入数字1"
android:id="@ id/et_num1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_marginBottom="15dp"
android:layout_marginTop="15dp"
android:textSize="20sp"
android:text=" "
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:hint="请输入数字2"
android:id="@ id/et_num2"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_marginBottom="15dp"
android:layout_marginTop="15dp"
android:textSize="20sp"
android:text="="
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@ id/tv_result"
android:text="结果:"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:textColor="#fff"
android:background="@android:color/holo_blue_light"
android:layout_marginTop="30dp"
android:id="@ id/btn_ok"
android:text="计算"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
我们要想客户端调用服务端的内容,那么就一定要定义标准的语言,所以客户端的aidl和服务端必须一致
记住,每次不通过的时候先编译一遍
MainActivity(客户端)
package com.lgl.aidlclients;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.lgl.aidlservice.ServiceAidlInterface;
/**
* AIDL 客户端
* @author lgl
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
//输入
private EditText et_num1 et_num2;
//结果
private TextView tv_result;
//AIDL远程访问
private Button btn_ok;
private ServiceAidlInterface aidl;
private ServiceConnection conn = new ServiceConnection() {
//连接上
@Override
public void onServiceConnected(ComponentName componentName IBinder iBinder) {
//拿到远程服务
aidl = ServiceAidlInterface.Stub.asInterface(iBinder);
}
//断开时
@Override
public void onServiceDisconnected(ComponentName componentName) {
//回收
aidl = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
bindServices();
}
private void initView() {
et_num1 = (EditText) findViewById(R.id.et_num1);
et_num2 = (EditText) findViewById(R.id.et_num2);
tv_result = (TextView) findViewById(R.id.tv_result);
btn_ok = (Button) findViewById(R.id.btn_ok);
btn_ok.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_ok:
int num1 = Integer.parseInt(et_num1.getText().toString());
int num2 = Integer.parseInt(et_num2.getText().toString());
try {
//结果
int res = aidl.add(num1 num2);
tv_result.setText("结果:" res);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
/**
* 开启服务
*/
private void bindServices() {
//获取服务端
Intent intent = new Intent();
//5.0之后的改变
intent.setComponent(new ComponentName("com.lgl.aidlservice" "com.lgl.aidlservice.IRemoteService"));
//绑定服务
bindService(intent conn Context.BIND_AUTO_CREATE);
}
/**
* 销毁服务
*/
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
这里逻辑也是十分的清晰,你创建了之后启动服务,我调用方法来计算,我们来演示一下,我们先启动服务端,再启动客户端
记得注册
<service android:name=".IRemoteService"
android:process=":remote"
android:exported="true" >
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="com.lgl.aidlservice.IRemoteService" />
</intent-filter>
</service>
写到这里才看到玉刚也写了相关的信息,这边android:exported="true"为权限问题,具体可以看下:
[android跨进程通信(IPC):使用AIDL](http://blog.csdn.net/singwhatiwanna/article/details/17041691)
不过这终究也只是一些小菜,我们项目中也不可能用到这么low的方法,毕竟AIDL还是很强大的,既然这样,那我们来玩玩高级一点的
四.AIDL数据传递
我们不能传递很大的数据这是总所周知的,那AIDL默认支持什么类型的尼?我们可不可以自定义尼?当然可以
1.默认数据类型
我们可以看一下官方文档
- 1.基本数据类型
- 2.String
- 3.CharSequence
- 4.List
- 5.Map
- 6.Parcelable(序列化)
我们新建一个aidl文件,他都会自己生成一个方法
void basicTypes(int anInt long aLong boolean aBoolean float aFloat
double aDouble String aString);
意指的就是基础类型
然而在我们的实际测试中,你会发现他是不支持short的,我们做这样的小测试
void basicTypes(byte b short s int anInt long aLong boolean aBoolean float aFloat
double aDouble String aString);
然后编译
/Users/lgl/Documents/Android/ASCode/AIDLService/app/src/main/aidl/com/lgl/aidlservice/IMyAidlInterface.aidl:11 parameter s (2) unknown type short
他报的错误就是不支持short
再比如说,你想写一个List< String>list的数据,你必须指定他是in还是out类型的
in List< String>list
2.自定义类型
这里我们先在服务端新建一个aidl文件-IMyAidlInterface,然后写入我们的方法
//我们返回的数据是一个List集合
List<Person>add(in Person person);
这里报错是毋庸置疑的,因为他不识别我们的Person,但是就算我们新建一个Person类也是无用的,因为aidl不支持这个类型,上面也说了,他只支持基本类型和其他有限的几种数据类型,这样的话,我们就需要自定义了,怎么来呢?让他implements Parcelable就可以
package com.lgl.aidlservice;
import android.os.Parcel;
import android.os.Parcelable;
/**
* 序列化实体类
* Created by lgl on 16/3/15.
*/
public class Person implements Parcelable{
private String name;
private int age;
//构造方法
public Person(String name int age){
this.name =name;
this.age = age;
}
protected Person(Parcel in) {
name = in.readString();
age = in.readInt();
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel int i) {
parcel.writeString(name);
parcel.writeInt(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];
}
};
}
这里值得注意的就是读写部分了,你是怎么写的就得怎么读,顺序错了就没用了,那我们编译一下,你会发现他还是提示错误,依然是位置的List数据类型
/Users/lgl/Documents/Android/ASCode/AIDLService/app/src/main/aidl/com/lgl/aidlservice/IMyAidlInterface.aidl:9 unknown return type List<Person>
这里就需要我们手动导入这个Person了不然人家也不认识啊!那要怎么做?还是需要创建一个Person的AIDL文件,内容十分的简单,说明文件
// IMyAidlInterface.aidl
package com.lgl.aidlservice;
parcelable Person;
可以看到,里面就一句话,好的,现在编译通过了,那我们重新来修改一下IRemoteService了,这次就不是返回计算数据的和了
package com.lgl.aidlservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import java.util.ArrayList;
import java.util.List;
/**
*
* 监听服务
* Created by lgl on 16/3/13.
*/
public class IRemoteService extends Service{
private ArrayList<Person>persons;
/**
* 当客户端绑定到该服务的时候启动onBind
* @param intent
* @return
*/
@Override
public IBinder onBind(Intent intent) {
persons = new ArrayList<Person>();
//绑定之后就计算
return iBinder;
}
/**
* 开始处理结果
*/
// private IBinder iBinder = new ServiceAidlInterface.Stub(){
//
// @Override
// public int add(int num1 int num2) throws RemoteException {
// Log.i("AIDL" "收到了请求 參數1" num1 "參數2" num2);
// return num1 num2;
// }
// };
/**
* 序列化
*/
private IBinder iBinder = new IMyAidlInterface.Stub(){
@Override
public List<Person> add(Person person) throws RemoteException {
//每增加一个人都能得到返回
persons.add(person);
return persons;
}
};
}
好的,服务端部分写完了,刚开始可能是有点混乱的,不过当你确实已经理解之后你会发现,也就这么点东西的逻辑,那我们继续来写客户端,看他是怎么进行序列化的处理的。
还是一样,我们把Person.aidl和IMyAidlInterface.aidl复制过去,顺便把Person类拷贝过来,这里要注意的事就是包名要一致
然后我们就开启服务了
package com.lgl.aidlclients;
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.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.lgl.aidlservice.IMyAidlInterface;
import com.lgl.aidlservice.Person;
import com.lgl.aidlservice.ServiceAidlInterface;
import java.util.ArrayList;
/**
* AIDL 客户端
* @author lgl
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
//输入
private EditText et_num1 et_num2;
//结果
private TextView tv_result;
//AIDL远程访问
private Button btn_ok;
private ServiceAidlInterface aidl;
private IMyAidlInterface imy;
private ServiceConnection conn = new ServiceConnection() {
//连接上
@Override
public void onServiceConnected(ComponentName componentName IBinder iBinder) {
//拿到远程服务
aidl = ServiceAidlInterface.Stub.asInterface(iBinder);
imy = IMyAidlInterface.Stub.asInterface(iBinder);
}
//断开时
@Override
public void onServiceDisconnected(ComponentName componentName) {
//回收
aidl = null;
imy = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindServices();
initView();
}
private void initView() {
et_num1 = (EditText) findViewById(R.id.et_num1);
et_num2 = (EditText) findViewById(R.id.et_num2);
tv_result = (TextView) findViewById(R.id.tv_result);
btn_ok = (Button) findViewById(R.id.btn_ok);
btn_ok.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_ok:
// int n1 = Integer.parseInt(et_num1.getText().toString());
// int n2 = Integer.parseInt(et_num2.getText().toString());
//
// try {
// //结果
// int res = aidl.add(n1 n2);
// tv_result.setText("结果:" res);
// } catch (RemoteException e) {
// e.printStackTrace();
// tv_result.setText("Error");
// }
try{
ArrayList<Person>persons = (ArrayList<Person>) imy.add(new Person("name" 21));
Log.i("Person" persons.toString());
}catch (RemoteException e){
e.printStackTrace();
}
break;
}
}
/**
* 开启服务
*/
private void bindServices() {
//获取服务端
Intent intent = new Intent();
//5.0之后的改变
intent.setComponent(new ComponentName("com.lgl.aidlservice" "com.lgl.aidlservice.IRemoteService"));
//绑定服务
bindService(intent conn Context.BIND_AUTO_CREATE);
}
/**
* 销毁服务
*/
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
这样的话,只要我们点击了计算按钮,Log打印一条消息,那事实是如此吗?我们运行一下
不会发现,我们每一次添加都是多增加了一行,相当于我们远程添加了一行
五.AIDL的工作原理
我们每次编译AIDL的文件的时候,都会编译成一个JAVA文件,具体是什么,我们一起研究研究,借用大神的一张图,你现在可能看不懂,等讲完原理你就懂了
编译好的JAVA文件在
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/lgl/Documents/Android/ASCode/AIDLService/aidlclients/src/main/aidl/com/lgl/aidlservice/IMyAidlInterface.aidl
*/
package com.lgl.aidlservice;
//他是继承系统的接口,也就是说他本身就是一个接口
public interface IMyAidlInterface extends android.os.IInterface
{
/** 存根,并且实现了自己的接口*/
public static abstract class Stub extends android.os.Binder implements com.lgl.aidlservice.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.lgl.aidlservice.IMyAidlInterface";
/**自己的构造方法*/
public Stub()
{
this.attachInterface(this DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.lgl.aidlservice.IMyAidlInterface interface
* generating a proxy if needed.
*/
public static com.lgl.aidlservice.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.lgl.aidlservice.IMyAidlInterface))) {
return ((com.lgl.aidlservice.IMyAidlInterface)iin);
}
return new com.lgl.aidlservice.IMyAidlInterface.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
//返回的this是Stub
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_add:
{
data.enforceInterface(DESCRIPTOR);
com.lgl.aidlservice.Person _arg0;
if ((0!=data.readInt())) {
//如果拿到传过来数值封装成一个data
_arg0 = com.lgl.aidlservice.Person.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
java.util.List<com.lgl.aidlservice.Person> _result = this.add(_arg0);
reply.writeNoException();
//最后通过这个又写回去了,这样就完成了整个的通讯
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code data reply flags);
}
//代理,他是Stud的内部类
private static class Proxy implements com.lgl.aidlservice.IMyAidlInterface
{
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;
}
//我们返回的数据是一个List集合
@Override public java.util.List<com.lgl.aidlservice.Person> add(com.lgl.aidlservice.Person person) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.lgl.aidlservice.Person> _result;
try {
//写入了类名
_data.writeInterfaceToken(DESCRIPTOR);
if ((person!=null)) {
//写了一个1,同时add数据,当你拿到了远程服务的时候,其实你拿到的只是远程服务的代理
_data.writeInt(1);
person.writeToParcel(_data 0);
}
else {
_data.writeInt(0);
}
//传入到onTransact
mRemote.transact(Stub.TRANSACTION_add _data _reply 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.lgl.aidlservice.Person.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION 0);
}
//我们返回的数据是一个List集合
//我们创建AIDL命名的这个方法
public java.util.List<com.lgl.aidlservice.Person> add(com.lgl.aidlservice.Person person) throws android.os.RemoteException;
}
Ok AIDL这边算是讲完了,如果有兴趣的话,可以再去深入研究一下,我这边也只是学习了大神的视频,然后做的一个小总结和小笔记
Demo下载:http://download.csdn.net/detail/qq_26787115/9462515