使用WebView组件实现应用与Web页面间的通信 - HarmonyOS技术社区 - 电子技术论坛 - 广受欢迎的专业电子论坛
分享 收藏 返回

[文章]

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

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:id="$+id:webview"
  2.     ohos:height="match_parent"
  3.     ohos:width="match_parent">
步骤 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方法,待应用调用,示例代码如下:
    在"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.     ......


    在"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. xmlns:ohos="http://schemas.huawei.com/res/ohos"
    2. ohos:height="match_parent"
    3. ohos:orientation="vertical"
    4. ohos:width="match_parent">
    5.     ohos:id="$+id:textField"
    6.     ohos:height="30vp"
    7.     ohos:width="match_parent"
    8.     ohos:left_margin="5vp"
    9.     ohos:right_margin="5vp"
    10.     ohos:text_alignment="vertical_center"
    11.     ohos:text="https://www.huawei.com/cn/"
    12.     ohos:hint="请输入网址"
    13.     ohos:basement="#000099"
    14.     ohos:text_size="20fp">

    15.     ohos:height="510vp"
    16.     ohos:width="match_parent"
    17.     ohos:top_margin="5vp"
    18.     ohos:background_element="$graphic:background_button"
    19.     ohos:orientation="horizontal">
    20.    
    21.         ohos:margin="5vp"
    22.         ohos:height="500vp"
    23.         ohos:id="$+id:webview"
    24.         ohos:width="match_parent"/>


    25.     ohos:height="30vp"
    26.     ohos:width="match_parent"
    27.     ohos:top_margin="5vp"
    28.     ohos:orientation="horizontal">
    29.    
    30.    



    在"resources/base/graphic/"目录下增加background_button.xml文件用来定义背景属性。


    1.     ohos:shape="rectangle">
    2.    
    3.         ohos:radius="6vp"/>
    4.    
    5.         ohos:color="#e9e9e9"/>

  • 首页代码
    在"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. 本地html

    2.    

    3.    

      这是一个本地HTML页面


    4.    

    5.    

    6.    


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


回帖(1)

挽你何用

2021-8-26 11:33:50
不错的教程,十分详细,受教了

更多回帖

×
发帖