AIDl
AIDL简介
AIDL(Android 接口定义语言),可以使用它定义客户端与服务端进程间通信(IPC)的编程接口。在Android系统中,每个进程都运行在一块独立的内存中,在其中完成自己的各项活动,与其他进程都分隔开来。可是有时候我们又有应用间进行互动的需求,比较传递数据或者任务委托等,AIDL就是为了满足这种需求而诞生的。通过AIDL,可以在一个进程中获取另一个进程的数据和调用其暴露出来的方法,从而满足进程间通信的需求。
AIDL是用于定义服务端和客户端通信接口的一种描述语言,可以拿来生产IPC代码,从某种意义上说AIDL其实就是一个模板,因为在使用过程中,实际起作用的并不是AIDL文件,而是据此生产的一个Interface的实例代码,AIDL其实是为了避免我们重复写代码而出现的一个模板。
**注意:**只有在需要不同应用的客户端通过 IPC 方式访问服务,并且希望在服务中进行多线程处理时,您才有必要使用 AIDL。如果您无需跨不同应用执行并发 IPC,则应通过实现 Binder来创建接口;或者,如果您想执行 IPC,但不需要处理多线程,请使用 Messenger 来实现接口。无论如何,在实现 AIDL 之前,请您务必理解绑定服务。
使用流程
1.创建.aidl文件
在 AIDL 中可以通过可带参数以及返回值的一个或多个方法来声明接口,参数和返回值可以是任意类型,AIDL 中支持的数据类型如下:
java 的 8 种数据类型:byte、short、int、long、float、double、boolean ,char
除此之外支持 String、charSequence、List、Map
自定义数据类型 List 或 Map
首先,在工程的 src 目录下创建 .aidl 文件,具体如下图所示:
2.打开IPersonAidlInterface.aidl,在 .aidl 文件中添加具体的业务方法,文件内容如下
// IMyAidlInterface.aidl
package com.hoop.myapp1;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
//具体的业务
void setAccount(String account);
void setPassword(String password);
String getInfo();
/**
* 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);
}
3.然后,重新 rebuild project , Android SDK 工具会在相应的目录生成对应的与 .aidl 文件同名的 .java
rebuild project
4.那么这个业务要体现在什么地方呢,从上面可知 Stub 是一个抽象类,那么它所提供的具体业务必
然需要一个具体的实现类来完成,下面实现这个具体的业务类,具体如下:
public class IPaidl extends IMyAidlInterface.Stub{
private String account;
private String password;
@Override
public void setAccount(String account) throws RemoteException {
this.account=account;
}
@Override
public void setPassword(String password) throws RemoteException {
this.password=password;
}
@Override
public String getInfo() throws RemoteException {
return "password"+password+"password"+password
+ }
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
}
5.向客户端公开接口
创建一个 Service 以便对外提供具体的业务,具体如下:
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
//返回的是new 上面的实现类
return new IPaidl();
}
当外部调用 bindService() 方法绑定服务时,就会调用 onBind() 方法返回 IBinder 对象,这个 IBinder对象也是具体的业务对象,如这里的 onBind() 方法返回的也是具体的业务对象,两者是统一的。此外,创建的 Service 要在 AndroidManifest.xml 文件中声明,具体如下:
<service
android:name=".PersonService"
android:enabled="true"
android:exported="true"
android:process=":remote">
</service>
其中使用 process 关键字表示为该服务开启一个独立的进程,remote 可任意,表示进程名称,":"将会
在主进程(进程名为包名)添加新名称作为新进程的名称,如 com.hopu.study 将会变成
com.hopu.study:remote。如果是在两个app上 android:process=":remote可省略
4.客户端远程调用
通过上面几步完成了服务的搭建,并将服务运行在独立进程中,下面主要就是客户端的具体调用
public class MainActivity extends AppCompatActivity {
private Button btn_start_service, btn_stop_service, btn_call_remote;
private static final String TAG = "MainActivity";
private IPersonAidlInterface iPersonAidlInterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_start_service = findViewById(R.id.btn_start_service);
btn_stop_service = findViewById(R.id.btn_stop_service);
btn_call_remote = findViewById(R.id.btn_call_remote);
btn_start_service.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
bindServiceClick(v);
}
});
btn_stop_service.setOnClickListener(new View.OnClickListener() {
@Override 、
public void onClick(View v) {
callRemoteClick(v);
}
}) ;
}
public void bindService Cick(View view) {
Log . i (TAG , "绑定服务 . . . ") ;
Intent intent = new Intent (this , PersonSe rvice . class) ;
// 绑定服务时自动创建服务
bindService (intent , conn , Context . BIND_AUTO_CREATE) ;
}
public void unbindSe rviceCl i ck(View view) {
Log . i (TAG , "解绑服务 . . . ") ;
unbindService (conn) ;
}
public void call RemoteClick(View view) {
Log . i (TAG , "远程调用具体服务 . . . ") ;
try {
iPersonAidl Interface . setName ("Tom") ;
iPersonAidl Interface . setAge (10) ;
String info = iPersonAidl Interface . getInfo () ;
System . out . println ("这是远程调用的服务信息: "+info) ;
} catch (RemoteExcepti on e) {
e . printStackTrace () ;
}
}
private Servi ceConnection conn = new Service Connection () {
@Override
public void onService Connected (ComponentName name , IBinde rservice) {
// 根据实际情况返回 IBinder 的本地对象或其代理对象
iPersonAidl Interface =
IPersonAidl Interface . Stub . asInterface (service) ;
System . out . println ("具体的业务对象: "+iPersonAi dlInterface) ;
}
@Override
public void onService Disconnected (ComponentName name) {
// Service 意外中断时调用
案例
App1(服务端)
IMyAidllnteace.aidl
// IMyAidlInterface.aidl
package com.hoop.myapp1;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
void setAccount(String account);
void setPassword(String password);
String getInfo();
/**
* 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);
}
MainActivity
package com.hoop.myapp1;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
MyService
package com.hoop.myapp1;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class MyService extends Service {
//无参构建方法
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return new IPaidl();
}
public class IPaidl extends IMyAidlInterface.Stub{
private String account;
private String password;
@Override
public void setAccount(String account) throws RemoteException {
this.account=account;
}
@Override
public void setPassword(String password) throws RemoteException {
this.password=password;
}
@Override
public String getInfo() throws RemoteException {
//使用数组模拟数组的账号密码
String[] accounts={"123","456"};
String[] passwords={"123","456"};
//对账号密码进行判断
for (int i = 0; i < accounts.length; i++) {
if (account.equals(accounts[i])){
if (password.equals(passwords[i])){
//进行跳转
Intent intent = new Intent();
intent.setClass(MyService.this,MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
return "登录成功";
}else {
return "登录失败";
}
}
}
return "没有找到账号";
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
}
}
App2(服务端)
需要点对点的将服务端的 IMyAidllnteace.aidl 文件复制到App2(服务端)文件下 同时还要在文件清单上添加 下面这行代码
<queries>
<package android:name="com.hoop.myapp1"/> //com.hoop.myapp1表示服务端的包名
</queries>
MainActivity
package com.hoop.myapp2;
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.Toast;
import com.google.android.material.textfield.TextInputEditText;
import com.hoop.myapp1.IMyAidlInterface;
public class MainActivity extends AppCompatActivity {
private TextInputEditText ti_zh,ti_mm;
private Button button;
private IMyAidlInterface iMyAidlInterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ti_zh=findViewById(R.id.ti_zh);
ti_mm=findViewById(R.id.ti_mm);
button=findViewById(R.id.textButton);
//绑定
Log.i("客户端","绑定线程...");
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.hoop.myapp1","com.hoop.myapp1.MyService"));
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
//登录按钮点击事件
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String zh = ti_zh.getText().toString();
String mm = ti_mm.getText().toString();
try {
iMyAidlInterface.setAccount(zh);
iMyAidlInterface.setPassword(mm);
String info=iMyAidlInterface.getInfo();
Toast.makeText(MainActivity.this, ""+info, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
// //解绑
// Log.i("客户端","解绑线程...");
// unbindService(serviceConnection);
}
});
}
ServiceConnection serviceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
iMyAidlInterface=IMyAidlInterface.Stub.asInterface(iBinder);
Log.i("serviceConnection",""+iMyAidlInterface);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
}
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"
android:gravity="center"
android:layout_margin="18dp"
tools:context=".MainActivity">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:gravity="center"
android:layout_height="wrap_content"
android:hint="请输入账号">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/ti_zh"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_marginTop="15dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入密码"
app:endIconMode="password_toggle">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/ti_mm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
/>
<Button
android:layout_marginTop="20dp"
android:id="@+id/textButton"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登录"
style="@style/Widget.MaterialComponents.Button.TextButton"
/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>