Android Widget触发NetworkOnMainThreadException问题排查求助
问题分析与解决方案
1. 崩溃原因:NetworkOnMainThreadException
你的代码在updateAppWidget方法里直接执行了网络请求(Jsoup.connect(...).get()),而AppWidgetProvider的回调方法(包括onUpdate和updateAppWidget)是在主线程运行的。Android从API 11开始禁止主线程执行网络操作,否则会抛出NetworkOnMainThreadException,目的是避免主线程阻塞导致应用无响应(ANR)。
修复方案:将网络请求移到后台线程
需要把网页抓取逻辑放到后台线程执行,完成后再回到主线程更新Widget。推荐使用ExecutorService创建后台线程,示例修改如下:
void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_design); SharedPreferences preferences = context.getSharedPreferences("PREFS", 0); int value = preferences.getInt("value", 1); views.setTextViewText(R.id.text, "" + value); // 先临时更新Widget显示加载状态(可选) appWidgetManager.updateAppWidget(appWidgetId, views); // 启动后台线程执行网络请求 ExecutorService executor = Executors.newSingleThreadExecutor(); executor.execute(() -> { Document doc = null; try { // 执行网页抓取 doc = Jsoup.connect("https://pageWeb...").get(); // 这里添加解析文档的逻辑,比如提取需要的数据 String fetchedData = doc.select("目标选择器").text(); // 回到主线程更新Widget new Handler(Looper.getMainLooper()).post(() -> { views.setTextViewText(R.id.text, fetchedData); appWidgetManager.updateAppWidget(appWidgetId, views); Log.d("WIDGET", "Widget更新完成!"); }); } catch (IOException e) { Log.e("WIDGET", "网页抓取失败", e); // 失败时更新Widget显示错误信息(可选) new Handler(Looper.getMainLooper()).post(() -> { views.setTextViewText(R.id.text, "加载失败"); appWidgetManager.updateAppWidget(appWidgetId, views); }); } }); executor.shutdown(); // 重新调度定时刷新 AlarmHandler alarmHandler = new AlarmHandler(context); alarmHandler.cancelAlarmManager(); alarmHandler.setAlarmManager(); }
需补充导入语句:
import android.os.Looper; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
2. 权限页面显示"无需任何权限"的原因
你在Manifest中声明的INTERNET和WAKE_LOCK都属于正常权限,这类权限不需要用户手动授权,系统会自动授予,因此不会在应用权限信息页面显示。只有危险权限(如位置访问、相机、存储等)才会在权限页面展示并需要用户确认。
这是正常行为,不是代码错误,你的权限声明是有效的。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

