Ok, Mark. I’ve cracked it. The problem stems from the implementation of the update(BackendlessUser, AsyncCallback) function. The problem is, I’m sending my user object off to be saved asynchronously, and while its being saved, I still want to use data from my user (i.e., their contacts). However, the update function directly mutates the object that I pass into it instead of making a copy. So, I’m assuming as part of the serialization process, relations on the user are cast to Object in order to make them generic and then by the time the update process is over they are cast back to their appropriate types. However, this causes problems when you continue to try to use your data during the asynchronous update process. This problem isn’t easy to code around either, since BackendlessUser implements neither a copy constructor or the Cloneable interface.
Here is code that will replicate the bug with the application you used in your prior example:
public class LaunchActivity extends ActionBarActivity {
BackendlessUser user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_launch);
Backendless.initApp(this, "757E349F-C515-5AF3-FF5D-EFF0379BFB00", "115455F0-5317-46D0-FFB2-82712757C900", "v1" );
Backendless.Data.mapTableToClass( "Contact", Contact.class );
Backendless.Data.of( BackendlessUser.class ).findById( "A8A2A0B6-472B-5C15-FF97-30751A2C6F00", 2, new AsyncCallback<BackendlessUser>()
{
@Override
public void handleResponse( BackendlessUser user )
{
LaunchActivity.this.user = user;
Object[] contacts = (Object[]) LaunchActivity.this.user.getProperty( "contacts" );
((Contact) contacts[ 0 ]).phoneNumber = "1111111";
Backendless.UserService.update(LaunchActivity.this.user, new AsyncCallback<BackendlessUser>() {
@Override
public void handleResponse(BackendlessUser response) {
System.out.println("Data has been saved");
}
@Override
public void handleFault(BackendlessFault fault) {
}
});
Object[] c = (Object[])LaunchActivity.this.user.getProperty("contacts");
Contact[] cTyped = new Contact[c.length];
System.arraycopy(c, 0, cTyped, 0, c.length);
}
@Override
public void handleFault( BackendlessFault fault )
{
}
} );
}
}
Thanks, Tim. I will give it a try. Of course, a workaround for the problem is to use the “Object[] contacts” array which you got before the update call ))
If you need to get a hold of the properties of the user object while it is being updated, then you should use the following call to update it on the backend:
Could you try changing the following lines to create “strongly-typed” generics and see if it makes a difference? Java does not have runtime enforcement of the generics and thus the information about the underlying type is lost by the time it gets to our serialization logic.
change this:
return new ArrayList<>();
to:
return new ArrayList<Contact>();
And change this:
return new ArrayList<>(Arrays.asList(cTyped));
to:
return new ArrayList<Contact>(Arrays.asList(cTyped));
The new contact is saved though, right? I just tried and got the same error, but I see that the object is saved. It means the error occurs with the response. P
Yes, the contact is saved but the error is still crashing my app. It appears to be happening internal to the Backendless SDK so I’m not sure what to do about it.
Tim, could you check if you have the latest version of Backendless SDK for Android? I tried the code again with the latest build (mine was a bit outdated) and the exception does not occur any more.
I upgraded from 2.0.1 (which was the latest on Maven Central) to 2.0.4 (the latest from the backendless website) and I am still getting the same crash.
Ok. I’m using the Backendless SDK from github now. I’m getting a similar but different error. Now, instead of a gripe about the ‘Contacts’ field not existing, it is griping about ‘User’ not existing.
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: BackendlessException{ code: 'IllegalArgumentException', message: 'User' }
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.backendless.FootprintsManager$Inner.updateFootprintForObject(FootprintsManager.java:354)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.backendless.FootprintsManager$Inner.updateFootprintForObject(FootprintsManager.java:342)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.backendless.Persistence$4.handleResponse(Persistence.java:187)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.backendless.async.message.AsyncMessage$ResponseHandler.handle(AsyncMessage.java:64)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.backendless.async.message.AsyncMessage.handleCallback(AsyncMessage.java:41)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.backendless.core.AndroidCarrier$1.handleMessage(AndroidCarrier.java:37)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:98)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at android.os.Looper.loop(Looper.java:136)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:5586)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at java.lang.reflect.Method.invokeNative(Native Method)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at java.lang.reflect.Method.invoke(Method.java:515)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1268)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1084)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at dalvik.system.NativeStart.main(Native Method)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: Caused by: java.lang.NoSuchFieldException: User
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at java.lang.Class.getDeclaredField(Class.java:596)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.backendless.utils.ReflectionUtil.getField(ReflectionUtil.java:65)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.backendless.utils.ReflectionUtil.getField(ReflectionUtil.java:71)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.backendless.utils.ReflectionUtil.getFieldValue(ReflectionUtil.java:39)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.backendless.FootprintsManager$Inner.updateFootprintForObject(FootprintsManager.java:260)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.backendless.FootprintsManager$Inner.updateFootprintForObject(FootprintsManager.java:342)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.backendless.Persistence$4.handleResponse(Persistence.java:187)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.backendless.async.message.AsyncMessage$ResponseHandler.handle(AsyncMessage.java:64)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.backendless.async.message.AsyncMessage.handleCallback(AsyncMessage.java:41)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.backendless.core.AndroidCarrier$1.handleMessage(AndroidCarrier.java:37)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:98)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at android.os.Looper.loop(Looper.java:136)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:5586)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at java.lang.reflect.Method.invokeNative(Native Method)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at java.lang.reflect.Method.invoke(Method.java:515)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1268)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1084)
10-23 15:41:25.546 24141-24141/com.domain.appname E/AndroidRuntime: at dalvik.system.NativeStart.main(Native Method)