美文网首页
AIDL的基本使用

AIDL的基本使用

作者: 笔墨Android | 来源:发表于2018-02-16 11:33 被阅读0次

    XSize的主页

    AIDL的使用

    AIDL是android中接口定义语言,用于进程间通信.这里大家其实应该好好理解一下这个进程间通信的问题,万事万物存在皆有其存在的意义.最开始我理解的AIDL是只有进程存货的存活的时候才能使用AIDL,但是通过我后来的尝试,其实AIDL不是这样的,简单一点的说,就是只要通过AIDL,即便是对方进程没有存活,你也是可以使用AIDL中相应方法的.网上有说新浪微博的三方登陆就是通过AIDL实现的,这里我没有研究,就不去过多的说明了,免得误导别人.以上就是我对AIDL的理解,如果有什么不正确的还请指出!!!

    本文中主要讲解在开发中的基本使用和案例分析,本文讲解的知识点如下:
    1.AIDL的简单使用
    2.AIDL的进阶使用

    这里大家应该明白一点,在AIDL的使用中,要明确相应的问题,什么是服务端,什么是客户端

    • 服务端 提供相应服务的那端,说人话就是能提供绑定服务的那一头
    • 客户端 使用相应服务的那端,说白了就是连接服务端,然后调用相应的方法

    1.AIDL的简单使用

    1.1创建AIDL文件(服务端)

    创建AIDL文件很简单,就是new->AIDL->AIDL File起个名,创建相应的AIDL文件,创建好后你会发现在main文件夹下面多了一个aidl的文件,里面会有相应的以aidl文件结尾的一个文件,这个就是你创建的那个aidl文件了,这里注意一点,创建完成之后,你要build一下工程,这样才算真正的生成相应的aidl文件了.切记

    1.2AIDL文件的说明

    创建好相应的文件之后,理论上是长成这个样子的.

    // IMyAidlInterface.aidl
    package com.hejin.aidldemo;
    
    // Declare any non-default types here with import statements
    
    interface IMyAidlInterface {
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
        void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
                double aDouble, String aString);
    }
    

    基本上都没有用,全部删除就可以了,只留下一个空的接口就可以了,但是这个文件有一下几点内容需要注意的

    • 这里面导入的包名,这里千万要注意.这里看好包名,因为一会的时候会用到的.
    • 写法和你平时写java代码的时候差不多,基本上没有出入

    1.3创建相应的服务

    创建好相应的AIDL之后,需要相应的服务才能对外提供相应的内容,也只有这样才能确保你的AIDL能够被别人使用.其实这里的创建服务和bindService基本上是一样的,先上代码,之后在进行讲解!

    public class AidlService extends Service {
        private String TAG = AidlService.class.getSimpleName();
    
        private IBinder mAddBinder = new IAddAidl.Stub() {
            @Override
            public int add(int a, int b) throws RemoteException {
                Log.e(TAG, "传递过来相应的内容");
                return a + b;
            }
        };
    
        public AidlService() {
            Log.e(TAG, "远程服务被创建了");
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            Log.e(TAG, "绑定成功");
            return mAddBinder;
        }
    }
    

    这里面值得注意的一点就是IBinder这个对象,其实是直接引用的你创建的aidl,这里会实现你在AIDL中定义的方法.剩下的就是绑定操作了,直接通过onBind()这个方法进行绑定就可以了,剩下的就没有什么好说的了!

    1.4客户端的调用

    其实客户端这边调用的话,和绑定一个服务没有什么太大的区别,只是在获取iBinder对象的时候有一些出入,但是有一点需要注意的,就是当客户端要有相应的AIDL文件,这个文件是从服务端拷贝过来的.至少我是这么做的.有的人会问了,那岂不是很不安全,你想象具体的服务是在服务端的,这里只能看到一个接口,但是具体实现你是看不到的,所以不存在安全的问题.

    1.4.1拷贝相应的AIDL文件

    拷贝这里应该好好说一下,有的人可能觉得会很简单,但是这里有一个问题就是AIDL的包名问题,这里一定要确保相应的AIDL的包名是一致的,所以我的做法是在AIDL文件夹下创建一个相应路径的文件在把AIDL文件粘过去.这样就能确保相应的包名一致了.

    1.4.2实现相应的逻辑

    一般我在实现逻辑的时候,都是在onCreat()的时候就绑定好相应的服务,这样,当你想使用的时候就可以直接进行调用了.然后在相应的onDestroy()解绑相应的服务就好了.代码时候这个样子滴:

            //绑定服务
            Intent intent = new Intent();
            //这里说明一下,前面的是项目的包名,后面的是相应AIDL绑定服务的名(全路径哦)
            intent.setComponent(new ComponentName("com.hejin.service", "com.hejin.service.aidl.AidlService"));
            bindService(intent, conn, Context.BIND_AUTO_CREATE);
    

    这里绑定服务是通络Component进行绑定的,不的可以看看Intent的隐式启动.这里只要注意好相应的全路径就可以了(这里的全路径都是相对于服务端的那边)

        //相应的ServiceConnection的代码
        private ServiceConnection conn = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                //这里注意写法,平时的都是直接强转的
                //这样绑定之后,就可以在相应的地方调用mIAddAidl的方法了
                mIAddAidl = IAddAidl.Stub.asInterface(iBinder);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                if (mIAddAidl != null) {
                    mIAddAidl = null;
                }
            }
        };
    

    这里在获取iBinder的时候和绑定服务是不一样的,绑定服务是强转得到的,但是这里是通过相应的AIDL的asInterface(IBinder iBinder)进行获取的,这里的mIAddAidl就是你拷贝过来的那个AIDL文件中获取的IBinder对象了,获取了相应的对象就可以调用相应的方法了.

    整体代码是这样滴:
    XML文件的代码:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.hejin.aidldemo.MainActivity">
    
        <EditText
            android:id="@+id/num1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="请输入数字"/>
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="+"
            android:textSize="20dp"/>
    
        <EditText
            android:id="@+id/num2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="请输入数字"/>
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="getContent"
            android:text="远程计算"/>
    
        <TextView
            android:id="@+id/tv_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="相应的远程计算结果为:"/>
    
    </LinearLayout>
    

    Activity中的代码:

    package com.hejin.aidldemo;
    
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.Bundle;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    import android.widget.EditText;
    import android.widget.TextView;
    
    import com.hejin.service.IAddAidl;
    
    public class MainActivity extends AppCompatActivity {
    
        private TextView mTv_content;
        private EditText mEt_num1;
        private EditText mEt_num2;
        private IAddAidl mIAddAidl;
        private ServiceConnection conn = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                //这里注意写法,平时的都是直接强转的
                //这样绑定之后,就可以在相应的地方调用mIAddAidl的方法了
                mIAddAidl = IAddAidl.Stub.asInterface(iBinder);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                if (mIAddAidl != null) {
                    mIAddAidl = null;
                }
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
            bind();
        }
    
        private void initView() {
            mEt_num1 = findViewById(R.id.num1);
            mEt_num2 = findViewById(R.id.num2);
            mTv_content = findViewById(R.id.tv_content);
        }
    
        /**
         * author :  贺金龙
         * create time : 2018/2/14 6:27
         * description : 绑定服务的操作
         */
        private void bind() {
            Intent intent = new Intent();
            //这里说明一下,前面的是项目的包名,后面的是相应AIDL绑定服务的名(全路径哦)
            intent.setComponent(new ComponentName("com.hejin.service", "com.hejin.service.aidl.AidlService"));
            bindService(intent, conn, Context.BIND_AUTO_CREATE);
        }
    
        /**
         * author :  贺金龙
         * create time : 2018/2/14 6:23
         * description : 通过点击事件,响应相应的远程服务
         */
        public void getContent(View view) {
            try {
                int sum = mIAddAidl.add(Integer.parseInt(mEt_num1.getText().toString()),
                        Integer.parseInt(mEt_num2.getText().toString()));
                mTv_content.setText(String.valueOf(sum));
            } catch (RemoteException e) {
                mTv_content.setText("数据错误");
                e.printStackTrace();
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unbindService(conn);
        }
    }
    

    相应AIDL的代码在上面:


    以上代码就实现了简单AIDL的使用了.但是有的人会问了.我想传递相应的实体类怎么办呢?这就引出了下面的话题!AIDL的进阶使用:

    2.AIDL的进阶使用

    • AIDL支持的类型8中数据类型中除去short类型
    • List<?> 但是要带上面的类型
      • 当作为参数的时候前面要加上 in 这个关键字
      • 当作为返回值的时候前面直接写就可以了.在服务端获取相应的返回值的时候要转成ArrayList
    • 序列化Parcelable对象,其实这个就是实现自定义的类型了(下面会详细讲解)

    2.1实现自定义类行的AIDL通信

    这个其实比较繁琐:

    • 生成一个相应的自定义类型的实体(这个实体一定要序列化,否则会报错)
    public class PersonBean implements Parcelable {
    
        private String name;
        private String age;
    
        public PersonBean(String name, String age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAge() {
            return age;
        }
    
        public void setAge(String age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "PersonBean{" +
                    "name='" + name + '\'' +
                    ", age='" + age + '\'' +
                    '}';
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(this.name);
            dest.writeString(this.age);
        }
    
        protected PersonBean(Parcel in) {
            this.name = in.readString();
            this.age = in.readString();
        }
    
        public static final Parcelable.Creator<PersonBean> CREATOR = new Parcelable.Creator<PersonBean>() {
            @Override
            public PersonBean createFromParcel(Parcel source) {
                return new PersonBean(source);
            }
    
            @Override
            public PersonBean[] newArray(int size) {
                return new PersonBean[size];
            }
        };
    }
    
    • 创建一个相应的AIDL文件(传递的内容是自定义的类型,这里其实也支持List<自定义类型>)
    // 传递自定义类型参数的AIDL内容
    // 这里说明一下:
    // 1.这里传递的自定义类型要进行相应的序列化
    // 2.这里传进来的自定义类型要进行相应的倒包,并且要在AIDL文件夹中生成以个对应的aidl文件
    import com.hejin.service.PersonBean;
    
    interface ICustomAidl {
        //这里传递的自定义类型,但是这里倒包应该是自己手动去写的
        List<PersonBean> add (in PersonBean personBean);
    }
    

    这里说明一下实体类行前面添加的in代表PresonBean是输入类型

    • 这里在AIDL的文件中要还要生成一个相应的文件(这个文件的作用主要是用来确定相应的自定义类型的)
    package com.hejin.service;
    
    // 这里序列化的实例对象,这里在写的时候一定要知道相应的写法,看好了
    // 前面是序列化的关键字,后面是序列化的实体类
    parcelable PersonBean;
    

    这里要注意这个文件的名称是PersonBean.aidl和上面的实体类的名称是一样的,就是文件类型是aidl类型的.

    • 写相应服务端的服务,提供给用户去操作
    public class AidlCustomService extends Service {
    
        private String TAG = AidlCustomService.class.getSimpleName();
        private List<PersonBean> mPersonBeans;
    
        private IBinder mIBinder = new ICustomAidl.Stub() {
            @Override
            public List<PersonBean> add(PersonBean personBean) throws RemoteException {
                mPersonBeans.add(personBean);
                return mPersonBeans;
            }
        };
    
        public AidlCustomService() {
            Log.e(TAG, "AidlCustomService: 这个方法执行吗???");
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            Log.e(TAG, "onBind: 绑定成功");
            mPersonBeans = new ArrayList<>();
            return mIBinder;
        }
    }
    

    这里由于我传递的是一个list集合所以这里我在绑定的时候初始化这个集合,这里应该注意一下,防止空指针问题

    • 然后把在服务端写的两个aidl文件拷贝到相应的包下(这里和上面是一样的,主要是包名的问题)
    • 把相应的实体类也要拷贝到相应的文件中去(这里也存在相应的包名问题)

    剩下的内容和普通的AIDL使用是一样的,这个就是传递自定义类型参数的基本写法,这里主要是介绍基本的写法,没有太多去研究,但是对于一般的操作够用了,等自己真正使用的时候在细细研究(代码我已经测试了,没问题),希望能帮助大家!!!

    具体代码请见中aidldemo(使用端)模块和service模块(服务端)

    相关文章

      网友评论

          本文标题:AIDL的基本使用

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