Can I send custom objects to Android Wear?
Asked Answered
J

1

12

I am just learning how to develop for Android Wear, I have created a full screen Activity for Smart Watches and in my mobile part of the application I get some JSON data and create a list of custom objects from this.

On my mobile app I show the information for these objects in a ListView.

On the Wear piece of my application I want to show a limited version of this list, for example the top 3 from the list will be show on my full screen app on the Wearable.

My problem is that there doesn't seem to be a way to send Parcelable Objects to Android Wear, there is no option to putParcelable in the DataItem.

It looks like the only option is to send an object in bytes, like this:

public void sendWearableData(GoogleApiClient aGoogleApiClient, ArrayList<MyObject> myObjectList, String path)
{

    googleApiClient = aGoogleApiClient;

    byte[] testBytes = new byte[0];

    if (googleApiClient.isConnected()) {
        PutDataMapRequest dataMapRequest = PutDataMapRequest.create(path);
        try {
            testBytes = BytesUtils.toByteArray(myObjectList);
        } catch (IOException e) {
            e.printStackTrace();
        }
        dataMapRequest.getDataMap().putByteArray(Constants.TEST_KEY, testBytes);
        PutDataRequest request = dataMapRequest.asPutDataRequest();
        Wearable.DataApi.putDataItem(googleApiClient, request);
    }
}

So I have to convert my object to bytes, send it to Android Wear and convert it back? This means that my Objects which I have implemented Parcelable on so I can send them via Intents now also need to implement Serializable, is this correct or is there a better way of doing it?

Jair answered 19/9, 2014 at 16:17 Comment(1)
Did you end up using the Bundle or Parcelable solution? Any recommendations?Remonaremonetize
G
18

Bundle-like solution:

In my app I've created a lightweight class, specially for sending it from phone to watch. Because the code is shared between mobile and wearable parts of app it can be easily packed and restored on both devices without code duplication. It provides Bundle-like mechanism, but using DataMap.

Sample implementation:

public class MyObject {

    public final long itemId;
    public final long sortOrder;
    public final String priceString;

    public MyObject(long itemId, long sortOrder, String priceString) {
        this.itemId = itemId;
        this.sortOrder = sortOrder;
        this.priceString = priceString;
    }

    public MyObject(DataMap map) {
        this(map.getLong("itemId"),
             map.getLong("sortOrder"),
             map.getString("priceString")
            );
    }

    public DataMap putToDataMap(DataMap map) {
        map.putLong("itemId", itemId);
        map.putLong("sortOrder", sortOrder);
        map.putString("priceString", priceString);
        return map;
    }
}

Writing such class will let you consider the what actually needs to be send between devices to send as little as possible. It will also not break when any field will be added or removed (in oppose to the next solution).

Answering to your Parcelable concerns:

If you don't want to write the new class and want to reuse your existing code you can try to use the code below. It will let you stay with only Parcelable interface (without need to implement Serializable interface). I haven't tested it while sending across devices but it successes to marshall() and unmarshall() byte array to/from Parcel and store it in DataMap.

NOTE: I don't know exactly how Google Play Services hold all these DataApi data, but I'm afraid that something may break when such class will be updated.
For example the class will be updated on Android Wear, user will launch the app that would try to read the current data from DataApi (that was "serialized" using old version of this class) and try to read it from byte[] as if it was updated version. These concerns should be tested, but I don't think that they made DataApi so primitive "just because" or to make harder to develop apps on Wear.

I strongly recommend to use Bundle-like solution and to not use the Parcelable solution.
Use this at your own risk.

import android.os.Parcel;
import android.os.Parcelable;

import com.google.android.gms.wearable.DataMap;

/**
 * <p>Allows to put and get {@link Parcelable} objects into {@link DataMap}</p>
 * <b>USAGE:</b>
 * <p>
 * <b>Store object in DataMap:</b><br/>
 * DataMapParcelableUtils.putParcelable(dataMap, "KEY", myParcelableObject);
 * </p><p>
 * <b>Restore object from DataMap:</b><br/>
 * myParcelableObject = DataMapParcelableUtils.getParcelable(dataMap, "KEY", MyParcelableObject.CREATOR);
 * </p>
 * I do <b>not recommend</b> to use this method - it may fail when the class that implements {@link Parcelable} would be updated. Use it at your own risk.
 * @author Maciej Ciemięga
 */
public class DataMapParcelableUtils {

    public static void putParcelable(DataMap dataMap, String key, Parcelable parcelable) {
        final Parcel parcel = Parcel.obtain();
        parcelable.writeToParcel(parcel, 0);
        parcel.setDataPosition(0);
        dataMap.putByteArray(key, parcel.marshall());
        parcel.recycle();
    }

    public static <T> T getParcelable(DataMap dataMap, String key, Parcelable.Creator<T> creator) {
        final byte[] byteArray = dataMap.getByteArray(key);
        final Parcel parcel = Parcel.obtain();
        parcel.unmarshall(byteArray, 0, byteArray.length);
        parcel.setDataPosition(0);
        final T object = creator.createFromParcel(parcel);
        parcel.recycle();
        return object;
    }
}

The code is also available on GitHub.

Glioma answered 20/9, 2014 at 12:2 Comment(5)
I don't think your concerns relating to unmarshalling Parcelable data are just; when the mobile app is updated, the wearable app is updated as well. In the wild, it would be highly unlikely that different versions would be used together, and the only scenario I can imagine is in the short time span of installing the updated wear APK.Hairless
A rather elegant solution would be to place the model that is marshalled into a shared module between the mobile and wearable modules.Hairless
@Maciej - are you still of the same opinion regarding using Parcelable?Remonaremonetize
@PaulLammertsma "when the mobile app is updated, the wearable app is updated as well" - even if the versionCode is incremented on the mobile app only?Nasion
I believe that the wear app is always updated when the mobile app is, entirely regardless of the versionCodes. You'll experience this when developing; on each run, the wear app is sent to the wearable.Hairless

© 2022 - 2024 — McMap. All rights reserved.