Android studio 下的aidl编程实现Android的夸进程间通信

前言

Android中夸进程间通信方式有很多种方式,比如:aidl,Messenger,文件共享,广播(BroadCast),ContentProvider,Socket(网络通信)。每种方式都有自己的使用场景和优缺点,接下来几篇博客我们一一学习他们是怎么使用的。这篇博客主要是介绍Android studio下实现aidl编程。

AIDL的使用步骤

aidl远程调用传递的参数和返回值支持Java的基本类型(int long booen char byte等)和String,List,Map等。当然也支持一个自定义对象的传递,不过此时就需要做一些特别处理了,后续会介绍aidl传递对象怎么处理。
有关aidl的相关概念这里就不详细解释了,我们来看看在Android Studio下怎么来实现aidl编程的。

服务端

生成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跨进程传递Bitamp对象

以上是最简单的 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对象的一个过程。

  • 注意:在亲测的过程中发现,利用aidl传递Bitamp对象是有一定的限制的,当Bitmap达到一定大小时是Aidl跨进程传递会失败。所以,利用Aidl跨进程可以传递较小的图片,至于多大可以根据你需求测试以免出错。这也是由于Aidl跨进程传递数据是有限的,数据过大就不适合利用aidl夸进程传递了,应该用Socket传递。因为Aidl跨进程间通信是一个实时的,同步的的一个过程,即Aidl的远程调用方法不能被阻塞,因此不能传递大数据。

总结

这是一个在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对象。

你可能感兴趣的:(andorid,开发,Android进程间通信)