【Flutter混编】InAppWebView常见配置&相册问题处理

【Flutter混编】InAppWebView常见配置&相册问题处理,第1张

 

Flutter自带WebView不想说啥了,就这样吧。

反正一番周折之后选择使用第三方的InAppWebView。看源码可以看出本质上是用了Platform调回原生平台的webview,但是性能就是比自己写的要好。

常见配置

1. InAppWebView.initialOptions

最基本的配置。InAppWebViewGroupOptions里面有三种,我这边用到crossPlatform和android

crossPlatform: InAppWebViewOptions(
 useShouldOverrideUrlLoading: true,
 applicationNameForUserAgent: AppConfigureManager().androidUserAgent,
),
android: AndroidInAppWebViewOptions(
 useHybridComposition: true,
),

useShouldOverrideUrlLoading就是启用shouldOverrideUrlLoading的回调,具体作用百度applicationNameForUserAgent在已有的UserAgent后面直接拼信息,这样就不需要在后续时机取已有UA再做拼接了

useHybridComposition等于使用Flutter自带WebView时的

if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();

反正就带上就是了,忘了不带会出啥问题了

2. InAppWebView.gestureRecognizers

手势识别,根据注释(Flutter的WebView也有的字段),如果不添加会只能响应点击事件。实践下来的话的确如此。因为我们的WebView放在了一个可以左右滑的视图里,如果不捕捉垂直滑动手势会导致WebView自身很难响应到上下滑手势。

gestureRecognizers: [
 Factory(() => VerticalDragGestureRecognizer()),
].toSet(),

3. InAppWebView.onWebViewCreated

onWebViewCreated: (InAppWebViewController controller) async{
 _webViewControllerAndroid = controller;
 _webViewControllerAndroid..addJavaScriptHandler(handlerName: 'flutter_bridge', callback: (args) async{
  List inArgs = [];
  for(var tmp in args.first) {
   if(tmp is String) {
    inArgs.add(tmp);
   }
   else {
    inArgs.add('');
   }
  }
  NativePageJumpChannel tmp= await NativePageJumpChannel().webViewBridge(inArgs);
  if((tmp.callbackStr??'').length > 0) {
   _webViewControllerAndroid.loadUrl(
    urlRequest: URLRequest(url: Uri.parse(
     tmp.callbackStr ?? '')));
   }
 });

要点在于InAppWebViewController提供的addJavaScriptHandler方法(Flutter的WebView不提供的),给JS提供了一个回调InAppWebView的方法。在JS中调用这个JS接口的方法:

window.flutter_inappwebview.callHandler('flutter_bridge',[参数])

flutter_inappwebview是固定的object,应该是提前注入的

flutter_bridge是自己起的handler名称

最后传参数被封装进数组args中

Flutter端在callback中来实现业务逻辑,这里是把桥接分发给原生安卓端处理

注:addJavaScriptHandler方法注明要在onWebViewCreated和onLoadStart中使用,应该是注入时机的考量

4.InAppWebView.onLoadStop

没啥好说的,可以在这时跑一个evaluateJavascript方法,就是简单的注入脚本,在网页加载完成之后,需要注意时机问题。

注:evaluateJavascript被标注为不要在onWebViewCreated和onLoadStart中使用,但实际上onLoadStart中使用也可能注入成功,但结果并不可控。

相册问题处理

表象:使用InAppWebView打开了一个H5页面,点击按钮H5调用了系统自带的相册,这时候走的是InAppWebView自己的响应逻辑,打开了其自定义的图片选择器,但是选择图片/放弃选择图片之后并没有返回结果(返回图片),再次点击也无法打开相同的照片选择界面了,变成无响应。

通过报错找到了InAppWebView响应H5打开相册事件的地方

路径:flutter_inappwebview-5.3.2/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewChromeClient.java

能找到onShowFileChooser、openFileChooser的实现,结果回调在onActivityResult

为了用上我们自己的打开相册逻辑,直接让openFileChooser/onShowFileChooser反射调用原生项目里的相册,回调也不走onActivityResult而是使用代理实现了原生里的回调

调用调整⬇️

  protected void openFileChooser(ValueCallback filePathCallback, String acceptType) {
    // startPhotoPickerIntent(filePathCallback, acceptType);
    showFileChooser(filePathCallback, null, acceptType);
  }

  protected void openFileChooser(ValueCallback filePathCallback) {
    // startPhotoPickerIntent(filePathCallback, "");
    showFileChooser(filePathCallback, null, null);
  }

  protected void openFileChooser(ValueCallback filePathCallback, String acceptType, String capture) {
    // startPhotoPickerIntent(filePathCallback, acceptType);
    showFileChooser(filePathCallback, null, acceptType);
  }

  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  @Override
  public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) {
    // String[] acceptTypes = fileChooserParams.getAcceptTypes();
    // boolean allowMultiple = fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE;
    // Intent intent = fileChooserParams.createIntent();
    // return startPhotoPickerIntent(filePathCallback, intent, acceptTypes, allowMultiple);
    showFileChooser(null, filePathCallback, null);
    return true;
  }

 随便看看⬇️

  //改动 文件选择器逻辑
  public class FileChooserHandler implements InvocationHandler{
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      Object result = null; 
      String methodName = method.getName(); 
      if(methodName.compareTo("onSelectFinish") == 0) {
        if(args.length == 2) {
          final int errorCode = (int)args[0];
          final List picPath = ((List)args[1])!= null ? (List)args[1] : Arrays.asList();
          Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : plugin.activity;
          activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
              boolean hasResult = false;
              String filePath = picPath.size() > 0 ? picPath.get(0): null;
              if(errorCode == 0) {//ISelectPIcObsv.SELECT_SUCC
                hasResult = true;
                File file = new File(filePath);
                if(InAppWebViewFlutterPlugin.filePathCallbackLegacy != null) {
                  InAppWebViewFlutterPlugin.filePathCallbackLegacy.onReceiveValue(Uri.fromFile(file));
                  InAppWebViewFlutterPlugin.filePathCallbackLegacy = null;
                }
                if(InAppWebViewFlutterPlugin.filePathCallback != null) {
                  InAppWebViewFlutterPlugin.filePathCallback.onReceiveValue(new Uri[]{Uri.fromFile(file)});
                  InAppWebViewFlutterPlugin.filePathCallback = null;
                }
              }
              else {
                hasResult = false;
                filePath = null;
                if(InAppWebViewFlutterPlugin.filePathCallbackLegacy != null) {
                  InAppWebViewFlutterPlugin.filePathCallbackLegacy.onReceiveValue(null);
                  InAppWebViewFlutterPlugin.filePathCallbackLegacy = null;
                }
                if(InAppWebViewFlutterPlugin.filePathCallback != null) {
                  InAppWebViewFlutterPlugin.filePathCallback.onReceiveValue(null);
                  InAppWebViewFlutterPlugin.filePathCallback = null;
                }
              }
            }
          });
        }
      }
      return result;
    }
  }  

 private void showFileChooser(ValueCallback filePathCallback, ValueCallback filePathCallback21, String acceptType) {
    InAppWebViewFlutterPlugin.filePathCallbackLegacy = filePathCallback;
    InAppWebViewFlutterPlugin.filePathCallback = filePathCallback21;
    Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : plugin.activity;
    try {
      ///TODO
      Class clz = Class.forName("com.ll.llgame.engine.image.local.PicChooseEngine");
      Method getInstance = clz.getMethod("getInstance");
      Object engine = getInstance.invoke(null);//PicChooseEngine
      Class iSelectPicObsv = Class.forName("com.ll.llgame.engine.image.local.ISelectPIcObsv");
      FileChooserHandler handler = new FileChooserHandler();
      Object fileChooserHandlerObj = Proxy.newProxyInstance(clz.getClassLoader(), new Class[]{iSelectPicObsv}, handler);
      Method selectPic = clz.getMethod("selectPic",new Class[]{Context.class, iSelectPicObsv});
      selectPic.invoke(engine, new Object[]{activity, fileChooserHandlerObj});
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
public interface ISelectPIcObsv {

    int SELECT_SUCC = 0;
    int SELECT_CANCEL = 1;
    int SELECT_FAIL = 2;
    int SELECT_FAIL_CAMERA_GALLRY_RESULT_NOT_OK = 3;
    int SELECT_FAIL_CAMERA_NULL_NOT_EXIST = 4;
    int SELECT_FAIL_CALL_CAMERA_FAIL = 5;
    int SELECT_FAIL_CLIP_PIC_FAIL = 6;
    int SELECT_FAIL_CLIP_PIC_SAVE_FAIL = 7;

    void onSelectFinish(int errorCode, List picPath);
}

 

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/996456.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存