【问题标题】:"Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++." After start a background service and closes the app“试图向 Flutter 发送平台消息,但 FlutterJNI 与原生 C++ 分离。”启动后台服务并关闭应用程序后
【发布时间】:2020-09-08 02:33:18
【问题描述】:

伙计们! 我正在尝试构建一个应用程序,它使用一些包作为位置 (https://pub.dev/packages/location) 和指南针 (https://pub.dev/packages/flutter_compass) 并保持后台服务跟踪用户位置。 一切正常,直到我启动服务来跟踪位置。

在服务处于活动状态时,整个应用程序永远不会停止,例如,当我在没有服务的情况下关闭应用程序时,指南针也会停止,但在服务运行时,指南针也会继续运行。实际上它返回一个错误“尝试向 Flutter 发送平台消息,但 FlutterJNI 已与本机 C++ 分离。无法发送。频道:hemanthraj/flutter_compass”。位置也发生了同样的事情:“尝试向 Flutter 发送平台消息,但 FlutterJNI 与本机 C++ 分离。无法发送。频道: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;
    }
}

另一个问题是即使我不关闭应用程序,它也会消耗大量电池 谢谢!

【问题讨论】:

  • 你解决了这个问题吗?

标签: android flutter dart service background


【解决方案1】:

我在使用 TFLite 检测相机上的物体时遇到了这个问题。 我不太确定,但每次我发现手机过热时,

如果有像我这样需要密集矩阵处理的事件,问题是你运行应用程序的ARM处理器做不到,但是电池相关的服务错误真的很有趣。

【讨论】:

    【解决方案2】:

    这是我最好的答案: 该错误会导致 Dart->Java 消息在 FlutterNativeView 仍然存在时被丢弃。这个问题看起来涉及到 Java->Dart 消息。

    【讨论】:

      【解决方案3】:

      从后台关闭应用程序。 运行“flutter clean” 运行“颤动运行” 它对我有用。

      【讨论】:

        【解决方案4】:

        将以下代码添加到原生android mainActivity.kt

        package <your.package.name>
        
        import androidx.annotation.NonNull;
        import io.flutter.embedding.android.FlutterActivity
        import io.flutter.embedding.engine.FlutterEngine
        import io.flutter.plugins.GeneratedPluginRegistrant
        
        class MainActivity: FlutterActivity() {
            override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
                GeneratedPluginRegistrant.registerWith(flutterEngine);
            }
        }
        

        【讨论】:

          【解决方案5】:

          从插件包开发者的角度来看:

          由于您使用的是导致此问题的软件包,因此我会提到我在我的软件包中导致了同样的问题。

          就我而言,我使用了错误(或旧) 方法通道。我从服务(广播接收器)运行时保存了一个方法通道的实例,并尝试使用它与正在运行的应用程序进行通信。

          在清理服务(或广播接收器)时,我破坏了 Flutter 引擎,因此将 Flutter JNI 从 C++ 引擎中分离出来。所以当我去使用那个使用分离的 Flutter JNI 的方法通道时,你会得到错误:

          试图向 Flutter 发送平台消息,但 FlutterJNI 与原生 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");
          }
          

          您的问题

          在您的依赖项的情况下,它们可能不支持在服务中使用:它们执行一些缓存/单例并重用不再附加的方法通道或 Flutter JNI。他们可能会假设插件被实例化一次,而不是在服务运行时重新实例化(或应用程序在服务已经运行时启动)。他们可能会注册回调以从操作系统接收数据,然后在创建带有新 Flutter 引擎的新插件时不会取消注册旧的回调。

          没有快速修复,必须构建包来支持它。

          【讨论】:

            猜你喜欢
            • 2020-01-16
            • 2019-04-26
            • 1970-01-01
            • 2019-05-31
            • 2023-04-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多