我使用Kotlin开发了一个Android应用程序,并且所有结构和功能都完整,但是当我反复快速点击(至少两次在执行API调用的按钮上)快速点击时,我注意到一个小问题。
对于API调用,我使用RetroFit2和GsonConverterFactory的组合。调用如下:
fun fetchInfo(id: Int) {
val retrofit = Retrofit.Builder()
.baseUrl("https://www.mysitesurl.com/api/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val api = retrofit.create(ApiService::class.java)
api.getInfo(id).enqueue(object: Callback<DataType> {
override fun onResponse(call: Call<DataType>, response: Response<DataType>) {
var resp = response.body()!!
my_image.setImageResource(resources.getIdentifier(resp.image, "drawable", context!!.packageName))
my_image.visibility = View.VISIBLE
my_label.text = resp.text
my_label.visibility = View.VISIBLE
}
override fun onFailure(call: Call<FechaDia>, t: Throwable) {
}
})
}
我对代码进行了一些编辑,以避免使用特定的变量名
因此,如前所述,此代码工作正常,当我快速两次单击导航按钮时,就会出现问题。据我了解,它尝试在当前API响应并获得null响应之前进行另一个API调用,因此我基本上尝试用null资源替换图像,并且向我显示此错误:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ImageView.setImageResource(int)' on a null object reference`
我尝试使用try / catch,但它仍会进行调用并仍然收到空请求。有什么方法可以阻止这种情况的发生,或者我在这里的过程中缺少什么?
主要问题是它不仅显示错误,还关闭了应用程序并显示App has stopped. Open app again
消息。
答案 0 :(得分:1)
使用这样的全局标志:
private boolean clicked = false;
在onClick中:
if(false){
callApi();
clicked = true;
}
在“成功”或“错误”响应中,将其设置为false:
clicked = false;
答案 1 :(得分:0)
如评论中所述,请尽量不要将可为null的类型强制为非可为null的类型,因为这样会产生副作用(例外)。
理想情况下,您还希望对某些事物进行解耦以提高代码的可读性:
fun retrofit(): Retrofit = Retrofit.Builder()
.baseUrl("https://www.mysitesurl.com/api/")
.addConverterFactory(GsonConverterFactory.create())
.build()
fun apiService(): ApiService = retrofit().create(ApiService::class.java)
fun fetchInfo(id: Int) =
apiService().getInfo(id).enqueue(object : Callback<Element.DataType> {
override fun onResponse(
call: Call<Element.DataType>,
response: Response<Element.DataType>
) {
response.body()?.run { renderView(this) }
}
override fun onFailure(call: Call<FechaDia>, t: Throwable) {}
})
fun renderView(response: DataType) = view?.apply {
val image = resources.getIdentifier(response.image, "drawable", context.packageName))
my_image.setImageResource(image)
my_image.visibility = View.VISIBLE
my_label.text = response.text
my_label.visibility = View.VISIBLE
}
如果您有适配器,则不必在renderView中分配它,因为您可能要做的只是在从API返回数据后才更新适配器。
在“活动”或“片段”中将适配器作为属性,然后在获得响应后调用适配器并发送列表。
为补充这个问题,因为没有太多代码要看,我想如果您使用底部导航而不使用jetpack导航库,则可以将底部导航与ViewPager
一起使用,并使用{{ 1}}在底部导航上,可在OnNavigationItemSelectedListener
适配器上的页面之间进行切换。
如果您拥有ViewPager
,则该片段将不会重新创建,因此仅会对API进行一次调用。
答案 2 :(得分:0)
对于去弹子来说,我觉得这是一个很好的情况。
去抖是一种模式,可以防止针对同一功能的多个信号连续触发得太快,并且如果在给定的时间范围内按下则只会触发一次。
我找到的第一个参考文献:Kotlin Android debounce
我建议您看一下SANAT的答案,这看起来是一个非常干净的实现,它将帮助您处理多次单击而不会触发多个功能。