在WebView中构建Web应用程序(二)

如果要将Web应用程序(或仅仅是网页)作为客户端应用程序的一部分提供,则可以使用WebView执行此操作。 WebView类是AndroidView类的扩展,允许您将Web页面显示为活动布局的一部分。它不包括完全开发的Web浏览器的任何功能,例如导航控件或地址栏。默认情况下,WebView所做的就是显示一个网页。

本文档向您展示了如何开始使用WebView以及如何执行其他操作,例如处理页面导航以及将网页中的JavaScript绑定到Android应用程序中的客户端代码。

一、将WebView添加到您的应用程序

要将WebView添加到应用程序,可以在活动布局中包含<WebView>元素,也可以将整个“活动”窗口设置为onCreate()中的WebView

在活动布局中添加WebView

将以下代码添加到活动的布局XML文件中:

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>

要在WebView中加载网页,请使用loadUrl()。 例如:

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");
在onCreate()中添加WebView

在活动的onCreate()方法中向您的应用添加WebView

WebView myWebView = new WebView(activityContext);
setContentView(myWebView);

然后加载页面:

myWebView.loadUrl("https://www.example.com");

或者从HTML字符串加载URL

// Create an unencoded HTML string
// then convert the unencoded HTML string into bytes, encode
// it with Base64, and load the data.
String unencodedHtml =
     "&lt;html&gt;&lt;body&gt;'%23' is the percent code for ‘#‘ &lt;/body&gt;&lt;/html&gt;";
String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(),
        Base64.NO_PADDING);
myWebView.loadData(encodedHtml, "text/html", "base64");

注意:此HTML可以执行的操作有一些限制。 有关编码选项的详细信息,请参阅loadData()loadDataWithBaseURL()

权限

在此之前,您的应用必须能够访问Internet。 请在清单文件中请求INTERNET权限:

<manifest ... >
    <uses-permission android:name="android.permission.INTERNET" />
    ...
</manifest>

这就是显示网页的基本WebView所需的全部内容。 此外,您可以通过修改以下内容来自定义WebView

  • 使用WebChromeClient启用全屏支持。 当WebView需要更改主机应用程序UI的权限时,也会调用此类,例如创建或关闭窗口以及向用户发送JavaScript对话框。
  • 处理影响内容呈现的事件,例如表单提交上的错误或使用WebViewClient导航。 您还可以使用此子类来拦截URL加载。
  • 通过修改WebSettings启用JavaScript
  • 使用JavaScript访问已注入WebViewAndroid框架对象。

二、在WebView中使用JavaScript

如果您计划在WebView中加载的网页使用JavaScript,则必须为WebView启用JavaScript。 启用JavaScript后,您还可以在应用代码和JavaScript代码之间创建接口。

启用JavaScript

默认情况下,在WebView中禁用JavaScript

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

WebSettings提供对您可能会发现有用的各种其他设置的访问。 例如,如果您正在开发专门为Android应用程序中的WebView设计的Web应用程序,则可以使用setUserAgentString()定义自定义用户代理字符串,然后在网页中查询自定义用户代理以验证,请求您的网页的客户实际上是您的Android应用。

将JavaScript代码绑定到Android代码

在开发专为Web应用程序中的WebView设计的Web应用程序时,您可以在JavaScript代码和客户端Android代码之间创建接口。 例如,您的JavaScript代码可以调用Android代码中的方法来显示Dialog,而不是使用JavaScriptalert()函数。

要绑定JavaScriptAndroid代码之间的新接口,请调用addJavascriptInterface(),向其传递一个类实例以绑定到您的JavaScript以及您可以调用以访问该类的接口名称。
例如,您可以在Android应用中包含以下类:

public class WebAppInterface {
    Context mContext;

    /** Instantiate the interface and set the context */
    WebAppInterface(Context c) {
        mContext = c;
    }

    /** Show a toast from the web page */
    @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }
}

警告:如果已将targetSdkVersion设置为17或更高,则必须将@JavascriptInterface注释添加到您希望JavaScript可用的任何方法(该方法也必须是公共的)。 如果您未提供注释,则在Android 4.2或更高版本上运行时,您的网页无法访问该方法。
在此示例中,WebAppInterface类允许网页使用showToast()方法创建Toast消息。
您可以使用addJavascriptInterface()将此类绑定到在WebView中运行的JavaScript,并将接口命名为Android。 例如:

WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");

这将在WebView中创建一个名为Android for JavaScript的界面。 此时,您的Web应用程序可以访问WebAppInterface类。 例如,以下是一些HTMLJavaScript,当用户单击按钮时,使用新界面创建Toast消息:

<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />

<script type="text/javascript">
    function showAndroidToast(toast) {
        Android.showToast(toast);
    }
</script>

无需从JavaScript初始化Android界面。 WebView会自动将其提供给您的网页。 因此,只需单击按钮,showAndroidToast()函数就会使用Android界面调用WebAppInterface.showToast()方法。

注意:绑定到JavaScript的对象在另一个线程中运行,而不是在构造它的线程中运行。

警告:使用addJavascriptInterface()可以让JavaScript控制您的Android应用。 这可能是一个非常有用的功能或危险的安全问题。 当WebView中的HTML不可信(例如,部分或全部HTML由未知的人或进程提供)时,攻击者可以包含执行客户端代码的HTML,也可能包括攻击者选择的任何代码。 因此,除非您编写了WebView中显示的所有HTMLJavaScript,否则不应使用addJavascriptInterface()。 您也不应允许用户在WebView中导航到不属于您自己的其他网页(而是允许用户的默认浏览器应用程序打开外部链接 - 默认情况下,用户的Web浏览器会打开所有URL链接,因此 只有在按照以下部分所述处理页面导航时才要小心)。

三、处理页面导航

当用户从WebView中的网页单击链接时,Android的默认行为是启动处理URL的应用程序。 通常,默认Web浏览器会打开并加载目标URL。 但是,您可以为WebView覆盖此行为,以便在WebView中打开链接。 然后,您可以允许用户在WebView维护的网页历史记录中前后导航。

注意:出于安全考虑,系统的浏览器应用程序不会与您的应用共享其应用程序数据。

要打开用户单击的链接,请使用setWebViewClient()WebView提供WebViewClient。 例如:

WebView myWebView =  (WebView) findViewById(R.id.webview); 
myWebView.setWebViewClient(MyWebViewClient);

现在,用户在WebView中单击加载的所有链接。

如果您想要更好地控制单击链接的加载位置,请创建自己的WebViewClient来覆盖shouldOverrideUrlLoading()方法。 例如:

private  class  MyWebViewClient  extends  WebViewClient  {  
    @Override  
    public  boolean shouldOverrideUrlLoading( WebView view,  String url)  {  
        if  (Uri.parse(url).getHost().equals("https://www.example.com"))  {  
        // This is my website, so do not override; let my WebView load the page  
        return  false;
    } 
    // Otherwise, the link is not for a page on my site, so launch another Activity         that handles URLs 
    Intent intent =  new  Intent(Intent.ACTION_VIEW,  Uri.parse(url));   
    startActivity(intent); 
    return  true;  
    } 
}

然后为WebView创建这个新WebViewClient的实例:

WebView myWebView =  (WebView) findViewById(R.id.webview); 
myWebView.setWebViewClient(new  MyWebViewClient());

现在,当用户单击链接时,系统调用shouldOverrideUrlLoading(),它会检查URL主机是否与特定域匹配(如上所述)。 如果它匹配,则该方法返回false以便不覆盖URL加载(它允许WebView像往常一样加载URL)。 如果URL主机不匹配,则会创建一个Intent以启动处理URL的默认活动(解析为用户的默认Web浏览器)。

浏览网页历史记录

WebView覆盖URL加载时,它会自动累积访问过的网页的历史记录。 您可以使用goBack()goForward()在历史记录中前后导航。

例如,以下是您的Activity如何使用设备后退按钮向后导航:

@Override  
public  boolean  onKeyDown(int keyCode,  KeyEvent event)  {  
    // Check if the key event was the Back button and if there's history  
    if  ((keyCode ==  KeyEvent.KEYCODE_BACK)  && myWebView.canGoBack())  { 
        myWebView.goBack(); 
        return  true;  
    }  
    // If it wasn't the Back key or there's no web page history, bubble up to the default  
    // system behavior (probably exit the activity)  
    return  super.onKeyDown(keyCode, event);  
    }  
}

如果实际存在用户要访问的网页历史记录,则canGoBack()方法返回true。 同样,您可以使用canGoForward()来检查是否存在转发历史记录。 如果您不执行此检查,那么一旦用户到达历史记录的末尾,goBack()goForward()就不会执行任何操作。

处理设备配置更改

在运行时期间,当设备的配置发生更改(例如,用户旋转设备或关闭输入法编辑器(IME)时)会发生活动状态更改。 这些更改将导致销毁WebView对象的活动并创建新活动,这也会创建一个新的WebView对象,该对象将加载销毁对象的URL。 要修改活动的默认行为,您可以更改其在清单中处理方向更改的方式。

四、管理窗口

默认情况下,将忽略打开新窗口的请求。 无论是通过JavaScript还是通过链接中的target属性打开它都是如此。 您可以自定义WebChromeClient以提供打开多个窗口的行为。

警告:为了使您的应用更安全,最好防止弹出窗口和新窗口打开。 实现此行为的最安全方法是将“true”传递给setSupportMultipleWindows(),但不覆盖setSupportMultipleWindows()依赖的onCreateWindow()方法。 但请记住,此逻辑还会阻止任何在其链接中使用target =“_ blank”的页面加载。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容

  • assets 文件夹下的html访问方式:"file:///android_asset/index.html" 如...
    李建彪阅读 1,726评论 0 7
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,061评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,651评论 18 139
  • (各位看官喜欢俺文字可以转发,但是不要打赏,俺收不到) 假期逃离婆婆家亲戚轮番喝酒聚餐,早早返京,就三个闲人,家里...
    林有钱阅读 1,224评论 9 3
  • 今天的状态,有些飘 【一】无所适从 有点莫名其妙的茫然。 画了两张黑砖。 【二】挑战徒手画圆
    冉听花开阅读 119评论 2 2