一、Service 开启和停止
二、Service 执行耗时工作
三、IPC常用方式
四、AIDL(一)同一应用中使用AIDL及原理
五、AIDL(二)不同应用中使用、自定义数据类型及定向Tag
六、AIDL(三)实现回调
七、AIDL(四)获取服务及绑定和Binder传递流程
AIDL(三)实现回调
前面AIDL
中,都是客服端主动向服务端请求数据,那可不可以注册回调函数,让服务端主动向客户端发送数据呢?分析AIDL
特点,可以有两种方式实现服务端主动向客户端传递数据:
- 客户端通过绑定服务端的
Service
,进而与服务端通信,那么反客为服,客户端也可以定义Service
,而后服务端通过绑定客户端,进而调用客户端的接口,主动给客户端传递消息。 - 客户端绑定了服务端的
Service
,两者之间就能够通信。实际上服务端传递了Binder
给客户端,客户端拿到Binder
之后就可以进行通信了,这就说明了Binder
对象本身能够跨进程传输。 于是改造之前的接口:客户端调用服务端接口的时候将自己生成的Binder传递给服务端,那么服务端发生变化的时候就可以通过这个Binder
来通知客户端了。
通过比对第一和第二种方式,第一种方式过于复杂,对于客户端、服务端的角色容易搞混。 第二种方式符合我们认知中的“回调”,也就是说跨进程的回调和同一个进程里的回调理解上是一致的。
6.1 AIDL回调实现
6.1.1 服务端声明回调接口
代码1
com/ieening/server/ScoreChangedCallback.aidl
// ScoreChangedCallback.aidl
package com.ieening.server;
// Declare any non-default types here with import statements
import com.ieening.server.Student; // 注释1
interface ScoreChangedCallback {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
oneway void onCallback(in Student student); // 注释2
}
- 注释1:
Student
是信息类,实现了Parcelable
接口; - 注释2:
onCallback
调用回调函数,in
表示数据从服务端传递客户端,oneway
表示调用onCallback
方法的线程立即返回,不阻塞等待方法调用结果。
6.1.2 服务端暴露注册回调函数接口
服务端定义了回调接口函数,客户端需要给服务端传递接口的实现。因此服务端还需要将注册回调的接口暴露给客户端。 定义AIDL
文件如下:
代码2
com/ieening/server/IStudentInfo.aidl
// IStudentInfo.aidl
package com.ieening.server;
// Declare any non-default types here with import statements
import com.ieening.server.Student;
import com.ieening.server.ScoreChangedCallback;
interface IStudentInfo {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
// 主动获取信息
Student getStudentInfo();
// 注册回调函数
oneway void register(in ScoreChangedCallback callback);
}
至此,服务端提供了两个方法:
getStudentInfo
:客户端调用此方法主动获取学生信息。register
:客户端调用此方法注册回调实例。
6.1.3 服务端调用回调函数机制
代码3
com.ieening.server.StudentService
package com.ieening.server;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import java.util.Objects;
public class StudentService extends Service {
private Student student;
private ScoreChangedCallback remoteCallback;
private boolean serviceThreadStopFlag = false;
public StudentService() {
}
private final IStudentInfo.Stub studentInfoBinder = new IStudentInfo.Stub() { // 注释1
@Override
public Student getStudentInfo() {
return student;
}
@Override
public void register(ScoreChangedCallback callback) {
remoteCallback = callback;
}
};
@Override
public void onCreate() {
super.onCreate();
student = new Student("ieening", 28);
new Thread(() -> { // 注释2
while (!serviceThreadStopFlag) {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
student.setScore((int) (Math.random() * 100));
try {
if (!Objects.isNull(remoteCallback)) {
remoteCallback.onCallback(student);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
stopSelf();
}).start();
}
@Override
public void onDestroy() {
serviceThreadStopFlag = true;
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return studentInfoBinder;
}
}
- 注释1:传递给客户端
Binder
; - 注释2:每秒更新分数,并在更新后主动调用
remoteCallback.onCallback
,进行回调操作,回调接口Binder
是客户端传递给服务端;
6.1.4 客户端调用逻辑
客户端需要完成的工作:
- 注释1:绑定服务;
- 注释2:发送
getStudentInfo
请求,手动更新Student
信息; - 注释3:自定义
ServiceConnection
,并创建实例,在onServiceConnected
中,接收服务端传递过来的IStudentInfo Binder
,并调用其中register
函数,注册回调,原理是将ScoreChangedCallback.Stub
传递给服务端,服务端每次更新studen info
后,主动调用,将结果反馈至客户端,实现自动更新Student
信息。
代码4
com.ieening.androidipccallback.MainActivity
package com.ieening.androidipccallback;
......
public class MainActivity extends AppCompatActivity {
......
private IStudentInfo studentServiceRemoteBinder = null;
Handler studentInfoHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
Student student = (Student) Objects.requireNonNull(msg.getData().getParcelable("student"));
binding.studentInfoAutoUpdateEditText.setText(student.toString());
}
}
};
ServiceConnection serviceConnection = new ServiceConnection() { // 注释3
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
studentServiceRemoteBinder = IStudentInfo.Stub.asInterface(service);
try {
studentServiceRemoteBinder.register(new ScoreChangedCallback.Stub() {
@Override
public void onCallback(Student student) {
//在子线程中创建一个消息对象
Message studentInfoMessage = new Message();
studentInfoMessage.what = 1;
Bundle bundle = new Bundle();
bundle.putParcelable("student", student);
studentInfoMessage.setData(bundle);
//将该消息放入主线程的消息队列中
studentInfoHandler.sendMessage(studentInfoMessage);
}
});
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
studentServiceRemoteBinder = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
......
setBindUnbindButtonOnClickListener();
binding.getStudentInfoButton.setOnClickListener(v -> { // 注释2
try {
binding.studentInfoEditText.setText(studentServiceRemoteBinder.getStudentInfo().toString());
} catch (RemoteException e) {
throw new RuntimeException(e);
}
});
}
private void setBindUnbindButtonOnClickListener() {
binding.bindStudentServiceButton.setOnClickListener(v -> { // 注释1
Intent bindIntent = new Intent(this, StudentService.class);
boolean bindResult = bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
if (bindResult) {
binding.bindStudentServiceButton.setEnabled(false);
binding.unbindStudentServiceButton.setEnabled(true);
binding.getStudentInfoButton.setEnabled(true);
Toast.makeText(this, "bind student service success", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "bind student service failed", Toast.LENGTH_SHORT).show();
}
});
binding.unbindStudentServiceButton.setOnClickListener(v -> {
unbindService(serviceConnection);
binding.bindStudentServiceButton.setEnabled(true);
binding.unbindStudentServiceButton.setEnabled(false);
binding.getStudentInfoButton.setEnabled(false);
});
}
@Override
protected void onDestroy() {
if (!Objects.isNull(studentServiceRemoteBinder) && studentServiceRemoteBinder.asBinder().isBinderAlive()) {
unbindService(serviceConnection);
}
super.onDestroy();
}
}