Binder与Service 通信机制详解四 (源码分析AIDL工作机制)

          这一篇我将给大家讲解AIDL是如何使得服务端和客户端能够进行通信的。我上传了一份简单的源码(前往下载),实现了不同进程服务端和客户端进行通信。大家在看这篇博客的时候务必要将这份源码导入工程跑起来。跟着讲解一步步走。当然,在阅读这篇文章的时候,最好将博客      看一遍。

源码执行过程分析:

          源码有上传也在下面贴出来了。

          1:首先把服务端app跑起来,服务端app界面里有两个按钮,一个是绑定服务,一个是解绑服务。

          2:点击绑定服务按钮,执行bindService(intent, conn, Service.BIND_AUTO_CREATE);执行该代码后,会相继执行MyService里面的onCreate->onBind。

              (在MyService.java 里面先会创建一个Binder类型的对象iPerson(private IPerson.Stub iPerson = new Person()),这个对象就是前面一直提到的服务端的那个Binder对象。然后服务执行onCreate函数启动服务,然后再执行public IBinder onBind(Intent intent)函数,该函数必须返回iPerson这个Binder类型的对象。我感觉只有这里返回之后才会开始执行上一篇博客贴的图5-3的流程,也就是AmS调用 scheduleBindService带的参数就包含这个服务端的Binder对象,继而会回调服务端MainActivity里面的conn接口,这也是为什么要conn接口会被触发,必须服务端要重载onBind这个函数的原因。)
         3:第二步中说到执行到了服务端MainActivity里面的conn接口,public void onServiceConnected(ComponentName name, IBinder service)函数会被执行,通过该参数调用IPerson iPerson = IPerson.Stub.asInterface(service);客户端就获取到了之前一直提到的客户端的Binder对象。(服务端onBind返回的对象为Person类型,Person类继承IPerson.Stub(为什么要添加这个Person类?因为IPerson.Stub是个抽象类,我们并不能直接通过它得到Binder的实例 ),IPerson.Stub继承Binder又implements了IPerson,所以此处可以认为IPerson 是Person的父类,把子类对象赋值给父类,再通过父类对象iPerson ,就可以调用到Person里面定义的setValue和getValue这两个函数,这也是常常说到的覆盖)。因为目前这个客户端和服务端处于同一个app当中,所以通过IPerson.Stub.asInterface(service);获取到的Binder类型的对象就是MyService.java里面onBinder返回的那个对象,也就是获取到的是服务端的那个Binder对象。若是客户端与服务端在不同的app当中,那么通过IPerson iPerson = IPerson.Stub.asInterface(service);获取到的Binder对象是Binder驱动中的那个mRemote对象。大家可以断点跟进asInterface中去看看。asInterface这个函数在AIDL生成的对应的java文件中也就是在IPerson.java这个文件中。如果上面的不是很能理解,可以先跳到下面我对AIDL生成的IPerson.java这个文件解释去先看。

        4:到这里服务端就执行完毕。那么我们接着把客户端app执行起来。

        5:客户端app启动,点击界面里的远程调用service按钮。那么会执行bindService(intent, conn, Service.BIND_AUTO_CREATE);代码。调用该代码并不会又去启动服务,因为服务端已经跑起来了,可以认为是为了客户端和服务端建立连接。断点跟踪服务端的MyService.java 里面的代码,发现不会执行onCreate,只会执行onBind,为什么会执行onBind,原因在步骤2中有解释。继而就会回调到客户端里面定义的conn接口。同样,客户端里面的conn接口里面的public void onServiceConnected(ComponentName name, IBinder service)函数会被执行。在该函数里面通过调用IPerson person= IPerson.Stub.asInterface(service);就可以获取到客户端所需要的这个Binder对象,不过此时该Binder对象其实是Binder驱动里面的那个mRemote对象。

        6:有了person这个对象,就可以调用服务端在Person里面定义的函数了(因为该对象其实就是来自服务端,所以自然可以调用服务端定义的函数)。接着代码往下走调用String name= person.getValue();。此时会调用到在客户端AIDL文件生成对应的IPerson.java这个文件里面的class Proxy里面的getValue函数,在该函数里面会调用mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0)(mRemote就是之前博客里一直提到的Binder驱动里面的那个Binder对象),此时客户端进入等待状态,等待服务端数据的返回。该函数被调用后,将代码切至服务端的AIDL文件生成对应的IPerson.java文件里面,此时会执行该文件里的onTransact函数,这样服务端就收到了客户端发送过来的数据,同时调用reply对象回复客户端,客户端收到回复后,从调用mRemote.transact之后的地方继续执行,然后就可以读取到服务端发送回来的消息。这也就是在博客二中,介绍Binder驱动时提到的那三点。

        7:好了,到了这里服务端和客户端就借助Binder对象真正的完成了跨进程间的一次通信,大家可以根据上述描述把代码跑起来,利用断点进行跟踪。接下来还一项任务就是分析AIDL文件生成的对应的Java 文件。


分析AIDL文件生成的对应的Java文件的组成

          源码为下面贴出的IPerson.java。该代码在gen目录下。
          首先看代码由三部分组成:

                  1:interface IPerson extends android.os.IInterface    
                  2:static abstract classStubextends android.os.Binder implements com.example.server.IPerson   
                  3:static class Proxy implements com.example.server.IPerson

          1: 在IPerson里面里面有类Stub,Proxy。同时还有在IPeson.java类里面声明的两个函数setValue和getValue。目的在于在conn接口,public void onServiceConnected(ComponentName name, IBinder service)里面通过IPerson iPerson = IPerson.Stub.asInterface(service);这种方式取得Binder对象时,可以直接通过iPerson调用到Peson.java里面的函数setValue和getValue。上面说过(Person类继承IPerson.Stub,IPerson.Stub继承Binder又implements了IPerson,所以此处可以认为IPerson 是Person的父类,把子类对象赋值给父类,再通过父类对象iPerson ,就可以调用到Person里面定义的setValue和getValue这两个函数,这也是常常说到的覆盖)。

          2:Stub 类继承至Binder并实现了IPerson接口。
                     a:Stub里面有个asInterface函数,用于返回这个接口对象类型。 
                                    参数是IBinder类型,通过该IBinder判断,如果服务端和客户端再同一个进程,

                                    那么得到的是该接口类型的对象。         

                                    如果服务端和客户端不在同一个进程,那么得到的是Proxy接口类型的对象。
                     b:Stub里面还有个asBinder函数,用于返回Stub类对象本身。
                     c:Stub里面还有个onTransact函数,用于接收客户端发送回来的数据信息。

         3:Proxy 类实现了IPerson接口。                       
                 a:该类的主要作用是提供给客户端使用,作为与服务端沟通的一个代理
                 b:Proxy里面有一个IBinder类型的mRemote成员变量,该变量其实是上面提到的Binder驱动里面的那个变量。
                 c:Proxy里面有个构造函数,用于给mRemote成员变量赋值,值的来源于Stub类里面asInterface函数的参数。
                 e:Proxy里面有步骤1中提到的getValue/setValue函数,并且提供了具体的实现,这些函数实际是给客户端调用的, 客户端获取到这个Proxy对象,然后调用Proxy里面getValue函数,getValue函数里面会调用mRemote.transact这个函数,目的在于给服务端发送消息,此时getValue函数会在mRemote.transact调用处进行等待服务端的消息返回。服务端接收到消息后就会触发步骤3中C里面提到的onTransact函数,然后服务端可以做出相应的回应。然后 getValue继续往下走,获取服务端返回的值,并返回给客户端。

          4:在客户端conn接口,可以通过 IPerson.Stub.asInterface(service);获取到IPerson类型的对象。然后可以调用IPerson接口里面声明的函数。


          好了,讲到这里也差不多讲完了,简单的说,这几篇博客讲的其实就是IPC进程间通信AIDL方式的实现原理及过程。不知道大家看到这里是否明白了。


服务端客户端源码:

首先我们看下服务端和客户端的源码结构:


服务端:

MainActivity.java

package com.example.server;

import com.example.server.IPerson;
import com.example.server.R;

import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener{

	private Button bindButton;
	private Button unbindButton;
	private IPerson iPerson;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		bindButton = (Button)findViewById(R.id.button1);
		unbindButton = (Button)findViewById(R.id.button2);
		bindButton.setOnClickListener(this);
		unbindButton.setOnClickListener(this);
	}

	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		switch (v.getId()) {
		case R.id.button1:
			Intent intent = new Intent(MainActivity.this,MyService.class);
			bindService(intent, conn, Service.BIND_AUTO_CREATE);
			break;
		case R.id.button2:
			unbindService(conn);
			break;

		default:
			break;
		}
	}
	
	private ServiceConnection conn = new ServiceConnection() {
		//连接对象
		@Override
		public void onServiceDisconnected(ComponentName name) {
			// TODO Auto-generated method stub
		}
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			// TODO Auto-generated method stub
			iPerson = IPerson.Stub.asInterface(service);
			if(iPerson!=null){
				try {
					iPerson.setValue("AIDL TEST");
					Toast.makeText(MainActivity.this, "赋值成功", Toast.LENGTH_LONG).show();
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					Toast.makeText(MainActivity.this, "赋值失败", Toast.LENGTH_LONG).show();
				}
			}
		}
	};

}

MyService.java

package com.example.server;

import com.example.server.IPerson;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class MyService extends Service{
	private IPerson.Stub iPerson = new Person();
	@Override
        public void onCreate()
        {
        }
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return iPerson;
	}
}

Person.java

package com.example.server;

import com.example.server.IPerson;
import android.os.RemoteException;

public class Person extends IPerson.Stub{
	private String name;
	
	@Override
	public void setValue(String name) throws RemoteException {
		// TODO Auto-generated method stub
		this.name = name;
	}
	
	@Override
	public String getValue() throws RemoteException {
		// TODO Auto-generated method stub
		return this.name;
	}
}
IPerson.aidl

package com.example.server;
interface IPerson{
	void setValue(String name);
	String getValue();
}


客户端:

MainActivity.java

package com.example.client;

import com.example.server.IPerson;

import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

	private Button btn;
	private IPerson person;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		btn = (Button)findViewById(R.id.button1);
		btn.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				Intent intent = new Intent();
				intent.setAction("com.example.server.MyService");
				bindService(intent, conn, Service.BIND_AUTO_CREATE);
			}
		});
	}
	
	private ServiceConnection conn = new ServiceConnection() {
		
		@Override
		public void onServiceDisconnected(ComponentName name) {
			// TODO Auto-generated method stub		
		}
		
		@Override
		public synchronized void onServiceConnected(ComponentName name, IBinder service) {
			// TODO Auto-generated method stub
			person = IPerson.Stub.asInterface(service);
			if(person != null){
				try {
					String name1 = person.getValue();
					Toast.makeText(MainActivity.this, "远程调用成功,值为:"+name1, Toast.LENGTH_LONG).show();
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					Toast.makeText(MainActivity.this, "远程调用失败", Toast.LENGTH_LONG).show();
				}
			}
		}
	};

}

IPerson.aidl 与服务端的IPerson.aidl一样


IPerson.java

在Eclipse里面发现有aidl文件,会根据该aidl文件,自动在服务端和客户端的gen目录下生成对应的IPerson.java,服务端和客户端该文件的内容一样。该文件内容比较重要,也是实现两者通信的关键,同时也是将前几篇博客将的内容对应到该代码中。,其内容如下:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: C:\\Users\\Administrator\\Desktop\\AIDLServiceDemo\\AIDLServiceServer\\src\\com\\example\\server\\IPerson.aidl
 */
package com.example.server;

public interface IPerson extends android.os.IInterface {
	/** Local-side IPC implementation stub class. */
	public static abstract class Stub extends android.os.Binder implements
			com.example.server.IPerson {
		private static final java.lang.String DESCRIPTOR = "com.example.server.IPerson";

		/** Construct the stub at attach it to the interface. */
		public Stub() {
			this.attachInterface(this, DESCRIPTOR);
		}

		/**
		 * Cast an IBinder object into an com.example.server.IPerson interface,
		 * generating a proxy if needed.
		 */
		public static com.example.server.IPerson asInterface(
				android.os.IBinder obj) {
			if ((obj == null)) {
				return null;
			}
			android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
			if (((iin != null) && (iin instanceof com.example.server.IPerson))) {
				return ((com.example.server.IPerson) iin);
			}
			return new com.example.server.IPerson.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_setValue: {
				data.enforceInterface(DESCRIPTOR);
				java.lang.String _arg0;
				_arg0 = data.readString();
				this.setValue(_arg0);
				reply.writeNoException();
				return true;
			}
			case TRANSACTION_getValue: {
				data.enforceInterface(DESCRIPTOR);
				java.lang.String _result = this.getValue();
				reply.writeNoException();
				reply.writeString(_result);
				return true;
			}
			}
			return super.onTransact(code, data, reply, flags);
		}

		private static class Proxy implements com.example.server.IPerson {
			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 void setValue(java.lang.String name)
					throws android.os.RemoteException {
				android.os.Parcel _data = android.os.Parcel.obtain();
				android.os.Parcel _reply = android.os.Parcel.obtain();
				try {
					_data.writeInterfaceToken(DESCRIPTOR);
					_data.writeString(name);
					mRemote.transact(Stub.TRANSACTION_setValue, _data, _reply,
							0);
					_reply.readException();
				} finally {
					_reply.recycle();
					_data.recycle();
				}
			}

			@Override
			public java.lang.String getValue()
					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_getValue, _data, _reply,
							0);
					_reply.readException();
					_result = _reply.readString();
				} finally {
					_reply.recycle();
					_data.recycle();
				}
				return _result;
			}
		}

		static final int TRANSACTION_setValue = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
		static final int TRANSACTION_getValue = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
	}

	public void setValue(java.lang.String name)
			throws android.os.RemoteException;

	public java.lang.String getValue() throws android.os.RemoteException;
}

Binder与Service 通信机制详解一 (前言)

Binder与Service 通信机制详解二 (Binder与Service理解)

Binder与Service 通信机制详解三 (服务端和客户端设计)

Binder与Service 通信机制详解四 (源码分析AIDL工作机制)




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值