AIDL的基本使用
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模块(服务端)