[文章]使用WebView组件实现应用与Web页面间的通信

阅读量0
0
0
1. 介绍      
开发者如果需要在自己的应用中嵌入Web页面,可以通过WebView组件进行开发。WebView组件派生于通用组件Component,可以像通用组件一样使用。
本教程以应用嵌入WebView组件为例,从WebView组件构建、Web页面加载、应用与WebView中Web页面间的交互三个方面出发,让您快速了解WebView组件的基本能力及使用方法。
WebView组件能力的详细介绍可以参考开发指南WebView。

说明
  • 请使用真机或模拟器运行查看WebView效果,预览器不支持WebView显示。
  • 只有预置WebView能力的真机设备才支持WebView功能。具体请以实际设备支持情况为准。智能穿戴设备不支持WebView。

2. 搭建HarmonyOS环境我们首先需要完成HarmonyOS开发环境搭建,可参照如下步骤进行。
  • 安装DevEco Studio,详情请参考下载和安装软件。
  • 设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:
    • 如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
    • 如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。
  • 开发者可以参考以下链接,完成设备调试的相关配置:
    • 使用真机进行调试
    • 使用模拟器进行调试

3. 增加一个WebView组件      

步骤 1 -  在"resources/base/layout/ability_main.xml"文件中创建WebView,示例代码如下:
  1. <ohos.agp.components.webengine.WebView
  2.     ohos:id="$+id:webview"
  3.     ohos:height="match_parent"
  4.     ohos:width="match_parent">
  5. </WebView>
复制代码
步骤 2 -  在"slice/MainAbilitySlice.java"文件中通过如下方式获取WebView对象,示例代码如下:
  1. WebView webview = (WebView) findComponentById(ResourceTable.Id_webview);
复制代码
—-结束

4. 通过WebView加载Web页面WebView加载页面分为加载Web页面和加载本地Web页面两种情况,接下来我们将分别进行介绍。
  • WebView加载网络Web页面
    在entry/src/main/config.json中申请网络权限ohos.permission.INTERNET,示例代码如下:
    1. module": {
    2. ......
    3. "reqPermissions": [
    4. {
    5.    "name": "ohos.permission.INTERNET"
    6. }
    7. ]
    8. }
    复制代码
    在"slice/MainAbilitySlice.java"文件中通过webview.load(String url)方法访问具体的Web页面,通过WebConfig类对WebView组件的行为进行配置,示例代码如下:
    1. WebConfig webConfig = webview.getWebConfig();
    2. // WebView加载URL,其中urlTextField为输入URL的TextField组件
    3. webview.load(urlTextField.getText());
    复制代码
    在Web页面进行链接跳转时,WebView默认会打开目标网址,通过WebAgent对象可以定制该行为,示例代码如下
    1. webview.setWebAgent(new WebAgent() {
    2.             [url=home.php?mod=space&uid=2735960]@Override[/url]
    3.             public boolean isNeedLoadUrl(WebView webView, ResourceRequest request) {
    4.                 if (request == null || request.getRequestUrl() == null) {
    5.                     LogUtil.info(TAG,"WebAgent isNeedLoadUrl:request is null.");
    6.                     return false;
    7.                 }
    8.                 String url = request.getRequestUrl().toString();
    9.                 if (url.startsWith("http:") || url.startsWith("https:")) {
    10.                     webView.load(url);
    11.                     return false;
    12.                 } else {
    13.                     return super.isNeedLoadUrl(webView, request);
    14.                 }
    15.             }
    16.         });
    复制代码
    除此之外,WebAgent对象还提供了相关的回调函数以观测页面状态的变更,如onLoadingPage、onPageLoaded、onError等方法。WebView提供Navigator类进行历史记录的浏览和处理,通过getNavigator()方法获取该类的对象,使用canGoBack()或canGoForward()方法检查是否可以向后或向前浏览,使用goBack()或goForward()方法向后或向前浏览,示例代码如下:
    1. Navigator navigator = webView.getNavigator();
    2. if (navigator.canGoBack()) {
    3.     navigator.goBack();
    4. }
    5. if (navigator.canGoForward()) {
    6.     navigator.goForward();
    7. }
    复制代码

WebView加载本地Web页面
将本地的HTML文件放在"resources/rawfile/"目录下,在本教程中命名为test.html。在HarmonyOS系统中,WebView要访问本地Web文件,需要通过DataAbility的方式进行访问,DataAbility的具体使用方法可以参考开发DataAbility。
在"entry/src/main/config.json"中完成DataAbility的声明,示例代码如下:
  1. module": {
  2. ......
  3. "abilities": [
  4. {
  5.   "name": "com.huawei.codelab.DataAbility",
  6.   "type": "data",
  7.   "uri": "dataability://com.huawei.codelab.DataAbility"
  8. }
  9. ]
  10. }
复制代码
另外需要实现一个DataAbility,通过实现openRawFile(Uri uri, String mode)方法,完成WebView对本地Web页面的访问,示例代码如下:
  1. public class DataAbility extends Ability {
  2.     ...
  3.     @Override
  4.     public RawFileDescriptor openRawFile(Uri uri, String mode) throws FileNotFoundException {
  5.         if (uri == null) {;
  6.             return super.openRawFile(uri, mode);
  7.         }
  8.         String path = uri.getEncodedPath();
  9.         int splitIndex = path.indexOf('/', 1);
  10.         String providerName = Uri.decode(path.substring(1, splitIndex));
  11.         String rawFilePath = Uri.decode(path.substring(splitIndex + 1));
  12.         RawFileDescriptor rawFileDescriptor = null;
  13.         try {
  14.             rawFileDescriptor = getResourceManager().getRawFileEntry(rawFilePath).openRawFileDescriptor();
  15.         } catch (IOException e) {
  16.             // 异常处理
  17.         }
  18.         return rawFileDescriptor;
  19.     }
  20. }
复制代码
在"slice/MainAbilitySlice.java"中声明需要访问的文件路径,通过webview.load(String url)方法加载本地Web页面,可以通过WebConfig类的对象对WebView访问DataAbility的能力进行配置,示例代码如下:
  1. private static final String URL_LOCAL = "dataability://com.huawei.codelab.DataAbility/resources/rawfile/test.html";
  2. // 配置是否支持访问DataAbility资源,默认为true
  3. webConfig.setDataAbilityPermit(true);
  4. webview.load(URL_LOCAL);
复制代码
5. 实现应用与WebView中的Web页面间的通信 本教程以本地Web页面"resources/rawfile/test.html"为例介绍如何实现应用与WebView中的Web页面间交互。
首先需要对WebConfig进行配置,使能WebView与Web页面JavaScript交互的能力,示例代码如下:
  1. // 配置是否支持JavaScript,默认值为false
  2. webConfig.setJavaScriptPermit(true);
复制代码
  • 应用调用Web页面
    在"resources/rawfile/test.html"中编写callJS方法,待应用调用,示例代码如下:
    1. <script type="text/javascript">
    2. // 应用调用Web页面
    3. function callJS(message) {
    4.     alert(message);
    5. }
    6. </script>
    复制代码
    在"slice/MainAbilitySlice.java"中实现应用对JavaScript的调用,示例代码如下:

    1. webview.executeJs("javascript:callJS('这是来自JavaSlice的消息')", msg -> {
    2.         // 在这里处理Js的方法的返回值
    3.     });
    复制代码
    我们可以通过setBrowserAgent方法设置自定义BrowserAgent对象,以观测JavaScript事件及通知等,通过复写onJsMessageShow方法来接管Web页面弹出Alert对话框的事件,示例代码如下:
    1. webview.setBrowserAgent(new BrowserAgent(this) {
    2.             @Override
    3.             public boolean onJsMessageShow(WebView webView, String url, String message, boolean isAlert, JsMessageResult result) {
    4.                 LogUtil.info(TAG,"BrowserAgent onJsMessageShow : " + message);
    5.                 if (isAlert) {
    6.                     // 将Web页面的alert对话框改为ToastDialog方式提示
    7.                     new ToastDialog(getApplicationContext()).setText(message).setAlignment(LayoutAlignment.CENTER).show();
    8.                     // 对弹框进行确认处理
    9.                     result.confirm();
    10.                     return true;
    11.                 } else {
    12.                     return super.onJsMessageShow(webView, url, message, isAlert, result);
    13.                 }
    14.             }
    15.         });
    复制代码

  • Web页面使用JavaScript调用应用
    在"resources/rawfile/test.html"中编写按钮,当按钮被点击时实现JavaScript对应用的调用,示例代码如下:
    1. <body>
    2.     ......
    3. <button id="button" onclick="sendData()" style="background-color:#70DBDB;height:30px;">调用Java方法</button>
    4. <script type="text/javascript">
    5.   function sendData() {
    6.           if (window.JsCallJava && window.JsCallJava.call) {
    7.               // Web页面调用应用
    8.               var rst = window.JsCallJava.call("这个是来自本地Web页面的消息");
    9.           } else {
    10.               alert('发送消息给WebviewSlice失败');
    11.           }
    12.   }
    13. </script>
    14. </body>
    复制代码
    在"slice/MainAbilitySlice.java"中实现应用对JavaScript发起的调用的响应,示例代码如下:
    1. private static final String JS_NAME = "JsCallJava";
    2. webview.addJsCallback(JS_NAME, str -> {
    3.         // 处理接收到的JavaScript发送来的消息,本教程通过ToastDialog提示确认收到Web页面发来的消息
    4.         new ToastDialog(this).setText(str).setAlignment(LayoutAlignment.CENTER).show();
    5.         // 返回给JavaScript
    6.         return "Js Call Java Success";
    7. });
    复制代码

6. 恭喜你
通过本教程的学习,你已学会如何使用WebView组件。
7. 完整示例
下面我们通过一个完整的示例来看下WebView组件的使用。本示例可以让用户选择加载手动输入的Web URL,也可以加载本地Web页面,并实现了前进、后退及应用与Web页面交互的基本功能,实现效果如下:
5.png

  • 点击"Load URL"按钮会加载上方网址的Web页面,通过"Go Back"和"Go Forward"按钮实现Web页面间的导航。
  • 点击"Load Local HTML"按钮加载本地Web页面,点击"Send Message to Local HTML"或者Web页面中的"调用Java方法"按钮,实现应用与Web页面间的交互。
具体实现代码如下:
  • 布局与样式
    在HUAWEI DevEco Studio中创建一个Phone的Empty Feature Ability(Java)模板工程,然后在生成的布局文件"resources/base/layout/ability_main.xml"中增加如下代码:
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <DirectionalLayout
    3. xmlns:ohos="http://schemas.huawei.com/res/ohos"
    4. ohos:height="match_parent"
    5. ohos:orientation="vertical"
    6. ohos:width="match_parent">
    7. <TextField
    8.     ohos:id="$+id:textField"
    9.     ohos:height="30vp"
    10.     ohos:width="match_parent"
    11.     ohos:left_margin="5vp"
    12.     ohos:right_margin="5vp"
    13.     ohos:text_alignment="vertical_center"
    14.     ohos:text="https://www.huawei.com/cn/"
    15.     ohos:hint="请输入网址"
    16.     ohos:basement="#000099"
    17.     ohos:text_size="20fp">
    18. </TextField>
    19. <DirectionalLayout
    20.     ohos:height="510vp"
    21.     ohos:width="match_parent"
    22.     ohos:top_margin="5vp"
    23.     ohos:background_element="$graphic:background_button"
    24.     ohos:orientation="horizontal">
    25.     <ohos.agp.components.webengine.WebView
    26.         ohos:margin="5vp"
    27.         ohos:height="500vp"
    28.         ohos:id="$+id:webview"
    29.         ohos:width="match_parent"/>
    30. </DirectionalLayout>
    31. <Button
    32.     ohos:id="$+id:loadUrl"
    33.     ohos:height="30vp"
    34.     ohos:left_margin="5vp"
    35.     ohos:right_margin="5vp"
    36.     ohos:top_margin="5vp"
    37.     ohos:text="Load URL"
    38.     ohos:background_element="$graphic:background_button"
    39.     ohos:text_size="20fp"
    40.     ohos:width="match_parent">
    41. </Button>
    42. <DirectionalLayout
    43.     ohos:height="30vp"
    44.     ohos:width="match_parent"
    45.     ohos:top_margin="5vp"
    46.     ohos:orientation="horizontal">
    47.     <Button
    48.         ohos:id="$+id:goback"
    49.         ohos:height="match_parent"
    50.         ohos:width="1"
    51.         ohos:left_margin="5vp"
    52.         ohos:right_margin="5vp"
    53.         ohos:weight="1"
    54.         ohos:text="Go Back"
    55.         ohos:background_element="$graphic:background_button"
    56.         ohos:text_size="20fp">
    57.     </Button>
    58.     <Button
    59.         ohos:id="$+id:forward"
    60.         ohos:height="match_parent"
    61.         ohos:width="1"
    62.         ohos:text="GO Forward"
    63.         ohos:right_margin="5vp"
    64.         ohos:left_margin="5vp"
    65.         ohos:weight="1"
    66.         ohos:background_element="$graphic:background_button"
    67.         ohos:text_size="20fp">
    68.     </Button>
    69. </DirectionalLayout>
    70. <Button
    71.     ohos:width="match_parent"
    72.     ohos:id="$+id:load_local_url"
    73.     ohos:height="30vp"
    74.     ohos:text="Load Local HTML"
    75.     ohos:left_margin="5vp"
    76.     ohos:top_margin="5vp"
    77.     ohos:right_margin="5vp"
    78.     ohos:background_element="$graphic:background_button"
    79.     ohos:text_size="20fp">
    80. </Button>
    81. <Button
    82.     ohos:id="$+id:callJS"
    83.     ohos:height="30vp"
    84.     ohos:width="match_parent"
    85.     ohos:left_margin="5vp"
    86.     ohos:top_margin="5vp"
    87.     ohos:right_margin="5vp"
    88.     ohos:text="Send Message to Local HTML"
    89.     ohos:background_element="$graphic:background_button"
    90.     ohos:text_size="20fp">
    91. </Button>
    92. </DirectionalLayout>
    复制代码
    在"resources/base/graphic/"目录下增加background_button.xml文件用来定义背景属性。

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
    3.     ohos:shape="rectangle">
    4.     <corners
    5.         ohos:radius="6vp"/>
    6.     <solid
    7.         ohos:color="#e9e9e9"/>
    8. </shape>
    复制代码

  • 首页代码
    在"slice/MainAbilitySlice.java"中添加如下代码:
    1. import com.huawei.codelab.ResourceTable;

    2. import ohos.aafwk.ability.AbilitySlice;
    3. import ohos.aafwk.content.Intent;
    4. import ohos.agp.components.Button;
    5. import ohos.agp.components.TextField;
    6. import ohos.agp.components.webengine.BrowserAgent;
    7. import ohos.agp.components.webengine.JsMessageResult;
    8. import ohos.agp.components.webengine.Navigator;
    9. import ohos.agp.components.webengine.ResourceRequest;
    10. import ohos.agp.components.webengine.WebAgent;
    11. import ohos.agp.components.webengine.WebConfig;
    12. import ohos.agp.components.webengine.WebView;
    13. import ohos.agp.utils.LayoutAlignment;
    14. import ohos.agp.window.dialog.ToastDialog;

    15. public class MainAbilitySlice extends AbilitySlice {
    16. private static final String URL_LOCAL = "dataability://com.huawei.codelab.DataAbility/resources/rawfile/test.html";
    17. private static final String JS_NAME = "JsCallJava";
    18. private WebView webview;
    19. private TextField urlTextField;
    20. private Navigator navigator;

    21. @Override
    22. public void onStart(Intent intent) {
    23.     super.onStart(intent);
    24.     super.setUIContent(ResourceTable.Layout_ability_main);
    25.     initView();
    26. }

    27. private void initView() {
    28.     webview = (WebView) findComponentById(ResourceTable.Id_webview);
    29.     urlTextField = (TextField) findComponentById(ResourceTable.Id_textField);
    30.     navigator = webview.getNavigator();
    31.     initButton();
    32.     configWebView();
    33. }

    34. private void configWebView() {
    35.     WebConfig webConfig = webview.getWebConfig();

    36.     // 是否支持Javascript,默认值false
    37.     webConfig.setJavaScriptPermit(true);
    38.     webview.setWebAgent(new WebAgent() {
    39.         @Override
    40.         public boolean isNeedLoadUrl(WebView webView, ResourceRequest request) {
    41.             if (request == null || request.getRequestUrl() == null) {
    42.                 return false;
    43.             }
    44.             String url = request.getRequestUrl().toString();
    45.             if (url.startsWith("http:") || url.startsWith("https:")) {
    46.                 webView.load(url);
    47.                 return false;
    48.             } else {
    49.                 return super.isNeedLoadUrl(webView, request);
    50.             }
    51.         }
    52.     });

    53.     webview.setBrowserAgent(new BrowserAgent(this) {
    54.         @Override
    55.         public boolean onJsMessageShow(WebView webView, String url, String message, boolean isAlert, JsMessageResult result) {
    56.             if (isAlert) {
    57.                 new ToastDialog(getApplicationContext()).setText(message).setAlignment(LayoutAlignment.CENTER).show();
    58.                 result.confirm();
    59.                 return true;
    60.             } else {
    61.                 return super.onJsMessageShow(webView, url, message, isAlert, result);
    62.             }
    63.         }
    64.     });

    65.     // 配置JS发来的消息处理
    66.     webview.addJsCallback(JS_NAME, str -> {
    67.         // 处理接收到的Js发送来的消息
    68.         new ToastDialog(this).setText(str).setAlignment(LayoutAlignment.CENTER).show();

    69.         // 返回给Js
    70.         return "Js Call Java Success";
    71.     });
    72. }

    73. private void initButton() {
    74.     initLoadUrlButton();
    75.     initGoBackButton();
    76.     initGoForwardButton();
    77.     initLoadLocalUrlButton();
    78.     initCallJsButton();
    79. }

    80. private void initCallJsButton() {
    81.     Button callJs = (Button) findComponentById(ResourceTable.Id_callJS);
    82.     callJs.setClickedListener(component -> {
    83.         webview.executeJs("javascript:callJS('这是来自JavaSlice的消息')", msg -> {
    84.             // 在这里处理Java调用Js的方法的返回值
    85.         });
    86.     });
    87. }

    88. private void initLoadLocalUrlButton() {
    89.     Button loadLocalUrlButton = (Button) findComponentById(ResourceTable.Id_load_local_url);
    90.     loadLocalUrlButton.setClickedListener(component -> {
    91.         webview.load(URL_LOCAL);
    92.     });
    93. }

    94. private void initLoadUrlButton() {
    95.     Button loadUrlButton = (Button) findComponentById(ResourceTable.Id_loadUrl);
    96.     loadUrlButton.setClickedListener(component -> {
    97.         webview.load(urlTextField.getText());
    98.     });
    99. }

    100. private void initGoBackButton() {
    101.     Button goBackButton = (Button) findComponentById(ResourceTable.Id_goback);
    102.     goBackButton.setClickedListener(component -> {
    103.         if (navigator.canGoBack()) {
    104.             navigator.goBack();
    105.         }
    106.     });
    107. }

    108. private void initGoForwardButton() {
    109.     Button goForwardButton = (Button) findComponentById(ResourceTable.Id_forward);
    110.     goForwardButton.setClickedListener(component -> {
    111.         if (navigator.canGoForward()) {
    112.             navigator.goForward();
    113.         }
    114.     });
    115. }

    116. @Override
    117. public void onActive() {
    118.     super.onActive();
    119. }

    120. @Override
    121. public void onForeground(Intent intent) {
    122.     super.onForeground(intent);
    123. }
    124. }
    复制代码

  • DataAbility实现
    创建一个DataAbility,并添加如下代码:
    1. import java.io.FileNotFoundException;
    2. import java.io.IOException;

    3. import ohos.aafwk.ability.Ability;
    4. import ohos.aafwk.content.Intent;
    5. import ohos.global.resource.RawFileDescriptor;
    6. import ohos.utils.net.Uri;

    7. public class DataAbility extends Ability {

    8. @Override
    9. public void onStart(Intent intent) {
    10.     super.onStart(intent);
    11. }

    12. @Override
    13. public RawFileDescriptor openRawFile(Uri uri, String mode) throws FileNotFoundException {
    14.     if (uri == null) {
    15.         return super.openRawFile(uri, mode);
    16.     }
    17.     String path = uri.getEncodedPath();
    18.     final int splitIndex = path.indexOf('/', 1);
    19.     final String providerName = Uri.decode(path.substring(1, splitIndex));
    20.     String rawFilePath = Uri.decode(path.substring(splitIndex + 1));
    21.     RawFileDescriptor rawFileDescriptor = null;
    22.     try {
    23.         rawFileDescriptor = getResourceManager().getRawFileEntry(rawFilePath).openRawFileDescriptor();
    24.     } catch (IOException e) {
    25.         // 处理异常
    26.     }
    27.     return rawFileDescriptor;
    28. }
    29. }
    复制代码

  • 本地Web页面实现
    在"resources/rawfile/"目录下创建本地Web页面test.html,示例代码如下:
    1. <!DOCTYPE html>
    2. <html>
    3. <meta charset="UTF-8">
    4. <title>本地html</title>
    5. <body onload="hello">
    6.     <br>
    7.     <h1 id="helloName">这是一个本地HTML页面</h1>
    8.     <br>
    9.     <button id="button" onclick="sendData()" style="background-color:#70DBDB;height:30px;">调用Java方法</button>

    10.     <script type="text/javascript">
    11.     // 应用调用Web页面
    12.     function callJS(message) {
    13.         alert(message);
    14.     }

    15.     function sendData() {
    16.         if (window.JsCallJava && window.JsCallJava.call) {
    17.             // Web页面调用应用
    18.             var rst = window.JsCallJava.call("这个是来自本地Web页面的消息");
    19.         } else {
    20.             alert('发送消息给WebviewSlice失败');
    21.         }
    22.     }
    23.     </script>
    24. </body>
    25. </html>
    复制代码

说明
以上代码仅demo演示参考使用,产品化的代码需要考虑数据校验和国际化。


回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友