I wanted to abstract out getting resources in Android, so I have written a class ResourceProvider that actually provides resources:

@Singleton
class ResourceProvider @Inject constructor(private val context: Context) {
fun getString(@StringRes id: Int): String {
    return context.getString(id)
}

fun getString(@StringRes id: Int, vararg formatArgs: Any): String {
    return context.getString(id, formatArgs)
}
...
}

Nothing special here, just calling methods on Context. I have a problem when I want to get String with parameters, I created following example:

var fromContext = requireContext().getString(R.string.one_parameter_string, "Text")
Log.i("fromContext", fromContext)
var fromWrapper = resourceProvider.getString(R.string.one_parameter_string, "Text")
Log.i("fromWrapper", fromWrapper)

fromContext = requireContext().getString(R.string.two_parameter_string, "Text", "Text")
Log.i("fromContext", fromContext)
fromWrapper = resourceProvider.getString(R.string.two_parameter_string, "Text", "Text")
Log.i("fromWrapper", fromWrapper)

Here are String resources:

<string formatted="false" name="two_parameter_string">Text with parameters: %s, %s</string>
<string formatted="false" name="one_parameter_string">Text with parameter: %s</string>

as you can see I'm calling the same methods directly on Context and on my ResourceProvider class. I would expect the same results, but in fact this is what is printed in console:

I/fromContext: Text with parameter: Text
I/fromWrapper: Text with parameter: [Ljava.lang.Object;@6d43f06
I/fromContext: Text with parameters: Text, Text
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: xxx.xxx.xxx, PID: 22963
    java.util.MissingFormatArgumentException: Format specifier '%s'
        at java.util.Formatter.format(Formatter.java:2522)
        at java.util.Formatter.format(Formatter.java:2458)
        at java.lang.String.format(String.java:2814)
        at android.content.res.Resources.getString(Resources.java:472)
        at android.content.Context.getString(Context.java:572)
        at xxx.xxx.xxx.utils.ResourceProvider.getString(ResourceProvider.kt:21)
        at xxx.xxx.xxx.views.trial.TrialFragment.onViewCreated(TrialFragment.kt:45)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManager.java:1471)
        at androidx.fragment.app.FragmentManagerImpl.addAddedFragments(FragmentManager.java:2646)
        at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2416)
        at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2372)
        at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2273)
        at androidx.fragment.app.FragmentManagerImpl$1.run(FragmentManager.java:733)
        at android.os.Handler.handleCallback(Handler.java:789)
        at android.os.Handler.dispatchMessage(Handler.java:98)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6944)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

正如你所看到的,直接在Context上调用它是没有缺陷的,但是用我的包装器调用相同的方法会使它打印Object.toString(),在第二种情况下会崩溃.

Here is the decompiled version of the getString(@StringRes id: Int, vararg formatArgs: Any) method:

@NotNull
public final String getString(@StringRes int id, @NotNull Object... formatArgs) {
  Intrinsics.checkParameterIsNotNull(formatArgs, "formatArgs");
  String var10000 = this.context.getString(id, new Object[]{formatArgs});
  Intrinsics.checkExpressionValueIsNotNull(var10000, "context.getString(id, formatArgs)");
  return var10000;
}

What is the problem and how to avid it?

推荐答案

You need to use the spread operator (*) to call context.getString, i.e. you need to use *formatArgs:

@Singleton
class ResourceProvider @Inject constructor(private val context: Context) {
  fun getString(@StringRes id: Int): String {
    return context.getString(id)
  }

  fun getString(@StringRes id: Int, vararg formatArgs: Any): String {
    return context.getString(id, *formatArgs)
  }
...
}

You can read more about it in the kotlin reference regarding variable number of arguments (varargs).

如果您不这样做,那么给定的对象(在本例中是一个formatArgs数组)将被视为您想要传递给vararg方法的单个对象,因此它将被包装到Object[] { formatArgs }中.

Kotlin相关问答推荐

最好的方法来创建一个 map 在kotlin从两个列表

Jetpack Compose中的数字 Select 器问题

如何为集成测试配置Gradle JVM测试套件?

如何避免使用公共类实现内部接口

如何接受任何派生类KClass

如何优雅地声明一个StateFlow?

如何使用multiset与JOOQ获取关联的记录列表?

为何Kotlin标准库中的AND和OR函数不能像&&和||一样进行短路运算?

如果不在可组合函数中,如何获取 stringResource

interface扩展

DatabaseManager_Impl 不是抽象的,不会覆盖 RoomDatabase 中的抽象方法 clearAllTables()

Kotlin 的 isNullOrBlank() 函数是否可以导入 xml 以用于数据绑定

什么是开放式property?为什么我不能将其设置器设为private私有?

从片段(fragment)中的点击事件启动协同程序

使用范围的稀疏sparse值列表

kotlin 委托有什么用?

内联 onFocusChange kotlin

在 Kotlin 中编写一个等于 Int.MIN_VALUE 的十六进制整数文字

如何在不绑定ViewModel(MVVM)中的UI的情况下使用android导航?

将协程更新到 1.2.0 后构建失败:META-INF/atomicfu.kotlin_module