Можно ли отправлять пользовательские объекты в Android Wear?

Я просто изучаю, как разрабатывать Android Wear, я создал полноэкранную активность для смарт-часов, а в моей мобильной части приложения я получаю некоторые данные JSON и создаю из этого список пользовательских объектов.

В мобильном приложении я покажу информацию для этих объектов в ListView.

В части Wear моего приложения я хочу показать ограниченную версию этого списка, например, верхние 3 из списка будут показаны в моем полноэкранном приложении на Wearable.

Моя проблема заключается в том, что, похоже, не существует способа отправить Parcelable Objects в Android Wear, нет возможности putParcelable в DataItem.

Похоже, что единственным вариантом является отправка объекта в байтах, например:

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);
    }
}

Итак, мне нужно преобразовать свой объект в байты, отправить его в Android Wear и преобразовать его обратно? Это означает, что мои объекты, которые я реализовал Parcelable, поэтому я могу отправить их через Intents, теперь также нужно реализовать Serializable, это правильно или есть лучший способ сделать это?

Ответ 1

Решение в виде пучка:

В моем приложении я создал легкий класс, специально для отправки его с телефона для просмотра. Поскольку код разделяется между мобильными и носимыми частями приложения, его можно легко упаковать и восстановить на обоих устройствах без дублирования кода. Он обеспечивает Bundle -подобный механизм, но использует DataMap.

Пример реализации:

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;
    }
}

Написание такого класса позволит вам рассмотреть, что на самом деле нужно отправлять между устройствами для отправки как можно меньше. Он также не сломается, когда какое-либо поле будет добавлено или удалено (в противостоянии следующему решению).

Отвечая на ваши соображения:

Если вы не хотите писать новый класс и хотите повторно использовать существующий код, вы можете попробовать использовать приведенный ниже код. Это позволит вам оставаться с интерфейсом Parcelable (без необходимости реализовывать интерфейс Serializable). Я не тестировал его при отправке через устройства, но он добился успеха marshall() и unmarshall() массива байтов в/из Parcel и сохранил его в DataMap.

ПРИМЕЧАНИЕ. Я не знаю точно, как Google Play Services сохраняет все эти данные DataApi, но я боюсь, что что-то может сломаться, когда такой класс будет обновлен.
Например, класс будет обновлен на Android Wear, пользователь запустит приложение, которое попытается прочитать текущие данные из DataApi (который был "сериализован" с использованием старой версии этого класса) и попытаться прочитать его из byte[] как если бы это была обновленная версия. Эти проблемы должны быть проверены, но я не думаю, что они сделали так DataApi "примитивным" только потому, что "или сделать сложнее разрабатывать приложения на Wear.

Я настоятельно рекомендую использовать Bundle -подобное решение и не использовать решение Parcelable.
Используйте это на свой страх и риск.

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;
    }
}

Код также доступен на GitHub.