Android Widget刷新按钮真机无响应及Toast失效问题求助
问题排查与修复方案
一、真机刷新按钮无响应问题
核心原因
- PendingIntent标记不符合高版本要求:Android 12(API 31)及以上系统要求创建
PendingIntent时必须指定FLAG_IMMUTABLE或FLAG_MUTABLE,原代码中使用0作为标记,在高版本真机上会导致广播无法触发。 - RemoteViews更新顺序错误:先执行耗时的爬取操作,再绑定点击事件并更新Widget,可能导致点击事件未及时生效;且最后调用空的
updateAppWidget方法会覆盖已设置的点击事件。 - 静态变量引发多Widget冲突:全局静态变量
alturaNueva等会在多个Widget实例间互相覆盖,导致数据异常。
修复步骤
修正PendingIntent标记
在创建PendingIntent时添加版本适配的标记:import android.os.Build; // 替换原PendingIntent创建代码 int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_IMMUTABLE : 0; PendingIntent pendingSync = PendingIntent.getBroadcast(context, 0, intentSync, flags);调整onUpdate执行顺序
先绑定点击事件并更新Widget,再执行数据爬取,移除无用的空方法调用:@Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { for (int appWidgetId : appWidgetIds) { RemoteViews remoteV = new RemoteViews(context.getPackageName(), R.layout.altura_rio_widget); Intent intentSync = new Intent(context, AlturaRioWidget.class); intentSync.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_IMMUTABLE : 0; PendingIntent pendingSync = PendingIntent.getBroadcast(context, 0, intentSync, flags); remoteV.setOnClickPendingIntent(R.id.refresh, pendingSync); // 先绑定点击事件再更新Widget appWidgetManager.updateAppWidget(appWidgetId, remoteV); // 再执行数据爬取 LoadDataRio(context, appWidgetManager, appWidgetId); } }替换静态变量为局部变量
在LoadDataRio中使用局部变量存储爬取结果,避免多Widget冲突,同时更新数据后重新绑定点击事件(防止被覆盖):public void LoadDataRio(Context context, AppWidgetManager appWidgetManager, int appWidgetId){ CountDownLatch latch = new CountDownLatch(1); // 使用数组存储局部变量(线程内可修改) final String[] alturaNueva = new String[1]; final String[] variacionNueva = new String[1]; final String[] fechaNueva = new String[1]; new Thread(new Runnable() { @Override public void run() { try { Document doc = Jsoup.connect(url).get(); Element row = doc.select("tr:contains(SANTA FE):not(:contains(San Javier))").first(); alturaNueva[0] = row.select("[data-label=Ultimo Registro:]").text(); variacionNueva[0] = row.select("[data-label=Variacion]").text(); fechaNueva[0] = row.select("[data-label=Fecha Hora:]").text(); Log.i("hola","data"); latch.countDown(); } catch (IOException e) { Log.i("Error", e.getMessage() + " ERRORRR"); } } }).start(); try { latch.await(); RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.altura_rio_widget); views.setTextViewText(R.id.altura_tv, alturaNueva[0]); views.setTextViewText(R.id.variacion_tv, variacionNueva[0]); String replaceFecha = fechaNueva[0].replace("/24"," "); String replaceFecha2 = replaceFecha.replace("/","-").replace(" ",""); String fechaEdit = replaceFecha2.substring(0,9)+":"+replaceFecha2.substring(9,11); views.setTextViewText(R.id.fecha_tv, fechaEdit); // 重新绑定点击事件,防止更新数据后丢失 Intent intentSync = new Intent(context, AlturaRioWidget.class); intentSync.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_IMMUTABLE : 0; PendingIntent pendingSync = PendingIntent.getBroadcast(context, 0, intentSync, flags); views.setOnClickPendingIntent(R.id.refresh, pendingSync); appWidgetManager.updateAppWidget(appWidgetId, views); Log.i("hola","seteos"); } catch (InterruptedException e ) { Thread.currentThread().interrupt(); Log.i("Error", e.getMessage() + " ERRORRR"); } }
二、Toast消息不显示问题
核心原因
AppWidgetProvider运行在系统广播接收器进程中,使用普通Toast时,由于上下文不是前台Activity上下文,且未在主线程执行,导致Toast无法显示;而Log不受进程限制,可正常输出。
修复方案
方案1:使用Notification替代Toast(推荐)
Notification是后台组件的标准提示方式,适配所有系统版本:
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import androidx.core.app.NotificationCompat; // 在需要提示的位置添加(比如爬取成功/失败时) NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel("WIDGET_CHANNEL", "Widget更新提示", NotificationManager.IMPORTANCE_LOW); notificationManager.createNotificationChannel(channel); } NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "WIDGET_CHANNEL") .setSmallIcon(R.drawable.ic_pesca) .setContentTitle("数据更新") .setContentText("数据更新完成") .setPriority(NotificationCompat.PRIORITY_LOW) .setAutoCancel(true); notificationManager.notify(1, builder.build());
方案2:强制在主线程显示Toast
如果坚持使用Toast,需通过Handler切换到主线程:
import android.os.Looper; import android.os.Handler; // 在需要显示Toast的位置添加 new Handler(Looper.getMainLooper()).post(() -> { Toast.makeText(context.getApplicationContext(), "数据更新完成", Toast.LENGTH_SHORT).show(); });
额外优化建议
避免使用CountDownLatch阻塞主线程,改用Coroutine异步爬取数据,防止ANR:
// 添加依赖:implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" import kotlinx.coroutines.CoroutineScope; import kotlinx.coroutines.Dispatchers; import kotlinx.coroutines.Job; import kotlinx.coroutines.launch; import kotlinx.coroutines.withContext; public void LoadDataRio(Context context, AppWidgetManager appWidgetManager, int appWidgetId){ CoroutineScope scope = CoroutineScope(Dispatchers.IO + Job()); scope.launch { try { Document doc = Jsoup.connect(url).get(); Element row = doc.select("tr:contains(SANTA FE):not(:contains(San Javier))").first(); String altura = row.select("[data-label=Ultimo Registro:]").text(); String variacion = row.select("[data-label=Variacion]").text(); String fecha = row.select("[data-label=Fecha Hora:]").text(); // 切换到主线程更新Widget withContext(Dispatchers.Main) { RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.altura_rio_widget); views.setTextViewText(R.id.altura_tv, altura); views.setTextViewText(R.id.variacion_tv, variacion); String replaceFecha = fecha.replace("/24"," "); String replaceFecha2 = replaceFecha.replace("/","-").replace(" ",""); String fechaEdit = replaceFecha2.substring(0,9)+":"+replaceFecha2.substring(9,11); views.setTextViewText(R.id.fecha_tv, fechaEdit); // 绑定点击事件 Intent intentSync = new Intent(context, AlturaRioWidget.class); intentSync.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_IMMUTABLE : 0; PendingIntent pendingSync = PendingIntent.getBroadcast(context, 0, intentSync, flags); views.setOnClickPendingIntent(R.id.refresh, pendingSync); appWidgetManager.updateAppWidget(appWidgetId, views); Log.i("hola","seteos"); } } catch (IOException e) { Log.i("Error", e.getMessage() + " ERRORRR"); withContext(Dispatchers.Main) { // 显示错误通知 NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel("WIDGET_CHANNEL", "Widget更新提示", NotificationManager.IMPORTANCE_LOW); notificationManager.createNotificationChannel(channel); } NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "WIDGET_CHANNEL") .setSmallIcon(R.drawable.ic_pesca) .setContentTitle("更新失败") .setContentText(e.getMessage()) .setPriority(NotificationCompat.PRIORITY_LOW) .setAutoCancel(true); notificationManager.notify(1, builder.build()); } } } }
内容的提问来源于stack exchange,提问作者Nacho Mascherpa
相关产品推荐
相关产品推荐

