Android AIDL使用介绍(1)基本使用

1.什么是AIDL

AIDL全称是Android Interface Definition Language,中文译为Android接口定义语言,AIDL的提出是为了解决进程间通讯,我们知道,在Android系统中,每个进程在内存中是相互独立的,类似一个个独立王国,一个进程通常情况下无法访问其他进程的内存,但进程之间不是老死不相往来的状态,他们之间有许多数据交互的需求,为此提出了AIDL,通过AIDL,定义双方认可的数据交互接口,再由AIDL翻译成操作系统理解的底层语言,进而实现进程间通讯。
在这里插入图片描述

2.AIDL的使用

进程间通讯转换成具体业务逻辑则是一个服务端和客户端的会话。服务端和客户端约定好AIDL接口,服务端实现接口的具体逻辑并暴露给客户端,客户端再调用接口。既然涉及到两方,则在工程中也创建服务端和客户端两个项目,见图2,我的是服务端和客户端放在一个工程下,分开放在两个工程目录下也是可行的。
在这里插入图片描述
具体的开发步骤如下:

1.服务端创建.aidl文件

AIDL接口和日常生活的合同比较相似,服务端和客户端约定AIDL接口相当于双方草拟业务合作合同,开发过程中一般都在服务端这边准备合同,也即在服务端创建.aidl文件,方法为:

a.在service模块下的main目录下,新建aidl文件,在其之下新建一个包(package),包名自定义,然后右键新建.aidl文件,如图所示,aidl文件名字自定义,确定之后,AS帮我们新建一个.aidl文件。
在这里插入图片描述

b.点开新建的aidl文件会发现不是空白,而是有了内容,这是AS自动生成帮忙填写的内容,其中basicTypes接口是演示AIDL可直接使用的基本数据类型,如果想用自己定义的数据类型,还需要做一些操作,这将在另一篇博客说明。删掉basicType后,填上要实现的接口,这里填写ServiceGreet,表示服务端跟客户端打个招呼,代码内容如下,添加之后记得同步一下工程,否则在调用还是默认的basicType函数,这样aidl文件创建好了。

package com.pm.service;

interface ServiceAidlInterface {
    String ServiceGreet();
}

2.服务端实现接口

aidl文件创建成功后,也就是草拟好了合同,接下来就是履行合同的过程,即服务端这边根据aidl文件实现接口。

我们注意到,aidl文件有别于java格式文件,aidl文件那怎么和java联系起来呢,毕竟开发语言是java。实际上在创建aidl文件时,Android SDK工具自动生成一个同名的java接口,例如ServiceAidlInterface.aidl生成的文件名是ServiceAidlInterface.java,服务端调用接口实际是和该java文件打交道,这也是为什么创建或者更改aidl文件要及时同步的原因,如果同步不及时,SDK工具可能不及时自动生成的更新java接口

AS根据aidl文件自动生成的java接口里有一个名为 Stub 的内部抽象类,Stub意思是存根,根据百度百科,存根意思是票据、证件等开出后所留的底子,这个抽象类的确有这个意思,具体需要另外展开文章来说明了,这里可先简单理解一下,我们把aidl进程间通讯理解成银行汇款过程,服务端通过该银行汇款(数据交互)给客户端,在aidl里声明的各种接口相当于汇款单,实现接口相当于打入钱的过程,从这个角度可勉强理解存根的意义,现实中拿到存根已经汇完钱了,而这里是拿到存根再汇钱(即实现接口),实际上,实现 .aidl 生成的接口是扩展生成的YourInterface.Stub接口的过程,实现接口的代码如下:

package com.pm.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class MyService extends Service {

    private ServiceAidlInterface.Stub serviceAidlInterface= new ServiceAidlInterface.Stub() {
        @Override
        public String ServiceGreet() throws RemoteException {
            return "Hello Client!";
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return serviceAidlInterface.asBinder();
    }
}

最后别忘了在AndroidManifest.xml注册服务,内容如下

 <service
            android:name=".MyService"
            android:exported="true">
            <intent-filter>
                <action android:name="com.pm.service.MyService" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>

这样服务端工作就完成了。

3.客户端调用接口
现实中合同都是一式两份的,这里也不例外,客户端也要拿到aidl文件,而且要求aidl所在的包名必须和服务端aidl所在的包名一致,最稳妥的办法是直接整个拷贝服务端的aidl目录,同样的,拷贝完aidl文件之后,也要及时同步一下项目,AS才能及时自动生成对应的java接口。
在这里插入图片描述

3.1 首先,建立一个服务连接,在连接上时,初始化接口,代码如下,我们注意到,这里还是用的Stub,这样和服务端的存根对得上

    private ServiceConnection connection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            serviceAidlInterface = ServiceAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

3.2 传入第一步创建的连接,绑定服务,这样客户端和服务端同呼吸共命运了

  private void bindService() {
        Intent intent = new Intent();
        intent.setAction("com.pm.service.MyService");
        intent.setPackage("com.pm.service");
        this.bindService(intent, connection, BIND_AUTO_CREATE);
    }

3.3 最后一步是调用接口了,这一步比较简单,调用服务器打招呼接口

String greet= serviceAidlInterface.ServiceGreet();
                    tvInfo.setText(greet);

3.总结

把aidl当成一个合同文件来理解,有助于理解aidl的作用,其中因为存根概念,又从银行汇款来理解。aidl创建后,Android SDK工具,这里是Android Studio自动帮忙生成对应的java接口,前提是要及时同步工程,工程不及时同步的话,aidl文件更新了,对应的java接口还是旧数据。aidl创建之后,服务器实现Stub存根里的方法,之后aidl拷贝给客户端,客户端创建连接,在连接里实例化存根,这样便取出了存根里的方法,最后绑定服务后调用即可。
在这里插入图片描述

最后附上完整客户端代码和布局

package com.pm.myaidldemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.pm.service.ServiceAidlInterface;

public class MainActivity extends AppCompatActivity {

    private Button bServiceGreet;
    private TextView tvInfo;

    private ServiceAidlInterface serviceAidlInterface;

    private ServiceConnection connection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            serviceAidlInterface = ServiceAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

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

        bServiceGreet=findViewById(R.id.btnServiceGreet);
        tvInfo=findViewById(R.id.tvInfo);

        bServiceGreet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    String greet= serviceAidlInterface.ServiceGreet();
                    tvInfo.setText(greet);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

            }
        });

    }

    private void bindService() {
        Intent intent = new Intent();
        intent.setAction("com.pm.service.MyService");
        intent.setPackage("com.pm.service");
        this.bindService(intent, connection, BIND_AUTO_CREATE);
    }
}

客户端布局文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tvInfo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btnServiceGreet"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
发布了10 篇原创文章 · 获赞 4 · 访问量 1724

猜你喜欢

转载自blog.csdn.net/lansoul1987/article/details/104350185