Android Native APP开发笔记:使用WebView控件加载网页

Posted Naisu Xu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Native APP开发笔记:使用WebView控件加载网页相关的知识,希望对你有一定的参考价值。

文章目录

目的

WebView是一个比较常用的控件,功能上也比较单一,就是用来加载网页的,可以加载远程的网页,也可以加载本地网页文件。简单来说就相当于一个浏览器。这篇文章将对WebView使用相关内容做个简单记录。

官方文档:https://developer.android.google.cn/guide/webapps

基础使用

WebView控件使用寄来挺简单,无非是在视图中添加该控件,然后在代码中设置控件相关属性并加载要加载的网页,最后需要开启相关权限。下面是个简单的示例:

activity_main.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 省略若干... >

    <!-- 添加WebView对象 -->
    <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.java 文件:

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;

public class MainActivity extends AppCompatActivity 

    @SuppressLint("SetjavascriptEnabled") // 忽略使能JS警告
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

         myWebView = findViewById(R.id.webview); // 获取WebView控件
        WebSettings webSettings = myWebView.getSettings();
        webSettings.setJavaScriptEnabled(true); // 使加载的网页可以运行JS代码
        myWebView.loadUrl("https://html5test.com/"); // 加载网页链接
        // https://html5test.com/是一个测试Html功能兼容性的网站
        // 你要是喜欢也可以使用https://www.baidu.com/等
    

AndroidManifest.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest 省略若干... >

	<!-- 下面一行使APP可以访问网络内容 -->
    <uses-permission android:name="android.permission.INTERNET" />

    <application
    	<!-- 下面一行使WebView可以访问基于HTTP协议的明文内容 -->
        android:usesCleartextTraffic="true"
        <!-- 省略若干... -->
    </application>

</manifest>

WebView使用基本上要涉及的东西都在上面演示中展示了,主要分为三个方面:

  • 在界面中放置WebView控件;
  • 在代码中设置WebView功能及需要加载的内容;
  • 在APP配置中启用相关权限;

前面演示中配置加载的网页可以使用JS代码,通常来说现在的网页为了获得更好的效果或是更多的功能通常都会有JS代码,所以这个功能基本上都需要打开。需要注意的是这会降低安全性,具体使用时需要根据实际情况来设置。

上面的 WebSettings 除了可以用来设置是否启用JS外,还有非常多的功能可以设置,比如是否可以缩放,是否可以跨域,是否可以缓存等功能,这些都可能是在一定应用下比较重要的功能。详细的内容可以参考下面链接:
https://developer.android.google.cn/reference/kotlin/android/webkit/WebSettings

处理网页导航

WebView使用上比较常用的需要考虑的就是处理网页导航。

在上面的演示中,如果网页中有别的链接,点击这些链接时默认会调用系统的浏览器来打开这些链接。很多时候我们可能比想要通过浏览器来打开,而是通过当前的WebView来打开这些链接,这就需要在代码中对WebView控件进行一些设置了:

import android.webkit.WebViewClient;

WebView myWebView = findViewById(R.id.webview);
myWebView.setWebViewClient(new WebViewClient()); // 就是这一行

当进行上面设置后WebView就像浏览器一样可以在页面间跳转,这写操作都会留下历史访问记录,我们可以使用WebView控件的 goBack()goForward() 方法来向后或向前浏览历史记录。在Android中因为Android设备有返回键,所以通常将返回键和 goBack() 方法绑定使用,比如下面这样:

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.KeyEvent;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class MainActivity extends AppCompatActivity 

    @SuppressLint("SetJavaScriptEnabled")
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WebView myWebView = findViewById(R.id.webview);
        myWebView.setWebViewClient(new WebViewClient()); // 在当前WebView打开新链接
        WebSettings webSettings = myWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        myWebView.loadUrl("https://blog.csdn.net/Naisu_kun");
    

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) 
        WebView myWebView = findViewById(R.id.webview);

        // 如果点击了返回键,并且myWebView当前可以返回,则执行其goBack()方法
        if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) 
            myWebView.goBack();
            return true;
        

        // 不满足前面条件则执行系统默认行为
        return super.onKeyDown(keyCode, event);
    

上面的 WebViewClient 封装了WebView控件各个事件的回调,比如其中的 shouldOverrideUrlLoading 就是来处理url跳转相关事务。

加载本地网页

加载本地网页最简单的就是以字符串形式加载:

String html = "<html><body><p>Hello world</p></body></html>";
String encodedHtml = Base64.encodeToString(html.getBytes(), Base64.NO_PADDING);
myWebView.loadData(encodedHtml, "text/html", "base64");

或者下面形式:

String html = "<html><body><p>Hello world</p></body></html>";
String baseUrl = "https://example.com/";
myWebView.loadDataWithBaseURL(baseUrl, html, "text/html", null, baseUrl);

当然上面方式通常只是简单的页面才用用,更多的是把网页文件打包到项目中,然后直接把文件加载到WebView中。比如我们可以在项目的 项目目录/app/src/main/ 下建立 assets 目录,然后将网页文件都放在该目录下,然后在代码中使用下面式来加载:

myWebView.loadUrl("file:///android_asset/index.html");
// 上面的file:///android_asset/这个路径默认指的就是 项目目录/app/src/main/assets/ 


上面方式因为同源策略(跨域),默认情况下是不允许JS加载其它资源的,得进行设置:

// 设置在WebView内部是否允许通过file url加载的 Js代码读取其他的本地文件
// webSettings.setAllowFileAccessFromFileURLs(true);
// 设置WebView内部是否允许通过 file url 加载的 Javascript 可以访问其他的源(包括http、https等源)
webSettings.setAllowUniversalAccessFromFileURLs(true);

当然上面方式被认为是不安全的,现在官方推荐使用 WebViewAssetLoader 来加载本地网页。这种方式的原理是把WebView发出的所有请求进行拦截,然后使用 WebViewAssetLoader 来分辨处理是加载本地资源还是远程资源。对例程的代码进行调整演示:

package com.example.myapplication;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.webkit.WebViewAssetLoader;
import androidx.webkit.WebViewClientCompat;

import android.annotation.SuppressLint;
import android.net.Uri;
import android.os.Bundle;
import android.view.KeyEvent;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;

public class MainActivity extends AppCompatActivity 

    @SuppressLint("SetJavaScriptEnabled")
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WebView myWebView = findViewById(R.id.webview);
        WebSettings webSettings = myWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);

        // WebViewClientCompat就等同于WebViewClient,封装了WebView控件各个事件的回调
        // 重写该类的shouldInterceptRequest方法,该方法对发出的请求进行拦截处理
        // 这里就将该拦截转给WebViewAssetLoader来处理
        class LocalContentWebViewClient extends WebViewClientCompat 

            private final WebViewAssetLoader mAssetLoader;

            LocalContentWebViewClient(WebViewAssetLoader assetLoader) 
                mAssetLoader = assetLoader;
            

            @Override
            @RequiresApi(21)
            public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) 
                return mAssetLoader.shouldInterceptRequest(request.getUrl());
            

            @Override
            @SuppressWarnings("deprecation") // to support API < 21
            public WebResourceResponse shouldInterceptRequest(WebView view, String url) 
                return mAssetLoader.shouldInterceptRequest(Uri.parse(url));
            
        

		// 创建WebViewAssetLoader对象
		// 设置资源路径/assets/,即 项目目录/app/src/main/assets/ 
        final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
                .addPathHandler("/assets/", new WebViewAssetLoader.AssetsPathHandler(this))
                .build();

		// 加载上面处理
        myWebView.setWebViewClient(new LocalContentWebViewClient(assetLoader));

		// 这里的https://appassets.androidplatform.net是个默认的路径
		// 下面最终其实访问的是 项目目录/app/src/main/assets/index.html 文件
        myWebView.loadUrl("https://appassets.androidplatform.net/assets/index.html");
    

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) 
        WebView myWebView = findViewById(R.id.webview);
        if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) 
            myWebView.goBack();
            return true;
        
        return super.onKeyDown(keyCode, event);
    

WebViewAssetLoader 也可以混合加载本地的和远程的资源,比如下面这样:

final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
		.setDomain("example.com") // 远程地址域名
        .addPathHandler("/assets/", new WebViewAssetLoader.AssetsPathHandler(this))
        .build();

上面的设置下 WebViewAssetLoader 会先从本地寻找资源进行加载,如果本地找不到就从远程加载。

WebViewAssetLoader 更多内容可以参考下面链接:
https://developer.android.google.cn/reference/kotlin/androidx/webkit/WebViewAssetLoader

Web和Native之间交互

Web和Native之间交互主要依赖WebView的 addJavascriptInterface 方法。参考下面演示:

class WebAppInterface 
    @JavascriptInterface
    public void webToNative(String str) 
        System.out.println(str);
    

    @JavascriptInterface
    public String nativeToWeb() 
        return "msg: native to web";
    


// 使用addJavascriptInterface方法将WebAppInterface对象传递给web的"native"对象
myWebView.addJavascriptInterface(new WebAppInterface(), "native");
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
    <button>传递信息给Native</button>
    <br>
    <button>从Native获取信息</button>
    <div></div>
    <script>
        btns = document.querySelectorAll('button');
        btns[0].onclick = () => 
            native.webToNative('msg: web to native');
        ;

        btns[1].onclick = () => 
            let msg = native.nativeToWeb();
            document.querySelector('div').innerText = msg;
        ;
    </script>
</body>
</html>

调试Web应用

默认情况下Web中JS打印输出到控制台的信息( console.log() )都会在Android Studio的Logcat窗口中显示。

我这里真机调试一直没问题,使用模拟器有时候就不行,不过一般也不会用模拟器来开啦。

总结

WebView控件控件的使用比较简单,大多数会遇到的问题都是与安全相关的各种权限问题,基本上都只要打开相关的权限即可。更多内容可以参考官方文档。

虽然文章标题说了Native APP,不过我其实是准备用WebView来做Hybrid App的。

以上是关于Android Native APP开发笔记:使用WebView控件加载网页的主要内容,如果未能解决你的问题,请参考以下文章

Android Native APP开发笔记:从新建到打包流程记录

Android Native APP开发笔记:多线程编程

Android Native APP开发笔记:双击返回键退出应用&单击返回键返回桌面

react native android 开发,基础配置笔记。

React Native笔记

React-Native 开发 android & ios App,共享一份代码