android WorkManager Data.Builder不支持Parcelable

nkoocmlb  于 2023-03-27  发布在  Android
关注(0)|答案(6)|浏览(159)

当你有一个包含大量变量(布尔值,整数,字符串)的大POJO,并且你想使用新的工作管理器来启动一个作业时,你可以创建一个数据文件,该文件被添加到一次性工作请求对象中。
构建这个数据文件的最佳实践是什么?(* 写100行代码只是说为每个变量在构建器上放置int是不对的。

答案

最后我把我的parcelable对象拆开了,因为我认为这是最好的实现。我不想使用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的原因是数据是持久的。*

不知道数据不支持可打包的详细答案是什么?

  • This answer解释其:
    Data是一个轻量级的容器,它是一个简单的键值Map,只能保存原语& String的值沿着它们的String版本。它实际上是用于轻量级的中间数据传输。它不应该用于也不能保存Serializable或Parcelable对象。
    请注意,序列化时数据的大小限制为10 KB。
sdnqo3pr

sdnqo3pr1#

使用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;
}
tkclm6bt

tkclm6bt2#

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

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.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()
    }
}

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

添加proguard规则

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

7tofc5zh3#

我在这里张贴我的解决方案,因为我认为它可能是有趣的其他人。请注意,这是我第一次尝试,我很清楚,我们可能会改进它,但这是一个很好的开始。
首先声明一个从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的功能来避免所有的默认值逻辑。如果有人可以,请在评论中告诉我。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)
    }

(Then您可以在此扩展返回时调用toWorkData(),或者使其返回Data,但这样可以在调用toWorkData()之前向Map添加更多键值对
现在你要做的就是创建SingleParameterWorker的子类,然后创建Data的“to”和“from”扩展到你需要的任何类。在我的情况下,因为我有很多需要相同类型POJO的Workers,这似乎是一个很好的解决方案。

p3rjfoxz

p3rjfoxz4#

接受的答案是正确的。但新的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类中的位图对象已经准备就绪。
上面是一个例子,如何在worker类中传递对象。

btqmn9zl

btqmn9zl5#

在Kotlin中,我就是这样做的
Object到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
 }
}
3bygqnnd

3bygqnnd6#

首先,您需要知道数据的值保持器是private val mValues,您不能完全使用它来向数据添加可打包的内容,但是有一个变通方法可以使这个过程至少不那么繁琐

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()

相关问题