首页 > 解决方案 > 将 URI 作为字符串存储到 SharedPreferences 会导致 Formatter 异常

问题描述

当我尝试将目录的 URI 作为字符串存储在 SharedPreferences 中时,我有一个奇怪的行为:

首先,我开始打算获取目录

val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
i.addCategory(Intent.CATEGORY_DEFAULT)
startActivityForResult(Intent.createChooser(i, "Choose directory"), REQUEST_TARGET_FOLDER)

然后在onActivityResult我尝试将结果存储为字符串(数据是意图)

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
// checks left out for simplicity
val targetDir = data.data.toString()

val storagePreference : ListPreference? = findPreference(resources.getString(R.string.settings_storage))
storagePreference?.summary = "External: $targetDir"
}

然后,当我选择一个文件夹(例如 Downloads/Rec)时,intent 返回content://com.android.externalstorage.documents/tree/primary%3ADownload%2FRec为 Uri,当我将其转换为 String 并尝试如上所示设置摘要时,应用程序崩溃:

   java.util.UnknownFormatConversionException: Conversion = 'F'
        at java.util.Formatter$FormatSpecifier.conversion(Formatter.java:2782)
        at java.util.Formatter$FormatSpecifier.<init>(Formatter.java:2812)
        at java.util.Formatter$FormatSpecifierParser.<init>(Formatter.java:2625)
        at java.util.Formatter.parse(Formatter.java:2558)
        at java.util.Formatter.format(Formatter.java:2505)
        at java.util.Formatter.format(Formatter.java:2459)
        at java.lang.String.format(String.java:2870)

原因是字符串中的 Url 编码部分,因为如果我将 '%2F' 替换为 '/' 存储工作。

storagePreference?.summary = "External: $targetDir"是失败的线。为什么摘要字符串不能包含“%F2”或类似字符串?

标签: android

解决方案


根据ListPreference源代码(见下文),summary 属性正在预测格式字符串,实际上您可以看到getSummary()using的方式String.format,这将阻塞 URI 中的编码字符。

似乎解码 Uri 字符串的建议是一个合理的解决方案。

/**
     * Returns the summary of this ListPreference. If the summary
     * has a {@linkplain java.lang.String#format String formatting}
     * marker in it (i.e. "%s" or "%1$s"), then the current entry
     * value will be substituted in its place.
     *
     * @return the summary with appropriate string substitution
     */
    @Override
    public CharSequence getSummary() {
        final CharSequence entry = getEntry();
        if (mSummary == null) {
            return super.getSummary();
        } else {
            return String.format(mSummary, entry == null ? "" : entry);
        }
    }

/**
     * Sets the summary for this Preference with a CharSequence.
     * If the summary has a
     * {@linkplain java.lang.String#format String formatting}
     * marker in it (i.e. "%s" or "%1$s"), then the current entry
     * value will be substituted in its place when it's retrieved.
     *
     * @param summary The summary for the preference.
     */
    @Override
    public void setSummary(CharSequence summary) {
        super.setSummary(summary);
        if (summary == null && mSummary != null) {
            mSummary = null;
        } else if (summary != null && !summary.equals(mSummary)) {
            mSummary = summary.toString();
        }
    }

虽然阅读开发人员文档并不明显ListPreference......


推荐阅读