将Bundle的内容打印到Logcat?

83

如果您无法记住所有键的名称(即使仅能打印键名也很酷),那么是否有一种简单的方法将 Bundle 的内容打印到Logcat中?

12个回答

134

Bundle#keySet()应该正常工作。

for (String key: bundle.keySet())
{
  Log.d ("myApplication", key + " is a key in the bundle");
}

如果您想获取Object,可以使用Bundle#get(String key)(也在我回答顶部链接的同一文档中)。但请记住使用通用的get()方法:

  • 您正在处理对象。如果只是打印到日志,toString()将被调用,一切都会很好。但是,如果您真正想要使用键的值对,则需要进行instanceof检查,以避免调用错误的方法。
  • 由于将调用toString,如果您有特殊对象(例如ArrayList、特殊的Serializable/Parcelable额外信息),您可能不会从打印输出中得到任何有用的信息。

2
我认为你的意思是 bundle.keySet()(因为你提供的文档链接没问题);否则这个是正确的。 - Cat
1
@Eric 哎呀,愚蠢的错误。至少我在链接时拼写是正确的。 - A--C

47

您可以通过以下方式打印映射值以获得更具体的结果:

for (String key : bundle.keySet())
{
    Log.d("Bundle Debug", key + " = \"" + bundle.get(key) + "\"");
}

26

捆绑成字符串转换器:

public static String bundle2string(Bundle bundle) {
    if (bundle == null) {
        return null;
    }
    String string = "Bundle{";
    for (String key : bundle.keySet()) {
        string += " " + key + " => " + bundle.get(key) + ";";
    }
    string += " }Bundle";
    return string;
}

使用示例:

Log.d(TAG,"details="+bundle2string(details));

并且输出:

details=Bundle{ RESPONSE_CODE => 5; }Bundle
注意箭头=>和分号;可以让你在键和值中提到空格。箭头前一个空格,箭头后一个空格,在分号前没有空格,分号后一个空格,在{之后一个空格,在}之前一个空格,而所有其他空格都存在于键或值中。

这是一个不错的解决方案,但为了更好的性能,我宁愿使用StringBuffer:`public static String bundle2string(Bundle bundle) { StringBuffer buf = new StringBuffer("Bundle{"); for (String key : bundle.keySet()) buf.append(" " + key + " => " + bundle.get(key) + ";"); buf.append(" }Bundle"); return buf.toString(); }` - Maurix
1
在我的看法中,bundle2string() 的性能与其目的无关。但是即使我们假设相反情况,根据 https://dev59.com/w2445IYBdhLWcg3w5OEH#4649160 ,字符串连接操作会被编译器转换为 StringBuilder 操作。我没有亲自检查过 Android 上的情况;如果有人关心这个问题,请确认或否定。 - 18446744073709551615
1
@Maurix 既然没有涉及到线程,StringBuilder 比 StringBuffer 更好:https://developer.android.com/reference/java/lang/StringBuffer.html - android developer
请注意,某些值可能包含多行,因此在单个日志条目中打印所有这些值可能会出现问题。 - Yoav Feuerstein

15
意识到这并没有完全回答问题,但我看到很多开发者试图将内容倒入logcat/console,因为他们不知道可以在Android Studio调试器中设置自定义对象呈现以显示调试时间,当您触碰断点时。而且在Bundle的情况下,您可以采用其他答案中显示的代码类型,并将其应用为自定义渲染器,这样您就不需要将转储传输到logcat和/或控制台。

(这些说明来自于Android Studio 3.1.3(2018年6月)...

  1. 选择“文件”,然后选择“设置”菜单选项/子选项。
  2. 在“设置”对话框中,在左侧,向下钻取并选择“构建、执行、部署”、“调试器”、“数据视图”、“Java类型渲染器”。
  3. 在对话框右侧,其中写着“渲染器名称”,输入您希望标识正在创建的渲染器的名称。
  4. 在对话框右侧,其中写着“将渲染器应用于以下类型的对象”,输入“android.os.Bundle”。
  5. 在对话框右侧,“渲染节点时”部分下,选择“使用以下表达式:”单选按钮。
  6. 在下面的文本字段中,键入以下内容...
StringBuilder builder = new StringBuilder();
for (String key : ((android.os.Bundle)this).keySet()) {
    Object value = ((android.os.Bundle)this).get(key);
    builder.append("[");
    builder.append(key);
    builder.append("]=[");
    builder.append(value);
    builder.append("](");
    builder.append((value != null) ? value.getClass().getSimpleName() : "null");
    builder.append("), ");
}
return builder.toString();
  • 按下“应用”/“确定”按钮。
  • 现在,当您运行应用程序并触发一个断点,显示一个类型为android.os.Bundle的变量时,您将在调试器窗口的变量部分中看到从上述代码生成的输出。

    我还将包括一张截图,展示我上面所描述的...screenshot


    1
    这是 Android Studio 的一个非常棒的功能,我之前并不知道它的存在。谢谢! - Julian A.
    @Julian A. 不用谢。是啊,我一直很惊讶为什么这个话题没有被更多地讨论,希望 Android Studio 不会包含常用类的预创建表达式,这样可以节省一些时间。另外,对于那些使用 Eclipse 的人来说,它也有类似的功能。 - Adrian Romanelli
    太棒了!事实证明,我们可以将渲染器表达式简化为 keySet(); toString(),因为访问 Bundle 的内容似乎会使其具体化,从而让 toString() 格式化所有元素。请参见我的答案。 - Jerry101

    9

    Kotlin 中的简单 Bundle 转字符串实现:

    val bundleToString = bundle.keySet()
                .joinToString(", ", "{", "}") { key ->
                    "$key=${bundle[key]}"
                }
    

    结果示例 {id=3, name="Jhon"}


    1
    谢谢,它有效。键按字母顺序排列(keySet()返回排序后的集合)。 - CoolMind

    6
    在Kotlin中,当它包含子捆绑包时,递归for when的用法如下:
    /**
     * Recursively logs the contents of a [Bundle] for debugging.
     */
    fun Bundle.printDebugLog(parentKey: String = "") {
        if (keySet().isEmpty()) {
            Log.d("printDebugLog", "$parentKey is empty")
        } else {
            for (key in keySet()) {
            when (val value = this[key]) {
                    is Bundle -> value.printDebugLog(key)
                    is Array<*> -> Log.d("printDebugLog", "$parentKey.$key : ${value.joinToString()}")
                    else -> Log.d("printDebugLog", "$parentKey.$key : $value")
                }
            }
        }
    }
    

    使用方法:myBundle.printDebugLog()

    'get(String!): Any?'已被弃用。在Java中已经过时。 - norbDEV

    3
    技巧:访问捆绑包的内容以使其具体化,然后 bundle.toString() 将格式化其元素。(仅用于调试目的。)
    在 AppWidgetProvider 中的示例中,调用 newOptions.keySet()
        @Override
        public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager,
                int appWidgetId, Bundle newOptions) {
            super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
    
            newOptions.keySet(); // <-- reify the Bundle so .toString() will elaborate
            Log.i(TAG, "*** WidgetOptionsChanged: " + newOptions);
        }
    

    结果:

    *** WidgetOptionsChanged: Bundle[{appWidgetMaxHeight=137, appWidgetCategory=1, appWidgetMaxWidth=603, appWidgetMinHeight=82, appWidgetMinWidth=338, appWidgetSizes=[338.0x137.5, 603.5x82.0]}]
    

    比...好得多:
    *** WidgetOptionsChanged: Bundle[mParcelledData.dataSize=408]
    

    3

    Kotlin

    如果您只有简单的Bundle(没有嵌套的bundle),那么Jerry101的提示已经可以帮助您解决大部分问题了(请查看他们的评论以了解为什么这样做有效)。以下是一行代码:

    Log.d(TAG, "bundle: ${bundle?.also { it.keySet() }?: "N/A"}")
    

    上面的输出内容为:
    bundle: Bundle[{a=42, s=hello, ...}]
    或者当bundle为null时:
    bundle: N/A
    如果您不确定上述代码总是有效的,几乎相同的结果可以通过稍微多写一些代码(使用其他人已经指出的keySet函数)来实现 - 也只需要一行代码:
    Log.d(TAG, "bundle: ${bundle?.keySet()?.associateWith { bundle[it] }?: "N/A"}")
    

    这将bundle转换为map并打印

    bundle: {a=42, s=hello, ...}


    2
    Java 8 流式操作一行代码:
    bundle.keySet().stream().forEach(k -> Log.d(TAG, k + " = " + bundle.get(k)));
    

    1
    一种 Kotlin 解决方案:
    val bundleFromNotifications: Bundle? = remoteMessage?.toIntent()?.extras
    bundleFromNotifications?.keySet()?.forEach{
        Log.d(LOG_TAG, it + "=> \"" + bundleFromNotifications.get(it) + "\"")
    }
    

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