You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 13:17:02