当前位置:   article > 正文

Unity如何与Webview进行交互_游戏内嵌入web怎么和客户端交互

游戏内嵌入web怎么和客户端交互

目前市面上大多数手游一般都会内嵌WebView用于显示社区、H5活动等页面。WebView技术对于大多数人来说并不陌生,但要做好WebView和游戏客户端的交互需求并不是一个简单的事,一不小心就可能踩坑。

Unity显示网页

Unity中显示网页的方案可以使用现成的插件,也可以自己实现。 自己实现需要分别在Android端和iOS端实现WebView功能,然后打包成插件放入Unity的Assets\Plugins目录中,Unity端通过API调用插件代码即可使用WebView。以Android端为例,典型的插件方案实现流程:首先插件中自定义Dialog,在Dialog中定义好对外提供的接口(如:loadUrl等),导出jar/aar包放到Assets\Plugins\Android目录,Unity端调用。常规的插件代码如下:

public class WebWiewDialog extends DialogFragment {

    private WebView mWebView;
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.layout_webview,false);
        mWebView=view.findViewById(R.id.webview);
        initWebSettings(mWebView);
        return view;
    }

    //对外暴露的接口
    public void loadUrl(String url){
        if(mWebView!=null){
            mWebView.loadUrl(url);
        }
    }

    private void initWebSettings(WebView webView){
        //配置项
        WebSettings webSettings=webView.getSettings();

        //5.0以上开启混合模式加载
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
        }
        
        //允许js代码
        webSettings.setJavaScriptEnabled(true);
      
        //设置UA
        webSettings.setUserAgentString(...);
        
        //自定义WebViewClient
         webView.setWebViewClient(...);
        
        //自定义WebChromeClient
         webView.setWebChromeClient(...);
        
        //注册js映射对象,方便进行交互
        webView.addJavascriptInterface(new AndroidJavaScriptObj(), "android");
        ...
        
    }
   ....
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

Unity与WebView的交互

这个问题可以分解成两个部分:Unity和Android/iOS原生代码进行交互、Android/iOS原生代码与H5进行交互。

Unity和Android/iOS原生代码进行交互

Unity调用Android端原生代码,一般通过调用API,以反射的方式实现。

   var unityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");

   //反射获取WebWiewDialog
   AndroidJavaObject mWebView = unityClass.GetStatic<AndroidJavaObject>("currentActivity").Call<AndroidJavaObject>("getWebView");
   
   //反射调用方法
   mWebView.Call("loadUrl","http://...");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

iOS端则比较方便,直接导入方法调用即可。

[DllImport("__Internal")]
public static extern void loadUrl(string url);
  • 1
  • 2

Android/iOS端如果需要传递数据给Unity端的话,都需要使用UnitySendMessage方法。

UnityPlayer.UnitySendMessage(name, "方法名", "参数");
  • 1
Android/iOS原生代码与H5进行交互

原生代码与H5进行交互通常需要使用js技术。这里也涉及到两个部分:原生调用js代码,js调用原生代码。二者的沟通桥梁通过WebView的API来实现。

Android端

Android端调用JS的方式有两种:WebView.loadUrl("javascript:函数名")WebView.evaluateJavascript("JS代码",回调接口)。前者通过给WebView加载JS的方式去调用,但是无法拿到函数的调用返回值。后者的话可以方便的获取函数返回值,且执行效率高,但是这个API是在Android4.4新增的,使用时务必注意项目支持的最低Android版本,以免引起兼容问题。

JS调用Android端有三种方式:

  1. 通过WebViewaddJavascriptInterface()进行对象映射。

    public class AndroidJavaScriptObj extends Object {
    
        @JavascriptInterface
        public int parseCode(String msg) {
            int code =0;
            System.out.println("JS调用了Android的parseCode方法");
            ...
            return code;
        }
    }
    //注册对象
    webView.addJavascriptInterface(new AndroidJavaScriptObj(), "android");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    JS端通过window.注册对象名.注册方法()即可调用Android端的代码。

    优点是使用起来非常简单,可以方便的拿到函数返回值。缺点是此方式在Android4.2之前存在远程代码执行漏洞。

  2. 通过 WebViewClientshouldOverrideUrlLoading ()方法回调拦截 url。

    此方式需要约定好url协议格式,比如:custom://xxx?k1=v1&k2=v2,尤其需要注意url中的特殊字符会被自动编码的问题。

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
    
                    Uri uri = Uri.parse(url);
                    if ( uri.getScheme().equals("custom")) {
                        //做相应处理
                        ...
                        return true;
                    }
                    return super.shouldOverrideUrlLoading(view, url);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    JS端通过document.location给Android端传递数据

     document.location = "custom://xxx?k1=v1&k2=v2";
    
    • 1

    此方法的缺点是只能单向传递数据,无法拿到返回值,且使用稍微复杂,需要注意编码转义问题。

  3. 通过 WebChromeClientonJsPrompt()方法回调拦截JS对话框prompt() 消息,此方式同样需要约定好通信协议,但协议比较自由,不强制使用uri的格式。无需关心编码转义问题。

     @Override
    public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
                    Uri uri = Uri.parse(message);//message格式自由,这里以Uri举例,但并不强制使用Uri格式
                    if ( uri.getScheme().equals("自定义协议")) {
                         //做相应处理
                        ...
                        result.confirm(返回值);
                        return true;
                    }
        return super.onJsPrompt(view, url, message, defaultValue, result);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    JS端通过prompt("...")调用即可,可以方便的拿到调用返回值。

以上三种方式在使用上各有优缺点,需要结合自己的项目的实际需求和版本兼容情况做出方案选择。

iOS端

iOS中有两个WebView控件:WKWebView、UIWebView。其中UIWebView性能较差,已经不建议使用,以下以WKWebView举例。

iOS端调用JS的方式有两种:

  1. 使用stringByEvaluatingJavaScriptFromString方法。此方法会同步返回结果,如果在JS中执行了耗时操作的话,可能会造成UI阻塞。

     NSString *result = [webView stringByEvaluatingJavaScriptFromString:jsStr];
    
    • 1
  2. 使用evaluateJavaScript方法,异步返回执行结果。

       [self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
            NSLog(@"%@----%@",result, error);
        }];
    
    • 1
    • 2
    • 3

JS调用iOS端有两种原生方式:

  1. iOS端使用WKNavigationDelegate中的代理方法,拦截自定义的URL来实现JS调用OC方法。同Android原理一样,通常需要拦截约定协议的url来实现,需要注意url编码问题。JS端通过document.location调用给iOS端传递数据,

    -(void)webView:(WKWebView  *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    NSURL *URL = navigationAction.request.URL;
        NSString *scheme = [URL scheme];
        if ([scheme isEqualToString:@"自定义协议"]) {
            //执行操作
            ...
                
            //返回执行结果(可选)
        NSString *jsStr = ...;
        [self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
            NSLog(@"%@----%@",result, error);
        }];
            
            decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
        decisionHandler(WKNavigationActionPolicyAllow);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  2. 通过MessageHandler来交互,此方式使用起来方便,不易出错。初始化WkWebView前需要通过addScriptMessageHandler注册MessageHandler,可以注册多个。

    UIView *unityView = UnityGetGLViewController().view;
    
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    config.userContentController = [WKUserContentController new];
    
    //注册MessageHandler
    [config.userContentController addScriptMessageHandler:self name:@"消息名"];
    
    WKWebView *webView = [[WKWebView alloc] initWithFrame:unityView.frame configuration:config];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    然后实现WKScriptMessageHandler协议中的didReceiveScriptMessage

    
    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
    
    {
    if ([message.name isEqualToString:@"消息名"])
    {
        NSString *value1 = [message.body valueForKey:@"key1"];
        NSDictionary *value2 = [message.body valueForKey:@"key2"];
        //执行函数操作
        ....
            
        //返回执行结果(可选)
        NSString *jsStr = ...;
        [self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
            NSLog(@"%@----%@",result, error);
        }];
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    JS端调用方式为:window.webkit.messageHandlers.<name>.postMessage(<messageBody>)

    //JS端调用
     var data = {
            "key1": "value1",
            "key2": {  
            }
        }; 
    window.webkit.messageHandlers.消息名.postMessage(data);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/121741
推荐阅读
相关标签
  

闽ICP备14008679号