Aidl的一个简单学习

为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用Remote Procedure CallRPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition LanguageIDL)来公开服务的接口。我们知道4Android应用程序组件中的3个(ActivityBroadcastReceiverContentProvider)都可以进行跨进程访问,另外一个Android应用程序组件Service同样可以。因此,可以将这种可以跨进程访问的服务称为AIDLAndroid Interface Definition Language)服务。

 

建立AIDL服务的步骤

建立AIDL服务要比建立普通的服务复杂一些,具体步骤如下:

1)在Eclipse Android工程的Java包目录中建立一个扩展名为aidl的文件。该文件的语法类似于Java代码,但会稍有不同。

2)如果aidl文件的内容是正确的,ADT会自动生成一个Java接口文件(*.java)。

3)建立一个服务类(Service的子类)。

4)实现由aidl文件生成的Java接口。

5)在AndroidManifest.xml文件中配置AIDL服务,尤其要注意的是,<action>标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值。

实现AIDL接口的说明:

1AIDL接口只支持方法,不能声明静态成员;

2)不会有返回给调用方的异常。

(以上来自百度百科)

 

下面写一个简单的测试小例子:

客户端打开后,直接链接服务器的服务,并返回服务器存储的名字和年龄!

 

准备,需要新建两个工程: 

客户端:ipcloginclient.Apk(包名com.yuan.ipcloginclient

服务器:ipcloginserver.Apk(包名com.yuan.ipcloginserver

 

客户端代码:

先建一个User.adil, 这个文件的作用是引入了一个序列化User供其他的AIDL文件使用

package com.yuan.aidl;

parcelable User;


定义 User.java, 实现序列化

public class User implements Parcelable {
	private String name;
	private int age;

	public User(){

	}

	public User(Parcel in) {
		name = in.readString();
		age = in.readInt();
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public static final Creator<User> CREATOR = new Creator<User>() {
		@Override
		public User createFromParcel(Parcel in) {
			return new User(in);
		}

		@Override
		public User[] newArray(int size) {
			return new User[size];
		}
	};

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

	@Override
	public void writeToParcel(Parcel dest, int flags) {
		// TODO Auto-generated method stub
		dest.writeString(name);
		dest.writeInt(age);
	}
	
	/**
     * 参数是一个Parcel,用它来存储与传输数据
     * @param dest
     */
    public void readFromParcel(Parcel dest) {
        //注意,此处的读值顺序应当是和writeToParcel()方法中一致的
        name = dest.readString();
        age = dest.readInt();
    }

    //方便打印数据
    @Override
    public String toString() {
        return "name : " + name + " , age : " + age;
    }

}

定义UserManger.aidl

//作用是定义方法接口
package com.yuan.aidl;

//导入所需要使用的非默认支持数据类型的包
import com.yuan.aidl.User;

interface UserManger {

    //所有的返回值前都不需要加任何东西,不管是什么数据类型
    List<User> getUsers();

    //传参时除了Java基本类型以及String,CharSequence之外的类型
    //都需要在前面加上定向tag,具体加什么量需而定
    void addUser(inout User user);
}

这个UserManger.aidl编译的时候会在gen里生成UserManager.java

下面是Activity 文件

public class MainActivity extends Activity {

	//由AIDL文件生成的Java类
	private UserManger mUserManger = null;

	//标志当前与服务端连接状况的布尔值,false为未连接,true为连接中
	private boolean mBound = false;

	//包含Book对象的list
	private List<User> mUser;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);	
	}

	/**
	 * 按钮的点击事件,点击之后调用服务端的addUserIn方法
	 *
	 * @param view
	 */
	public void addUser(View view) {
		//如果与服务端的连接处于未连接状态,则尝试连接
		if (!mBound) {
			attemptToBindService();
			Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show();
			return;
		}
		if (mUserManger == null) return;

		User user = new User();
		user.setName("李老三");
		user.setAge(23);
		try {
			mUserManger.addUser(user);
			Log.e(getLocalClassName(), user.toString());
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 尝试与服务端建立连接
	 */
	private void attemptToBindService() {
		Intent intent = new Intent();
		intent.setAction("com.yuan.login");
		intent.setPackage("com.yuan.ipcloginserver");
		bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
	}

	@Override
	protected void onStart() {
		super.onStart();
		if (!mBound) {
			attemptToBindService();
		}
	}

	@Override
	protected void onStop() {
		super.onStop();
		if (mBound) {
			unbindService(mServiceConnection);
			mBound = false;
		}
	}

	private ServiceConnection mServiceConnection = new ServiceConnection() {
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			Log.e(getLocalClassName(), "service connected");
			mUserManger = UserManger.Stub.asInterface(service);
			mBound = true;

			if (mUserManger != null) {
				try {
					mUser = mUserManger.getUsers();
					Log.e(getLocalClassName(), mUser.toString());
				} catch (RemoteException e) {
					e.printStackTrace();
				}
			}
		}
		@Override
		public void onServiceDisconnected(ComponentName name) {
			Log.e(getLocalClassName(), "service disconnected");
			mBound = false;
		}
	};
}

注意:

bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);

也是bindService哦!

也必须 new 一个 ServiceConnection也就是mServiceConnection


还要注意

mUserManger = UserManger.Stub.asInterface(service);

跟一般的绑定服务有差别!

比如:

@Override  
public void onServiceConnected(ComponentName name, IBinder service) {  
   // TODO Auto-generated method stub  
   MyBinder binder = (MyBinder)service;  
    bindService = binder.getService();  
   bindService.BindMethod();  
   connFlag = true;  
}  

下面是服务器:

s服务器:

User.aidl        User.java       UserManager.aidl

这三个文件必须要跟客户端的包名 路径等一模一样!都是 com.yuan.aidl

其实是客户端应该跟服务器的标准一样,否则连不上,以服务器为准!


public class LoginService extends Service{
	public final String TAG = this.getClass().getSimpleName();

	//包含User对象的list
	private List<User> mUsers = new ArrayList<User>();

	//由AIDL文件生成的UserManger
	private final UserManger.Stub mUserManger = new UserManger.Stub() {
		@Override
		public List<User> getUsers() throws RemoteException {
			synchronized (this) {
				Log.e(TAG, "invoking getUsers() method , now the list is : " + mUsers.toString());
				if (mUsers != null) {
					return mUsers;
				}
				return new ArrayList<User>();
			}
		}

		@Override
		public void addUser(User user) throws RemoteException {
			synchronized (this) {
				if (mUsers == null) {
					mUsers = new ArrayList<User>();
				}
				if (user == null) {
					Log.e(TAG, "User is null in In");
					user = new User();
				}
				//尝试修改user的参数,主要是为了观察其到客户端的反馈
				user.setAge(66);
				if (!mUsers.contains(user)) {
					mUsers.add(user);
				}
				//打印mUsers列表,观察客户端传过来的值
				Log.e(TAG, "invoking addUsers() method , now the list is : " + mUsers.toString());
			}
		}
	};

	@Override
	public void onCreate() {
		User user = new User();
		user.setName("刘老六");
		user.setAge(28);
		mUsers.add(user);
		super.onCreate();
	}

	@Nullable
	@Override
	public IBinder onBind(Intent intent) {
		Log.e(getClass().getSimpleName(), String.format("on bind,intent = %s", intent.toString()));
		return mUserManger;
	}
}

记得在服务器的 AndroidManifest.xml

<service
       android:name=".service.LoginService"
       android:exported="true" >
        <intent-filter>
             <action android:name="com.yuan.login" />
             <category android:name="android.intent.category.DEFAULT" />
           </intent-filter>
</service>

注意:

private final UserManger.Stub mUserManger = new UserManger.Stub()

在IBinder onBind(Intent intent){ } 的时候,返回的是mUserManger 

 

一般的绑定服务定义的是一个Binder

比如

private MyBinder myBinder = new MyBinder();  

在IBinder onBind(Intent intent){}的时候,返回的是myBinder 


差不多就这样子哦!

当制作音乐播放器的时候可以这么做哦!






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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值