Flutter Platform Channels - 在android上调用channel方法,挂起ui

Eli*_*iss 5 multithreading flutter

我正在尝试使用以下包在 flutter 中使用 Tesseract https://github.com/arrrrny/tesseract_ocr

\n\n

我已经下载了应用程序并运行。

\n\n

问题是extractTextUI 挂起。

\n\n

查看Java代码:

\n\n
  Thread t = new Thread(new Runnable() {\n    public void run() {\n      baseApi.setImage(tempFile);\n      recognizedText[0] = baseApi.getUTF8Text();\n      baseApi.end();\n    }\n  });\n  t.start();\n  try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); }\n  result.success(recognizedText[0]);\n
Run Code Online (Sandbox Code Playgroud)\n\n

我可以看到它正在一个新线程上运行,所以我希望它不会挂起应用程序,但它仍然如此。

\n\n

我找到了这个例子:

\n\n
            new Handler(Looper.getMainLooper()).post(new Runnable() {\n                @Override\n                public void run() {\n                    // Call the desired channel message here.\n                    baseApi.setImage(tempFile);\n                    recognizedText[0] = baseApi.getHOCRText(0);\n                    baseApi.end();\n                    result.success(recognizedText[0]);\n\n                }\n            });\n
Run Code Online (Sandbox Code Playgroud)\n\n

来自https://flutter.dev/docs/development/platform-integration/platform-channels#channels-and-platform-threading

\n\n

但它也会挂起 UI。

\n\n

文档还说

\n\n
**Channels and Platform Threading**\nInvoke all channel methods on the platform\xe2\x80\x99s main thread when writing code on the platform side.\n
Run Code Online (Sandbox Code Playgroud)\n\n

有人可以澄清这句话吗?

\n\n

根据Richard Heap答案,我尝试从本机调用方法到 dart,传递结果:

\n\n

省道面:

\n\n
_channel.setMethodCallHandler((call) {\n  print(call);\n  switch (call.method) {\n    case "extractTextResult":\n      final String result = call.arguments;\n      print(result);\n  }\n  var t;\n  return t;\n});\n
Run Code Online (Sandbox Code Playgroud)\n\n

Java端:

\n\n

通道.invokeMethod("extractTextResult","hello");

\n\n

如果我从主线程调用这个方法,这工作正常,但线程会阻塞。

\n\n

如果我做

\n\n
            Thread t = new Thread(new Runnable() {\n                public void run() {\n                    channel.invokeMethod("extractTextResult","test1231231");\n\n                }\n            });\n            t.start();\n\n            result.success("tst"); // return immediately\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后应用程序崩溃并显示以下消息:

\n\n

在此输入图像描述

\n\n

我也尝试过:

\n\n
           Thread t = new Thread(new Runnable() {\n                public void run() {\n                    new Handler(Looper.getMainLooper()).post(new Runnable() {\n                        @Override\n                        public void run() {\n                            // Call the desired channel message here.\n                            baseApi.setImage(tempFile);\n                            recognizedText[0] = baseApi.getHOCRText(0);\n                            baseApi.end();\n                            result.success(recognizedText[0]);\n                            //                                channel.invokeMethod("extractTextResult", "test1231231");\n                        }\n                    });\n\n                }\n            });\n            t.start();\n\n            result.success("tst");\n
Run Code Online (Sandbox Code Playgroud)\n\n

这就是我理解Richard Heap最后一条评论的意思,但它仍然挂起用户界面。

\n

小智 5

我遇到了同样的问题,并使用 TesseractOcrPlugin.java 中的 MethodCallWrapper 修复了它

此代码适用于我(不需要更改 Dart 代码):

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();
  }

}
Run Code Online (Sandbox Code Playgroud)


Ric*_*eap 1

通过使用,join您可以让主线程等待后台线程,并阻止它。您必须删除连接并立即返回结果。

那么,如何返回ocr结果呢?这个结果不会立即可用。当它可用时,您可以从 Native 调用一个方法到 dart,并传递结果。在 dart 结束时,您可以将结果作为任何异步事件进行处理。

你的问题最后一段的要点是你的结果将在你的后台线程上可用,所以你想在那里调用原生的 dart 方法。你不能。您必须将方法调用代码发布到主循环程序 - 您已经显示了一些用于发布到主循环程序的代码,您可以将其用作示例。