android - "Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++."启动后台服务并关闭应用后

标签 android flutter dart service background

我正在尝试构建一个应用程序,它使用一些包作为位置( https://pub.dev/packages/location )和指南针( https://pub.dev/packages/flutter_compass )并保持后台服务跟踪用户位置。
一切正常,直到我启动服务来跟踪位置。
当服务处于 Activity 状态时,整个应用程序永远不会停止,例如,当我在没有服务的情况下关闭应用程序时,指南针也会停止,但随着服务的运行,指南针也会继续运行。实际上它返回一个错误“尝试向 Flutter 发送平台消息,但 FlutterJNI 已与 native C++ 分离。无法发送。 channel :hemanthraj/flutter_compass”。位置发生了同样的事情:“尝试向 Flutter 发送平台消息,但 FlutterJNI 与 native C++ 分离。无法发送。 channel :lyokone/locationstream”。
在此之后,即使我再次打开该项目,它也不再起作用......
我正在尝试使服务完全独立于项目的其余部分。
我将向您展示服务实现(Android)

public class CompassApplication extends FlutterApplication {

    @Override
    public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel("messages", "Messages", NotificationManager.IMPORTANCE_LOW);
            NotificationManager manager = getSystemService(NotificationManager.class);
            if (manager != null) {
                manager.createNotificationChannel(channel);
            }
        }
    }
}
class MainActivity: FlutterActivity() {

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine)

        val intent = Intent(this, LocationService::class.java)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(intent)
        } else {
            startService(intent)
        }
    }
}
public class LocationService extends Service {

    static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10 * 60 * 1000; // 10 minutes
    static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = UPDATE_INTERVAL_IN_MILLISECONDS / 2;

    private LocationRequest mLocationRequest;
    private FusedLocationProviderClient mFusedLocationClient;
    private LocationCallback mLocationCallback;

    @Override
    public void onCreate() {
        super.onCreate();

        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);

        mLocationCallback = new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                super.onLocationResult(locationResult);
                onNewLocation(locationResult.getLastLocation());
            }
        };

        createLocationRequest();
        getLastLocation();
        requestLocationUpdates();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "messages")
                    .setSmallIcon(R.mipmap.ic_launcher_foreground);

            startForeground(101, builder.build());
        }
    }

    private void createLocationRequest() {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    }

    private void getLastLocation() {
        try {
            mFusedLocationClient.getLastLocation()
                    .addOnCompleteListener(task -> {
                        if (task.isSuccessful() && task.getResult() != null) {
                            onNewLocation(task.getResult());
                        }
                    });
        } catch (SecurityException ignored) {}
    }

    public void requestLocationUpdates() {
        Utils.setRequestingLocationUpdates(this, true);
        startService(new Intent(getApplicationContext(), LocationUpdatesService.class));
        try {
            mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
        } catch (SecurityException unlikely) {
            Utils.setRequestingLocationUpdates(this, false);
        }
    }

    private void onNewLocation(Location location) {
        // TODO: deal with current location
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}
另一个问题是即使我不关闭应用程序,它也会消耗大量电池。
谢谢!

最佳答案

从插件包开发者的角度来看:
由于您使用的是导致此问题的包,我会提到我在我的包中导致了同样的问题。
就我而言,我使用的是 错误(或旧)方法 channel 。我从服务(广播接收器)运行时保存了一个方法 channel 的实例,并尝试使用它与正在运行的应用程序进行通信。
在清理服务(或广播接收器)时,我有 销毁 Flutter 引擎,因此将 Flutter JNI 从 C++ 引擎中分离出来。所以当我去使用那个使用分离的 Flutter JNI 的方法 channel 时,你会得到错误:

Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++.


我只需要使用 FlutterActivity(或 FlutterFragment 或 Application)提供的 FlutterEngine 即可获得新的 MethodChannel。
就我而言,我正在开发一个 Flutter 包插件(在 Android 上)。这意味着在插件代码内部,我不应该意外地缓存 methodChannels:
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    final MethodChannel methodChannel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "example-method-channel-name");
}
你的问题
在您的依赖项的情况下,它们可能不支持在服务中使用:它们执行一些缓存/单例并重用不再附加的方法 channel 或 Flutter JNI。他们可能会假设插件已实例化 一次 ,并且在服务运行时不会重新实例化(或应用程序在服务已经运行时启动)。他们可能会注册回调以从操作系统接收数据,然后在创建带有新 Flutter 引擎的新插件时不会取消注册旧的回调。
没有快速修复,必须构建包来支持这一点。

关于android - "Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++."启动后台服务并关闭应用后,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61934900/

相关文章:

dart - Flutter将记录的用户数据获取到其他屏幕 View

flutter - 将状态变量引用传递给函数

android - 为什么应用程序的内存使用量在不同设备上差异太大?

android - 添加特定的存储库以构建文件

java - 共享偏好

java - 在 Android 上使用 Scribe OAuth Java 库时出错

Flutter 如何处理 Image.network 错误(如 404 或错误的 url)

visual-studio-code - 带有 VSCode 的 Flutter 显示未安装,即使已安装

android - 如何使 sweepGradient 开始在 flutter 中从圆的顶部开始应用?

dart - 解析一个json {"result": DIFFERENTS TYPES}} using differents types nested in the result