WebView Apps

Web Apps

实现一个Android应用的两个基本方式是:客户端应用和Web应用。前者使用Android SDK开发并且以一个APK方式安装在用户的设备上,后者使用标准的Web开发,不需要在用户设备上安装,通过一个Web浏览器获取。

如果我们为Android设备提供一个基于web的app,我们可以确定的是主流的Android浏览器(WebView框架)允许我们指定视窗和样式属性,确保我们的网页在所有屏幕配置上都能够呈现出合适的尺寸和大小。

我们不应该开发一个app仅仅用来展示我们的网站。相反的,网页应该嵌入在我们的app中。我们甚至可以在Android应用和网页之间定义一个接口,以此允许我们网页中的JavaScript可以调用Android应用的APIs。

使Web Apps支持不同的屏幕

因为各种Android设备有不同的屏幕大小及像素密度,我们在web设计的时候应该考虑这些因素,以便我们的网页总能以合适的大小展现出来。

当网页的目标设备是Android设备的时候,有两个方面的因素需要我们考虑:

Viewport:Viewpost是一个矩形区域,它为我们的网页提供一个可绘制区域。我们可以指定不同的Viewpost属性,例如大小和初始缩放百分比。最重要的是Viewport的宽度,它定义了这个网页视图的横向可用像素数(可用CSS像素的数量)。

屏幕密度:Web类和Android大部分的网页浏览器将CSS像素值转换为独立设备像素值。因此一个网页在中等密度的屏幕上(160dpi)呈现出相同的可感知尺寸。如果图形是我们网页设计重要元素,我们应该更加考虑出现在不同密度屏幕上的缩,因为一个300像素宽的图片在一个320dpi的屏幕上将被按比例放大(每CSS像素使用更多的物理像素),这将会导致伪影(模糊像素)。

使用WebView构建Web Apps

如果我们想实现一个web应用程序(或只是一个网页)作为一个客户端应用程序的一部分,我们可以使用WebView。WebView类是Android中View类的扩展,它允许我们将网页作为我们布局的一部分进行展示。它不包含非常完善的web浏览器所具备的任何特性,比如导航控件或一个地址栏。WebView默认能做的是显示一个网页。

一个常见的场景,比如我们想要在应用程序中展示一些信息,这些需要更新,如一个终端用户协议或用户指南,此时使用WebView是有益的。我们可以创建一个仅含有一个WebView的Activity来展示在线托管的文档。

另一个场景,如果我们的应用提供给用户的数据总是需要一个网络连接来检索数据,比如邮件,此时使用WebView是有用的。我们将会发现在应用中使用WebView来显示用户的所有数据比执行一个网络请求、解析数据、布局展示更加简单方便。我们可以为Android设备设计一个网页,然后在Android应用中实现一个WebView中加载这个网页。

向应用中添加一个WebView

1.布局中添加WebView

2.代码中找到WebView,并以url为参数调用WebView对象的loadUrl()方法

3.添加网络权限

在WebView中使用JavaScript

如果要加载的网页使用到的JavaScript,我们必须使WebView启用JavaScript。一旦JavaScript被启用,我们可以在我们的应用代码和JavaScript代码之间创建接口。

启用JavaScript

WebView默认禁用JavaScript。我们可以通过WebView的WebSettings来启用它。我们可以通过getSettings()方法来获取WebSettings,然后通过setJavaScriptEnabled()方法来启用它。

例如:

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

WebSettings提供了大量非常有用的其他设置。例如,如果我们开发一个Web应用程序,它被设计为仅用WebView,我们可以通过setUserAgentString()方法定义一个用户代理字符串,然后在网页中查询这个用户代理字符串,来验证用户请求网页实际是请求我们的Android应用。

JavaScript代码绑定到Android代码

我们可以在JavaScript代码和客户端的Android代码之间创建一个接口,例如我们的JavaScript代码可以调用我们的Android代码来显示一个Dialog,而不是使用JavaScript的alert()方法。

在二者之间绑定接口需要调用addJavascriptInterface()方法,传递给这个方法一个类的实例来绑定到JavaScript,再传递一个接口名,让我们的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或更高,必须在JavaScript可以调用的方法上添加@JavascriptInterface注解,该方法也必须使公有的。若不添加,Android 4.2或更高设备上该方法将无法执行。

WebAppInterface类允许网页使用showToast()方法来创建一个吐司信息。我们可以如下将该类绑定至JavaScript。

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

这为JavaScript创建了一个名为Android的接口,此时,我们的web应用可以获取WebAppInterface类了。例如,下面的HTML和JavaScript当用户点击按钮的时候使用这个新接口创建了一个吐司信息。

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

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

在JavaScript中不需要初始化这个接口,WebView自动使它可用于您的web页面。

提示:绑定在JavaScript的对象运行在其他线程,而不再它被构造的线程。

注意:使用addJavascriptInterface()方法允许JavaScript控制我们的Android应用,它非常有用但也产生了严重的安全问题。例如在WebView中的HTML是不可靠的(例如HTML的一部分或所有由未知的人或进程提供),然后攻击者可以执行你的包括HTML客户端代码,并且可能选择攻击任意代码。就此,我们不应该使用addJavascriptInterface()方法,除非我们写的所有HTML和JavaScript出现在我们的WebView中。我们也不应该允许用户在我们的WebView中导航至那些不属于我们的网页中(相反,默认允许用户的默认浏览器应用打开外部链接,用户的网页浏览器打开所有的URL链接)。

处理页面导航

当用户在我们的WebView中点击一个链接的时候,默认处理方式是Android运行一个应用来处理所有的URLs。通常,是默认网页浏览器来打开和加载这个目标URL。然而,我们在WebView中可以复写这个行为,使得链接在我们的WebView中打开。然后我们允许用户在WebView中通过网页历史来前后导航至某一网页。

为了打开用户点击的链接,仅需调用setWebViewClient()方法为WebView提供一个WebViewClient,如下:

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

就是这样,然后我们的WebView就可以加载所有用户点的链接了。

如果我们想要更多地控制被点击的链接,需要通过继承自定义WebViewClient并重写shouldOverrideUrlLoading()方法,如下:

private class MyWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (Uri.parse(url).getHost().equals("www.example.com")) {
            // This is my web site, 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(通常允许WebView加载这个网页)。如果不匹配,创建一个Intent来启动默认Activity来处理URL。

浏览网页的历史

让我们的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);
}

Android 4.4的WebView(需要注意的地方)

Android4.4引进了全新的基于Chrominum的WebView。这个变化升级了WebView的性能。所有的在Android4.4或更高版本的Android设备使用WebView的Apps将继承这些优化。

调试Web Apps

开发Web Apps的一些建议

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

推荐阅读更多精彩内容