简体   繁体   中英

"Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++." After start a background service and closes the app

I'm trying to build an app that uses some packages as Location ( https://pub.dev/packages/location ) and Compass ( https://pub.dev/packages/flutter_compass ) and keep a background service tracking user location. Everything works fine until I start the service to track the location.

With the service active the whole app never stops, for example, when I close the app without service the compass stops too, but with the service running, compass keeps running too. Actually it returns a error "Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++. Could not send. Channel: hemanthraj/flutter_compass". The same thing happens with location: "Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++. Could not send. Channel: lyokone/locationstream". After this even if I open again the project it don't work anymore... I'm trying to make a service totally independent from rest of project.

I'll show you the service implementation (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;
    }
}

Another problem is even when I don't close the app, it is draining a lot of battery. Thanks!

From the plugin package developer perspective:

Since you are using a package that is causing this, I'll mention that I was causing the same issue in my package.

In my case, I was using the wrong (or old) method channel. I had saved an instance of a method channel from when the service (broadcast receiver) was running, and tried to use that to communicate with the running application.

When cleaning up the service (or Broadcast receiver), I had destroyed the Flutter Engine and therefore detached the Flutter JNI from the C++ engine. So when I went to use that method channel, which uses the detached Flutter JNI, you get the error:

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

I just had to use the FlutterEngine provided by the FlutterActivity (or FlutterFragment or Application) to get a new MethodChannel.

In my case, I am working on a Flutter package plugin (on Android). What this means is inside the plugin code, I should not any caching of methodChannels accidentally:

public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    final MethodChannel methodChannel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "example-method-channel-name");
}

Your issue

In your dependency's case, it might be that they do not support usage in a service: they do some caching / singletons and re-use method channels or Flutter JNIs that are no longer attached. They might assume that the plugin is instantiated once , and not re-instantiated when the service runs (or app starts whilst service is already running). They might register callbacks to receive data from the OS, and then not unregister the old one when a new plugin with a new Flutter Engine is created.

There is no quick fix, and the package has to be built to support this.

add the follwoing code to native 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);
    }
}

This problem occurred to me while detecting objects on camera with TFLite. I'm not quite sure, but every time I've noticed the phone is overheating,

If there was an event that requires intensive matrix processing like me, the problem is that the ARM processor you run the application cannot do, but battery-related service errors are really interesting.

and this is my best answer: That bug would cause Dart->Java messages to be dropped at a time when the FlutterNativeView was still alive. This issue looks like it involves Java->Dart messages.

Close the application from the background. Run "flutter clean" Run "flutter run" It worked for me.

in my case i just removed laptop from charging

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM