【问题标题】:Retrofit-Kotlin NullPointerException on non-null object type非空对象类型上的 Retrofit-Kotlin NullPointerException
【发布时间】:2018-02-28 23:47:56
【问题描述】:

在使用 RetrofitKotlin 实现 GitHub API 时,我遇到了一个非常奇怪的问题。

这是我第一次在 Andorid Arch 和 Retrofit 上工作,如果我在这个问题上犯了任何错误,请原谅我。

究竟发生了什么是我使用Retrofit 调用GitHub API 来获取用户的存储库。一个仓库在description 中有null。当 API 返回 null 时,我的应用程序不会崩溃,当我将 Intent 中的该对象传递给另一个 Activity 时,它会崩溃。我的数据类是Parcelable

下面是我的数据类:

data class GitHubRepository(
    var id: String = "",
    var name: String = "",
    var full_name: String = "",
    var owner: GitHubUser = GitHubUser(),
    var description: String = "",            // This is getting null even when it is of non-null type
    var html_url: String = "",
    var watchers_count: String = ""
) : Parcelable {
constructor(parcel: Parcel) : this(
        parcel.readString(),
        parcel.readString(),
        parcel.readString(),
        parcel.readParcelable(GitHubUser::class.java.classLoader),
        parcel.readString(),                // This is where i am getting error
        parcel.readString(),
        parcel.readString())

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(id)
        parcel.writeString(name)
        parcel.writeString(full_name)
        parcel.writeParcelable(owner, flags)
        parcel.writeString(description)
        parcel.writeString(html_url)
        parcel.writeString(watchers_count)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<GitHubRepository> {
        override fun createFromParcel(parcel: Parcel): GitHubRepository {
            return GitHubRepository(parcel)
        }

        override fun newArray(size: Int): Array<GitHubRepository?> {
            return arrayOfNulls(size)
        }
    }
}

以下是我创建 Retrofit 对象的方式:

val BASE_URL = "https://api.github.com/"

private var retrofit: Retrofit? = null

fun getClient(): Retrofit {

    if (retrofit == null) {
        retrofit = Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
    }

    return retrofit!!
}

以下是我的 API 请求:

@GET("users/{username}/repos")
fun getReposOfUser(
        @Path("username") username: String,
        @Query("page") pageNumber: Int,
        @Query("per_page") perPage: Int = 10
): Call<ArrayList<GitHubRepository>>

描述中有null 值的API: https://api.github.com/users/kislyuk/repos

以下是回复:

[
...
{
    "id": 1924281,
    "name": "akutils",
    "full_name": "kislyuk/akutils",
    "owner": {...},
    "private": false,
    "html_url": "https://github.com/kislyuk/akutils",
    "description": null, // This is the null value
    ...
}
...
]

下面是 LogCat,其中 GitHubModels.kt 是我的 Kotlin 文件,其中还包含其他数据类。有错误的是我上面显示的GitHubRepository数据类:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.tarunchauhan.githubapp, PID: 18276
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.tarunchauhan.githubapp/com.tarunchauhan.githubapp.activities.RepoDetailsActivity}: java.lang.IllegalStateException: parcel.readString() must not be null
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6541)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
     Caused by: java.lang.IllegalStateException: parcel.readString() must not be null
        at com.tarunchauhan.githubapp.models.GitHubRepository.<init>(GitHubModels.kt:29)
        at com.tarunchauhan.githubapp.models.GitHubRepository$CREATOR.createFromParcel(GitHubModels.kt:49)
        at com.tarunchauhan.githubapp.models.GitHubRepository$CREATOR.createFromParcel(GitHubModels.kt:47)
        at android.os.Parcel.readParcelable(Parcel.java:2781)
        at android.os.Parcel.readValue(Parcel.java:2675)
        at android.os.Parcel.readArrayMapInternal(Parcel.java:3042)
        at android.os.BaseBundle.unparcel(BaseBundle.java:257)
        at android.os.Bundle.getParcelable(Bundle.java:888)
        at android.content.Intent.getParcelableExtra(Intent.java:7075)
        at com.tarunchauhan.githubapp.activities.RepoDetailsActivity.onCreate(RepoDetailsActivity.kt:51)
        at android.app.Activity.performCreate(Activity.java:6975)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892) 
        at android.app.ActivityThread.-wrap11(Unknown Source:0) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593) 
        at android.os.Handler.dispatchMessage(Handler.java:105) 
        at android.os.Looper.loop(Looper.java:164) 
        at android.app.ActivityThread.main(ActivityThread.java:6541) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767) 

我什至在 API 请求中搜索了将所有空值转换为字符串,但没有得到任何可靠的想法。

请对此进行调查并告诉我任何解决方法。

谢谢:-)

【问题讨论】:

    标签: java android kotlin retrofit retrofit2


    【解决方案1】:

    您可能在此处将null 写到您的包裹中:parcel.writeString(description)

    您可能希望确保描述绝不是 null 与以下代码一起使用:parcel.writeString((if (description == null) "" else name))

    【讨论】:

    • 您好,谢谢您的回复...让我检查您的答案,然后我会回复您:)
    • 耶!你的回答很好,但我想知道是否有任何简单的方法可以做到这一点。因为有了这个,我必须将此检查添加到我的每个字段中,因为我不确定哪个字段何时为空。
    • 还有一件事..你能解释一下当我的描述是非空类型时它是如何变为空的。即使我在writeParcel 中添加此检查,我也会收到Elvis operator(?:) 始终为parcel.writeString(description ?: "") 返回非可空类型字符串的左操作数 消息。你能解释一下它是如何变为空的吗?是 GSON 做的还是 Retrofit 做的?或者仅仅是 Kotlin 中的一个错误?解释将帮助我更多地了解这里的工作原理。
    • @TarunChauhan 问题是 kotlin 的 Java 互操作性。所有非空安全检查都是在编译时进行的,但它们是 kotlin 方面的人为检查。在运行时,retrofit 读取 "description": null 并将其传递给您在 kotlin var description 中的 Java 等效引用(因为在 Java 中任何引用都可以分配给 null): String,完全绕过编译器检查,因为我们是在运行时执行的。你就是这样,编译器会告诉你引用不能为空,而输出调试行会告诉你相反的不一致
    猜你喜欢
    • 2023-02-03
    • 1970-01-01
    • 1970-01-01
    • 2017-05-17
    • 1970-01-01
    • 2017-08-25
    • 2019-05-01
    • 2022-01-22
    • 1970-01-01
    相关资源
    最近更新 更多