passing custom object to android service in different process
I have a service that is set to start in a separate process:
<service android:name=".services.UploadService"
android:process=":UploadServiceProcess" />
And I can successfully bind to it using b开发者_开发问答indService(). My issue occurs when I try to send a message by calling Messenger.send():
service.send(Message.obtain(null, UploadService.MESSAGE_UPLOAD_REQUEST, uploadRequest));
where uploadRequest is a custom object that implements Parcelable
public class UploadRequest implements Parcelable {
public File file;
public boolean deleteOnUpload;
public UploadRequest(File file, boolean deleteOnUpload) {
this.file = file;
this.deleteOnUpload = deleteOnUpload;
}
private UploadRequest(Parcel in) {
this.file = new File(in.readString());
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.file.getPath());
}
public static final Parcelable.Creator<UploadRequest> CREATOR = new Parcelable.Creator<UploadRequest>() {
public UploadRequest createFromParcel(Parcel in) {
return new UploadRequest(in);
}
public UploadRequest[] newArray(int size) {
return new UploadRequest[size];
}
};
}
I set a breakpoint in my services handleMessage, but my app never gets to the breakpoint. However, if instead of using my custom UploadRequest object I send null, I get to the handleMessage breakpoint like I'd expect, but obviously I can't do anything at that point. I've verified file.getPath() when calling writeToParcel returns a non null String. This leads me to believe that something is off in my UploadRequest class, but from googling I can't see anything wrong with my class. Any ideas?
The documentation of Message member obj says:
An arbitrary object to send to the recipient. When using Messenger to send the message across processes this can only be non-null if it contains a Parcelable of a framework class (not one implemented by the application). For other data transfer use setData(Bundle). Note that Parcelable objects here are not supported prior to the FROYO release.
My guess is you are seeing an issue because you are creating your own parcelable which is not allowed when crossing the process boundary. Instead, you'll have to package your object into a bundle. This also means your object will need to implement Serializable but will not need to be Parcelable.
tried this just now.
Message.obj can transfer a framework class, such as ContentValues.
and Message.SetData can transfer a Bundle across processes and you can put any Parcelable objects into the bundle.
just remember to call setClassLoader on bundle, when received the Messag.
send side
Message localMsg = Message.obtain();
localMsg.what = TPServiceConnection.MSG_REPLY_SERVICE_HELLO;
Bundle data = new Bundle();
ContentValues cv = new ContentValues();
cv.put("KEY", mRand.nextInt());
data.putParcelable("KEY", cv);
TPServiceDataModal modal = new TPServiceDataModal(mRand.nextInt());
data.putParcelable("KEY2", modal);
localMsg.setData(data);
receive side
Bundle data = msg.getData();
data.setClassLoader(this.getClass().getClassLoader());
Parcelable parcelable = data.getParcelable("KEY");
if (parcelable instanceof ContentValues) {
ContentValues cv = (ContentValues) parcelable;
Log.d(TAG, "reply content: " + cv.getAsInteger("KEY"));
}
Parcelable parcelable2 = data.getParcelable("KEY2");
if (parcelable2 instanceof TPServiceDataModal) {
TPServiceDataModal modal = (TPServiceDataModal) parcelable2;
Log.d(TAG, "reply modal: " + modal.mData);
}
where TPServiceDataModal is a Parcelable calss.
Example to pass parcelable object via Bundle
, remember to use classLoader
and define object with correct package
Client app
val msg: Message = Message.obtain(null, MSG_SAY_HELLO)
msg.data = Bundle().apply {
putParcelable("KEY_MY_OBJECT", MyObject("an", 12))
}
mService?.send(msg)
.
package com.example.androidmessengerclient
@Parcelize
class MyObject(val name: String, val age: Int) : Parcelable
Server app
override fun handleMessage(msg: Message) {
when (msg.what) {
123 -> {
msg.data.classLoader = MyObject::class.java.classLoader
val o = msg.data.getParcelable<MyObject>("KEY_MY_OBJECT")
}
}
}
. The model in the Server
app needs to have the the same package like the Client
app
package com.example.androidmessengerclient
@Parcelize
class MyObject(val name: String, val age: Int) : Parcelable
精彩评论