Flutter Platform Channels - Invoke channel method on android, hangs the ui
Solution 1
I had the same Issue and fixed it with a MethodCallWrapper in TesseractOcrPlugin.java
This Code works for me (no Dart-code change is needed):
package io.paratoner.tesseract_ocr;
import com.googlecode.tesseract.android.TessBaseAPI;
import android.os.Handler;
import android.os.Looper;
import android.os.AsyncTask;
import java.io.File;
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;
import io.flutter.plugin.common.PluginRegistry.Registrar;
/** TesseractOcrPlugin */
public class TesseractOcrPlugin implements MethodCallHandler {
private static final int DEFAULT_PAGE_SEG_MODE = TessBaseAPI.PageSegMode.PSM_SINGLE_BLOCK;
/** Plugin registration. */
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "tesseract_ocr");
channel.setMethodCallHandler(new TesseractOcrPlugin());
}
// MethodChannel.Result wrapper that responds on the platform thread.
private static class MethodResultWrapper implements Result {
private Result methodResult;
private Handler handler;
MethodResultWrapper(Result result) {
methodResult = result;
handler = new Handler(Looper.getMainLooper());
}
@Override
public void success(final Object result) {
handler.post(new Runnable() {
@Override
public void run() {
methodResult.success(result);
}
});
}
@Override
public void error(final String errorCode, final String errorMessage, final Object errorDetails) {
handler.post(new Runnable() {
@Override
public void run() {
methodResult.error(errorCode, errorMessage, errorDetails);
}
});
}
@Override
public void notImplemented() {
handler.post(new Runnable() {
@Override
public void run() {
methodResult.notImplemented();
}
});
}
}
@Override
public void onMethodCall(MethodCall call, Result rawResult) {
Result result = new MethodResultWrapper(rawResult);
if (call.method.equals("extractText")) {
final String tessDataPath = call.argument("tessData");
final String imagePath = call.argument("imagePath");
String DEFAULT_LANGUAGE = "eng";
if (call.argument("language") != null) {
DEFAULT_LANGUAGE = call.argument("language");
}
calculateResult(tessDataPath, imagePath, DEFAULT_LANGUAGE, result);
} else {
result.notImplemented();
}
}
private void calculateResult(final String tessDataPath, final String imagePath, final String language,
final Result result) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
final String[] recognizedText = new String[1];
final TessBaseAPI baseApi = new TessBaseAPI();
baseApi.init(tessDataPath, language);
final File tempFile = new File(imagePath);
baseApi.setPageSegMode(DEFAULT_PAGE_SEG_MODE);
baseApi.setImage(tempFile);
recognizedText[0] = baseApi.getUTF8Text();
baseApi.end();
result.success(recognizedText[0]);
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
}
}.execute();
}
}
Solution 2
By using join
you're making the main thread wait for the background thread, blocking it. You have to remove the join and return a result immediately.
So, how do you return the ocr result, which won't be available immediately. When it becomes available, you then call a method from native to dart, passing the result. At the dart end, you then handle the result as any async event.
The point of the last paragraph of your question is that your result will become available on your background thread, so you'd want to call the native to dart method there. You can't. You have to post the method call code to the main looper - you already show some code for posting to the main looper which you can use as an example.
Elia Weiss
Updated on December 17, 2022Comments
-
Elia Weiss over 1 year
I'm trying to use Tesseract in flutter using the following package https://github.com/arrrrny/tesseract_ocr
I've download the app and run in.
The problem is that the
extractText
hangs the UI.Looking at the Java code:
Thread t = new Thread(new Runnable() { public void run() { baseApi.setImage(tempFile); recognizedText[0] = baseApi.getUTF8Text(); baseApi.end(); } }); t.start(); try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } result.success(recognizedText[0]);
I can see that it is running on a new thread, so I expect it not to hang the app, but it still does.
I found this example:
new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { // Call the desired channel message here. baseApi.setImage(tempFile); recognizedText[0] = baseApi.getHOCRText(0); baseApi.end(); result.success(recognizedText[0]); } });
but it also hangs the UI.
The docs also say
**Channels and Platform Threading** Invoke all channel methods on the platform’s main thread when writing code on the platform side.
Can someone clarify this sentence?
According to
Richard Heap
answer, I tried to call a method from native to dart, passing the result:Dart side:
_channel.setMethodCallHandler((call) { print(call); switch (call.method) { case "extractTextResult": final String result = call.arguments; print(result); } var t; return t; });
Java side:
channel.invokeMethod("extractTextResult","hello");
if I call this method from the main thread, this works fine, but then the thread is blocking.
If I do
Thread t = new Thread(new Runnable() { public void run() { channel.invokeMethod("extractTextResult","test1231231"); } }); t.start(); result.success("tst"); // return immediately
Then the app crashes with the following message:
I also tried:
Thread t = new Thread(new Runnable() { public void run() { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { // Call the desired channel message here. baseApi.setImage(tempFile); recognizedText[0] = baseApi.getHOCRText(0); baseApi.end(); result.success(recognizedText[0]); // channel.invokeMethod("extractTextResult", "test1231231"); } }); } }); t.start(); result.success("tst");
which is what I understand that
Richard Heap
last comment meant, but It still hangs the ui.