Android——WebView网页应用

实验报告

课程名称 Android应用编程实验

实验名称:网页应用-WebView
实验目的:学习WebView的相关功能
实验仪器设备:电脑、Android Studio、Android手机
实验内容:

  1. 应用WebView对象浏览网页;
  2. 调用本地HTML网页文件的JavaScript;
  3. 用Android程序操纵JavaScript对话框;

一、应用WebView对象浏览网页

1.1实验原理

WebKit是一个开源的浏览器引擎,WebKit内核具有非常好的网页解析机制,很多系统都使用WebKit做浏览器内核。在WebKit的API包中,最重要、最常用的类是Android.webKit. WebKit。WebView类是WebKit模块Java层的视图类,所有需要使用Web浏览功能的Android应用程序都要创建该视图对象,用于显示和处理请求的网络资源。

1.2实验过程记录

1.2.1布局文件

为实现基本的相机程序效果,使用最简单的线性布局,通过组件的嵌套生成需要的布局。首先指定最外层的线性布局对齐方式为垂直布局,在其中嵌套一个LinearLayout布局,之后再编写EditText组件用来输入网站网址,加入一个button组件用于点击确定。之后在外层的线性布局中加入WebView组件用来展示需要显示的网页,如图1.1所示。
在这里插入图片描述

图1.1布局文件

1.2.2控制文件

1.2.2.1实例化对象

在MainActivity.java文件中修改文件,以实现对程序的控制。首先如下图1.2所示实例化所需的对象。
在这里插入图片描述

图1.2实例化对象

在上图1.2中,分别实例化了用于网页浏览的“WebView”类的“webView”对象、用于点击确定的“Button”类的“openWebBtn”对象、用于编辑输入网址的“EditText”类的“editText”。

1.2.2.2重载构造函数

对构造函数进行修改,使其实现“关联布局文件和控制文件,设置按钮监听事件”的功能。如图1.3所示。
在这里插入图片描述

图1.3重载构造函数

通过findViewById方法关联图1.1中的相关组件,并为“openWeb”设置监听事件,以实现在鼠标之后,显示网页界面的功能。

1.2.2.3编写mClick函数

为按钮的监听事件编写 mClick 类,以实现拍照和退出功能。如图1.4所示:
在这里插入图片描述

图1.4 mClick类

上图中,构造了一个继承于OnClickListener的mClick类。首先定义一个url字符串,在点击按钮之后,通过getText().toString()方法获取文本框中的字符串,将其赋值给url。通过findViewById方法将webView对象与布局文件中的组件相关联,通过loadUrl方法设置网页的网址,从而显示网页。

1.2.3添加权限

由于程序中需要实现访问网页的功能,所以需要在程序中添加允许程序网络访问的权限。如下图1.5所示,在 AndroidManifest.xml 文件中添加下列信息,以申请权限。
在这里插入图片描述

图1.5添加权限

1.2.4模拟器测试

编写程序结束之后,打开AndroidStudio中的安卓模拟器进行测试,但程序不能正常运行。

1.3实验过程中存在的问题及解决方案

1.3.1调用系统自带的浏览器

在之前的程序中,使用了webView.loadUrl () 方法去加载网页的内容。在安卓7.1.1系统的手机模拟器下,运行结果如图1.6所示:
在这里插入图片描述

图1.6 调用系统浏览器

在这里插入图片描述

图1.7 原始效果

在输入URL之后,点击“打开网页”按钮,会出现上图1.6所示的系统提示。该提示意思是,询问用户是以WebView Browser浏览器打开,还是以Chrome浏览器打开网页。
点击选择WebView Browser浏览器打开网页,则会显示如图1.7所示的效果。这样的话,系统会在程序之外调用WebView Browser浏览器来显示网页。
也就是说,用这种方式打开的网页资源是依赖于系统自带的浏览器,而不是WebView组件,若想用自身WebView组件去实现,需要去调用setWebViewClient()方法来实现。现在将代码修改,添加上setWebViewClient()方法。代码如下图1.8所示:
在这里插入图片描述

图1.8 添加setWebViewClient()方法

在setWebViewClient()方法中重载shouldOverrideUrlLoading,返回值若为true将用webview,false则是系统自身浏览器,现在将返回值改为true,使程序调用webview。
重新运行安卓模拟器,显示的效果如下图1.9所示:
在这里插入图片描述

图1.9 改进webview效果

现在可以看到,在手机界面的上半部分,是EditText和button组件,下半部分是webview组件。程序没有调用系统自带的浏览器,而是使用了内嵌的webview来浏览网页的。

1.3.2 返回键问题

现在已经可以实现不依靠系统自带浏览器打开我们的url资源,但这里会出现一个问题,只要我们一点手机上的返回键,整个程序就直接退出了。我们想要的应该是和浏览器一样的效果,即按下返回键应该还是向后退一步,回到之前浏览的网页,而不是直接退出程序。
解决方法,是去监听物理返回键并做出对应的相应事件,即重载onKeyDown函数,如图1.10所示:
在这里插入图片描述

图1.10 重载onKeyDown方法

上图中,重载了onKeyDown方法,具体含义为:keyCode代表按键的数字标识符,通过该标识符判断是否为返回键。如果是返回键,则判断目前的网页是否可以返回,如果可以返回,则调用goBack方法,返回网页前一页面;如果不能返回,则退出该程序。

1.3.3动态权限问题

Android 6.0 之前我们申请权限直接在配置文件中配置一下即可,但是6.0之后,谷歌官方将权限分为普通权限和危险权限。对于危险权限来说,我们就需要进行动态设置。经过查阅资料,发现访问互联网的权限并不属于危险权限,所以暂时不考虑动态申请了。

1.4实验结果

现在已经实现了实验所要求的功能,在Android7.1.1版本的模拟器中运行,并将运行过程录制的视频记录如下:http://47.95.13.239/Study/Android/show/ex6_1.mp4

二、调用本地HTML网页文件的JavaScript

2.1实验原理

WebSettings类用于对WebView对象的属性进行设置;WebViewClient类用于对WebView对象中各种事件的处理,通过重写该方法,可以对WebView对象在页面载入、资源载入、页面访问错误等情况发生时进行各种操作;WebChromeClient是辅助WebView处理JavaScript对话框、网页的标题、网页的图标、加载进度条等操作的类。用户可以在Android程序中调用本地的HTML网页文件的JavaScript。

2.2实验过程记录

2.2.1布局文件

本次布局使用相对布局,需要添加的组件仅仅是一个WebView组件,如图2.1所示。
在这里插入图片描述

图2.1 布局文件

2.2.2控制文件

2.2.2.1实例化对象

在MainActivity.java文件中修改文件,以实现对程序的控制。首先如下图1.2所示实例化所需的对象。
在这里插入图片描述

图2.2实例化对象

在上图2.2中,分别实例化了用于网页浏览的“WebView”类的“webView”对象、用于线程间通信的“Handler”类的“handler”对象、用于处理JavaScript的alert对话框的“MWebChromeClient”类的“WebChromeClient”对象。

2.2.2.2重载构造函数

对构造函数进行修改,使其实现“关联布局文件和控制文件,设置WebView属性、加载本地HTML资源”的功能。如图2.3所示。
在这里插入图片描述

图2.3重载构造函数

通过findViewById方法关联图2.1中的相关组件,通过WebSettings类设置WebView的相关属性。属性设置的相关含义见表2.1。
表2.1WebSettings类的方法
方法 功能
setAllowFileAccess(false) 设置在WebView内部是否允许访问文件,默认允许访问。
setJavaScriptEnabled(true) 设置支持JavaScript脚本
setBuiltInZoomControls(true) 设置支持缩放
setDefaultFontSize(24) 设置默认字体大小
之后通过addJavascriptInterface方法,设置JavaScript接口,实现Android与JavaScript的通信。通过loadUrl方法设置需要打开的HTML文件。

2.2.2.3编写MObiect类

下面编写MObiect类,用来调用JavaScript,代码如下图2.4所示:
在这里插入图片描述

图2.4编写MObiect类

这里让我奇怪的是,android_show()这个函数没有被调用过,估计程序会出现问题。

2.2.2.4编写MWebChromeClient类

编写MWebChromeClient类,用以在Android中响应JavaScript的调用,如图2.5所示:
在这里插入图片描述

图2.5 编写MWebChromeClient类

2.2.2.5编写本地HTML资源文件

为了实现该程序“调用本地HTML网页文件中的JavaScript”的功能,需要新建一个HTML文件,如图2.15所示,创建asset文件夹。之后在asset文件夹下,创建test.html网页文件。并在文件中写入相关代码,如图2.16所示。
在这里插入图片描述

图2.16 HTML文件

在这里插入图片描述

图2.15创建asset文件夹

2.2.3模拟器测试

编写代码完成,启动安卓模拟器进行测试。程序运行不正常,只显示了空白的页面,如图2.6所示:
在这里插入图片描述

图2.6空白界面

2.3实验过程中存在的问题及解决方案

首次启动模拟器运行时,正如上图2.6所示,程序不能正常运行,显示界面为空白。仔细阅读代码可以看到,在onCreate方法中,我已经使用了loadUrl方法来设置本地的HTML资源路径,如图2.7红框中的部分所示。为了确定是不是本地的HTML文本资源有问题,我修改了HTML文件,添加了如图2.8红框中的部分。
在这里插入图片描述

图2.7控制文件

在这里插入图片描述

图2.8 添加测试语句

在HTML文件中添加该语句之后,如果程序能够正常访问本地HTML资源,应该会在手机界面显示hello段落,如果不能显示“hello”的话,说明本地HTML资源有问题。
下面运行模拟器进行测试,测试结果如下图2.9所示:
在这里插入图片描述

图2.9测试

可以看到界面中显示了hello,说明程序确实已经访问到了本地的HTML文件。出现刚才的不显示现象,是因为Script脚本这方面出现了某些问题。
之后我注意到了之前看到的android_show()这个函数,如图2.10红框所示。它在整个程序中是没有被调用过的,我怀疑是这里出现了问题,于是阅读这段代码。
在这里插入图片描述

图2.10未调用的函数

可以看到这段代码中,应该是用handler在Android和js之间传递了信息,通过loadUrl()方法,调用JavaScript中的show_alert()函数,以实现某些功能。但是再回到test.html文件中,发现根本就没有写show_alert () 函数,只写了一个名为addAll的function。我以为是程序错了,就将test.html文件中的addAll函数改名成了show_alert () 。运行程序发现,依然不显示文字。
最后通过与同学的交流,发现问题出现在test.html文件中,如图2.11红框中所示。
在这里插入图片描述

图2.11 错误定位

问题出现在红色箭头所指的位置。通过查阅资料发现,document.write是JavaScript中对document.open所开启的文档流操作的API方法,它能够直接在文档流中写入字符串,在本程序中,使用了带格式的文本字符串,也就可以将HTML格式的文本写入到文档流中。之前的错误在于,没有将作为字符串输入文档流,破坏了HTML文本标记语言的格式,导致文本不能正常显示。
现在运行程序,可以看到如下图2.12所示的界面。
在这里插入图片描述

图2.12 显示界面

虽然现在看来程序算是运行正常,但是回到编译器中看看代码,发现刚才的android_show()依然未被调用过。我怀疑android_show()函数并不没有起到调用JavaScript的功能,于是将该函数中的内容删除,只保留函数名,改成如下图2.13所示的内容。
在这里插入图片描述

图2.13 修改文件

重新在模拟器运行程序,结果程序正常显示,效果与图2.12一致。这说明android_show()函数确实没有起作用。经过查阅资料得知,该程序在JS调用Android的过程中,采用的是addJavascriptInterface()方法,通过该方法将 Java 对象和 JS 对象进行映射。程序中定义的MObject类,也就是一个JavaScript接口函数,其中最重要的就是写上“@JavascriptInterface”来表明它是一个JavaScript接口,同时加上这个也是因为安全问题。其他内容在本次程序中暂时没有用到。程序中如图2.14所示的代码,将MObjcet类的对象映射到js的test对象,再通过document.write方法将文本流输出到手机界面。
在这里插入图片描述

图2.14 对象映射

2.4实验结果

现在已经实现了实验所要求的功能,在Android7.1.1版本的模拟器中运行,运行过程录制的视频如下:http://47.95.13.239/Study/Android/show/ex6_2.mp4

三、用Android程序操纵JavaScript对话框

3.1实验原理

通过addJavascriptInterface()方法实现Java 对象和 JS 对象进行映射,在script脚本标签中定义show_alert()函数,通过alert()方法描述警告框,并通过onJsAlert()方法进行显示处理。

3.2实验过程记录

由于例题6_3与例题6_2大段代码相似,其中“布局文件”和“控制文件”的代码与例题6_3相同,下面不再重复记录这两部分。

3.2.1编写HTML文件

下面对HTML文件进行编写,如图3.1所示:
在这里插入图片描述

图3.1HTML文件

上图3.1中,同样是编写了一个

3.2.2模拟器测试

将编写完成的程序在安卓模拟器上运行,查看运行结果,如图3.2所示。
在这里插入图片描述

图3.2运行截图

在界面的顶部输入测试字符串“zhanghoujin”,点击顶部右侧的“call Android”按钮进行提交,此时会在页面的底部出现一个Toast提示框,输出“Hello zhanghoujin”字样。

3.3实验过程中存在的问题及解决方案

因为有了例题6_2的经验,在此次例题6_3中未出现明显的问题。

3.4实验结果

现在已经实现了实验所要求的功能,在Android7.1.1版本的模拟器中运行,运行过程录制的视频如下:http://47.95.13.239/Study/Android/show/ex6_3.mp4

四、源代码

我已经将整理过的该项目的源代码上传到了GitHub:
https://github.com/ZHJ0125/AndroidLeaning

4.1 ex6_1

4.1.1 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:layout_gravity="center_horizontal"
    android:gravity="center">
    <LinearLayout
        android:id="@+id/LinearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <EditText
            android:layout_width="270dp"
            android:layout_height="wrap_content"
            android:id="@+id/editText1"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/button1"
            android:width="1dp"
            android:text="打开网页"/>
    </LinearLayout>
    <WebView
        android:id="@+id/webView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

4.1.2 MainActivity.java

package zhj.com.ex6_1;

import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends Activity {
    WebView webView;
    Button openWebBtn;
    EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        openWebBtn = (Button) findViewById(R.id.button1);
        editText = (EditText)findViewById(R.id.editText1);
        openWebBtn.setOnClickListener(new mClick());
    }

    class mClick implements OnClickListener{
        @Override
        public void onClick(View v) {
            String url = editText.getText().toString();
            webView = (WebView)findViewById(R.id.webView1);
            webView.setWebViewClient(new WebViewClient() {
                /**
                 * 重写shouldOverrideUrlLoading,返回值若为true将用webview,false则是系统自身浏览器
                 */
                @Override
                public boolean shouldOverrideUrlLoading(WebView view, String url) {
                    view.loadUrl(url);
                    return true;
                }
            });
            webView.loadUrl("http://" + url);
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (webView.canGoBack()) {
                webView.goBack();
                return true;
            } else {
                System.exit(0);
            }
        }
        return super.onKeyDown(keyCode, event);
    }
}

4.1.3 AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="zhj.com.ex6_1">
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>

4.2 ex6_2

4.2.1 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:id="@+id/hello"/>
    <WebView
        android:id="@+id/webView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>


### 4.2.2 MainActivity.java
```java
package zhj.com.ex6_2;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.JavascriptInterface;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
    WebView webView;
    Handler handler = new Handler();
    TextView textView;
    MWebChromeClient mWebChromeClient;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        webView = (WebView)findViewById(R.id.webView1);
        textView = (TextView) findViewById(R.id.hello);
        WebSettings webSettings = webView.getSettings();
        webSettings.setAllowFileAccess(true);
        webSettings.setJavaScriptEnabled(true);
        webSettings.setBuiltInZoomControls(true);
        webSettings.setDefaultFontSize(24);
        MObject mObject = new MObject();
        webView.addJavascriptInterface(mObject, "test");
        mWebChromeClient = new MWebChromeClient();
        webView.setWebChromeClient(mWebChromeClient);
        webView.loadUrl("file:///android_asset/test.html");
    }
    class MObject extends Object{
        @JavascriptInterface
        public void android_show(){
        }
    }
    class MWebChromeClient extends WebChromeClient{
        @Override
        public boolean onJsAlert (WebView view, String url,String message, JsResult result){
            Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
            return true;
        }
    }
}

4.2.3 test.html

<!DOCTYPE html>
<html>
    <head>
        <title>一个简单的JavaScript例程</title>
    </head>
    <body>
        <script language="javascript" type="text/javascript">
            function addAll(a,b,c){
                return a+b+c;
            }
            var total = addAll(30,40,50);
            var str = "Ran 5 hours,<br> finally finished the ";
            document.write("<html><B> "+ str + total + " km! </B></html>");
        </script>
    </body>
</html>

4.3 ex6_3

4.3.1 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <WebView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="Hello World!"
        android:id="@+id/webView1"/>
</LinearLayout>

4.3.2 MainActivity.java

package zhj.com.ex6_3;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.webkit.JavascriptInterface;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Toast;

public class MainActivity extends Activity {
    WebView webView;
    Handler handler = new Handler();
    MWebChromeClient mWebChromeClient;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        webView = (WebView)findViewById(R.id.webView1);
        WebSettings webSettings = webView.getSettings();
        webSettings.setAllowFileAccess(true);
        webSettings.setJavaScriptEnabled(true);
        webSettings.setBuiltInZoomControls(true);
        webSettings.setDefaultFontSize(24);
        MObject mObject = new MObject();
        webView.addJavascriptInterface(mObject, "test");
        mWebChromeClient = new MWebChromeClient();
        webView.setWebChromeClient(mWebChromeClient);
        webView.loadUrl("file:///android_asset/test.html");
    }
    class MObject extends Object{
        @JavascriptInterface
        public void android_show(){
            handler.post(new Runnable() {
                @Override
                public void run() {
                    System.out.println("提示:使用了多线程的run()方法!!");
                    webView.loadUrl("javascript: show_alert()");
                }
            });
        }
    }
    class MWebChromeClient extends WebChromeClient{
        @Override
        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
            Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
            return true;
        }
    }
}

4.3.3 test.html

<html>
<head>
    <title>JavaScript与Android交互</title>
</head>
<script type="text/javascript">
    function show_alert(){
        var a = document.getElementById("text").value;
        alert("Hello " + a);
        }
</script>
<body>
<form action="">
    <input type="text" id="text" value=""/>
    <input type="button" id="button" onclick="window.test.android_show()" value="call Android"/>
</form>
</body>
</html>

五、实验总结

这次安卓实验不太顺利,在例题6_2上花了一些时间,但最后问题都解决了。通过这次实验我意识到,只有把每个细节都搞明白,才能更好地避免错误的发生。虽然这次实验中出现了很多问题,但通过解决问题,我也收获了很多编程方面的知识。
在Android编程方面,主要掌握了以下知识:

  1. WebView对象的基本功能
  2. 调用本地HTML网页文件的JavaScript脚本
  3. 文档流写入的基本操作
  4. 使用Android操纵JavaScript对话框

六、部分参考资料

  1. Android与JS交互的细节问题 https://www.jianshu.com/p/01e5d4fa1c79
  2. Android:这是一份全面 & 详细的Webview使用攻略
    https://www.jianshu.com/p/3c94ae673e2a
  3. 最全面总结 Android WebView与 JS 的交互方式
    https://www.jianshu.com/p/345f4d8a5cfa
  4. 你不知道的 Android WebView 使用漏洞 https://www.jianshu.com/p/3a345d27cd42
  5. Android调用js的坑 https://www.jianshu.com/p/01cb2f52c0b0
  6. 前端WebView指南之Android交互https://75team.com/post/android-webview-and-js.html
  7. 安卓开发笔记——WebView组件 https://www.cnblogs.com/lichenwei/p/3959345.html
  8. document.write的用处! https://blog.csdn.net/qq_34986769/article/details/52160532
  9. 124、@JavascriptInterface https://www.cnblogs.com/androidsj/p/6414891.html
发布了44 篇原创文章 · 获赞 25 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ZHJ123CSDN/article/details/90732522