Android中夸进程间通信方式有很多种方式,比如:aidl,Messenger,文件共享,广播(BroadCast),ContentProvider,Socket(网络通信)。每种方式都有自己的使用场景和优缺点,接下来几篇博客我们一一学习他们是怎么使用的。这篇博客主要是介绍Android studio下实现aidl编程。
aidl远程调用传递的参数和返回值支持Java的基本类型(int long booen char byte等)和String,List,Map等。当然也支持一个自定义对象的传递,不过此时就需要做一些特别处理了,后续会介绍aidl传递对象怎么处理。
有关aidl的相关概念这里就不详细解释了,我们来看看在Android Studio下怎么来实现aidl编程的。
新建一个MyAidlDemoServer工程,然后在main目录下右键新建一个aidl目录,然后在该目录下新建一个IMyAidlInterface.aidl文件,代码如下:
// IMyAidlInterface.aidl
package com.example.xjp.aidla;
interface IMyAidlInterface {
int add(int arg1, int arg2);
}
定义了一个IMyAidlInterface接口,接口里面定义了一个add方法用于计算两个数的和。然后Build当前工程,在app/build/generated/source/aidl/debug目录下会生成一个与IMyAidlInterface.aidl文件同样包名的一个文件,该文件下面自动生成IMyAidlInterface文件,该文件里面自动实现了一些方法用于远程调用,IMyAidlInterface代码如下:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\WorkPlace\\MyAidlDemoServer\\app\\src\\main\\aidl\\com\\example\\xjp\\aidla
* \\IMyAidlInterface.aidl
*/
package com.example.xjp.aidla;
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.xjp.aidla
.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.example.xjp.aidla.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.xjp.aidla.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.example.xjp.aidla.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.xjp.aidla.IMyAidlInterface))) {
return ((com.example.xjp.aidla.IMyAidlInterface) iin);
}
return new com.example.xjp.aidla.IMyAidlInterface.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_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);
}
private static class Proxy implements com.example.xjp.aidla.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;
}
@Override
public int add(int arg1, int arg2) 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);
_data.writeInt(arg1);
_data.writeInt(arg2);
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);
}
public int add(int arg1, int arg2) throws android.os.RemoteException;
}
以上代码都是AS自动生成的,如此一来,开发者更加容易来实现AIDL跨进程间通信。如有对以上代码感兴趣者,可自行学习。
新建MyServer类实现远程服务代码如下:
public class MyServer extends Service {
IMyAidlInterface.Stub mStub = new IMyAidlInterface.Stub() {
public int add(int arg1, int arg2) {
return arg1 + arg2;
}
};
@Override
public IBinder onBind(Intent intent) {
return mStub;
}
}
服务代码也很简单,仅仅实现了IMyAidlInterface.Stub类中的 add方法,然后重写了Service的onBind方法。
记得在AndroidManifest.xml中配置MyServer服务,代码如下:
<manifest package="com.example.xjp.aidl"
xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<service
android:name="com.example.xjp.aidl.MyServer"
android:process=":remote">
<intent-filter>
<action android:name="com.xjp.myService">action>
intent-filter>
service>
application>
manifest>
以上代码给当前service设置了 android:process属性为“:remote”,这个属性有两种赋值,一是:”:remote”,一是:”remote”,区别在于字符串前面带有”:”,后面的字符串不一定是remote,开发者可以随意设置。
两者代表不同意思,带有”:”的表示该服务所在的进程是私有的,即只要有客户端去启动该服务,系统就会创建一个新的进程来运行该服务。
不带有”:”的表示该服务所在的进程是共享的,即当前系统不管有几个客户端去启动该服务,系统中只有一个进程来运行该服务。
客户端代码相对简单些,新建MyAidlDemoCustomer工程,然后直接把服务端的aidl目录直接拷贝到客户端的main目录下。这么一来客户端的aidl就无需编写了,直接和服务端的一模一样。包括路径的包名等。虽然你可以自己在客户端在写一遍aidl代码,为了不出错,请直接将服务端的aidl代码直接拷贝过来。
package com.example.xjp.myaidldemocustomer;
import android.app.Activity;
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.util.Log;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.xjp.aidla.IMyAidlInterface;
public class MainActivity extends Activity {
IMyAidlInterface mStub;
TextView txt;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("xjp", "the Connected====>" + System.currentTimeMillis());
mStub = IMyAidlInterface.Stub.asInterface(service);
if (mStub == null) {
Log.e("xjp", "the mStub is null");
} else {
try {
int value = mStub.add(1, 8);
txt.setText(value + "");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.text);
Intent intent = new Intent();
//android 5.0以后直设置action不能启动相应的服务,需要设置packageName或者Component。
intent.setAction("com.xjp.myService");
intent.setComponent(new ComponentName("com.example.xjp.aidl", "com.example.xjp.aidl.MyServer"));
//绑定服务
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
Log.e("xjp", "the bindServer start..====>" + System.currentTimeMillis());
}
@Override
protected void onDestroy() {
//解绑服务
unbindService(serviceConnection);
super.onDestroy();
}
}
如此一来,客户端也写好了。先安装服务端,在安装客户端。进入客户端之后实现了远程调用Service。
以上是最简单的 aidl跨进程传递java基本类型数据,最近在项目中遇到这么个需求,跨进程传递bitmap图片。当时看到这个需求就懵逼了,后来仔细分析其实是可以实现Android 利用Aidl实现跨进程传递 Bitmap对象。那么该怎么实现呢?
我们都知道Aidl支持跨进程传递 Java基本数据类型中包括byte这个类型,那么思路是不是来了呢?我们可以在服务端将需要传递的图片由bitmap转换成 byte[]类型。代码如下:
public byte[] getBitmap() {
Bitmap bitmap = BitmapFactory.decodeResource(MyServer.this.getResources(), R.drawable.bg_top);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 90, baos);//压缩位图
return baos.toByteArray();//创建分配字节数组
}
如此一来就将Bitmap对象转换成byte[]类型了,现在aidl就可以跨进程传递byte[]类型数据。
在客户端你只需要将远程调用得到的byte[]类型转换成Bitmap对象,然后显示即可。客户端代码如下:
byte[] bytes = mStub.getBitmap();
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
img.setImageBitmap(bitmap);
是不是很简单,其实Aidl传递图片只是将Bitamp对象转换成byte[]类型,然后由byte[]类型在转换回Bitmap对象的一个过程。
这是一个在AS下最简单的一个AIDL编程:
1.服务端创建一个aidl目录,然后在该目录下新建一个.aidl为后缀的接口类,该类定义远程调用的接口方法。
2.build编译之后会在app/build/generated/source/aidl/debug目录下会生成aidl远程实现类,该类是AS自动生成的。
3.在AndroidManifest.xml下配置Service的action和process属性。
4.将服务端的aidl目录拷贝到客户端相应的目录下,然后编写客户端调用代码,AS下简单的aidl编程就ok了。
5.利用Aidl实现跨进程传递较小的Bitmap对象。