Expo集成Android Widget添加至桌面崩溃:PendingIntent异常排查
Android Widget 集成 Expo React Native 后配置界面崩溃(PendingIntent$CanceledException)
这个错误的核心是:Widget配置页试图通过PendingIntent把结果返回给AppWidgetManager时,这个PendingIntent已经被系统取消了,大概率和Widget ID传递错误、Intent配置不当,或是Expo集成时的Manifest覆盖有关。下面是具体排查和修复步骤:
1. 盯死Widget ID的传递逻辑
配置Activity启动时,必须从Intent里正确拿到Widget ID,别搞丢了:
// 正确获取Widget ID的代码,直接抄 val appWidgetId = intent?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID) ?: AppWidgetManager.INVALID_APPWIDGET_ID if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { setResult(RESULT_CANCELED) finish() return }
全程用局部变量传递这个ID,别存全局——系统回收内存时容易丢失。
2. 修复返回结果的PendingIntent
这是崩溃的重灾区,确保PendingIntent的创建逻辑没问题:
// 配置完成后返回结果的正确写法 val resultIntent = Intent().apply { putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) } val pendingIntent = PendingIntent.getActivity( this, appWidgetId, // 必须用Widget ID当requestCode,保证每个Widget实例的PendingIntent唯一 resultIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE // Android 12+必须加FLAG_IMMUTABLE ) setResult(RESULT_OK, resultIntent) finish()
重点:requestCode一定要用Widget ID,不然多个Widget会互相干扰;Android 12及以上必须加FLAG_IMMUTABLE(如果需要后续修改Intent就用FLAG_MUTABLE)。
3. 检查Expo集成后的Manifest配置
Expo的app.json或者自动生成的AndroidManifest.xml很可能改了你的Activity配置,必须确保:
- 配置Activity的
launchMode是standard或singleTop,绝对不能是singleInstance/singleTask:<activity android:name=".WidgetConfigActivity" android:launchMode="standard" android:exported="true"> <!-- 必须开exported,系统才能启动这个Activity --> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" /> </intent-filter> </activity> - AppWidgetProvider的配置里要正确关联配置Activity,
widget_info.xml里必须加:<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:configure="com.yourpackage.WidgetConfigActivity" <!-- 其他Widget属性 --> />
4. API请求别抢在结果返回前执行
如果配置页有API请求,一定要等请求完成再返回结果,别提前调用finish():
// 错误示范:先finish再请求,直接导致PendingIntent失效 setResult(RESULT_OK, resultIntent) finish() apiService.fetchData().enqueue(...) // 正确示范:请求完成后再处理结果 apiService.fetchData().enqueue(object : Callback<Data> { override fun onResponse(call: Call<Data>, response: Response<Data>) { // 先把配置参数存起来 saveWidgetConfig(appWidgetId, response.body()) // 再返回结果并关闭页面 setResult(RESULT_OK, resultIntent) finish() } override fun onFailure(call: Call<Data>, t: Throwable) { // 请求失败也要返回取消结果,别挂着 setResult(RESULT_CANCELED) finish() } })
5. 生产构建的混淆检查
Expo生产构建会开混淆,别把Widget相关类搞没了,在proguard-rules.pro里加:
-keep class com.yourpackage.WidgetConfigActivity -keep class com.yourpackage.MyWidgetProvider -keepclassmembers class com.yourpackage.WidgetConfigActivity { void onCreate(android.os.Bundle); }
内容的提问来源于stack exchange,提问作者Sergiu Tonț
相关产品推荐
相关产品推荐

