提问人:Soham Chougule 提问时间:10/27/2023 最后编辑:Brian Tompsett - 汤莱恩Soham Chougule 更新时间:10/27/2023 访问量:36
如何解决与 retrofit2 相关的 NullPointerException。DefaultCallAdapterFactory$ExecutorCallbackCall
How do I resolve this NullPointerException related to retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall
问:
我正在尝试制作一个 Pokedex 应用程序,该应用程序使用 retrofit2 从 API 获取口袋妖怪数据。 当我尝试运行它时,它会记录以下错误:
FATAL EXCEPTION: main
Process: com.example.pokemonapp, PID: 10766
java.lang.NullPointerException: Parameter specified as non-null is null: method com.example.pokemonapp.PokeAdapter.<init>, parameter pokes
at com.example.pokemonapp.PokeAdapter.<init>(Unknown Source:7)
at com.example.pokemonapp.MainActivity$getPokemon$1.onResponse(MainActivity.kt:31)
at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall$1.lambda$onResponse$0$retrofit2-DefaultCallAdapterFactory$ExecutorCallbackCall$1(DefaultCallAdapterFactory.java:89)
at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall$1$$ExternalSyntheticLambda0.run(Unknown Source:6)
at android.os.Handler.handleCallback(Handler.java:958)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8177)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
MainActivity.kt (英语)
class MainActivity : AppCompatActivity() {
lateinit var adapter: PokeAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getPokemon()
}
private fun getPokemon() {
val pokeball: Call<PokeApiResponse> = PokeInterface.ApiService.pokeInstance.getPokemonList()
pokeball.enqueue(object: Callback<PokeApiResponse> {
override fun onResponse(
call: Call<PokeApiResponse>,
response: Response<PokeApiResponse>
) {
val pokedex = response.body()
if(pokedex!= null)
{
Log.d("CHECK", pokeball.toString())
adapter = PokeAdapter(this@MainActivity, pokedex.pokemons)
val pokeList = findViewById<RecyclerView>(R.id.pokemonList)
pokeList.adapter = adapter
pokeList.layoutManager = LinearLayoutManager(this@MainActivity)
}
}
override fun onFailure(call: Call<PokeApiResponse>, t: Throwable) {
Log.d("CHECK", "Error in fetching Pokemon", t)
}
})
}
}
ApiService.kt 接口
const val BASE_URL = "https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/"
interface PokeInterface{
@GET("pokedex.json")
fun getPokemonList() : Call<PokeApiResponse>
//https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json
object ApiService{
val pokeInstance: PokeInterface
init{
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
pokeInstance = retrofit.create(PokeInterface::class.java)
}
}
}
PokeApiResponse.kt (英语)
data class PokeApiResponse(
val pokemons: List<Poke>
)
戳.kt
data class Poke(
val name: String,
val img: String
)
PokeAdapter.kt (英语)
class PokeAdapter(val context: Context, val pokes: List<Poke>): RecyclerView.Adapter<PokeAdapter.PokemonViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PokemonViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.item_layout, parent, false)
return PokemonViewHolder(view)
}
override fun getItemCount(): Int {
return pokes.size
}
override fun onBindViewHolder(holder: PokemonViewHolder, position: Int) {
val pokemon = pokes[position]
holder.pokeTitle.text = pokemon.name
}
class PokemonViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var pokeImage = itemView.findViewById<ImageView>(R.id.pokeImage)
var pokeTitle = itemView.findViewById<TextView>(R.id.pokeName)
}
}
build.gradle
implementation ("com.squareup.retrofit2:retrofit:2.9.0")
implementation ("com.squareup.retrofit2:converter-gson:2.9.0")
在 MainActivity.kt 中,我尝试记录使用标签“CHECK”获取的数据,它记录了以下消息:
改造2.DefaultCallAdapterFactory$ExecutorCallbackCall@6d104a9
早些时候,当我没有实现时,改造可以正确获取数据,并且获取的数据也正确记录在标签“CHECK”下,但是现在它一直在给出消息。RecyclerView
DefaultCallAdapterFactory$ExecutorCallbackCall
我花了几个小时试图在网上找到解决方案,但没有任何效果。 在这方面的任何帮助将不胜感激。
答:
我认为 NPE 可能来自您的日志
override fun onResponse(call: Call<PokeApiResponse>, response: Response<PokeApiResponse>) {
val pokedex = response.body()
if (pokedex != null) {
Log.d("CHECK", "Received ${pokedex.pokemons.size} Pokemon")
adapter = PokeAdapter(this@MainActivity, pokedex.pokemons)
val pokeList = findViewById<RecyclerView>(R.id.pokemonList)
pokeList.adapter = adapter
pokeList.layoutManager = LinearLayoutManager(this@MainActivity)
}
}
尝试记录 pokedex 列表而不是 pokeball
请尝试按照您的 json 建议使用此数组名称
data class PokeApiResponse(
@SerializedName("pokemon")
val pokemons: List<Poke>
)
评论
您的问题可能是以下 2 种情况中的 1 种(也可能是 2 种情况的混合)
1)
GSON 库不考虑 Kotlin 的可空性。
您的模型中有一个名为 的列表。
此列表标记为非 null。PokeApiResponse
pokemons
但是,当 GSON 解析 JSON 数据时,JSON 响应不包含具有该名称的列表,然后 GSON 只需将列表的值分配给 null。
它可以做到这一点,因为它在内部使用 Java Reflection 来访问该字段。pokemon
然后,当您在 Activity 中获取响应并尝试访问它时,该字段将为 null,因此您会得到 NullPointerException。
根据您的代码和模型,这应该是不可能的。但是由于 GSON 使用 Java Reflection,因此这是可能的。这是GSON不是一个好用的库的主要原因。这很容易,但也充满了这样的主要缺点。
我个人花了几周时间在我们的项目中完全摆脱了 GSON,并用 Moshi (https://github.com/square/moshi) 取而代之,它将以更好和可控的方式处理此类情况。
2)
如果您在文件中设置了 to,它将混淆您的代码。
这可能会导致在代码中调用的列表在编译的代码中被调用。
然后,当 GSON 想要将 JSON 数据解析到您的模型时,JSON 数据有一个名为 的列表,但您的模型列表被调用,因此它无法正确解析它。minifyEnabled
true
build.gradle
pokemons
a
pokemons
a
若要解决此问题,请添加到模型(以及所有模型中的所有字段)@SerializedName("pokemons")
data class PokeApiResponse(
@SerializedName("pokemons")
val pokemons: List<Poke>
)
评论