Xamarin Forms应用开机自启动约2分钟后崩溃问题求助
开发的Xamarin Forms应用通过Broadcast Receiver实现开机自启动,重启设备后应用能正常启动,但约2分钟后崩溃,且仅在开机自启动场景下出现该问题。
测试设备:三星Galaxy S8(Android 9)、部分Android TV设备
已尝试无效方案:
- 启用前台服务
- 将应用排除在电池优化外
AndroidManifest.xml配置
<uses-sdk android:targetSdkVersion="29" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.DEVICE_POWER" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-feature android:name="android.hardware.touchscreen" android:required="false" /> <uses-feature android:name="android.software.leanback" android:required="false" /> <application android:label="ABC" android:icon="@drawable/icon" android:banner="@drawable/banner"> <receiver android:name=".MyCustomReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.QUICKBOOT_POWERON" /> </intent-filter> </receiver> </application>
Broadcast Receiver代码
[BroadcastReceiver(Enabled = true, DirectBootAware = true, Exported = true)] [IntentFilter(new[] { Intent.ActionBootCompleted }, Priority = (int)IntentFilterPriority.HighPriority)] public class MyCustomReceiver : BroadcastReceiver { public override void OnReceive(Context context, Intent intent) { if(Intent.ActionBootCompleted.Equals(intent.Action)) { Intent actIntent = new Intent(context, typeof(MainActivity)); actIntent.AddFlags(ActivityFlags.NewTask); context.StartActivity(actIntent); } } }
dalvik.system.BaseDexClassLoader.findClass BaseDexClassLoader.java, line 134 java.lang.ClassNotFoundException: Didn't find class " com.abc.app.MyCustomReceiver" on path: DexPathList[[zip file "/data/app/ com.abc.app-l5EOpz_TN-hTJakX5ZMe5Q==/base.apk"],nativeLibraryDirectories=[/data/app/ com.abc.app-l5EOpz_TN-hTJakX5ZMe5Q==/lib/arm64, /data/app/ com.abc.app-l5EOpz_TN-hTJakX5ZMe5Q==/base.apk!/lib/arm64-v8a, /system/lib64, /system/vendor/lib64]] dalvik.system.BaseDexClassLoader.findClass BaseDexClassLoader.java:134 java.lang.ClassLoader.loadClass ClassLoader.java:379 java.lang.ClassLoader.loadClass ClassLoader.java:312 android.app.AppComponentFactory.instantiateReceiver AppComponentFactory.java:84 android.app.ActivityThread.handleReceiver ActivityThread.java:3561 android.app.ActivityThread.access$1300 ActivityThread.java:237 android.app.ActivityThread$H.handleMessage ActivityThread.java:1796 android.os.Handler.dispatchMessage Handler.java:106 android.os.Looper.loop Looper.java:214 android.app.ActivityThread.main ActivityThread.java:7045 java.lang.reflect.Method.invoke Method.java com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run RuntimeInit.java:493 com.android.internal.os.ZygoteInit.main ZygoteInit.java:964 android.app.ActivityThread.handleReceiver ActivityThread.java:3568 android.app.ActivityThread.access$1300 ActivityThread.java:237 android.app.ActivityThread$H.handleMessage ActivityThread.java:1796 android.os.Handler.dispatchMessage Handler.java:106 android.os.Looper.loop Looper.java:214 android.app.ActivityThread.main ActivityThread.java:7045 java.lang.reflect.Method.invoke Method.java com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run RuntimeInit.java:493 com.android.internal.os.ZygoteInit.main ZygoteInit.java:964 -------------------------------- java.lang.RuntimeException: Unable to instantiate receiver com.abc.app.MyCustomReceiver: java.lang.ClassNotFoundException: Didn't find class " com.abc.app.MyCustomReceiver" on path: DexPathList[[zip file "/data/app/ com.abc.app-l5EOpz_TN-hTJakX5ZMe5Q==/base.apk"],nativeLibraryDirectories=[/data/app/ com.abc.app-l5EOpz_TN-hTJakX5ZMe5Q==/lib/arm64, /data/app/ com.abc.app-l5EOpz_TN-hTJakX5ZMe5Q==/base.apk!/lib/arm64-v8a, /system/lib64, /system/vendor/lib64]] android.app.ActivityThread.handleReceiver ActivityThread.java:3568 android.app.ActivityThread.access$1300 ActivityThread.java:237 android.app.ActivityThread$H.handleMessage ActivityThread.java:1796 android.os.Handler.dispatchMessage Handler.java:106 android.os.Looper.loop Looper.java:214 android.app.ActivityThread.main ActivityThread.java:7045 java.lang.reflect.Method.invoke(Native Method) com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run RuntimeInit.java:493 com.android.internal.os.ZygoteInit.main ZygoteInit.java:964 Caused by: java.lang.ClassNotFoundException: Didn't find class " com.abc.app.MyCustomReceiver" on path: DexPathList[[zip file "/data/app/ com.abc.app-l5EOpz_TN-hTJakX5ZMe5Q==/base.apk"],nativeLibraryDirectories=[/data/app/ com.abc.app-l5EOpz_TN-hTJakX5ZMe5Q==/lib/arm64, /data/app/ com.abc.app-l5EOpz_TN-hTJakX5ZMe5Q==/base.apk!/lib/arm64-v8a, /system/lib64, /system/vendor/lib64]] dalvik.system.BaseDexClassLoader.findClass BaseDexClassLoader.java:134 java.lang.ClassLoader.loadClass ClassLoader.java:379 java.lang.ClassLoader.loadClass ClassLoader.java:312 android.app.AppComponentFactory.instantiateReceiver AppComponentFactory.java:84 android.app.ActivityThread.handleReceiver ActivityThread.java:3561
1. 移除AndroidManifest.xml中手动添加的Receiver配置
Xamarin会通过[BroadcastReceiver]特性自动在生成的Manifest中注册Receiver,手动添加的<receiver>节点会导致重复注册,且android:name=".MyCustomReceiver"这种相对类名可能和Xamarin生成的全类名不匹配(日志中类名前的空格也可能由此导致)。直接删除Manifest里的以下内容:
<receiver android:name=".MyCustomReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.QUICKBOOT_POWERON" /> </intent-filter> </receiver>
如果需要监听QUICKBOOT_POWERON,直接在C#的IntentFilter特性里添加:
[IntentFilter(new[] { Intent.ActionBootCompleted, Intent.ActionQuickbootPoweron }, Priority = (int)IntentFilterPriority.HighPriority)]
2. 确保Receiver类被打包进主Dex
Android 9及以上,开机广播触发时系统可能只加载主Dex文件,如果Receiver类被打包到次Dex中,就会出现ClassNotFoundException。在Xamarin中开启MultiDex并配置主Dex列表:
- 在Android项目的
Properties->Android Options->Advanced中勾选Enable MultiDex - 创建
multidex-config.txt文件,添加Receiver的全类名:
com.abc.app.MyCustomReceiver
- 在项目属性的
Android Options->Additional Options中添加:
--multi-dex-config=multidex-config.txt
3. 优化开机启动逻辑,避免进程被回收后触发Receiver异常
开机后直接启动Activity,应用进程可能在闲置时被系统回收,后续如果有广播再次触发Receiver,就会因类加载失败崩溃。改为启动前台服务来维持进程:
public override void OnReceive(Context context, Intent intent) { if (Intent.ActionBootCompleted.Equals(intent.Action) || Intent.ActionQuickbootPoweron.Equals(intent.Action)) { // 启动前台服务 var serviceIntent = new Intent(context, typeof(StartupService)); if (Build.VERSION.SdkInt >= BuildVersionCodes.O) { context.StartForegroundService(serviceIntent); } else { context.StartService(serviceIntent); } // 再启动Activity Intent actIntent = new Intent(context, typeof(MainActivity)); actIntent.AddFlags(ActivityFlags.NewTask | ActivityFlags.ClearTop); context.StartActivity(actIntent); } }
前台服务StartupService的示例:
[Service(Exported = true, ForegroundServiceType = ForegroundService.TypeSpecialUse)] public class StartupService : Service { public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) { // 创建通知,维持前台服务状态 var notification = new Notification.Builder(this, "startup_channel") .SetContentTitle("应用启动中") .SetContentText("正在维持进程") .SetSmallIcon(Resource.Drawable.icon) .Build(); if (Build.VERSION.SdkInt >= BuildVersionCodes.O) { var channel = new NotificationChannel("startup_channel", "Startup Channel", NotificationImportance.Low); var notificationManager = GetSystemService(NotificationService) as NotificationManager; notificationManager.CreateNotificationChannel(channel); } StartForeground(1, notification); // 可以在延迟后停止前台服务,或者根据业务需求维持 Task.Delay(TimeSpan.FromMinutes(5)).ContinueWith(t => { StopForeground(true); StopSelf(); }); return StartCommandResult.Sticky; } public override IBinder OnBind(Intent intent) { return null; } }
4. 检查类名中的空格问题
崩溃日志中类名前有空格" com.abc.app.MyCustomReceiver",确认项目的命名空间没有多余空格,同时清理项目并重新生成,避免编译时生成错误的类名。
内容的提问来源于stack exchange,提问作者Zaheer Mehmood

