Flutter Side Door Left - How do I skip Activity and execute Dart directly?

Start by cutting directly into the topic and focusing on:

private void startBackgroundIsolate() {
	//...
	sBackgroundFlutterEngine = new FlutterEngine(mContext);
	DartExecutor executor = sBackgroundFlutterEngine.getDartExecutor();

	FlutterCallbackInformation callbackInfo = FlutterCallbackInformation.lookupCallbackInformation(mRegistrationCallbackId);

	if (callbackInfo == null) {
		Log.e(TAG, "Fatal: failed to find callback: " + mRegistrationCallbackId);
		return;
	}
	
	String appBundlePath = FlutterMain.findAppBundlePath();
	AssetManager assets = mContext.getAssets();
	
	DartExecutor.DartCallback dartCallback = new DartExecutor.DartCallback(assets, appBundlePath, callbackInfo);
	executor.executeDartCallback(dartCallback);
}

This code creates a FlutterEngine and finds callbackInfo = Flutte based on the mRegistrationCallbackId stored in SharedPreference RCallbackInformation.lookupCallbackInformation(mRegistrationCallbackId), then find dartCallback, and finally execute DartExecutor.executeDartCallback(dartCallback) Completes the call to the Dart function.

  • This call skips the process where Activity creates FlutterView, registers MethodChannel, and calls the Dart function one step at a time, which is particularly powerful.

Then the problem is: there must always be this dartCallback, and it must also be hooked to the mRegistrationCallbackId.So what does this function look like, what are the requirements, and how can I register it?


import 'package:flutter/services.dart';
import 'package:flutter/material.dart';

static const MethodChannel _methodChannel = const MethodChannel(_METHOD_CHANNEL_NAME);

static Future<bool> registerXxxTask(Function callback) async {
	Completer completer = new Completer<bool>();

	// Here's the point: two functions are registered.
	List<int> ids = [
		PluginUtilities.getCallbackHandle(_xxxCallbackDispatcher).toRawHandle(),
		PluginUtilities.getCallbackHandle(callback).toRawHandle()
	];
	_methodChannel.invokeMethod('registerXxxTask', ids)
		.then((dynamic success) {
			completer.complete(true);
		}).catchError((error) {
		String message = error.toString();
		print('[registerXxxTask] ‼️ $message');
		completer.complete(false);
	});
	return completer.future;
}

This Dart code implements the registration of functions.First, PluginUtilities will _The xxxCallbackDispatcher and callback are converted to two unique numeric IDS (that is, the reflected identifier) and then stored through the Method Channel to the Native side (Java code for Android).

Ultimately, we want Native's ArtExecutor to call the parameter callback directly to the registerXxxTask(Function callback). Why register a function here?It's actually for the sake of data transmission.DartExecutor.executeDartCallback(dartCallback) This dartCallback should only be parameterless (to be verified).The basic idea is: first invoke parameterless function, then use it to create MethodChannel to achieve flexible calls to other functions.

void _xxxCallbackDispatcher() {
	WidgetsFlutterBinding.ensureInitialized();
	const MethodChannel _headlessChannel = MethodChannel("$_PLUGIN_PATH/xxx", JSONMethodCodec());
	_headlessChannel.setMethodCallHandler((MethodCall call) async {
		final args = call.arguments;
		try {
			final Function callback = PluginUtilities.getCallbackFromHandle(
				CallbackHandle.fromRawHandle(args['callbackId']));
			if (callback == null) {
				print('[_xxxCallbackDispatcher] ERROR: Failed to get callback from handle: $args');
				return;
			}
			callback(args['taskId']);
		} catch (e, stacktrace) {
			print('[_xxxCallbackDispatcher] ‼️ Callback error: ' +
				  e.toString());
			print(stacktrace);
		}
	});
	//...
}

// Define the function to be executed
_backgoundTask(String taskId) {
	//...
}
// Complete registration
registerXxxTask(_backgoundTask)

This Dart code was executed successfully at the beginning DartExecutor.executeDartCallback(dartCallback) called function.This function performs the necessary initialization WidgetsFlutterBinding.ensureInitialized() Then create a MethodChannel, which listens for callbackId and taskId and ultimately executes this callback in the last code, registerXxxTask(Function callback).The steps are also simple: pass CallbackHandle.fromRawHandle(args['callbackId']) Get the true callback function entry, and then pass the parameter callback(args['taskId']) to execute the function call.This completes the _we ultimately wantCall to the backgoundTask(String taskId) function.

Is there anything missing?

sDispatchChannel = new MethodChannel(executor, METHOD_CHANNEL_NAME, JSONMethodCodec.INSTANCE);
sDispatchChannel.setMethodCallHandler(this);

JSONObject response = new JSONObject();
try {
	response.put("callbackId", mClientCallbackId);
	response.put("taskId", mTaskId);
	sDispatchChannel.invokeMethod("", response);
} catch (JSONException e) {
	Log.e(TAG, "JSONException:", e);
}

Native is completingDartExecutor.executeDartCallbackAfter (dartCallback), you also need to pass parameters through the method channel.

=====

Did you forget the registration process?

import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;

mMethodChannel = new MethodChannel(messenger, METHOD_CHANNEL_NAME);
mMethodChannel.setMethodCallHandler(new MethodCallHandler {
	@Override
	public void onMethodCall(MethodCall call, @NonNull Result result) {
		if (call.method.equals(ACTION_REGISTER_xxx)) {
			storeToSharedPreference((List<Object>) call.arguments, result);
		}
	}
});

How do I get BinaryMessenger?

public class Xxx implements FlutterPlugin, ActivityAware {
    @Override
    public void onAttachedToEngine(FlutterPlugin.FlutterPluginBinding binding) {
		binding.getApplicationContext()
		binding.getBinaryMessenger()
		//...
    }
}
  • Done.

Tags: Mobile Java Android

Posted on Tue, 23 Jun 2020 14:53:40 -0400 by McInfo