android的AIDL简单介绍

1. aidl简单介绍

AIDL全称是android interface define language, 它主要是为了实现RPC, 使得进程之间的数据传输像函数调用一样简单,在android系统中使用aidl常见的场景就是远程服务向客户端应用程序提供功能性接口,客户端调用这些接口就可以和远程服务进行通信。而这些接口即需要使用aidl来定义出来。

2. aidl接口的参数修饰符号

    aidl中的接口传递的数据,简单数据类型可以直接传递,而对于其它数据类型,比如自定义数据结构或者或者由aidl生成的接口则有其它特殊的要求。以下是aidl中支持的数据类型:

        - Java的原生类型

        - String, CharSequence

        - List, Map对象的元素必须是aidl支持的数据类型,如果是非原生数据类型,还必须import 对应的类,并且需要加上稍后提到的参数修饰符号in, out或者inout

        - 由aidl生成的接口作为参数,但需要import接口

        - 实现Parcelable接口的自定义类,需要import这个类

    aidl 中有以下几个参数修饰符号:

        - in 表示是由客户端传输到远程服务端的数据

        - out 表示是由远程服务端传输到客户端的数据

        - inout 表示由客户端传输到远程服务端的数据,而服务端可以修改这个数据并且传回客户端

3. 以下是一个简单的例子,定义一个.aidl文件,在service中实现aidl文件中定义的接口,以及如何使用这些接口。

    3.1 首先我们定义一个Person.aidl,因为需要在进程之间传递数据,所以定义的这个Person需要implements Parcelable接口,从而实现序列化

    

package com.example.testapp;
 
parcelable Person;
    3.2 定义Person.java, implements Parcelable接口

package com.example.testapp;

import android.os.Parcel;
import android.os.Parcelable;

public class Person implements Parcelable{

	public int mAge;
	public int mWeight;

	public Person() {
		mAge = 0;
		mWeight = 0;
	}
	
	public Person(int age, int weight) {
		mAge = age;
		mWeight = weight;
	}

	public Person(Parcel source) {
		readFromParcel(source);
	}

	@Override
	public int describeContents() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void writeToParcel(Parcel arg0, int arg1) {
		// TODO Auto-generated method stub
		arg0.writeInt(mAge);
		arg0.writeInt(mWeight);
	}

	public void readFromParcel(Parcel _reply) {
		// TODO Auto-generated method stub
		mAge = _reply.readInt();
		mWeight = _reply.readInt();
	}
	
	public static final Parcelable.Creator<Person> CREATOR=new Creator<Person>() {
		@Override
		public Person[] newArray(int size) {
			return new Person[size];
		}

		@Override
		public Person createFromParcel(Parcel source) {
		    return new Person(source);
		}
	};
}

    3.3 定义DataListener.aidl,这个回调接口需要调用者去实现作为参数传输到远程服务(见RemoteService.aidl的register接口),可以看到DataListener中定义一个dataChange的方法,Person作为参数。之前做项目的时候遇到了这样一个问题,原以为dataChange函数的Person参数是远程服务传输到调用者,只需要用out修饰符就行了,但是实际结果证明,在服务器的Person并不能传送到调用者。需要使用inout修饰符

package com.example.testapp;

import com.example.testapp.Person;

interface DataListener {
	void dataChange(inout Person p);
}

    3.4 定义RemoteService.aidl, 这里我们使用了in修饰符,调用者传输数据到远程服务;inout调用者传输数据到远程服务,并且远程服务可修改这个数据

package com.example.testapp;

import com.example.testapp.DataListener;
import com.example.testapp.Person;

interface RemoteService {
    void print(in Person p);
    void modify(inout Person p);
    void register(DataListener listener);
}

    3.5 定义MyRemoteService.java实现在RemoteService.aidl中提供的接口

package com.example.testapp;

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

public class MyRemoteService extends Service{
    public static final String TAG = "MyRemoteService";
  
    private class MyServiceImpl extends RemoteService.Stub{  
  
        @Override  
        public void print(Person p) throws RemoteException {
        	Log.d(TAG, "MyRemoteService, process id:" + android.os.Process.myPid());
            Log.d(TAG, "age:" + p.mAge + "; weight:" + p.mWeight);
        }  
  
        @Override  
        public void modify(Person p) throws RemoteException {  
            p.mAge = 30;
            p.mWeight = 140;
        }

		@Override
		public void register(DataListener listener) throws RemoteException {
			// TODO Auto-generated method stub
			Person p = new Person(23, 120);
			listener.dataChange(p);
		}
  
    }  
      
    @Override  
    public IBinder onBind(Intent arg0) {  
        //return implementation of AIDL  
    	Log.e(TAG, "onBind");
        return new MyServiceImpl();  
    }
      
    @Override  
    public void onDestroy() {  
        Log.e(TAG, "Release MyService");  
        super.onDestroy();  
    }
}

    3.5 定义一个MainActivity.java去bind service, 调用接口

package com.example.testapp;

public class MainActivity extends Activity {
	
    private static String TAG = "MainActivity";
    private Button btn = null;
	private RemoteService iService = null;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		btn = (Button)findViewById(R.id.startServiceBtn);
		btn.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				bindMyService();
			}
			
		});
	}
	
	private void bindMyService(){  
        <span style="white-space:pre">	</span>Intent intent = new Intent("com.example.testapp.startService");  
        <span style="white-space:pre">	</span>Log.d(TAG, "bindMyService, process id:" + android.os.Process.myPid());
        <span style="white-space:pre">	</span>bindService(intent, conn, Context.BIND_AUTO_CREATE);  
    <span style="white-space:pre">	</span>}
	
	private ServiceConnection conn = new ServiceConnection(){  

        @Override  
        public void onServiceConnected(ComponentName name, IBinder service) {  
            //return AIDL object,then call methods of AIDL  
            iService = RemoteService.Stub.asInterface(service);  
            try {
            	Log.d(TAG, "onServiceConnected, process id:" + android.os.Process.myPid());
            	Person p = new Person(40, 143);
         
                iService.print(p);
                iService.modify(p);
                
                Log.d(TAG, "age:" + p.mAge + "; weight:" + p.mWeight);

                iService.register(new DataListener.Stub(){

					@Override
					public void dataChange(Person p) throws RemoteException {
						// TODO Auto-generated method stub
						Log.d(TAG, "person.age:" + p.mAge);
						Log.d(TAG, "person.weight:" + p.mWeight);
					}
                });
            } catch (RemoteException e) {  
                Log.d(TAG, "Error while call on iService!");  
                e.printStackTrace();
            }  
        }  
  
        @Override  
        public void onServiceDisconnected(ComponentName arg0) {  
            Log.d(TAG, "release iService");  
        }  
    };  
}

运行程序, 打印结果如下:

可见modify方法修改了由调用者传输过去的数据,而register方法注册到远程服务的DataListener, 数据也正确的传输到调用者了。

09-21 22:08:16.561: D/MainActivity(24293): bindMyService, process id:24293
09-21 22:08:16.646: D/MainActivity(24293): onServiceConnected, process id:24293
09-21 22:08:16.648: D/MainActivity(24293): age:30; weight:140
09-21 22:08:16.650: D/MainActivity(24293): person.age:23
09-21 22:08:16.650: D/MainActivity(24293): person.weight:120

09-21 22:08:16.643: E/MyRemoteService(24329): onBind
09-21 22:08:16.648: D/MyRemoteService(24329): MyRemoteService, process id:24329
09-21 22:08:16.648: D/MyRemoteService(24329): age:40; weight:143

*****************************************************************************************************************************************

在3.3 提到了在dataChange函数里,如果使用out修饰符,远程服务的数据不能传输到调用者,以下是截取了自定生成文件DataListener.java的部分代码.

    - 使用inout修饰

@Override public void dataChange(com.example.testapp.Person p) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((p!=null)) {
_data.writeInt(1);
<span style="color:#ff6666;">p.writeToParcel(_data, 0);</span>
}
else {
_data.writeInt(0);
}
<span style="color:#ff6666;">mRemote.transact(Stub.TRANSACTION_dataChange, _data, _reply, 0);</span>


@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case TRANSACTION_dataChange:
{
data.enforceInterface(DESCRIPTOR);
com.example.testapp.Person _arg0;
if ((0!=data.readInt())) {
<span style="color:#ff6666;">_arg0 = com.example.testapp.Person.CREATOR.createFromParcel(data);</span>
}
else {
_arg0 = null;
}
<span style="color:#ff6666;">this.dataChange(_arg0);</span>

    - 使用out 修饰

@Override public void dataChange(com.example.testapp.Person p) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
<span style="color:#ff6666;">mRemote.transact(Stub.TRANSACTION_dataChange, _data, _reply, 0);</span>

通过对比上面的代码,可以看到使用inout修身的参数Person, 会调用writeToParcel写入_data, 然后调用onTransact函数,最后转换成了Person对象,最终调到了this.dataChange(_arg0)。 而对于out修饰的参数,并没有经过这一系列的转化,所以调用者没有获取到远程服务传输的数据。










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值