Android入门——远程Remote Service AIDL详解及应用

引言

之前一篇文章总结了Android入门——Service详解及应用(一)本地服务Local Service只用于服务和访问者在同一个进程中(同一应用)的通信。而使用远程服务Remote Service和AIDL接口语言用于跨进程之间的通信。在Android中每个应用都有自己的进程,当需要在不同进程之间传递对象时可以通过AIDL。

一AIDL(Android Interfate Definition Language)的概念

接口定义语言,用于约束两个进程之间的通讯规则,供编译器生成代码实现android设备上两个进程间的通信(IPC),IPC会首先转换成AIDL协议消息,然后发生给对方,对方收到AIDL消息之后再转换成相应的对象,由于进程之间通信需要双向转换,所以android采用代理类在背后实现了信息的双向转换,代理类由android编译器生成,对于开发人员来说是透明的。简单来说就是进程之间的通信只能通过Android系统本身,而AIDL就是系统约束通信的规则。

二创建AIDL文件四个步骤

假设App A要与B 进行通信,调用B中的getStr方法,B应用以Service方式向A提供服务,需要以下四步。
- 由于AIDL的格式与接口差不多,可以先采用new 一个interface
- 定义用于进程之间通信的方法
- 再把接口的java文件的后缀改为aidl,再刷新下项目
这里写图片描述
- 当aidl文件创建之后,编译器会自动在gen文件中同步生成相同名称的接口文件(接口文件件里生成一个Stub的抽象类,里面包含aidl定义的方法,和其他一些辅助方法,值得注意的是asInterface(IBinder ibinder)方法用于返回接口的实例,对于远程服务调用,返回给客户端的对象为代理对象,客户端在onServiceConnected(ComponentName name,IBinder service)方法引用该对象时不直接转成接口的实例,应用asInterface(IBinder ibinder)进行类型转换)

三编写AIDL需要注意的几点

  • 接口名与aidl文件名相同
  • 接口和方法前不添加权限修饰符public、protected、private等,也不能用final、static。
  • aidl默认支持的类型包括java基本类型(int、long、boolean等)和(String、List、Map、CharSequence)使用这些类型时不需要import声明,对于List和Map的元素类型必须是aidl支持的类型。但如果使用自定义类型作为参数或返回值时,必须实现Parcelable接口。
  • 自定义类型和AIDL生成的其他接口类型在aidl描述文件中,应该显式import,即便是在该类和定义的包在同一个包中。
  • 在aidl文件中所有非Java基本类型参数必须加上in、out、inout标记,以表明参数是输入参数、输出参数还是输入输出参数。
  • Java 默认的基本类型参数的标记是in,不能为其他标记。

四远程服务Remote Service的简单应用

例:AIDLClient(客户端)利用AIDL远程调用AIDLService(服务端)中的getStr方法

4.1 实现远程服务端

  • 创建AIDL初始文件,把接口文件名AIDLServiceDemo.java的后缀改为AIDLServiceDemo.aidl刷新AIDLService项目
package cmo.test.aidl;
//原始为aidl接口文件AIDLServiceDemo.java
interface AIDLServiceDemo {
    /**
     * 用于AIDLService和AIDLClient的aidl
     */
    String getStr();//定义用于进程之间通信的接口方法描述规则,是供编译器自动生成通信代码的
}
//gen里的aidl文件
/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\Android WorkPlace\\AIDLService\\src\\cmo\\test\\aidl\\AIDLServiceDemo.aidl
 */
package cmo.test.aidl;
public interface AIDLServiceDemo extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements cmo.test.aidl.AIDLServiceDemo
{
private static final java.lang.String DESCRIPTOR = "cmo.test.aidl.AIDLServiceDemo";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an cmo.test.aidl.AIDLServiceDemo interface,
 * generating a proxy if needed.
 */
public static cmo.test.aidl.AIDLServiceDemo asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof cmo.test.aidl.AIDLServiceDemo))) {
return ((cmo.test.aidl.AIDLServiceDemo)iin);
}
return new cmo.test.aidl.AIDLServiceDemo.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_getStr:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getStr();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements cmo.test.aidl.AIDLServiceDemo
{
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;
}
/**
     * 用于AIDLService和AIDLClient的aidl
     */
@Override public java.lang.String getStr() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getStr, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getStr = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
/**
     * 用于AIDLService和AIDLClient的aidl
     */
public java.lang.String getStr() throws android.os.RemoteException;
}
  • 继承Service,定义IBinder对象(无论是远程还是本地服务交互的实都是通过IBinder对象的,不同的是本地服务直接继承Binder,而远程服务则继承aidl里的(AIDLServiceDemo.Stub)Stub抽象类),重写并在onBind方法返回ibinder对象的实例
package cmo.test.remoteservice;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import cmo.test.aidl.*;//记得手动import aidl文件

public class RemoteService extends Service {
    /**
     * 继承aidl文件里的Stub封装IBinder对象,已经具备了远程服务的特点
     */
    private final static String Tag="SERVICE_REMOTESERVICE";
    private IBinder binder=new RemoteBinder();
    @Override
    public IBinder onBind(Intent arg0) {
        Log.d(Tag,"bindService执行后返回binder");
        return binder;
    }

    private String getName(){
        Log.d(Tag,"这个就是远程服务!");
        return "这个就是远程服务!";
    }

    private final class RemoteBinder extends AIDLServiceDemo.Stub{
        @Override
        public String getStr() throws RemoteException { 
            Log.d(Tag,"客户端与服务端将要进行通信的接口方法");
            return getName();
        }
    }
}
  • 在清单文件中声明服务,并设置对应的action
<!--action值 即是客户端远程服务隐式激活intent对象的action-->
<service android:name="cmo.test.remoteservice.RemoteService">
            <intent-filter >
                <action android:name="cmo.test.service.remote"/>
            </intent-filter>
        </service>

4.2 实现远程客户端

  • 由于两个进程需要互相通信必须要遵守相同的规则,所以直接把服务端的src下对应包的aidl文件拷贝到客户端下的src刷新即可
  • 还是和本地服务的通信一样,实现一个继承自ServiceConnection的内部类,在onServiceConnected方法里把IBinder对象转为我们实际要调用的接口实例(与本地服务的区别有两点,一本地服务用的是本地的自定义的接口实例而远程服务用的是与aidl文件名称相同的接口;二、不能**直接强制转换,而是通过aidl里Stub的asInterface方法进行转换**AIDLServiceDemo.Stub.asInterface(agr1))
  • 在Activity的声明周期里,绑定服务和取消绑定
  • 通过接口去调用暴露出来的方法
package cmo.test.aidlclient;

import android.app.Activity;
import android.content.ComponentName;
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 cmo.test.aidl.*;

public class MainActivity extends Activity {
    private final static String remoteAction="cmo.test.service.remote";
    private final static String Tag="CLIENT_REMOTESERVICE";
    private RemoteServiceConn conn=new RemoteServiceConn();//只要是进程通信都需要实现,因为系统是在这个ServiceConnecttion类的相关方法通过回调返回Ibinder对象的 
    private AIDLServiceDemo Iaidl;//真正暴露出给客户端的接口实例,远程服务的方法是通过这个接口实例去调用的

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent=new Intent(remoteAction);
        bindService(intent,conn,BIND_AUTO_CREATE);
        Log.d(Tag,"绑定远程服务");
    }
    @Override
    protected void onDestroy() {
        unbindService(conn);
        Log.d(Tag,"取消绑定远程服务");
        super.onDestroy();
    }

    public void callRemoteServic(View view) throws RemoteException{

        Log.d(Tag,Iaidl.getStr());
    }
    private final class  RemoteServiceConn implements ServiceConnection{
        @Override
        public void onServiceConnected(ComponentName arg0, IBinder arg1) {
            Iaidl=AIDLServiceDemo.Stub.asInterface(arg1);//把返回来的IBinder代理对象转为接口
            Log.d(Tag,"客户端与远程服务端连接成功");
        }
        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            Iaidl=null;
            Log.d(Tag,"客户端与远程服务端断开连接");
        }
    }
}

运行结果
这里写图片描述

小结

无论是本地服务还是远程服务,他们之间都是借助不同IBinder对象来实现的,本地服务的IBInder对象是继承自Binder类的,而远程服务的IBinder对象则是继承自aidl里的Stub抽象类的,而客户端的调用的基本原理也是在ServiceConnection中的onServiceConnected接收到系统回调返回服务端封装的IBinder对象,并转为对应的接口,再通过接口去调用服务中的方法。同理,只要我们构造了对应的系统服务aidl文件,我们就可以通过AIDL去远程访问对应的系统服务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CrazyMo_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值