资讯专栏INFORMATION COLUMN

AIDL基本使用

brianway / 2657人阅读

摘要:可以看到和服务端的基本是一行一行对应的。的作用其实就是为我们创建驱动,即服务端与客户端连接的桥梁。下面看运行结果是不是很好的实现了我们两个应用程序间的通讯,并没有使用文件,也从侧面分析了我们上述分析是正确的。

1、概述

Binder能干什么?Binder可以提供系统中任何程序都可以访问的全局服务。这个功能当然是任何系统都应该提供的,下面我们简单看一下Android的Binder的框架

Android Binder框架分为服务器接口、Binder驱动、以及客户端接口;简单想一下,需要提供一个全局服务,那么全局服务那端即是服务器接口,任何程序即客户端接口,它们之间通过一个Binder驱动访问。

服务器端接口:实际上是Binder类的对象,该对象一旦创建,内部则会启动一个隐藏线程,会接收Binder驱动发送的消息,收到消息后,会执行Binder对象中的onTransact()函数,并按照该函数的参数执行不同的服务器端代码。

Binder驱动:该对象也为Binder类的实例,客户端通过该对象访问远程服务。

客户端接口:获得Binder驱动,调用其transact()发送消息至服务器

如果大家对上述不了解,没关系,下面会通过例子来更好的说明,实践是检验真理的唯一标准嘛

2、AIDL的使用

如果对Android比较熟悉,那么一定使用过AIDL,如果你还不了解,那么也没关系,下面会使用一个例子展示AIDL的用法。

我们使用AIDL实现一个跨进程的加减法调用

1、服务端
新建一个项目,创建一个包名:com.zhy.calc.aidl,在包内创建一个ICalcAIDL文件:

package com.zhy.calc.aidl;  
interface ICalcAIDL  
{  
    int add(int x , int y);  
    int min(int x , int y );  
}  

注意,文件名为ICalcAIDL.aidl

然后在项目的gen目录下会生成一个ICalcAIDL.java文件,暂时不贴这个文件的代码了,后面会详细说明

然后我们在项目中新建一个Service,代码如下:

package com.example.zhy_binder;  
  
import com.zhy.calc.aidl.ICalcAIDL;  
  
import android.app.Service;  
import android.content.Intent;  
import android.os.IBinder;  
import android.os.RemoteException;  
import android.util.Log;  
  
public class CalcService extends Service  
{  
    private static final String TAG = "server";  
  
    public void onCreate()  
    {  
        Log.e(TAG, "onCreate");  
    }  
  
    public IBinder onBind(Intent t)  
    {  
        Log.e(TAG, "onBind");  
        return mBinder;  
    }  
  
    public void onDestroy()  
    {  
        Log.e(TAG, "onDestroy");  
        super.onDestroy();  
    }  
  
    public boolean onUnbind(Intent intent)  
    {  
        Log.e(TAG, "onUnbind");  
        return super.onUnbind(intent);  
    }  
  
    public void onRebind(Intent intent)  
    {  
        Log.e(TAG, "onRebind");  
        super.onRebind(intent);  
    }  
  
    private final ICalcAIDL.Stub mBinder = new ICalcAIDL.Stub()  
    {  
  
        @Override  
        public int add(int x, int y) throws RemoteException  
        {  
            return x + y;  
        }  
  
        @Override  
        public int min(int x, int y) throws RemoteException  
        {  
            return x - y;  
        }  
  
    };  
  
}  

在此Service中,使用生成的ICalcAIDL创建了一个mBinder的对象,并在Service的onBind方法中返回

最后记得在AndroidManifest中注册

  
             
                 
  
                 
             
   

这里我们指定了一个name,因为我们一会会在别的应用程序中通过Intent来查找此Service;这个不需要Activity,所以我也就没写Activity,安装完成也看不到安装图标,悄悄在后台运行着。
到此,服务端编写完毕。下面开始编写客户端

2、客户端
客户端的代码比较简单,创建一个布局,里面包含4个按钮,分别为绑定服务,解除绑定,调用加法,调用减法

布局文件:

  
  
    

主Activity

package com.example.zhy_binder_client;  
  
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.util.Log;  
import android.view.View;  
import android.widget.Toast;  
  
import com.zhy.calc.aidl.ICalcAIDL;  
  
public class MainActivity extends Activity  
{  
    private ICalcAIDL mCalcAidl;  
  
    private ServiceConnection mServiceConn = new ServiceConnection()  
    {  
        @Override  
        public void onServiceDisconnected(ComponentName name)  
        {  
            Log.e("client", "onServiceDisconnected");  
            mCalcAidl = null;  
        }  
  
        @Override  
        public void onServiceConnected(ComponentName name, IBinder service)  
        {  
            Log.e("client", "onServiceConnected");  
            mCalcAidl = ICalcAIDL.Stub.asInterface(service);  
        }  
    };  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState)  
    {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
  
    }  
      
    /** 
     * 点击BindService按钮时调用 
     * @param view 
     */  
    public void bindService(View view)  
    {  
        Intent intent = new Intent();  
        intent.setAction("com.zhy.aidl.calc");  
        bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);  
    }  
    /** 
     * 点击unBindService按钮时调用 
     * @param view 
     */  
    public void unbindService(View view)  
    {  
        unbindService(mServiceConn);  
    }  
    /** 
     * 点击12+12按钮时调用 
     * @param view 
     */  
    public void addInvoked(View view) throws Exception  
    {  
  
        if (mCalcAidl != null)  
        {  
            int addRes = mCalcAidl.add(12, 12);  
            Toast.makeText(this, addRes + "", Toast.LENGTH_SHORT).show();  
        } else  
        {  
            Toast.makeText(this, "服务器被异常杀死,请重新绑定服务端", Toast.LENGTH_SHORT)  
                    .show();  
  
        }  
  
    }  
    /** 
     * 点击50-12按钮时调用 
     * @param view 
     */  
    public void minInvoked(View view) throws Exception  
    {  
  
        if (mCalcAidl != null)  
        {  
            int addRes = mCalcAidl.min(58, 12);  
            Toast.makeText(this, addRes + "", Toast.LENGTH_SHORT).show();  
        } else  
        {  
            Toast.makeText(this, "服务端未绑定或被异常杀死,请重新绑定服务端", Toast.LENGTH_SHORT)  
                    .show();  
  
        }  
  
    }  
  
} 

很标准的绑定服务的代码。

直接看运行结果:

我们首先点击BindService按钮,查看log

08-09 22:56:38.959: E/server(29692): onCreate  
08-09 22:56:38.959: E/server(29692): onBind  
08-09 22:56:38.959: E/client(29477): onServiceConnected  

可以看到,点击BindService之后,服务端执行了onCreate和onBind的方法,并且客户端执行了onServiceConnected方法,标明服务器与客户端已经联通

然后点击12+12,50-12可以成功的调用服务端的代码并返回正确的结果

下面我们再点击unBindService

08-09 22:59:25.567: E/server(29692): onUnbind  
08-09 22:59:25.567: E/server(29692): onDestroy  

由于我们当前只有一个客户端绑定了此Service,所以Service调用了onUnbind和onDestory

然后我们继续点击12+12,50-12,通过上图可以看到,依然可以正确执行,也就是说即使onUnbind被调用,连接也是不会断开的,那么什么时候会端口呢?

即当服务端被异常终止的时候,比如我们现在在手机的正在执行的程序中找到该服务:

点击停止,此时查看log

08-09 23:04:21.433: E/client(30146): onServiceDisconnected  

可以看到调用了onServiceDisconnected方法,此时连接被断开,现在点击12+12,50-12的按钮,则会弹出Toast服务端断开的提示。

说了这么多,似乎和Binder框架没什么关系,下面我们来具体看一看AIDL为什么做了些什么。

3、分析AIDL生成的代码

1、服务端
先看服务端的代码,可以看到我们服务端提供的服务是由

private final ICalcAIDL.Stub mBinder = new ICalcAIDL.Stub()  
    {  
  
        @Override  
        public int add(int x, int y) throws RemoteException  
        {  
            return x + y;  
        }  
  
        @Override  
        public int min(int x, int y) throws RemoteException  
        {  
            return x - y;  
        }  
  
    };  

ICalcAILD.Stub来执行的,让我们来看看Stub这个类的声明:

public static abstract class Stub extends android.os.Binder implements com.zhy.calc.aidl.ICalcAIDL  

清楚的看到这个类是Binder的子类,是不是符合我们文章开通所说的服务端其实是一个Binder类的实例

接下来看它的onTransact()方法:

@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;  
}  
case TRANSACTION_min:  
{  
data.enforceInterface(DESCRIPTOR);  
int _arg0;  
_arg0 = data.readInt();  
int _arg1;  
_arg1 = data.readInt();  
int _result = this.min(_arg0, _arg1);  
reply.writeNoException();  
reply.writeInt(_result);  
return true;  
}  
}  
return super.onTransact(code, data, reply, flags);  
}  

文章开头也说到服务端的Binder实例会根据客户端依靠Binder驱动发来的消息,执行onTransact方法,然后由其参数决定执行服务端的代码。

 

可以看到onTransact有四个参数

code , data ,replay , flags

code 是一个整形的唯一标识,用于区分执行哪个方法,客户端会传递此参数,告诉服务端执行哪个方法

data客户端传递过来的参数

replay服务器返回回去的值

flags标明是否有返回值,0为有(双向),1为没有(单向)

我们仔细看case TRANSACTION_min中的代码

data.enforceInterface(DESCRIPTOR);与客户端的writeInterfaceToken对用,标识远程服务的名称

int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();

接下来分别读取了客户端传入的两个参数

int _result = this.min(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);

然后执行this.min,即我们实现的min方法;返回result由reply写回。

add同理,可以看到服务端通过AIDL生成Stub的类,封装了服务端本来需要写的代码。

2、客户端

客户端主要通过ServiceConnected与服务端连接

private ServiceConnection mServiceConn = new ServiceConnection()  
    {  
        @Override  
        public void onServiceDisconnected(ComponentName name)  
        {  
            Log.e("client", "onServiceDisconnected");  
            mCalcAidl = null;  
        }  
  
        @Override  
        public void onServiceConnected(ComponentName name, IBinder service)  
        {  
            Log.e("client", "onServiceConnected");  
            mCalcAidl = ICalcAIDL.Stub.asInterface(service);  
        }  
};  

如果你比较敏锐,应该会猜到这个onServiceConnected中的IBinder实例,其实就是我们文章开通所说的Binder驱动,也是一个Binder实例
在ICalcAIDL.Stub.asInterface中最终调用了:

return new com.zhy.calc.aidl.ICalcAIDL.Stub.Proxy(obj);  

这个Proxy实例传入了我们的Binder驱动,并且封装了我们调用服务端的代码,文章开头说,客户端会通过Binder驱动的transact()方法调用服务端代码

直接看Proxy中的add方法

@Override public int add(int x, int y) 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(x);  
_data.writeInt(y);  
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);  
_reply.readException();  
_result = _reply.readInt();  
}  
finally {  
_reply.recycle();  
_data.recycle();  
}  
return _result;  
}  

首先声明两个Parcel对象,一个用于传递数据,一个用户接收返回的数据

_data.writeInterfaceToken(DESCRIPTOR);与服务器端的enforceInterfac对应

_data.writeInt(x);
_data.writeInt(y);写入需要传递的参数

mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);

终于看到了我们的transact方法,第一个对应服务端的code,_data,_repay分别对应服务端的data,reply,0表示是双向的

_reply.readException();
_result = _reply.readInt();

最后读出我们服务端返回的数据,然后return。可以看到和服务端的onTransact基本是一行一行对应的。

到此,我们已经通过AIDL生成的代码解释了Android Binder框架的工作原理。Service的作用其实就是为我们创建Binder驱动,即服务端与客户端连接的桥梁。

AIDL其实通过我们写的aidl文件,帮助我们生成了一个接口,一个Stub类用于服务端,一个Proxy类用于客户端调用。那么我们是否可以不通过写AIDL来实现远程的通信呢?下面向大家展示如何完全不依赖AIDL来实现客户端与服务端的通信。

4、不依赖AIDL实现程序间通讯

1、服务端代码
我们新建一个CalcPlusService.java用于实现两个数的乘和除

package com.example.zhy_binder;  
  
import android.app.Service;  
import android.content.Intent;  
import android.os.Binder;  
import android.os.IBinder;  
import android.os.Parcel;  
import android.os.RemoteException;  
import android.util.Log;  
  
public class CalcPlusService extends Service  
{  
    private static final String DESCRIPTOR = "CalcPlusService";  
    private static final String TAG = "CalcPlusService";  
  
    public void onCreate()  
    {  
        Log.e(TAG, "onCreate");  
    }  
  
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId)  
    {  
        Log.e(TAG, "onStartCommand");  
        return super.onStartCommand(intent, flags, startId);  
    }  
  
    public IBinder onBind(Intent t)  
    {  
        Log.e(TAG, "onBind");  
        return mBinder;  
    }  
  
    public void onDestroy()  
    {  
        Log.e(TAG, "onDestroy");  
        super.onDestroy();  
    }  
  
    public boolean onUnbind(Intent intent)  
    {  
        Log.e(TAG, "onUnbind");  
        return super.onUnbind(intent);  
    }  
  
    public void onRebind(Intent intent)  
    {  
        Log.e(TAG, "onRebind");  
        super.onRebind(intent);  
    }  
  
    private MyBinder mBinder = new MyBinder();  
  
    private class MyBinder extends Binder  
    {  
        @Override  
        protected boolean onTransact(int code, Parcel data, Parcel reply,  
                int flags) throws RemoteException  
        {  
            switch (code)  
            {  
            case 0x110:  
            {  
                data.enforceInterface(DESCRIPTOR);  
                int _arg0;  
                _arg0 = data.readInt();  
                int _arg1;  
                _arg1 = data.readInt();  
                int _result = _arg0 * _arg1;  
                reply.writeNoException();  
                reply.writeInt(_result);  
                return true;  
            }  
            case 0x111:  
            {  
                data.enforceInterface(DESCRIPTOR);  
                int _arg0;  
                _arg0 = data.readInt();  
                int _arg1;  
                _arg1 = data.readInt();  
                int _result = _arg0 / _arg1;  
                reply.writeNoException();  
                reply.writeInt(_result);  
                return true;  
            }  
            }  
            return super.onTransact(code, data, reply, flags);  
        }  
  
    };  
  
}  

我们自己实现服务端,所以我们自定义了一个Binder子类,然后复写了其onTransact方法,我们指定服务的标识为CalcPlusService,然后0x110为乘,0x111为除;

记得在AndroidMenifest中注册

  
             
                 
                 
             
  

服务端代码结束。
2、客户端代码
多带带新建了一个项目,代码和上例很类似

package com.example.zhy_binder_client03;  
  
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.view.View;  
import android.widget.Toast;  
  
public class MainActivity extends Activity  
{  
  
    private IBinder mPlusBinder;  
    private ServiceConnection mServiceConnPlus = new ServiceConnection()  
    {  
        @Override  
        public void onServiceDisconnected(ComponentName name)  
        {  
            Log.e("client", "mServiceConnPlus onServiceDisconnected");  
        }  
  
        @Override  
        public void onServiceConnected(ComponentName name, IBinder service)  
        {  
  
            Log.e("client", " mServiceConnPlus onServiceConnected");  
            mPlusBinder = service;  
        }  
    };  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState)  
    {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
  
    }  
  
    public void bindService(View view)  
    {  
        Intent intentPlus = new Intent();  
        intentPlus.setAction("com.zhy.aidl.calcplus");  
        boolean plus = bindService(intentPlus, mServiceConnPlus,  
                Context.BIND_AUTO_CREATE);  
        Log.e("plus", plus + "");  
    }  
  
    public void unbindService(View view)  
    {  
        unbindService(mServiceConnPlus);  
    }  
  
    public void mulInvoked(View view)  
    {  
  
        if (mPlusBinder == null)  
        {  
            Toast.makeText(this, "未连接服务端或服务端被异常杀死", Toast.LENGTH_SHORT).show();  
        } else  
        {  
            android.os.Parcel _data = android.os.Parcel.obtain();  
            android.os.Parcel _reply = android.os.Parcel.obtain();  
            int _result;  
            try  
            {  
                _data.writeInterfaceToken("CalcPlusService");  
                _data.writeInt(50);  
                _data.writeInt(12);  
                mPlusBinder.transact(0x110, _data, _reply, 0);  
                _reply.readException();  
                _result = _reply.readInt();  
                Toast.makeText(this, _result + "", Toast.LENGTH_SHORT).show();  
  
            } catch (RemoteException e)  
            {  
                e.printStackTrace();  
            } finally  
            {  
                _reply.recycle();  
                _data.recycle();  
            }  
        }  
  
    }  
      
    public void divInvoked(View view)  
    {  
  
        if (mPlusBinder == null)  
        {  
            Toast.makeText(this, "未连接服务端或服务端被异常杀死", Toast.LENGTH_SHORT).show();  
        } else  
        {  
            android.os.Parcel _data = android.os.Parcel.obtain();  
            android.os.Parcel _reply = android.os.Parcel.obtain();  
            int _result;  
            try  
            {  
                _data.writeInterfaceToken("CalcPlusService");  
                _data.writeInt(36);  
                _data.writeInt(12);  
                mPlusBinder.transact(0x111, _data, _reply, 0);  
                _reply.readException();  
                _result = _reply.readInt();  
                Toast.makeText(this, _result + "", Toast.LENGTH_SHORT).show();  
  
            } catch (RemoteException e)  
            {  
                e.printStackTrace();  
            } finally  
            {  
                _reply.recycle();  
                _data.recycle();  
            }  
        }  
  
    }  
}  

为了明了,我直接在mulInvoked里面写了代码,和服务端都没有抽象出一个接口。首先绑定服务时,通过onServiceConnected得到Binder驱动即mPlusBinder;

然后准备数据,调用transact方法,通过code指定执行服务端哪个方法,代码和上面的分析一致。

下面看运行结果:

是不是很好的实现了我们两个应用程序间的通讯,并没有使用aidl文件,也从侧面分析了我们上述分析是正确的。

  

  

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/2978.html

相关文章

  • AIDL基本使用

    摘要:显然,到目前为止能够得到对象的类只有通过自动生成的类。开发流程稍高级使用分支此处文档说明基于开发流程最基础使用章节,通过列出不同点进行说明。 对于AIDL的一些使用:最基础使用、稍高级使用、...... 基本概念 此章节可能和本文没有太大关系,了解一下即可 图片解释 showImg(https://segmentfault.com/img/remote/146000001226377...

    bbbbbb 评论0 收藏0
  • IPC机制之AIDL

    摘要:在我的理解里,定向是这样的中的定向表示了在跨进程通信中数据的流向,其中表示数据只能由客户端流向服务端,表示数据只能由服务端流向客户端,而则表示数据可在服务端与客户端之间双向流通。一类是用来定义方法接口,以供系统使用来完成跨进程通信的。 正文 1,概述 AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言。...

    骞讳护 评论0 收藏0
  • Aidl进程间通信详细介绍

    摘要:实际开发中案例操作通信业务需求多进程通信应用服务端某客户端调试工具。当服务端和客户端位于同一个进程时,方法调用不会走跨进程的过程,当两者处于不同晋城市,方法调用走过程,这个逻辑由的内部代理类完成。 目录介绍 1.问题答疑 2.Aidl相关属性介绍 2.1 AIDL所支持的数据类型 2.2 服务端和客户端 2.3 AIDL的基本概念 3.实际开发中案例操作 3.1 aidl...

    pepperwang 评论0 收藏0
  • Aidl进程间通信详细介绍

    摘要:实际开发中案例操作通信业务需求多进程通信应用服务端某客户端调试工具。当服务端和客户端位于同一个进程时,方法调用不会走跨进程的过程,当两者处于不同晋城市,方法调用走过程,这个逻辑由的内部代理类完成。 目录介绍 1.问题答疑 2.Aidl相关属性介绍 2.1 AIDL所支持的数据类型 2.2 服务端和客户端 2.3 AIDL的基本概念 3.实际开发中案例操作 3.1 aidl...

    CoreDump 评论0 收藏0
  • Android进程间通信(一):AIDL使用详解

    一、概述 AIDL是Android Interface Definition Language的缩写,即Android接口定义语言。它是Android的进程间通信比较常用的一种方式。 Android中,每一个进程都有自己的Dalvik VM实例,拥有自己的独立的内存空间,进程与进程之间不共享内存,这就产生了进程间通信的需求。 二、语法 AIDL是Android接口定义语言,是一门语言,所以它拥有自...

    RobinTang 评论0 收藏0
  • 进程间通信--IPC

    摘要:前言进程间通信简称就是指进程与进程之间进行通信一般来说一个只有一个进程但是可能会有多个线程所以我们用得比较多的是多线程通信比如但是在一些特殊的情况下我们会需要多个进程或者是我们在远程服务调用时就需要跨进程通信了设置多进程设置多进程的步骤很 前言: 进程间通信(Inter-Process Communication),简称IPC,就是指进程与进程之间进行通信.一般来说,一个app只有一个...

    thursday 评论0 收藏0

发表评论

0条评论

brianway

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<