AIDL的基本使用

2018-02-16  本文已影响0人  笔墨Android

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);
}

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

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的进阶使用

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内容
// 这里说明一下:
// 1.这里传递的自定义类型要进行相应的序列化
// 2.这里传进来的自定义类型要进行相应的倒包,并且要在AIDL文件夹中生成以个对应的aidl文件
import com.hejin.service.PersonBean;

interface ICustomAidl {
    //这里传递的自定义类型,但是这里倒包应该是自己手动去写的
    List<PersonBean> add (in PersonBean personBean);
}

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

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

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

上一篇下一篇

猜你喜欢

热点阅读