How To Use Interface To Communicate Between Activities After Process Death?
Solution 1:
Update:
Do a right click on the project tree and add a new AIDL file called IMyAidlInterface.aidl:
package com.test.aidlsample;
import com.test.aidlsample.MyData;
interfaceIMyAidlInterface {
List<MyData> getData(long id);
}
If you need to return objects to your client you need to declare and define them as parcelable and import them in aidl file too, here is the MyData.aidl that should be beside the other aidl file:
package com.test.aidlsample;
// Declare MyData so AIDL can find it and knows that it implements// the parcelable protocol.
parcelable MyData;
and this is MyData.java in the java folder:
publicclassMyDataimplementsParcelable {
privatelong productId;
private String productName;
privatelong productValue;
publicMyData(long productId, String productName, long productValue) {
this.productId = productId;
this.productName = productName;
this.productValue = productValue;
}
@OverridepublicintdescribeContents() {
return0;
}
@OverridepublicvoidwriteToParcel(Parcel dest, int flags) {
dest.writeLong(this.productId);
dest.writeString(this.productName);
dest.writeLong(this.productValue);
}
protectedMyData(Parcel in) {
this.productId = in.readLong();
this.productName = in.readString();
this.productValue = in.readLong();
}
publicstaticfinal Parcelable.Creator<MyData> CREATOR = newParcelable.Creator<MyData>() {
@Overridepublic MyData createFromParcel(Parcel source) {
returnnewMyData(source);
}
@Overridepublic MyData[] newArray(int size) {
returnnewMyData[size];
}
};
}
Now build the project so Stub class gets built. After a successful build continue with the service:
publicclassSdkServiceextendsService {
private IMyAidlInterface.Stubbinder=newIMyAidlInterface.Stub() {
@Overridepublic List<MyData> getData(long id)throws RemoteException {
//TODO: get data from db by id;
List<MyData> data = newArrayList<>();
MyDataaData=newMyData(1L, "productName", 100L);
data.add(aData);
return data;
}
};
@Nullable@Overridepublic IBinder onBind(Intent intent) {
return binder;
}
}
and add the service to the sdk manifest. If you are adding sdk as a dependency to the client like: implementation project(':sdk')
you don't need to add AIDL files to client. If not, you have to add them and build the client application. Now, only remains to implement the client activity:
publicclassMainActivityextendsAppCompatActivity {
IMyAidlInterface mService;
/**
* Class for interacting with the main interface of the service.
*/privateServiceConnection mConnection = newServiceConnection() {
publicvoidonServiceConnected(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.
}
}
publicvoidonServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been// unexpectedly disconnected -- that is, its process crashed.
mService = null;
}
};
privatevoidupdateUi(List<MyData> data) {
//TODO: Update UI here
}
@OverrideprotectedvoidonResume() {
if (mService == null) {
Intent serviceIntent = newIntent();
//CAREFUL: serviceIntent.setComponent(new ComponentName("your.client.package", "your.sdk.service.path"));
serviceIntent.setComponent(newComponentName("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();
}
@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
every time your client activity gets visibility, it gets data from sdk service. Just build your logic over this template. In sdk activity save data to a database and in service query them from database. I've used simple parameters in this sample.
I assumed your sdk is a library in the client app. If not, you need to do some small modifications maybe. And as I mentioned before you can find more details here: Android Interface Definition Language (AIDL). There are lots of samples and even more Q/A here in the SO on the subject. Good luck.
Original: You need to get callbacks from an activity that is currently invisible since your SDK activity is in front, right? To do that you can create a database for your SDK, persist data to your database and get data via an AIDL in the starting activity:
SdkService sdkService;
CallbackData callbackData
privateServiceConnection mConnection = newServiceConnection() {
// Called when the connection with the service is establishedpublicvoidonServiceConnected(ComponentName className, IBinder service) {
sdkService = SdkService.Stub.asInterface(service);
}
// Called when the connection with the service disconnects unexpectedlypublicvoidonServiceDisconnected(ComponentName className) {
Log.e(TAG, "Service has unexpectedly disconnected");
sdkService = null;
}
};
in onCreate:
Intenti=newIntent()
i.setClassName("your.sdk.packageName", "your.sdk.service.path.and.name");
bindService(i, mConnection, Context.BIND_AUTO_CREATE);
and in whenever needed:
if(sdkService!=null){callbackData=sdkService.getCallbacks();updateUI();}
Just be careful getting a binder is an async job so if you call bindService and right after call sdkService.getCallbackData you get a NullPointerException. So you might want to move getCallbacks and updateUI inside the onServiceConnected and call bindService in onResume so every time activity becomes visible you would check if there is CallbackData so you can update your UI or whatever.
Solution 2:
You cannot use interfaces directly to communicate between activities.
As soon as you start a new activity and new activity becomes visible android OS can kill 1st activity anytime (you can try this with a flag inside developer option "Don't keep activities"). So user of your SDK will complain about certain random "null pointer exception".
So, Now if you want to share data between current and previous screen, you might have to rethought your solution using Fragments. Exposing your UI using a fragment and communicating back your result to activity which then would update proper fragment which needs the data.
I faced similar issue in one existing app which I was asked to fix. I switched entire app to fragments and single activity, first to release a hot fix.
Solution 3:
The problem occurs after process death. The interface becomes null, because it is not serialised and I can't return callback to client anymore. How should I edit my code to tackle this issue? Is it even possible?
This is not possible. If the client process dies, all of its executing code - including your SDK - gets wiped away.
I know that client can initialise SDK in the application class, then it will be re-set after process death. However, such approach will result in difficulty for client to communicate results back to activity from application class.
So what? If the client Activity is restarted, it should call the SDK again to set a new callback instance which you can use from that point forward.
Solution 4:
You can use a sharedviewmodel that is bound to both activities; have a mutablelivedata variable that you can observe from the two activities.
ideally on the first activity you can just put the value inside the mutablelivedata variable. Then on the second activity get the activity.
Follow the following link to give you a guideline.
Post a Comment for "How To Use Interface To Communicate Between Activities After Process Death?"