fluttercommunity / flutter_workmanager

A Flutter plugin which allows you to execute code in the background on Android and iOS.
853 stars 267 forks source link

🐞 Unhandled Exception: MissingPluginException. #6

Closed timrijckaert closed 5 years ago

timrijckaert commented 5 years ago

As discovered in #4.
flutter_workmanager: 0.0.6+1

It seems like it is currently impossible to use other plugins inside your background task as they are not registered.

[ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: MissingPluginException(No implementation found for method FirebaseApp#appNamed on channel plugins.flutter.io/firebase_core)

Simple example:

pubspec:

name: workmanager_example
description: Demonstrates how to use the workmanager plugin.
publish_to: 'none'

environment:
  sdk: ">=2.1.0 <3.0.0"

dependencies:
  path_provider: ^1.2.0
  workmanager: 0.0.6+1
  flutter:
    sdk: flutter
  cupertino_icons: ^0.1.2

flutter:
  uses-material-design: true

main.dart

import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'dart:async';

import 'package:workmanager/workmanager.dart';

void callbackDispatcher() {
  Workmanager.defaultCallbackDispatcher((echoValue) async {
    switch (echoValue) {
      case simpleTaskKey:
        Directory tempDir = await getTemporaryDirectory();
        String tempPath = tempDir.path;
        break;
    }

    return Future.value(true);
  });
}
timrijckaert commented 5 years ago

It seems like we need to register all plugins the users has in the application class rather than in the Activity which is standard. Ideally the user does not need to reference our CustomApplication for it to work.
Let's explore some options.

Preparing a proof of concept

timrijckaert commented 5 years ago

After some investigation it seems impossible to handle this completely from the plugin itself.
The GeneratedPluginRegistrant is a generated class on the implementers classpath.

timrijckaert commented 5 years ago

Uploaded on pub.dev 0.0.6+2

Breaking change.

A user should extend a custom Application and register it in its AndroidManifest.xml

class App : FlutterApplication(), PluginRegistry.PluginRegistrantCallback {
    override fun onCreate() {
        super.onCreate()
        WorkmanagerPlugin.setPluginRegistrantCallback(this)
    }

    override fun registerWith(reg: PluginRegistry?) {
        GeneratedPluginRegistrant.registerWith(reg)
    }
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="be.tramckrijte.workmanager_example">

    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
         calls FlutterMain.startInitialization(this); in its onCreate method.
         In most cases you can leave this as-is, but you if you want to provide
         additional functionality it is fine to subclass or reimplement
         FlutterApplication and put your custom class here. -->
    <application
        android:name=".App" //Replace io.flutter.app.FlutterApplication with .App
        android:icon="@mipmap/ic_launcher"
        android:label="workmanager_example"
        tools:replace="android:name">
        <activity
            android:name=".MainActivity"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:windowSoftInputMode="adjustResize">
            <!-- This keeps the window background of the activity showing
                 until Flutter renders its first frame. It can be removed if
                 there is no splash screen (such as the default splash screen
                 defined in @style/LaunchTheme). -->
            <meta-data
                android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
                android:value="true" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
MiguelSOliveira commented 5 years ago

I'm getting this error, am I missing something?

Application.java:12: error: cannot find symbol
    WorkmanagerPlugin.setPluginRegistrantCallback(this);
                     ^
  symbol:   method setPluginRegistrantCallback(Application)
  location: class WorkmanagerPlugin
1 error

FAILURE: Build failed with an exception.

Application.java

package`package_name`;

import be.tramckrijte.workmanager.WorkmanagerPlugin;
import io.flutter.app.FlutterApplication;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class Application extends FlutterApplication implements PluginRegistry.PluginRegistrantCallback {
  @Override
  public void onCreate() {
    super.onCreate();
    WorkmanagerPlugin.setPluginRegistrantCallback(this);
  }

  @Override
  public void registerWith(PluginRegistry registry) {
    GeneratedPluginRegistrant.registerWith(registry);
  }
}

I've added the custom application to android manifest.

I have the dependency: workmanager: ^0.0.11

timrijckaert commented 5 years ago

Please open a new issue.

TomOerlemans commented 4 years ago

I've been trying to get this to work for multiple days now. I made an bare-minimum app and explained exactly what I have done at each step. If someone cares to take a look, I would appreciate it very much: https://stackoverflow.com/questions/60262425/how-to-make-flutter-workmanager-plugin-and-location-plugin-work-together

peterIrving commented 3 years ago

I am having the same issue on IOS right now with the Path Provider plugin.

HTTP works fine though - I wonder why?

I have forked the Workmanager plugin and implemented it slightly differently but I think the issue is coming from creating a NEW flutter engine, and those plugins not being registed to that engine.

Maybe registering the plugins you want when creating that flutter engine would work. Obviously not ideal if you are just using the plugin... but I feel this is the place where the registration needs to occur.


var flutterEngine: FlutterEngine? = FlutterEngine(name: flutterThreadLabelPrefix, project: nil, allowHeadlessExecution: true)

// ??? Do something here with the flutter engine to register the plugins you want to call in the background function ???

flutterEngine!.run(withEntrypoint: flutterCallbackInformation.callbackName, libraryURI: flutterCallbackInformation.callbackLibraryPath)

WorkManagerPlugin.flutterPluginRegistrantCallback?(flutterEngine!)`
roly151 commented 2 years ago

HTTP works fine though - I wonder why?

Are you using a package? I am getting g the error when I try to use the http package in the callback. I have tried to register all plugins, but the http package does not get registered - so just wondering what you are using for http that it works?