在安卓中将Map转换为Bundle

56

有没有一种简单的方法可以在不进行显式迭代的情况下将Map转换为Android中的Bundle?

为什么这样做?

Firebase返回Notification getData()的Map格式数据。我需要将这些数据传递给一个intent。以前GCM会返回bundle,因此我不需要担心这个问题。


3
在Android中,是否有一种简单的方法可以将Map转换为Bundle而无需显式迭代?请注意,Map不一定等同于Bundle。Map可以具有非字符串键,也可以具有不支持的数据类型值。 - CommonsWare
1
是的,但在我的情况下它们都是原始类型和字符串。 - Nouvel Travay
你可以对它进行序列化。 - cyroxis
1
RemoteMessage对象是可包装的,您可以直接将其发送到意图中。 - morepork
11个回答

59

我想一个传统的for循环是最简单的方法:

    Bundle bundle = new Bundle();
    for (Map.Entry<String, String> entry : getData().entrySet()) {
        bundle.putString(entry.getKey(), entry.getValue());
    }

6
由于第一个参数“google_sent_time”是Long类型,并且您正在使用putString,所以可能会引发异常。您最好使用intent来处理类型变化,并使用intent.putExtra(entry.getKey(), entry.getValue()),这样不同的数据类型就不会为您带来问题。 - Amir Ziarati
2
@AmirZiarati 我认为这不再是真的了,RemoteMessage#getData()现在返回一个Map<String, String>,排除了许多内部值。 - Mikael Ohlson

28

使用展开运算符的简洁 Kotlin 解决方案将如下所示:

fun Map<String, Any?>.toBundle(): Bundle = bundleOf(*this.toList().toTypedArray())

1
值得注意的是,展开运算符在性能方面并不是最佳选择,上述解决方案将在某些静态分析工具(例如Detekt)中生成性能警告: https://detekt.github.io/detekt/performance.html#spreadoperator - patrick.elmquist
值得一提的是,这不会为嵌套地图创建嵌套包,如果您正在使用类似Firebase Analytics的东西,则需要这个。 - kassim

16

我在使用 Firebase Messaging 时遇到了同样的问题,并创建了一个 Kotlin 扩展函数解决它。这里是 gist,以下是代码。虽然我在使用这种方法,但还有一些注意事项:

  • 它不能涵盖可以放入 bundle 中的所有类型
  • 它仍在开发中,尚未完全测试

请记住这一点,将其用作指南而不是最终解决方案。随着它的演变,我会保持 gist 最新。

import android.os.Bundle 
import android.os.IBinder
import android.os.Parcelable
import java.io.Serializable

fun <V> Map<String, V>.toBundle(bundle: Bundle = Bundle()): Bundle = bundle.apply {
  forEach {
    val k = it.key
    val v = it.value
    when (v) {
      is IBinder -> putBinder(k, v)
      is Bundle -> putBundle(k, v)
      is Byte -> putByte(k, v)
      is ByteArray -> putByteArray(k, v)
      is Char -> putChar(k, v)
      is CharArray -> putCharArray(k, v)
      is CharSequence -> putCharSequence(k, v)
      is Float -> putFloat(k, v)
      is FloatArray -> putFloatArray(k, v)
      is Parcelable -> putParcelable(k, v)
      is Serializable -> putSerializable(k, v)
      is Short -> putShort(k, v)
      is ShortArray -> putShortArray(k, v)

//      is Size -> putSize(k, v) //api 21
//      is SizeF -> putSizeF(k, v) //api 21

      else -> throw IllegalArgumentException("$v is of a type that is not currently supported")
//      is Array<*> -> TODO()
//      is List<*> -> TODO()
    }
  }
}

今天之前从未听说过Kotlin;有趣的发现。 - CCJ

12

现在你可以使用fun bundleOf(vararg pairs: Pair<String, Any?>)


1

您可以使用writeToParcel(Parcel out, int flags)方法生成一个Parcel对象,这个对象与Bundle类似,而且它是RemoteMessage类的一部分,方便地内置于Firebase框架中。相关文档在此


ParcelBundle 的超类,因此它们可能同样有用,但不能互换使用。 - Pierre-Luc Paour
是的,我确实提到了它是它的父类。 - SQLiteNoob
1
你的答案可能更好,但缺乏解释或示例会使情况变得更糟。 - Stoycho Andreev

1
这是我在 Kotlin 中的实现方式。
val bundle = Bundle()

for (entry in data.entries)
    bundle.putString(entry.key, entry.value)

0
在 Kotlin 中,您可以构建一个函数扩展,将 Map 转换为 Bundle,而不需要显式迭代,就像这样:
override fun onMessageReceived(remoteMessage: RemoteMessage) {
        val payload = remoteMessage.data.toBundle()
        Log.d(TAG, "Notification payload: $payload")
        // ...
}

toBundle() 的定义如下:

inline fun <reified T> Map<String, T>.toBundle(): Bundle = bundleOf(*toList<String, T?>().toTypedArray())

或者再次:

inline fun <reified T> Map<String, T>.toBundle(): Bundle = bundleOf(*map { it.key to it.value }.toTypedArray())

0
以 Kotlin 的方式:
private fun Map<String, Any>.toBundle(): Bundle {
    val pairs = this.map { entry ->
        Pair(entry.key, entry.value)
    }.toTypedArray()
    return bundleOf(*pairs)
}

0
这是 Kotlin 中的一个扩展方法:
fun Map<String, Any>.toBundle() = bundleOf(*toList().toTypedArray())

-1

供日后参考

将Map转换为字符串

  fun Map<String, String>.convertExtraMapToString(): String? {
    return keys.stream()
        .map { key: String -> "$key=" + this[key] }.collect(Collectors.joining(", ", "{", "}"))
}

将字符串转换为Map,然后转换为Bundle。
fun String.convertExtraMapToBundle(): Bundle {
    return Arrays.stream(split(",".toRegex()).toTypedArray())
        .map { entry: String -> entry.split("=".toRegex()).toTypedArray() }
        .collect(Collectors.toMap(
            { entry: Array<String> -> entry[0] }) { entry: Array<String> -> entry[1] }).let {
            bundleOf(*it.toList().toTypedArray())
        }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接