流程死亡后如何使用接口在活动之间进行通信



我正在构建一个SDK,需要在没有实际完成活动的情况下实现活动之间的回调。我以前使用onActivityResult向调用者活动提供结果。但是,这将关闭活动,并且我需要在不完成SDK活动的情况下交付回调。我目前的实现:

fun initializeSDK(){
SDK.getInstance().initialize(resultsCallbackImpl)
}
val resultsCallbackImpl:ResultsCallback = object : ResultsCallback {
override fun response1() {

}
override fun response2() {

}
};

例如,客户端在单击按钮后从其活动中调用initializeSDK()。然后客户端将接口作为参数传递,该参数在SDK singleton中设置为属性。然后我使用该接口返回结果。

该问题发生在进程死亡之后。接口变为null,因为它没有序列化,并且我不能再向客户端返回回调。我应该如何编辑我的代码来解决这个问题?这可能吗?

我知道客户端可以在应用程序类中初始化SDK,然后在进程死亡后重新设置。但是,这种方法将导致客户端难以将结果从应用程序类传达回活动。

更新:

右键单击项目树并添加一个名为IMyAidlInterface.AIDL:的新AIDL文件

package com.test.aidlsample;
import com.test.aidlsample.MyData;
interface IMyAidlInterface {
List<MyData> getData(long id);
}

如果你需要将对象返回给你的客户端,你需要声明并定义它们为parcelable,并将它们导入到aidl文件中,这里是MyData.aidl,它应该在另一个aidl文件旁边:

package com.test.aidlsample;
// Declare MyData so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable MyData;

这是java文件夹中的MyData.java:

public class MyData implements Parcelable {
private long productId;
private String productName;
private long productValue;
public MyData(long productId, String productName, long productValue) {
this.productId = productId;
this.productName = productName;
this.productValue = productValue;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(this.productId);
dest.writeString(this.productName);
dest.writeLong(this.productValue);
}
protected MyData(Parcel in) {
this.productId = in.readLong();
this.productName = in.readString();
this.productValue = in.readLong();
}
public static final Parcelable.Creator<MyData> CREATOR = new Parcelable.Creator<MyData>() {
@Override
public MyData createFromParcel(Parcel source) {
return new MyData(source);
}
@Override
public MyData[] newArray(int size) {
return new MyData[size];
}
};
}

现在构建项目,以便构建Stub类。成功构建后继续使用服务:

public class SdkService extends Service {
private IMyAidlInterface.Stub binder = new IMyAidlInterface.Stub() {
@Override
public List<MyData> getData(long id) throws RemoteException {
//TODO: get data from db by id;
List<MyData> data = new ArrayList<>();
MyData aData = new MyData(1L, "productName", 100L);
data.add(aData);
return data;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}

并将该服务添加到sdk清单中。如果您将sdk作为依赖项添加到客户端(如:implementation project(':sdk')),则不需要将AIDL文件添加到客户端。如果没有,则必须添加它们并构建客户端应用程序。现在,只剩下实现客户端活动:

public class MainActivity extends AppCompatActivity {
IMyAidlInterface mService;
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service.  We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = IMyAidlInterface.Stub.asInterface(service);
try {
List<MyData> data = mService.getData(1L);
updateUi(data);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
}
};
private void updateUi(List<MyData> data) {
//TODO: Update UI here
}
@Override
protected void onResume() {
if (mService == null) {
Intent serviceIntent = new Intent();

//CAREFUL: serviceIntent.setComponent(new ComponentName("your.client.package", "your.sdk.service.path"));
serviceIntent.setComponent(new ComponentName("com.test.sampleclient", "com.test.aidlsample.SdkService"));
bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
} else {
try {
updateUi(mService.getData(1L));
} catch (RemoteException e) {
e.printStackTrace();
}
}
super.onResume();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

每次您的客户端活动获得可见性时,它都会从sdk服务获取数据。只需在此模板上构建您的逻辑即可。在sdk活动中,将数据保存到数据库中,并在服务中从数据库中查询数据。我在这个示例中使用了简单的参数。

我假设你的sdk是客户端应用程序中的库。如果没有,你可能需要做一些小的修改。正如我之前提到的,你可以在这里找到更多的细节:Android接口定义语言(AIDL)。SO中有很多关于这个主题的样本,甚至还有更多的问答。祝你好运

原文:您需要从当前不可见的活动中获取回调,因为您的SDK活动在前面,对吗?要做到这一点,你可以为你的SDK创建一个数据库,将数据持久化到你的数据库中,并在启动活动中通过AIDL获取数据:

SdkService sdkService;
CallbackData callbackData
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
sdkService = SdkService.Stub.asInterface(service);
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "Service has unexpectedly disconnected");
sdkService = null;
}
};

在onCreate:中

Intent i = new Intent()
i.setClassName("your.sdk.packageName", "your.sdk.service.path.and.name");
bindService(i, mConnection, Context.BIND_AUTO_CREATE);

以及在任何需要的时候:

if(sdkService != null){
callbackData = sdkService.getCallbacks();
updateUI();
}

请注意,获取绑定器是一项异步作业,因此如果您调用bindService,并且在调用sdkService.getCallbackData之后,您会得到一个NullPointerException。因此,您可能需要将getCallbacks和updateUI移动到onServiceConnected中,并在onResume中调用bindService,这样每次活动可见时,您都会检查是否有CallbackData,以便更新UI或其他内容。

不能直接使用接口在活动之间进行通信。

一旦你开始了一个新的活动,并且新的活动变得可见,安卓操作系统就可以随时杀死第一个活动(你可以在开发者选项"不要保留活动"中使用标志来尝试)。所以你的SDK的用户会抱怨某些随机的";空指针异常";。

因此,现在,如果您想在当前和以前的屏幕之间共享数据,您可能必须使用Fragments重新考虑您的解决方案。使用片段展示您的UI,并将结果反馈给活动,然后活动将更新需要数据的适当片段。

我在一个现有的应用程序中遇到了类似的问题,我被要求修复这个问题。我将整个应用程序切换到片段和单个活动,首先发布了一个热修复程序。

问题发生在进程死亡之后。接口变为null,因为它不是串行的,我不能再向客户端返回回调了。我应该如何编辑我的代码来解决这个问题?这可能吗?

这是不可能的。如果客户端进程死了,它的所有执行代码——包括你的SDK——都会被擦除。

我知道客户端可以在应用程序类中初始化SDK,然后在进程死亡后重新设置。然而,这种方法将导致客户端难以将结果从应用程序类传递回活动。

那又怎样?如果客户端"活动"重新启动,它应该再次调用SDK来设置一个新的回调实例,从那时起您可以使用该实例。

您可以使用绑定到这两个活动的sharedviewmodel;有一个可变的数据变量,您可以从这两个活动中观察到它。

理想情况下,在第一个活动中,您可以将值放在mutablelivedata变量中。然后在第二个活动中获取活动。

按照以下链接为您提供指导。

ViewModel概览

最新更新