美文网首页
Android 之 Service(二)AIDL

Android 之 Service(二)AIDL

作者: 帅气的欧巴 | 来源:发表于2016-08-23 16:24 被阅读228次

    一、介绍

    AIDL(Android Interface Definition Language)是一种接口定义语言。
    它允许你定义一个编程接口,用于约束两个进程间的通讯规则,提供给编译器生成代码,实现Android设备上的两个进程间通信(IPC)。
    在Android系统中,一个进程通常不能访问另一个进程的内存。所以他们需要将他们的对象拆解成操作系统所能识别的原语数据(primitives),然后传入到另一个进程之后再替你组装成对象。
    也就是说进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象。
    由于进程之间的通信信息需要双向转换,所以Android采用代理类在背后实现了信息的双向转换,代理类由Android编译器生成。

    二、适用场景

    一般适用于为其它应用程序提供公共服务的Service,这种Service即为系统常驻的Service(如:天气服务等)。

    三、优缺点

    优点
    1.AIDL有自己的独立进程,不会受到其它进程的影响;
    2.可以被其它进程复用,提供公共服务;
    3.具有很高的灵活性。
    缺点
    相对普通服务,占用系统资源较多,使用AIDL进行IPC也相对麻烦。

    四、具体操作(此处我们创建一个服务端程序,一个客户端程序)

    1.生成AIDL文件(服务端创建)
    选择 new->aidl->aidl file


    Studio生成AIDL文件

    填写创建的名称


    创建的 aidl的名称.png
    会在main目录下为你创建一个aidl文件夹,并在里面与你的包名对应创建了你的aidl文件,此文件默认会给你创建一个方法,该方法只是作为类型案例解释,可以不需要
    创建的aidl文件 Gradle构建
    点击Gradle图标让编译器为我们生成对应AIDL的 文件

    2.创建Service提供服务,AIDL涉及到IPC通信,所以需要使用绑定服务。

    public class MyService extends Service{
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return aidl;
        }
    
        IMyAidlInterface.Stub aidl = new IMyAidlInterface.Stub(){
    
            public static final String TAG = "AIDL";
    
            //此处重写所有的抽象方法,实现提供对外暴露的接口
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
                Log.i(TAG,"服务正在执行...");
            }
        };
    }
    

    3.注册服务

      <application android:allowBackup="true"
            android:label="@string/app_name"
            android:icon="@mipmap/ic_launcher"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
    
             <!--注册服务-->
            <service android:name=".MyService"
                     android:process="com.my.remote.service" >
                <intent-filter>
                    <action android:name="com.myaidl.server"/>
                </intent-filter>
            </service>
    
        </application>
    

    如果客户端与服务端在同个App中,AndroidManifest.xml中设置Remote Service的andorid:process属性时,如果被设置的进程名是以一个冒号(:)开头的,则这个新的进程对于这个应用来说是私有的,当它被需要或者这个服务需要在新进程中运行的时候,这个新进程将会被创建。如果这个进程的名字是以小写字符开头的,则这个服务将运行在一个以这个名字命名的全局的进程中,当然前提是它有相应的权限。这将允许在不同应用中的各种组件可以共享一个进程,从而减少资源的占用。

    4.拷贝服务端aidl文件到客户端目录下(文件夹路径不可变化)
    点击Gradle图标让编译器为我们生成对应AIDL的 文件

    拷贝AIDL文件

    5.客户端连接代码

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Intent intent = new Intent();
            //服务注册的行为
            intent.setAction("com.myaidl.server");
            //5.0之后 需要设置服务所在的包名(服务APP的服务所在包名)
            //否则会报 IllegalArgumentException: Service Intent must be explicit
            intent.setPackage("nightingale.aidl_server");
            //绑定服务
            bindService(intent,conn,BIND_AUTO_CREATE);
        }
    
        ServiceConnection conn = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                //通过我们自己定义的AIDL文件的Stub.asInterface方法 传入服务传递过来的iBinder对象 返回我们的AIDL对象
                IMyAidlInterface aidl = IMyAidlInterface.Stub.asInterface(iBinder);
    
                try {
                    //此处方法为你在AIDL文件中,自己定义的方法 此处对应我们的AIDL文件
                    aidl.basicTypes(0,0,true,0,0,null);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
    
            }
    
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
    
            }
        };
    
        @Override
        protected void onDestroy() {
            unbindService(conn);
            super.onDestroy();
        }
    }
    

    如果无法调用AIDL对象(自定义的IMyAidlInterface)那么需要使用Gradle 构建一下,或者检查你是否拷贝了服务端的aidl文件到客户端

    6.安装服务端,安装客户端执行结果

    执行结果.png

    五、AIDL参数的传递(基础数据类型 与 自定类型)

    AIDL默认支持下面几种数据类型:
    Java编程语言中的基础数据类型(int,long,char,boolean等)
    String,CharSequence,List,Map,在List 中的所有元素必须是上面支持的类型或者是其他由AIDL生成的接口,或者你申明的实现了Parcelable接口的类型。
    List可能被选用为泛型类,比如 List<String>.实际在接受服务一侧生成的类为永远是 ArrayList。尽管生成的方法使用的是 List接口。
    Map中的虽有元素必须是上面类型或者是其他由AIDL生成的接口,或者你申明的实现了Parcelable接口的类型。泛型例如 Map<String,Integer>不支持。实际在接受服务一侧生成的类为永远是HashMap。尽管生成的方法使用的是 Map接口。
    你必须要为每一个上面未列出类型添加import申明,尽管他们是作为接口定义在同一个包里面。

    示例:
    1.写一个类 实现 Parcelable

    public class User implements Parcelable {
        private String name;
        private int age;
    
        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 User(){
    
        }
        protected User(Parcel in) {
            name = in.readString();
            age = in.readInt();
        }
        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() {
            return 0;
        }
        @Override
        public void writeToParcel(Parcel parcel, int i) {
            parcel.writeString(name);
            parcel.writeInt(age);
        }
    }
    

    2.在AIDL中定义获取该类的方法

    package nightingale.aidl_server;
    
    //***导入对应的类,必须***
    import nightingale.aidl_server.User;
    
    interface IMyAidlInterface {
    
       //默认的 可删除 可不官 这里案例使用 所以 仍在这里吧
        void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
                double aDouble, String aString);
       //获取用户对象
        User getUser();
    }
    

    3.在aidl文件夹中声明User对象

    声明User对象

    4.拷贝aidl中的文件到客户端

    拷贝aidl中的文件到客户端

    5.拷贝服务端的User类到客户端
    注意:此处拷贝的时候,拷贝过去的类所在包名需要跟服务器所在包名一致,此处我创建了一个 aidl_server包


    拷贝服务端的User类到客户端

    6.服务端Service代码

    public class MyService extends Service{
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return aidl;
        }
    
        IMyAidlInterface.Stub aidl = new IMyAidlInterface.Stub(){
    
            public static final String TAG = "AIDL";
    
            //此处重写所有的抽象方法,实现提供对外暴露的接口
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
                Log.i(TAG,"服务正在执行...");
            }
    
            @Override
            public User getUser() throws RemoteException {
                return null;
            }
        };
    }
    

    7.客户端使用代码

    public class MainActivity extends AppCompatActivity {
    
        private String TAG="aidl";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Intent intent = new Intent();
            //服务注册的行为
            intent.setAction("com.myaidl.server");
            //5.0之后 需要设置服务所在的包名(服务APP的服务所在包名)
            //否则会报 IllegalArgumentException: Service Intent must be explicit
            intent.setPackage("nightingale.aidl_server");
            //绑定服务
            bindService(intent,conn,BIND_AUTO_CREATE);
        }
    
    
        ServiceConnection conn = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                //通过我们自己定义的AIDL文件的Stub.asInterface方法 传入服务传递过来的iBinder对象 返回我们的AIDL对象
                IMyAidlInterface aidl = IMyAidlInterface.Stub.asInterface(iBinder);
    
                try {
    
                    User user = aidl.getUser();
                    String name = user.getName();
                    if(!TextUtils.isEmpty(name)){
                        Log.i(TAG,name);
                    }
    
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
    
            }
    
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
    
            }
        };
    
        @Override
        protected void onDestroy() {
            unbindService(conn);
            super.onDestroy();
        }
    }
    

    执行结果


    执行结果

    至此 AIDL的相关操作就介绍完了😊,不好请吐槽!谢谢

    关于Messenger的介绍,推荐阅读:陈育 Android IPC机制(五):详解Bundle与“信使”——Messenger:http://www.jianshu.com/p/6e23037d6d20

    本文代码下载:http://download.csdn.net/detail/kooeasy/9610705

    相关文章

      网友评论

          本文标题:Android 之 Service(二)AIDL

          本文链接:https://www.haomeiwen.com/subject/hyqnsttx.html