Flutter锁屏时MethodChannel调用异常,如何激活屏幕离线时的方法通道?
问题描述
我用MethodChannel实现了锁屏状态下解锁屏幕查看来电的功能,屏幕点亮时获取电池电量等MethodChannel功能正常,但锁屏后调用getBatteryLevel会抛出MissingPluginException(No implementation found for method getBatteryLevel on channel myChannel)错误,请问怎么在屏幕关闭时让应用的MethodChannel保持可用?
原代码示例
MainActivity.java
import androidx.annotation.NonNonNull; import io.flutter.embedding.android.FlutterActivity; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugins.GeneratedPluginRegistrant; import android.app.KeyguardManager.KeyguardLock; import android.os.BatteryManager; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.app.KeyguardManager; import android.content.Context; public class MainActivity extends FlutterActivity { private static final String CHANNEL = "myChannel"; @Override public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine); new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL) .setMethodCallHandler( (call, result) -> { if (call.method.equals("getBatteryLevel")) { int batteryLevel = getBatteryLevel(); if (batteryLevel != -1) { result.success(batteryLevel); } else { result.error("UNAVAILABLE", "Battery level not available.", null); } } else { result.notImplemented(); } } ); } private int getBatteryLevel() { int batteryLevel = -1; if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE); batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY); } else { Intent intent = new ContextWrapper(getApplicationContext()). registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); } return batteryLevel; } }
Dart代码
const platform = const MethodChannel('myChannel'); Future<int> getBatteryLevel() async { int batteryLevel; try { batteryLevel = await platform.invokeMethod('getBatteryLevel'); } on PlatformException catch (e) { print("Error: '${e.message}'."); batteryLevel = -1; // Return -1 if there's an error } return batteryLevel; }
AndroidManifest权限
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
问题原因与解决方案
锁屏后系统会优先挂起/回收后台进程,导致Flutter引擎被暂停,MethodChannel的通信链路中断。要解决这个问题,需要确保进程保持活跃,并且MethodChannel的注册不受Activity生命周期影响,具体步骤如下:
- 用前台服务保活进程:通过前台服务让应用进程在锁屏后不会被系统轻易回收
- 在Application类中初始化MethodChannel:脱离Activity生命周期,只要进程存活,MethodChannel就保持可用
- 唤醒并解锁屏幕:在触发来电等场景时,先唤醒屏幕并解锁,确保Flutter引擎能正常响应调用
修改后的代码示例
1. 自定义Application类(MyApplication.java)
import android.app.Application; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.FlutterEngineCache; import io.flutter.embedding.engine.dart.DartExecutor; import io.flutter.plugin.common.MethodChannel; import android.os.Build; import android.content.Intent; import android.content.ContextWrapper; import android.content.IntentFilter; public class MyApplication extends Application { private static final String CHANNEL = "myChannel"; public static final String FLUTTER_ENGINE_ID = "my_engine"; @Override public void onCreate() { super.onCreate(); // 预初始化并缓存Flutter引擎 FlutterEngine flutterEngine = new FlutterEngine(this); flutterEngine.getDartExecutor().executeDartEntrypoint( DartExecutor.DartEntrypoint.createDefault() ); // 注册MethodChannel new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL) .setMethodCallHandler((call, result) -> { switch (call.method) { case "getBatteryLevel": int batteryLevel = getBatteryLevel(); if (batteryLevel != -1) { result.success(batteryLevel); } else { result.error("UNAVAILABLE", "Battery level not available.", null); } break; case "startForegroundService": Intent serviceIntent = new Intent(this, LockScreenService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(serviceIntent); } else { startService(serviceIntent); } result.success(null); break; default: result.notImplemented(); } }); // 缓存引擎供Activity复用 FlutterEngineCache.getInstance().put(FLUTTER_ENGINE_ID, flutterEngine); } private int getBatteryLevel() { int batteryLevel = -1; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { android.os.BatteryManager batteryManager = (android.os.BatteryManager) getSystemService(android.content.Context.BATTERY_SERVICE); batteryLevel = batteryManager.getIntProperty(android.os.BatteryManager.BATTERY_PROPERTY_CAPACITY); } else { Intent intent = new ContextWrapper(getApplicationContext()) .registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); batteryLevel = (intent.getIntExtra(android.os.BatteryManager.EXTRA_LEVEL, -1) * 100) / intent.getIntExtra(android.os.BatteryManager.EXTRA_SCALE, -1); } return batteryLevel; } }
2. 修改MainActivity.java
import androidx.annotation.NonNull; import io.flutter.embedding.android.FlutterActivity; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.plugins.GeneratedPluginRegistrant; public class MainActivity extends FlutterActivity { @Override public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine); // 复用Application中初始化的MethodChannel,不再重复注册 } @Override protected String getCachedEngineId() { // 返回缓存的Flutter引擎ID return MyApplication.FLUTTER_ENGINE_ID; } }
3. 实现前台服务(LockScreenService.java)
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; import android.content.Intent; import android.os.Build; import android.os.IBinder; import androidx.core.app.NotificationCompat; public class LockScreenService extends Service { private static final String CHANNEL_ID = "LockScreenServiceChannel"; @Override public void onCreate() { super.onCreate(); createNotificationChannel(); // 启动前台服务,确保进程不被回收 Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("来电服务运行中") .setContentText("保持后台活跃以处理锁屏来电") .setSmallIcon(R.mipmap.ic_launcher) .build(); startForeground(1, notification); } @Override public IBinder onBind(Intent intent) { return null; } private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel serviceChannel = new NotificationChannel( CHANNEL_ID, "Lock Screen Service", NotificationManager.IMPORTANCE_LOW ); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(serviceChannel); } } }
4. 修改AndroidManifest.xml
<!-- 注册自定义Application --> <application android:name=".MyApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.App"> <!-- 注册前台服务 --> <service android:name=".LockScreenService" android:foregroundServiceType="phoneCall" /> <!-- 修改MainActivity配置 --> <activity android:name=".MainActivity" android:launchMode="singleTop" android:exported="true" android:theme="@style/Theme.App.Light.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>
5. 更新Dart端逻辑
const platform = MethodChannel('myChannel'); Future<int> getBatteryLevel() async { int batteryLevel; try { batteryLevel = await platform.invokeMethod('getBatteryLevel'); } on PlatformException catch (e) { print("Error: '${e.message}'."); batteryLevel = -1; } return batteryLevel; } // 启动前台服务,在应用启动或来电触发时调用 Future<void> startLockScreenService() async { try { await platform.invokeMethod('startForegroundService'); } on PlatformException catch (e) { print("Service error: '${e.message}'."); } }
内容的提问来源于stack exchange,提问作者softwarest
相关产品推荐
相关产品推荐

