为什么在获取JSON时会收到NullPointerException?

时间:2018-07-09 09:39:58

标签: kotlin retrofit retrofit2

我在return place处不断收到NullPointerException。
在调试应用程序时,代码将跳过onFailure()onResponse()方法。

以前,这可行,但是我将其重构为当前类。

class Repository private constructor() {

    private val baseUrl: String = "http://api.openweathermap.org/"

    val client = OkHttpClient.Builder()
            .addInterceptor(HttpLoggingInterceptor()
            .setLevel(HttpLoggingInterceptor.Level.BODY))
            .build()

    val retrofit = Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(MoshiConverterFactory.create())
            .client(client)
            .build()

    val networkApi = retrofit.create(NetworkApi::class.java)


    private object Holder { val INSTANCE = Repository() }

    companion object {

        val instance: Repository by lazy { Holder.INSTANCE }

    }

    fun fetchWeatherData(placeName: String): Place {

        var place: Place? = null

        val call: Call<Place> = networkApi.getPlaceWeather(placeName)

        call.enqueue(object : Callback<Place> {

            override fun onFailure(call: Call<Place>?, t: Throwable?) {
                println(t?.message)
            }

            override fun onResponse(call: Call<Place>?, response: Response<Place>?) {

                if (response != null && response.isSuccessful && response.body() != null) {

                    place = response.body() as Place

                    println(place.toString())
                }
            }
        })

        return place!!
    }
}


class MainPresenter(private val view: MainContract.View, val context: Context) : MainContract.Presenter {

    val repository = Repository.instance

    ...

    override fun updateListOfPlaces() {

        var places = mutableListOf<Place>()

        for (index in 0 until favPlaceStrings.size) {
            places.add(repository.fetchWeatherData(favPlaceStrings.elementAt(index)))
        }

        view.showFavouritePlaces(places)
    }

}

2 个答案:

答案 0 :(得分:1)

您使用改造的方式使其具有异步行为,这意味着onFailureonResponse中的代码可能在您有机会从fetchWeatherData返回之前或之后运行。换句话说,您无法假设place将从fetchWeatherData返回时具有值,而这实际上是正在发生的情况,place仍然是null并调用{{1 }}会导致您遇到的空指针异常。

要解决此问题,您可以将改造使用的方式更改为同步,或者使用类似回调的方法。

我个人更喜欢回调方法/响应流,您可以检查here

使代码同步很可能会导致其他问题,例如不允许在主线程上进行网络调用,并导致应用程序崩溃。

答案 1 :(得分:0)

您需要颠倒逻辑。您不能简单地从正在等待的网络呼叫中“返回数据”

相反,循环遍历列表,进行请求,然后显示/更新视图

for (index in 0 until favPlaceStrings.size) {
    val call: Call<Place> = networkApi.getPlaceWeather(favPlaceStrings.elementAt(index))
    call.enqueue(object : Callback<Place> {
        override fun onFailure(call: Call<Place>?, t: Throwable?) {
            println(t?.message)
        }

        override fun onResponse(call: Call<Place>?, response: Response<Place>?) {

            if (response != null && response.isSuccessful && response.body() != null) {

                val place: Place = response.body() as Place
                places.add(place)  // move this list to a field 
                println(place.toString())
                view.showFavouritePlaces(places) // this is fine that it's inside a loop
            }
        }
    })

}