【发布时间】:2026-01-12 12:05:02
【问题描述】:
我想将互联网上的汽车列表加载到小部件中的 ListView 中。当我在 onDatasetChanged 方法中请求汽车时,它不会更新列表。但是,当我手动单击刷新按钮时,它会刷新它。可能是什么问题呢?为了提供尽可能多的信息,我放了complete source into a ZIP file,可以在Android Studio中解压并打开。我也会把它嵌入到下面。 注意:我将 RxJava 用于异步请求,并且代码是用 Kotlin 编写的,尽管问题不受语言限制。
CarsWidgetProvider
class CarsWidgetProvider : AppWidgetProvider() {
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
Log.d("CAR_LOG", "Updating")
val widgets = appWidgetIds.size
for (i in 0 until widgets) {
Log.d("CAR_LOG", "Updating number $i")
val appWidgetId = appWidgetIds[i]
// Get the layout
val views = RemoteViews(context.packageName, R.layout.widget_layout)
val otherIntent = Intent(context, ListViewWidgetService::class.java)
views.setRemoteAdapter(R.id.cars_list, otherIntent)
// Set an onclick listener for the action and the refresh button
views.setPendingIntentTemplate(R.id.cars_list, getPendingSelfIntent(context, "action"))
views.setOnClickPendingIntent(R.id.refresh, getPendingSelfIntent(context, "refresh"))
// Tell the AppWidgetManager to perform an update on the current app widget
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
private fun onUpdate(context: Context) {
val appWidgetManager = AppWidgetManager.getInstance(context)
val thisAppWidgetComponentName = ComponentName(context.packageName, javaClass.name)
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidgetComponentName)
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.cars_list)
onUpdate(context, appWidgetManager, appWidgetIds)
}
private fun getPendingSelfIntent(context: Context, action: String): PendingIntent {
val intent = Intent(context, javaClass)
intent.action = action
return PendingIntent.getBroadcast(context, 0, intent, 0)
}
@SuppressLint("CheckResult")
override fun onReceive(context: Context?, intent: Intent?) {
super.onReceive(context, intent)
if (context != null) {
if ("action" == intent!!.action) {
val car = intent.getSerializableExtra("car") as Car
// Toggle car state
Api.toggleCar(car)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
Log.d("CAR_LOG", "We've got a new state: $it")
car.state = it
onUpdate(context)
}
} else if ("refresh" == intent.action) {
onUpdate(context)
}
}
}
}
ListViewWidgetService
class ListViewWidgetService : RemoteViewsService() {
override fun onGetViewFactory(intent: Intent): RemoteViewsService.RemoteViewsFactory {
return ListViewRemoteViewsFactory(this.applicationContext, intent)
}
}
internal class ListViewRemoteViewsFactory(private val context: Context, intent: Intent) : RemoteViewsService.RemoteViewsFactory {
private var cars: ArrayList<Car> = ArrayList()
override fun onCreate() {
Log.d("CAR_LOG", "Oncreate")
cars = ArrayList()
}
override fun getViewAt(position: Int): RemoteViews {
val remoteViews = RemoteViews(context.packageName, R.layout.widget_list_item_car)
// Get the current car
val car = cars[position]
Log.d("CAR_LOG", "Get view at $position")
Log.d("CAR_LOG", "Car: $car")
// Fill the list item with data
remoteViews.setTextViewText(R.id.title, car.title)
remoteViews.setTextViewText(R.id.description, car.model)
remoteViews.setTextViewText(R.id.action, "${car.state}")
remoteViews.setInt(R.id.action, "setBackgroundColor",
if (car.state == 1) context.getColor(R.color.colorGreen) else context.getColor(R.color.colorRed))
val extras = Bundle()
extras.putSerializable("car", car)
val fillInIntent = Intent()
fillInIntent.putExtras(extras)
remoteViews.setOnClickFillInIntent(R.id.activity_chooser_view_content, fillInIntent)
return remoteViews
}
override fun getCount(): Int {
Log.d("CAR_LOG", "Counting")
return cars.size
}
@SuppressLint("CheckResult")
override fun onDataSetChanged() {
Log.d("CAR_LOG", "Data set changed")
// Get a token
Api.getToken()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { token ->
if (token.isNotEmpty()) {
Log.d("CAR_LOG", "Retrieved token")
DataModel.token = token
// Get the cars
Api.getCars()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
cars = it as ArrayList<Car>
Log.d("CAR_LOG", "Found cars: $cars")
}
} else {
Api.getCars()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
cars = it as ArrayList<Car>
Log.d("CAR_LOG", "Found cars: $cars")
}
}
}
}
override fun getViewTypeCount(): Int {
Log.d("CAR_LOG", "Get view type count")
return 1
}
override fun getItemId(position: Int): Long {
Log.d("CAR_LOG", "Get item ID")
return position.toLong()
}
override fun onDestroy() {
Log.d("CAR_LOG", "On Destroy")
cars.clear()
}
override fun hasStableIds(): Boolean {
Log.d("CAR_LOG", "Has stable IDs")
return true
}
override fun getLoadingView(): RemoteViews? {
Log.d("CAR_LOG", "Get loading view")
return null
}
}
数据模型
object DataModel {
var token: String = ""
var currentCar: Car = Car()
var cars: ArrayList<Car> = ArrayList()
init {
Log.d("CAR_LOG", "Creating data model")
}
override fun toString(): String {
return "Token : $token \n currentCar: $currentCar \n Cars: $cars"
}
}
汽车
class Car : Serializable {
var id : String = ""
var title: String = ""
var model: String = ""
var state: Int = 0
}
编辑
收到一些答案后,我开始想出一个临时修复方法,即在 AsyncTask 上调用 .get(),使其同步
@SuppressLint("CheckResult")
override fun onDataSetChanged() {
cars = GetCars().execute().get() as ArrayList<Car>
}
class GetCars : AsyncTask<String, Void, List<Car>>() {
override fun doInBackground(vararg params: String?): List<Device> {
return Api.getCars().blockingFirst();
}
}
【问题讨论】:
-
强烈建议您不要在 AppWidgetProvider 中进行 api 调用。 AppWidgetProvider 只是一个 BroadcastReceiver,如果 api 需要几秒钟才能完成,它可能会在完成之前终止。 developer.android.com/guide/topics/appwidgets/…
标签: android kotlin widget rx-java rx-java2