AIDL 进程间的通信(一)

来源:互联网 发布:网络语吃鸡是什么意思 编辑:程序博客网 时间:2024/06/03 05:30
一、前言
通过上一篇的Service基本介绍,相信大家有了初步的认识了,如果你还没见看过前面那篇文章,可以先阅读一下:Service 基本介绍   http://blog.csdn.net/qq_21126979/article/details/53672881。因为本章里面的代码是在上一篇文章的基础上修改的。
接下来我将会继续讲Service的更加高端的使用技巧,即AIDL 进程间的通信。

二、AIDL 基本介绍
aidl 是 Android Interface definition language的缩写,一看就明白,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口。它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。

创建AIDL的两种方式:Eclipse工具和Android studio工具
Android studio工具:
第一步:点击包名,右击->New->AIDL->AIDL File即可。

新建好的AIDL文件:

在IMyAidlInterface.aidl文件中编写相应的方法:
package com.hjyProject.aidl;interface IMyAidlInterface {    //定义个获取详情方法    String getDetial(String name);    //定义个set对象方法    void setObject(String obj);}

注意:如果aidl文件中出现编写格式不对,或者忘记导入引用包就不会生成一下的java文件。

第二步:生成相应的java文件:

生成java文件:


Eclipse 工具:
第一步:点击包名,右击->new->File

新建完文件后,编写以下程序,如下:
package com.hjyProject.util.aidl;import java.lang.Object;interface IMyAidlInterface {    //定义个获取详情方法    String getDetial(String name);    //定义个set对象方法    //void setObject(Object obj);}
编写好IMyAidlInterface文件后,如果语法还有格式没有出错的话,就会在gen文件下出现以下java文件:

我们将在两种工具下创建AIDL文件,接下去我将用AS工具进一步分析。

三、AIDL使用
接着上一篇的代码,在这里做几处修改。
1、在AndroidManifest.xml配置文件中写入以下代码:
<service android:name="com.hjyProject.service.TestService"    android:process=":remote" >    <!--隐式启动-->    <intent-filter>        <action android:name="com.hjyProject.aidl.IMyAidlInterface"/>    </intent-filter></service>
这边为什么使用隐式呢?
以下是上一篇文章中,bindService绑定服务的代码:
Intent intent = new Intent(ServiceActivity.this, TestService.class);bindService(intent,connection,BIND_AUTO_CREATE);
看到bindService启动Intent我们就知道Intent使用TestService.class来指定要绑定哪一个Service的,但是在另一个应用程序中去绑定Service的时候并没有TestService这个类,这时就必须使用到隐式Intent了。所以只能做隐式跳转。

2、接下来修改TestService服务中的代码:
@Overridepublic IBinder onBind(Intent intent) {    Log.d(TAG,"onBind");    //return new MyBinder();    return mStub;}private IMyAidlInterface.Stub mStub = new IMyAidlInterface.Stub(){    private String str;    @Override    public String getDetial(String name) throws RemoteException {        Log.d(TAG,"getDetial");        return str+";"+name;    }    @Override    public void setObject(String obj) throws RemoteException {        Log.d(TAG,"setObject");        this.str = obj;    }};
这里先是对MyAIDLService.Stub进行了实现,重写里了getDetial()和setObject ()这两个方法。这两个方法的作用分别是将塞字符串,另外一个是拼接字符串。然后在onBind()方法中将MyAIDLService.Stub的实现返回。这里为什么可以这样写呢?因为Stub其实就是Binder的子类,所以在onBind()方法中可以直接返回Stub的实现。

3、最后我们修改ServiceActivity,代码如下:
private IMyAidlInterface myAIDLService;//Activity与Service建立关联与接触关联时候被调用private ServiceConnection connection = new ServiceConnection() {    @Override    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {        myAIDLService = IMyAidlInterface.Stub.asInterface(iBinder);        try {            myAIDLService.setObject("我");            String result = myAIDLService.getDetial("Hello!");            Log.d("test",result);        } catch (RemoteException e) {            e.printStackTrace();        }    }    @Override    public void onServiceDisconnected(ComponentName componentName) {    }};/*** 界面初始化* @author hjy* created at 2016/12/15 13:46*/private void initUI(){    bind = (Button) findViewById(R.id.bind);    //绑定服务    bind.setOnClickListener(new View.OnClickListener() {        @Override        public void onClick(View view) {            //Intent intent = new Intent(ServiceActivity.this, TestService.class);            //BIND_AUTO_CREATE表示在Activity和Service建立关联后自动创建Service,            // 这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行。            Intent intent = new Intent("com.hjyProject.aidl.IMyAidlInterface");            bindService(intent,connection,BIND_AUTO_CREATE);        }    });}
我们只是修改了ServiceConnection中的代码。可以看到,这里首先使用了MyAIDLService.Stub.asInterface()方法将传入的IBinder对象传换成了MyAIDLService对象,接下来就可以调用在MyAIDLService.aidl文件中定义的所有接口了。在这里我setObject方法中传入“我”,getDetial方法中传入“Hello”,然后做字符串的拼接打印出来。结果如下:
java.lang.IllegalArgumentException: Service Intent must be explicit:

啊!为什么会出现这样的异常呢?后来我查找了资料,才知道Android5.0以后不支持隐式启动,那怎么办呢?我们只能用隐式。有一句话说的好,上有政策下有对策,google官方推荐使用的解决方法。 
在此附上地址供大家参考:http://developer.android.com/goo ... tml#billing-service,有兴趣的可以去看看。

于是,我就引用了google官方推荐的方法,将隐式启动转换成显示。代码如下:
/** * 将隐式启动转换为显示启动 * @author hjy * created at 2016/12/16 14:07 */public static Intent getExplicitIntent(Context context, Intent implicitIntent) {    // Retrieve all services that can match the given intent    PackageManager pm = context.getPackageManager();    List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);    // Make sure only one match was found    if (resolveInfo == null || resolveInfo.size() != 1) {        return null;    }    // Get component info and create ComponentName    ResolveInfo serviceInfo = resolveInfo.get(0);    String packageName = serviceInfo.serviceInfo.packageName;    String className = serviceInfo.serviceInfo.name;    ComponentName component = new ComponentName(packageName, className);    // Create a new intent. Use the old one for extras and such reuse    Intent explicitIntent = new Intent(implicitIntent);    // Set the component to be explicit    explicitIntent.setComponent(component);    return explicitIntent;}/*** 界面初始化* @author hjy* created at 2016/12/15 13:46*/private void initUI(){    bind = (Button) findViewById(R.id.bind);    //绑定服务    bind.setOnClickListener(new View.OnClickListener() {    @Override    public void onClick(View view) {        Intent mIntent = new Intent();        mIntent.setAction("com.hjyProject.aidl.IMyAidlInterface");        Intent eintent = new Intent(getExplicitIntent(ServiceActivity.this,mIntent));        //startService(eintent);        bindService(eintent,connection,BIND_AUTO_CREATE);    }});}
引入google官方的方法getExplicitIntent后,在修改一下bind点击事件的方法中调用,完美解决。打印出log。


虽然我们已经简单实现了不同进程之间数据传递,但是你有没有发现如果我想传递复杂的对象,怎么办呢?上面只能传java的基本数据类型、字符串、List或者Map等。接下来我们将解刨怎么去传递自定类。接下去我将会写一篇 AIDL 进程间的通信(二)来说明进程间传递自定义对象。

如果有出错或者需要改进的地方,欢迎指点或者交流。







0 0
原创粉丝点击