【问题标题】:WorkManager Data.Builder does not support ParcelableWorkManager Data.Builder 不支持 Parcelable
【发布时间】:2018-06-11 12:58:07
【问题描述】:

当您有一个带有大量变量(布尔值、整数、字符串)的大型 POJO 并且您想使用新的工作管理器来开始工作时。然后,您创建一个数据文件,该文件将添加到一次性工作请求对象中。

构建此数据文件的最佳做法是什么? (写 100 行代码只是说在构建器上为每个变量设置 int 感觉是错误的。)

回答

我最终分解了我的可包裹对象,因为我认为这是最好的实现。我不想使用 gson lib,因为它会为我的对象添加另一层序列化。

    Data.Builder builder = new Data.Builder();
        builder.putBoolean(KEY_BOOL_1, stateObject.bool1);
        builder.putBoolean(KEY_BOOL_2, stateObject.bool2);
        builder.putBoolean(KEY_BOOL_3, stateObject.bool3);
        builder.putInt(KEY_INT_1, stateObject.int1);
        builder.putInt(KEY_INT_2, stateObject.int2);
        builder.putString(KEY_STRING_1, stateObject.string1);
        return builder.build();

更新

正如@CommonsWare 指出的那样,我的问题的部分答案是:

不支持 Parcelable 的原因是数据被持久化了。

不确定 Data not support parcelable 的详细答案是什么?

- This answer 解释了它:

Data 是一个轻量级容器,它是一个简单的键值映射 并且只能保存原始和字符串的值及其 字符串版本。它实际上是用于轻量级的中间传输 数据。它不应该用于并且不能保持 可序列化或 Parcelable 对象。

请注意,序列化时数据的大小限制为 10KB。

【问题讨论】:

  • 恕我直言,您只在Data 中添加工作独有的内容。其他所有内容都属于作为工作的一部分加载的其他模型对象,这些模型对象与 Room、Realm、Retrofit 甚至不以 R 开头的东西一起存储。:-) Parcelable 的原因不是支持的是数据被持久化。据我所知,键是独一无二的,所以你的两个 putString() 调用应该被一个 putStringArray() 调用替换。
  • @CommonsWare 我想我的问题的答案是你的_不支持 Parcelable 的原因是数据是持久的。 _ 谢谢您的帮助。另请注意,这两个字符串只是一个示例。我现在更新我的帖子以反映新信息
  • 那么通过工作发送可序列化额外内容的解决方案是什么?
  • @noob-Sci-Bot 如果您不需要担心 parcelable,那么 gson 可能是最好的解决方案。
  • 我们知道为什么不支持Parcelable,但仅仅解决整个问题是一个很大的倒退。现在每个开发人员都必须构建自己的序列化方法,而 JSON 虽然是最容易编码的,但性能却比 parcelable 差得多。

标签: android


【解决方案1】:

使用GSON 超级简单: https://stackoverflow.com/a/28392599/5931191

// Serialize a single object.    
public String serializeToJson(MyClass myClass) {
    Gson gson = new Gson();
    String j = gson.toJson(myClass);
    return j;
}
// Deserialize to single object.
public MyClass deserializeFromJson(String jsonString) {
    Gson gson = new Gson();
    MyClass myClass = gson.fromJson(jsonString, MyClass.class);
    return myClass;
}

【讨论】:

  • 这是我想做的一个选项,但没有走这条路,因为我的 POJO 实现了 parcelable 并且不想添加另一层“序列化”但是如果你有一个 POJO,这应该是公认的答案没有实现序列化层。
  • 令人惊讶的是,gson 转换的字符串对我也不起作用。
  • JSON 在空间和时间上都是一种低效的序列化形式。更不用说,简单地让每个开发人员来确定序列化方法是一个很大的倒退。
【解决方案2】:

我在这里发布我的解决方案,因为我认为它可能对其他人来说很有趣。 请注意,这是我第一次尝试它,我很清楚我们可能会改进它,但这是一个不错的开始。

首先声明一个从 Worker 扩展的抽象类,如下所示:

abstract class SingleParameterWorker<T> : Worker(), WorkManagerDataExtender{

    final override fun doWork(): WorkerResult {
        return doWork(inputData.getParameter(getDefaultParameter()))
    }

    abstract fun doWork(t: T): WorkerResult

    abstract fun getDefaultParameter(): T
}

WorkManagerDataExtender 是一个对数据进行扩展的接口。 getParameter() 是这些扩展之一:

 fun <T> Data.getParameter(defaultValue: T): T {
    return when (defaultValue) {
        is ClassA-> getClassA() as T
        is ClassB-> getClassB() as T
        ...
        else -> defaultValue
    }
}

不幸的是,我无法使用 inlined + reified 的强大功能来避免所有默认值逻辑。如果有人可以,请在 cmets 中告诉我。 getClassA() 和getClassB() 也是同一个接口上的扩展。以下是其中之一的示例:

fun Data.getClassA(): ClassA {
        val map = keyValueMap
        return ClassA(map["field1"] as String,
                map["field2"] as Int,
                map["field3"] as String,
                map["field4"] as Long,
                map["field5"] as String)
    }

fun ClassA.toMap(): Map<String, Any> {
        return mapOf("field1" to field1,
                "field2" to field2,
                "field3" to field3,
                "field4" to field4,
                "field5" to field5)
    }

(那么你可以在这个扩展返回时调用toWorkData(),或者让它返回Data,但是这样你可以在调用toWorkData()之前向Map添加更多的键值对

你去吧,现在你所要做的就是创建 SingleParameterWorker 的子类,然后为你需要的任何类创建 Data 的“to”和“from”扩展。 就我而言,因为我有很多需要相同类型 POJO 的 Worker,这似乎是一个不错的解决方案。

【讨论】:

【解决方案3】:

此解决方案不使用 JSON,直接序列化为字节数组。

package com.andevapps.ontv.extension

import android.os.Parcel
import android.os.Parcelable
import androidx.work.Data
import java.io.*

fun Data.Builder.putParcelable(key: String, parcelable: Parcelable): Data.Builder {
    val parcel = Parcel.obtain()
    try {
        parcelable.writeToParcel(parcel, 0)
        putByteArray(key, parcel.marshall())
    } finally {
        parcel.recycle()
    }
    return this
}

fun Data.Builder.putParcelableList(key: String, list: List<Parcelable>): Data.Builder {
    list.forEachIndexed { i, item ->
        putParcelable("$key$i", item)
    }
    return this
}

fun Data.Builder.putSerializable(key: String, serializable: Serializable): Data.Builder {
    ByteArrayOutputStream().use { bos ->
        ObjectOutputStream(bos).use { out ->
            out.writeObject(serializable)
            out.flush()
        }
        putByteArray(key, bos.toByteArray())
    }
    return this
}

@Suppress("UNCHECKED_CAST")
inline fun <reified T : Parcelable> Data.getParcelable(key: String): T? {
    val parcel = Parcel.obtain()
    try {
        val bytes = getByteArray(key) ?: return null
        parcel.unmarshall(bytes, 0, bytes.size)
        parcel.setDataPosition(0)
        val creator = T::class.java.getField("CREATOR").get(null) as Parcelable.Creator<T>
        return creator.createFromParcel(parcel)
    } finally {
        parcel.recycle()
    }
}

inline fun <reified T : Parcelable> Data.getParcelableList(key: String): MutableList<T> {
    val list = mutableListOf<T>()
    with(keyValueMap) {
        while (containsKey("$key${list.size}")) {
            list.add(getParcelable<T>("$key${list.size}") ?: break)
        }
    }
    return list
}

@Suppress("UNCHECKED_CAST")
fun <T : Serializable> Data.getSerializable(key: String): T? {
    val bytes = getByteArray(key) ?: return null
    ByteArrayInputStream(bytes).use { bis ->
        ObjectInputStream(bis).use { ois ->
            return ois.readObject() as T
        }
    }
}

添加保护规则

-keepclassmembers class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator CREATOR;
}

【讨论】:

    【解决方案4】:

    接受的答案是正确的。但是新的android开发人员不容易理解,所以我给出了另一个答案并给出了正确的解释。

    我的要求是传递 Bitmap 对象。 (你可以根据你的要求通过)

    在你的 gradle 文件中添加依赖

    分级:

    dependencies {
      implementation 'com.google.code.gson:gson:2.8.5'
    }
    

    使用下面的方法来序列化和反序列化对象

     // Serialize a single object.
        public static String serializeToJson(Bitmap bmp) {
            Gson gson = new Gson();
            return gson.toJson(bmp);
        }
    
        // Deserialize to single object.
        public static Bitmap deserializeFromJson(String jsonString) {
            Gson gson = new Gson();
            return gson.fromJson(jsonString, Bitmap.class);
        }
    

    序列化对象。

     String bitmapString = Helper.serializeToJson(bmp);
    

    传递给数据对象。

     Data.Builder builder = new Data.Builder();
     builder.putString("bmp, bitmapString);
     Data data = builder.build();
            OneTimeWorkRequest simpleRequest = new OneTimeWorkRequest.Builder(ExampleWorker.class)
                    .setInputData(data)
                    .build();
            WorkManager.getInstance().enqueue(simpleRequest);
    

    在您的 Worker 类中处理您的对象。

    Data data = getInputData();
    String bitmapString = data.getString(NOTIFICATION_BITMAP);
    Bitmap bitmap = Helper.deserializeFromJson(bitmapString);
    

    现在您的位图对象已在 Worker 类中准备就绪。

    以上是示例,如何在您的工作人员类中传递对象。

    【讨论】:

      【解决方案5】:

      在 Kotlin 中,我就是这样做的

      对象到 Json

      inline fun Any.convertToJsonString():String{
       return Gson().toJson(this)?:""
      }
      

      要转换回模型,

      inline fun <reified T> JSONObject.toModel(): T? = this.run {
        try {
          Gson().fromJson<T>(this.toString(), T::class.java)
        }
          catch (e:java.lang.Exception){ e.printStackTrace()
          Log.e("JSONObject to model",  e.message.toString() )
      
          null }
      }
      
      
      inline fun <reified T> String.toModel(): T? = this.run {
        try {
          JSONObject(this).toModel<T>()
         }
         catch (e:java.lang.Exception){
          Log.e("String to model",  e.message.toString() )
      
          null
       }
      }
      

      【讨论】:

        【解决方案6】:

        首先,您需要知道数据的价值持有者是私有的 val mValues,您不能完全使用它来将 parcelable 添加到数据中,但是有一种解决方法可以使该过程至少不那么乏味

        val Data.parcelables by lazy {
           mutableMapOf<String, Parcelable>()
        }
        
        fun Data.putParcelable(key:String, parcelable:Parcelable) : Data{
          parcelables[key] = parcelable
          // to allow for chaining 
          return this
        }
        // in order to get value in the Work class created  Wrok.doWork method
        fun Data.getParcelable(key:String): Parcelable? = parcelables[key]
        
         // build  process
        
         /// you can not add putParcelable to Builder class either since mValues in the builder is also private and Builder.build() return Data(mValues)
         val data = Data.Builder()
                    .putBoolean("one",false)
        
                    .build()
                    .putParcelable("name",parcelable)
        
        val request = OneTimeWorkRequest.Builder().setInputData(data).build()
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多