远程Service与Activity的交互(AIDL的应用)
首先我们先上一个通俗的情景:在应用1中Activity绑定了一个Service,并且设置了一些值,此时我们想在应用2中调用该service并且想得到该值应该怎么做?我们都知道每个应用程序都运行在各自的进程中,并且android平台是不允许不同进程间进行直接的对象数据等传递的。如果必须进行跨进程之间的数据传递,那么我们就应该使用AIDL(Android Interface Definition Language)。
AIDL只支持方法,不能定义静态成员,并且方法也不能有类似public等的修饰符;AIDL运行方法有任何类型的参数和返回值,在java的类型中,以下的类型使用时不需要导入包(import),基本数据类型、String、Map、List.当然为了避免出错,建议只要使用了,就导入包。
使用AIDL的步骤:
服务端(提供服务):
第1步:定义一个*.aidl文件,该文件里是符合aidl语言规范的接口定义,里面定义了外部应用可以访问的方法。当我们保存该文件的时候,eclipse会自动为我们在gen文件夹下生成一个相应的java接口文件。如:
- package com.android.macdroid;
- interface IPerson {
- void setValue(String name);
- String getValue();
- }
第2步:实现AIDL文件生成的JAVA接口Stub
- class Person extends IPerson.Stub {
- private String name;
- @Override
- public void setValue(String name) throws RemoteException {
- this.name = name ;
- }
- @Override
- public String getValue() throws RemoteException {
- return name;
- }
- }
第3步:定义一个自己的service, 并将其注册到androidManifest.xml文件中,例如:
- public class ServiceC extends Service {
- private Stub iPerson = new Person();
- @Override
- public IBinder onBind(Intent arg0) {
- Log.i("service", "onBind...");
- return iPerson;
- }
- }
- <service android:name="com.android.macdroid.ServiceC" >
- <intent-filter >
- <action android:name="forServiceAidl" > </action>
- </intent-filter>
- </service>
我们都知道,在实现自己的service时,为了其他应用可以通过bindService来和我们的service进行交互,我们都要实现service中的onBind()方法,并且返回一个继承了Binder的内部类;在这里,eclipse自动为我们生成的RemoteServiceInterface.java中有一个实现了Binder的内部类,RemoteServiceInterface.Stub。AIDL要求我们,在这里不能再直接去实现Binder类了,而是去实现,AIDL提供给我们的Stub类。 实现stub类的同时,AIDL还要求我们同时实现我们在接口中定义的各种服务的具体实现。至此为止,我们的服务端已经和我们的aidl文件绑定到一起了哦。
同一个应用中的Activity为该Service中赋值:
- package com.android.macdroid;
- import android.app.Activity;
- import android.app.Service;
- import android.content.ComponentName;
- import android.content.DialogInterface;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.content.Intent;
- import android.content.ServiceConnection;
- import android.os.Bundle;
- import android.os.IBinder;
- import android.os.RemoteException;
- import android.widget.Button;
- import android.widget.Toast;
- import com.android.macdroid.IPerson;
- public class AIDLTestActivity extends Activity {
- private IPerson iPerson;
- private Button bindBtn, unbindBtn;
- private ServiceConnection conn = new ServiceConnection() {
- // 断开连接时调用
- @Override
- public void onServiceDisconnected(ComponentName arg0) {
- }
- // 连接时调用
- @Override
- public void onServiceConnected(ComponentName arg0, IBinder binder) {
- iPerson = IPerson.Stub.asInterface(binder);
- if (iPerson != null) {
- try {
- iPerson.setValue("Service AIDL");
- Toast.makeText(AIDLTestActivity.this, "赋值成功!",
- Toast.LENGTH_LONG).show();
- } catch (RemoteException e) {
- e.printStackTrace();
- Toast.makeText(AIDLTestActivity.this, "赋值失败!",
- Toast.LENGTH_LONG).show();
- }
- }
- }
- };
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- bindBtn = (Button) findViewById(R.id.bindBtn);
- unbindBtn = (Button) findViewById(R.id.unbindBtn);
- bindBtn.setOnClickListener((android.view.View.OnClickListener) listener);
- bindBtn.setOnClickListener(listener);
- unbindBtn.setOnClickListener(listener);
- }
- private OnClickListener listener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- switch (v.getId()) {
- case R.id.bindBtn:
- //本应用中需要在manifest中配置RemoteService
- bindService(new Intent("forServiceAidl"), conn, Service.BIND_AUTO_CREATE);
- break;
- case R.id.unbindBtn:
- unbindService(conn);
- break;
- default:
- break;
- }
- }
- };
- }
客户端:
第1步:客户端要想使用该服务,肯定要先知道我们的服务在aidl文件中到底对外提供了什么服务,对吧?所以,第一步,我们要做的就是,将aidl文件拷贝一份到客户端的程序中(这里一定要注意,包路径要和服务端的保持一致哦,例如服务端为cn.com.chenzheng_java.remote.a.aidl,那么在客户端这边也应该是这个路径)。
第2步:我们都知道,想要和service交互,我们要通过bindService方法,该方法中有一个ServiceConnection类型的参数。而我们的主要代码便是在该接口的实现中。
第3步:在ServiceConnection实现类的onServiceConnected(ComponentName name, IBinder service)方法中通过类似remoteServiceInterface = RemoteServiceInterface.Stub.asInterface(service);方式就可以获得远程服务端提供的服务的实例,然后我们就可以通过remoteServiceInterface 对象调用接口中提供的方法进行交互了。(这里的关键是通过*.Stub.asInterface(service);方法获取一个aidl接口的实例哦)
我们前面在服务端中说过了,必须提供一个intent-filter来匹配请求是否合法,所以我们在客户端访问服务的时候,还必须传递包含了匹配action的Intent哦。
客户端中使用服务端中的service范例:
- private ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceDisconnected(ComponentName arg0) {
- }
- //因为有可能有多个应用同时进行RPC操作,所以同步该方法
- @Override
- public synchronized void onServiceConnected(ComponentName arg0, IBinder binder) {
- //获得IPerson接口
- person = IPerson.Stub.asInterface(binder);
- if(person != null){
- try {
- //RPC方法调用
- String name = person.getValue();
- Toast.makeText(DemoAIDLActivity.this, "远程进程调用成功!值为 : "+name, Toast.LENGTH_LONG).show();
- } catch (RemoteException e) {
- e.printStackTrace();
- Toast.makeText(DemoAIDLActivity.this, "远程进程调用失败! ", Toast.LENGTH_LONG).show();
- }
- }
- }
- };
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- btn = (Button)findViewById(R.id.btn);
- btn.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- //该应用中不需要在manifest中配置RemoteService
- bindService(new Intent("forServiceAidl"), conn, Service.BIND_AUTO_CREATE);
- }
- });
- }
参考博客:Android Service学习之AIDL, Parcelable和远程服务