从AIDL看Android跨进程通信

  AIDL是Android实现IPC的一种重要的方式,理解它的原理对理解Android进程间通信有很大的帮助。AIDL的定义,已经有很多介绍的文章了,这里就不做详解了。我们直接从实例入手来分析AIDL实现原理。
  AIDL的使用
  首先需要定义AIDL接口IMyService.aidl:
  

// IMyService.aidl
package com.chuck.aidldemo;

// Declare any non-default types here with import statements

interface IMyService {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
     String getValue();
}

  定义了getValue()方法,返回String。我们知道定义好aidl文件后,IDE在编译项目时会自动帮我们生成一个文件IMyService.java文件,稍后会详细介绍。
  接下来需要定义一个service,这里定义MyService.java
  

public class MyService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("myService","onCreate"    );
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return ims;
    }

    IMyService.Stub ims=new IMyService.Stub() {
        @Override
        public String getValue() throws RemoteException {
            return   "hello AIDL";
        }
    };

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("myService","onDestroy");
    }
}

  重写onBind方法,返回IMyService.Stub对象ims,IMyService.Stub是定义在IMyService.java中,其实现了IBinder,所以这里可以在OnBinder中作为返回对象。同时在AIDL中定义getValue方法的真正实现,就是在这里。我们仅仅是返回一个”hello AIDL”字符串。
  为了实现跨进程,我们还需要在AndroidMenifast.xml文件中设置process=”:Remote”,这样service就和client不在同一个进程中了:
  

<service
            android:name=".MyService"
            android:process=":Remote" />

  最后在定义client,为了方便我们就直接在MainActivity.java实现了:
  

public class MainActivity extends AppCompatActivity {
    private IMyService ims;
    private String text;

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

    public void onClick(View view) {
      if (view.getId() == R.id.btn_bind) {
            Intent intent = new Intent(this, MyService.class);
            bindService(intent, sc, BIND_AUTO_CREATE);
        }else if (view.getId()==R.id.btn_unbind){
            unbindService(sc);
        }
    }

    private ServiceConnection sc = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            ims = IMyService.Stub.asInterface(service);
            try {
                text = ims.getValue();
                Log.e("text",text);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
}

  这和平时bindService使用是一样的,需要一个ServiceConnection实例,将该实例作为bindService参数传入。这样client就可以启动并绑定MyService了。
  一般我们使用AIDL的基本步骤就是这些,现在我们需要着重分析自动生成的IMyService.java了,如果不知道文件位置,直接在IDE搜索一下就可以了。先把代码贴出来:
  

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/chenkai/Android/codeExcise/aidl/AidlDemo/app/src/main/aidl/com/chuck/aidldemo/IMyService.aidl
 */
package com.chuck.aidldemo;
// Declare any non-default types here with import statements

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

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

        /**
         * Cast an IBinder object into an com.chuck.aidldemo.IMyService interface,
         * generating a proxy if needed.
         */
        public static com.chuck.aidldemo.IMyService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.chuck.aidldemo.IMyService))) {
                return ((com.chuck.aidldemo.IMyService) iin);
            }
            return new com.chuck.aidldemo.IMyService.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_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.chuck.aidldemo.IMyService {
            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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @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_getValue = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public java.lang.String getValue() throws android.os.RemoteException;
}

  代码结构很清晰,先来看一下类图:
  
  
  1.IInterface,我们定义的Binder接口都需要继承自它。
  2.我们定义的IMyService接口继承自接口IInterface。
  3.IBinder 远程对象的接口。
  4.Binder实现了IBinder,Android通过Binder进行IPC
  5.BinderProxy是在定义在Binder.java中的,也是实现了IBinder接口,这里只要知道它是Binder的代理就可以了。我们Client中拿到的就是这个类的对象,之后会有讲。
  6.IMyService.Stub继承自Binder并且实现了IMyService。也就是说Stub其实是个Binder。还记得吗?在前文MyService中我们生成了一个Stub的实例ims,Stub的本意是存根,这里的用意就是Service的存根,通过它,我们在Service端就拥有了一个Binder。这样我们就可以通过底层的Binder驱动进行跨进程通信了。
  7.IMyService.Proxy,顾名思义它是一个代理,主要是实现了IMyService接口,客户端通过它发起远程请求。
  简单的介绍了涉及到的类,下面来分析一下请求的流程。
  我们知道,在client bindService(这是一个比较复杂的过程,涉及到AMS,本身也是一个跨进程的通信)之后,如果远程服务,这里是MyService,处理完请求之后,通过一系列复杂的操作,client中的onServiceConnected方法将会回调,为了方便我把前边相关的部分代码放在这:
  

private ServiceConnection sc = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            ims = IMyService.Stub.asInterface(service);
            try {
                text = ims.getValue();
                Log.e("text",text);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

  第3行当回调onServiceConnected时,会传过来一个IBinder对象service,client就是需要通过它来发起远程请求的,那么service的具体实现到底是Binder还是BinderProxy呢,如果对Binder的机制比较清楚的都会知道其实是BinderProxy的,为了验证这个我们可以调试一下:
  这里写图片描述

  我们得到BinderProxy后会将其传给IMyService.Stub.asInterface(service),asInterface接受一个IBinder对象作为参数,这里就是刚才说的BinderProxy对象。具体看看asInterface做了什么:
  

 public static com.chuck.aidldemo.IMyService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.chuck.aidldemo.IMyService))) {
                return ((com.chuck.aidldemo.IMyService) iin);
            }
            return new com.chuck.aidldemo.IMyService.Stub.Proxy(obj);
        }

  看第5行,因为我们设置了MyService的process属性,也就是说他和client是不在同一个进程的,所以obj.queryLocalInterface(DESCRIPTOR)这个方法返回为空,也就是iin为null,那么将会执行第9行代码,对没错new了一个IMyService.Proxy对象。回到前边onServiceConnected方法中第4行ims就是刚刚生成的IMyService.Proxy对象。第6行ims.getValue()方法执行的就是IMyService.Proxy中的getValue方法:
  

/**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @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;
            }

  第7.8行_data, _reply分别是进行远程调用时的的请求和相应参数。第11行将DESCRIPTOR = “com.chuck.aidldemo.IMyService”作为Token写入 _data.第12行mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0);mRemote是在IMyService.Proxy构造方法中被赋值的,也就是我们在new IMyService.Proxy对象时传进来的BinderProxy对象。所以mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0)的实现应该是在BinderProxy中,这里需要注意一下第一个参数Stub.TRANSACTION_getValue,他是标识需要调用方法的code,在后边会有用到。我们看看他的源码:
  

 public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
        return transactNative(code, data, reply, flags);
    }

  最终会调用transactNative()方法,这是一个native方法,真正的实现是用c实现,他是实现在Android源码对应/frameworks/base/core/jni/android_util_Binder.cpp中的static jboolean android_os_BinderProxy_transact函数,将java层的transact转换成c层transact,有兴趣的可以去研究一下。
  通过底层Binder的一系列操作,最终会回调到远程Stub的onTransact方法,至于怎么调用的,需要去了解Binder机制。这里只要知道会回调onTransact()方法就好了。不过还是要注意的是,这里的Stub是在远程服务端的,他和client不是在同一个进程中的。他是在远程服务的Binder线程池中。跟进去看onTransact()方法做了什么处理:
  

 @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_getValue: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getValue();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

  还记得在transact方法时提到的第一个参数Stub.TRANSACTION_getValue吗,它就是这里onTransact的第一个参数code。所以会执行8-13行代码,第10行java.lang.String _result = this.getValue();很关键这个this是Stub,还记得我们在哪儿有生成它的实例吗?没错就是在MyService中,我们把它作为onBind方法的返回参数。所以这里的this.getValue就是MyService中我们写的那个getValue方法,会返回一个”hello AIDL”的字符串。并把它赋值给_result。在第12行将其写入_reply中。最后在通过Binder的一系列操作,我们将在client中得到这个字符串。
  总的来说AIDL本身使用比较简单,理解起来也比较简单,但是其内部的Binder机制还是比较复杂的,我们先理解Java层的AIDL对学习Binder也是有帮助的。并且Messager通信是基于AIDL的,理解了AIDL也就理解了Messager。

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
OC与Swift两种实现方式基本上区别不大,主要是在一些对象或方法的调用方式不同 OC代码样式: self.view.backgroundColor = [UIColor blackColor];          //加载颗粒状的火花图片     CAEmitterLayer *emitterLa = [CAEmitterLayer layer];     emitterLa.emitterPosition = CGPointMake(self.view.bounds.size.width/2, sel
前言 相信很多朋友在开发中都会遇到图片上传的情况,尤其是多图上传,最 经典的莫过于微信的图片选择了。所有很多情况下会使用到多图选择。 所以就有了这篇文章,今天抽点时间写了个控件。 支持自定义选择图片的样式 支持设置图片选择数量 支持图片预览,删除 支持图片拍照 先来看看效果 实现分析 假如不定义控件,我们要实现这样一个功能,无非是写个GridView在item点击的时候去显示图片进行选择,在返回界面的时候进行GridView的数据刷新。我们把这些逻辑写在我们自定义的GridView中,就成了一个新的控件。
在360对DroidPlugin的特点介绍中有云: 插件的四大组件完全不需要在Host程序中注册,支持Service、Activity、BroadcastReceiver、ContentProvider四大组件。 实现了进程管理,插件的空进程会被及时回收,占用内存低。 之所以支持Service,Activity,ContentProvider三大组件,是因为DroidPlugin在AndroidManifest文件中预先注册了8个运行插件的进程,每个进程预注册Service一个, ContentProvi
本篇介绍ListView控件,这是Android中比较重要也比较复杂的控件,这里只谈到使用ViewHolder机制优化即可。 一、ListView简介 ListView是Android系统中显示列表的控件,每个ListView都可以包含很多个列表项。 二、ListView的使用 概念不多说,直接来介绍使用方法。 ListView中比较复杂的是数据适配器,其作用是把复杂的数据(数组、链表、数据库、集合等)填充在指定视图界面,是连接数据源和视图界面的桥梁。常见的Android原生的适配器有ArrayAdapt
欢迎转载,转载请注明出处: http://blog.csdn.net/dmk877/article/details/51912104   相信不管做了多长时间开发的人都用过Tween动画,从刚开始工作到现在我也是用了N次Tween动画,但是每一次使用总感觉掌握的不够全面,所以花了点时间详细的总结了下Tween动画,其实在android中熟练掌握动画,能够帮助我们实现一些非常酷炫的效果从而使我们的app在交互或者用户体验上有一个更好的体验,鉴于此详细的学习动画还是很有必要的,相信通过本篇的学习大家会对Twe

Java(Android)回调函数详解 - 2016-07-23 17:07:29

一、前言 本周有位入行开发不久的朋友问我回调究竟是个什么概念,在网上看了很多的回调函数解释,但是越看越乱。虽然回调函数这个梗已经不新鲜了,这里还是用书面的形式记录下。 如果有了解的,就无需再看。 二、概念 概念上,这里引用百度百科的解释,如下: 回调函数就是一个通过 函数指针 调用的函数。如果你把函数的 指针 (地址)作为 参数传递 给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或

Android_聊天_表情 - 2016-07-23 17:07:26

接下来就进入聊天界面了,我的界面效果如下几个图所示: 这其中包括两个点:仿微信按住说话功能,表情管理 第一个,按住说话 按钮的功能,通过重写Button完成, /** * 控制录音Button * 1、重写onTouchEvent;(changeState方法、wantToCancel方法、reset方法); * 2、编写AudioDialogManage、并与该类AudioRecorderButton进行整合; * 3、编写AudioManage、并与该类AudioRecorderButton进行整合;

JAVA 面向对象 隐藏和封装 - 2016-07-23 17:07:20

本页面更新日期: 2016年07月22日 前言 在前面程序中,经常出现通过 某个对象直接访问其成员变量的情况. 这可能引起一些潜在问题,比如将某个 Person 的 age 成员变量直接设为 1000. 这在语法上没有任何问题, 但显然违背了当前的自然规律. 人怎么可能活到 1000岁 - - . (就现在的科学来讲) Java也考虑到了这种情况, 为你提供了 类和对象的成员变量 进行封装的方法,来保护成员变量不被恶意修改. 理解封装 封装(Encapsulation) 是面向对象的三大特征之一.(另外两
上一篇文章分析过DroidPlugin对Activity的处理过程,不得不为对DroidPlugin的工程师们钦佩不已,那么是不是Service可以像Activity的处理过程一样来处理呢?前面讲过每一个代理进程只是预定义了一个Service,如果某一个插件中有多个Service,那岂不是某一个时刻只能有一个Service运行呢?由此可以判定可能Service的处理和Activity不一样。 一方面:平时使用Activity主要是用于展示界面和用户交互,Activity的生命周期可能受用户控制,当用户操作

IntentService使用及源码分析 - 2016-07-23 14:07:58

IntentService使用及源码分析 转载请注明 原博客地址: http://blog.csdn.net/gdutxiaoxu/article/details/52000680 本篇博客主要简介一下三个问题: 什么是IntentService? 怎样使用IntentService IntentSerice()源码分析 1)什么是IntentService? 我们知道Service和Activity一样是Android的四大组件之一,Service简称为后台服务,具有较高的优先级别。我们平时在Activ