WebView-HyBird开发的桥梁

转载 2018年04月16日 20:53:35

前言

  通过了解HyBrid技术,我才深刻的体会到了WebView这个View的强大。
  在网上,很少有关于HyBird开发的技术性博文。我找了好久都没有找到关于这部分的技术博文。前阵子在学习了ヾ(o◕∀◕)ノヾ叶小钗博主的博文后,我才了解并学习了HyBird开发技术。各位看官可以看看博主的博文,不过博文是讲Web部分的,并没有Android部分的。


  HyBrid技术指的是混合开发技术,融合了Html&CSS&JS技术和Android技术(主要容器是WebView)。HyBird App底层依赖于Native提供的容器(WebView),上层建筑的搭建采用Html&CSS&JS技术。换而言之,就是HyBrid开发中,Android Native部分只要专注于开发提供JS调用Native的接口以及一些通讯设计、并发设计、异常处理、日志监控、安全模块等等的功能,而H5部分则做业务开发,底层透明化、上层多多样化这些部分部分。ヽ(°◇° )ノ干正事~
  这里,我们来一起学习这个View,从学习中找到HyBrid开发的方法。

继承关系

  • java.lang.Object
    • android.view.View
      • android.view.View
        • android.widhet.AbsoluteLayout
          • android.webkit.WebView

介绍

  WebView,简而言之,就是一个用于浏览网页的UI控件。

官方介绍:

A View that displays web pages. This class is the basis upon which you can roll your own web browser or simply display some online content within your Activity. It uses the WebKit rendering engine to display web pages and includes methods to navigate forward and backward through a history, zoom in and out, perform text searches and more.

  大致就是说,这是一个基于WebKit渲染引擎的具有缩放显示,网页浏览历史,前进后退等等功能的用于呈现网页信息的View。

  WebView中还提供一些强大的接口给我们,使我们可以定制我们需要的一些功能。

权限设置

使用WebView需要用到Android的网络权限,因此,需要在Manifest.xml中增加

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

基本用法

WebView

我们先定义一个Uri。

private String TestUri = "http://www.baidu.com";
  • 1

说明:以下都是加载在线的资源的。

一、基本用法一

/**
* 直接调用Android手机内的默认浏览器来打开网页
*/
private void Usage1() {
    Uri uri = Uri.parse(TestUri);
    Intent intent = new Intent(Intent.ACTION_VIEW, uri);
    startActivity(intent);
}

二、基本用法二

/**
 * 我们换种方式,使用webView,还是打开了系统默认的浏览器~
 */
private void Usage2() {
    WebView webView = new WebView(this);
    // 加载网页
    webView.loadUrl(TestUri);
    this.setContentView(webView);
}

但是这两中方法调用的都是系统默认的浏览器,原因是我们没有调用setWebViewClient()方法,设置WebView视图。

三、设置WebView视图

/**
 * 使用webView的setWebViewClient()
 */
private void Usage3() {
    WebView webView = new WebView(this);
    // 加载网页
    webView.loadUrl(TestUri);
    webView.setWebViewClient(new WebViewClient());
    this.setContentView(webView);
}

WEBVIEW

四、重写返回事件

但是,在这里我们也会发现一个问题,一旦我们按了返回键的时候,就直接退出了该Activity了,而不是回退到历史记录的前一个页面。这里,我们需要重写一个方法:在AppCompatActivity中就直接重写onBackPressed()方法,在低版本中就重写onKeyDown()方法。

说明:

  • webview.canGoBack();
    • 英文版:Gets whether this WebView has a back history item。
    • 假如WebView中有浏览历史且当前页面的前一个页面存在的话就返回当前页面的前一个历史页面。

1.在AppCompatActivity中重写onBackPressed()方法

@Override
public void onBackPressed() {
    // 判断webView中是否还有返回页面,如果有就返回webview历史缓存的页面,没有就直接退出当前Activity
    if (mWebView.canGoBack()) {
        // 返回webview历史缓存的页面
        mWebView.goBack();
    } else {
        // 退出当前Activity
        super.onBackPressed();
    }
}

2.在低版本中重写onKeyDown()方法

@Override 
public boolean onKeyDown(int keyCode, KeyEvent event) {  
    if ((keyCode == KeyEvent.KEYCODE_BACK) && webview.canGoBack()) {  
        webview.goBack(); //goBack()表示返回WebView的浏览历史的上一页面  
        return true;  
    }  
    return false;  
} 

五、加载本地Html

  当我们加载本地HTML资源,是不需要设置setWebViewClient()的。
  Html放置的文件夹就是项目目录下的asset下,则url格式为file:///android_asset/…

/**
* 加载本地的HTML
*/
private void Usage6() {
    WebView webView = new WebView(this);
    // 加载网页
    webView.loadUrl("file:///android_asset/index.html");
    webView.addJavascriptInterface(new JSBridge(), "android");
    this.setContentView(webView);
}

本地的HTML

<!DOCTYPE html>  
<html lang="en">  
<head>  
    <meta charset="UTF-8">  
    <title>Title</title>  
    <h1>这是本地的Html文件</h1>
    <input type="button" value="js调native" onclick="button()">  
</head>  
<body>  
    <script type="text/javascript">  
        function button() {  
          android.toastMessage("阴吹思婷");
      }  
  </script>  
</body>  
</html>  

六、JS调用Native

我们使用下前面的源码,改下,让他支持JS

/**
 * JS交互
 */
private void Usage4() {
    WebView webView = new WebView(this);
    // 支持JS
    webView.getSettings().setJavaScriptEnabled(true);
    // 加载网页
    webView.loadUrl("file:///android_asset/index.html");
    webView.setWebViewClient(new WebViewClient());
    webView.addJavascriptInterface(new JSBridge(), "android");
    this.setContentView(webView);
}

JS调用Native

七、Native调用JS

核心代码:

webView.loadUrl("javascript:toast()");
  • 1

主要代码:

/**
  * Native调用JS
  */
private void Usage7() {
    final WebView webView = new WebView(this);
    // 支持JS
    webView.getSettings().setJavaScriptEnabled(true);
    // 加载网页
    webView.loadUrl("file:///android_asset/index.html");
    webView.addJavascriptInterface(new JSBridge(), "android");
    // 添加一个LinearLayou作为容器,包含一个Button和webview
    LinearLayout l = new LinearLayout(this);
    l.setOrientation(LinearLayout.VERTICAL);
    LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
        LinearLayout.LayoutParams.MATCH_PARENT,
        LinearLayout.LayoutParams.MATCH_PARENT
        );
    l.setLayoutParams(layoutParams);
    Button button = new Button(this);
    button.setText("点击调用JS");
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            webView.loadUrl("javascript:toast()");
        }
    });
    l.addView(button);
    l.addView(webView);

    this.setContentView(l);
}

html文件

<!DOCTYPE html>  
<html lang="en">  
<head>  
    <meta charset="UTF-8">  
    <title>Title</title>  
    <h1>这是本地的Html文件</h1>
    <p id = "p1">66666</p>
    <input type="button" value="js调native" onclick="button()">  
</head>  
<body>  
    <script type="text/javascript">  
      function button() {
        android.toastMessage("阴吹思婷");
      }
      function toast() {
        document.getElementById("p1").innerHTML="Hahahahha~";
      }
  </script>  
</body>  
</html>  

八、常用的Methods


类型 方法 介绍
boolean canGoBack() 判断是否可以后退一个历史浏览页面
boolean canGoBackOrForward(int steps) 判断当前页面是否可以返回或前进steps个页面
boolean canGoForward() 判断是否可以前进一个历史浏览页面
void addJavascriptInterface(Object Object,String name) 将Java方法注入到WebView中,供JS调用。Object参数就是要注入的JAVA对象,String参数则是提供给JS调用的函数名
void removeJavascriptInterface(String name) 移除先前注入的Java对象
int getContentHeight() 返回当前Html内容的高度
WebSettings getSettings() 获取WevView的设置对象WebSettings。获取到对象后我们就可以修改WebView的设置了。
String getUrl() 获取当前页面的URL
String getTitle() 获取当前页面的标题
void goBack() 返回WebView历史记录中的的当前页面的前一页,没有则不返回
void goForward() 返回WebView历史记录中的当前页面的上一页,没有则不返回
void loadUrl(String url) 加载URL
void loadUrl(String url,Map<String,String > additonalHttpHeaders) 加载包含请求头header的URL
boolean performLongClick() 长按
void reload() 重新加载页面
void setWebViewClient(WebViewClient client) 为WebView添加WebViewClient对象
void setWebChromeClient(WebChromeClient client) 为WebView添加WebChromeClient对象
static void setWebContentsDebuggingEnabled(boolean enabled) 允许调试HTML/CSS/JS

九、WebSettings

使用WebView.getSettings()获取到WebSettings对象。

1.适应手机屏幕

webView.getSettings().setUseWideViewPort(true);
webView.getSettings().setLoadWithOverviewMode(true);

2.放大设置

webView.getSettings().setDisplayZoomControls(true);
  • 1

3.无限放大

webView.getSettings().setBuiltInZoomControls(true);  
  • 1

4.比例缩放

webView.setInitialScale(50);

5.允许使用JavaScript

  • JS与Native交互必须将以下设置为true。
webView.getSettings().setJavaScriptEnabled(true)
类型 方法 介绍
abstract boolean setAllowFileAccess(boolean) 允许或者禁止WebView可以访问Assets和resources文件夹。使用file:///android_asset和file:///android_res来访问这两个文件夹。默认为开
abstract void setJavaScriptEnabled(boolean) 允许或者禁止WebView执行JS代码。默认为关。

有大神写的很详细了,让我们看大神的博文吧~

WebViewClient

设置WebView加载事件

  • 以下两个方法可以用来做一些加载时的显示操作的,比如当加载时间过长的时候,我们可以加上个进度条,优化用户体验。
类型 方法 介绍
void onPageStarted(WebView view,String url,Bitmap favicon) 当WebView开始加载的时候
void onPageFinished(WebView view,String url) 当页面加载结束的时候

URL拦截

类型 方法 介绍
boolean shouldOverrideUrlLoading(WebView view,String url) 这个函数会在加载超链接时回调过来,即拦截URL请求。在这个方法里面,用户可以更改URL,也可以为URL做出一些必要的响应,比如做出页面切换效果。
public boolean shouldOverrideUrlLoading(WebView view, String url) {  
    return false;  
}3

  这个函数的作用是给予开发者一个机会可以重置URL在加载的时候的动作,甚至改变URL。
  假如我们要告诉APP我们已经拦截了URL并且已经对URL做出了处理,需要离开当前WebView的时候,就返回true,否则返回false,交由系统用默认的方法处理当前URL。

其他

类型 方法 介绍
void onReceivedError() 错误发生时响应该方法
void onScaleChanged() 当页面的缩放程度发生变化时

扩展应用

  在WebView中有很多可定制化的接口,我们可以在这些接口上扩展出我们需要的功能。
  比如,我们可以通过继承WebChromeClient这个类,重写其中某些方法(进度条、JS……)来满足业务开发。
  又比如,我们可以通过继承WebViewClient这个类,重写其中的shouldOverrideUrlLoading()来拦截当前浏览的网页的响应点击事件的URL,通过拦截该URL,我们可以做很多事情……ヾ(o◕∀◕)ノヾ比如,我们可以在网页跳转的时候加入点动画特效,或者说,我们可以把URL拦截、解析之后做出相应的Actionヽ(°◇° )ノ……
  看到这里,你可能会想,我要是在Android和IOS上搭建好底层,提供一致的,约定好的API接口,那么就是一套H5拯救全世界的节奏了。
  很牛逼,是吧?我也是这么觉得的。在学习了HyBrid的开发思想后,我深觉混合开发有潜在的价值。比如,当你要快速开发市场的时候,一套H5代码就可以搞定所有端的业务逻辑代码,那岂不是节省了很多时间(Time is moneyʅ(´◔౪◔)ʃ)……
  可以查看以下博文了解下Hybrid。

HyBird开发步骤

在此,我们可以推测出Hybrid的开发流程。

  1. 新建一个WebView
  2. 创建WebChromeClient的子类,重写其中的方法,并用webView.setWebViewClient(WebChromeClient)
    • 其实这个也不是很必要。
  3. 创建WebViewClient的子类,重写其中部分方法
    1. onPageStarted():页面加载时
    2. onPageFinished():页面加载结束
    3. shouldOverrideUrlLoading():拦截、处理URL
    4. onReceivedError():收到错误信息的时候,执行一些方法
    5. shouldInterceptRequest():拦截并通知App来自WebView上的页面的资源请求,并允许App返回数据
    6. ……
  4. 修改WebView的WebSettings
    1. setCacheMode(int mode) :设置缓存
      • LOAD_DEFAULT 默认加载方式
      • LOAD_CACHE_ELSE_NETWORK 按网络情况使用缓存
      • LOAD_NO_CACHE 不使用缓存
      • LOAD_CACHE_ONLY 只使用缓存
    2. setDomStorageEnabled(boolean)
      • 是否存储页面的DOM结构,默认是false
    3. setAppCacheEnabled(boolean)
      • 是否允许Cache,默认false。
      • 考虑需要存储缓存,应该为缓存指定存储路径setAppCachePath
    4. setUserAgentString(String)
      • 设置WebView代理,默认使用默认值
    5. setAllowFileAccess(boolean)
      • 是否允许访问WebView内部文件,默认true
    6. setJavaScriptEnabled(boolean)
      • 最主要的是设置这个方法,这个方法可以允许我们将Java Object注入到WebView中,让WebView可以调用Java Object,进而实现H5和Native的交互。
    7. ……
  5. 注入JAVA OBJECT到WebView中。
    • 在HyBird中,JS和Native交互一般都是JS调用Native来实现页面效果。
    • 使用addJavascriptInterface(Object,String)方法注入。
    • 第一个是Java对象,第二个是一个String,这个String是提供给H5调用的
    • 这里有个隐患:假如在Activity中直接将this注入到WebView中的话,那么有一些人可能利用这个漏洞随意调用我们的方法~o_O……
    • 所以,我们一般都是重新写个类,把必要的东西放在这个类里……再通过适当方法传递到其他需要的类中。
  6. 处理一些安全问题:
    • 至于WebView的安全问题,可以去:启舰 - WebView使用详解(一)——Native与JS相互调用(附JadX反编译)这里看看
    • 腾讯修复了WebView的一些安全问题,即TBS-X5SDK
    • X5SDK是通过调用微信/手机QQ/空间的X5内核,解决系统webview兼容性差、加载速度慢、功能缺陷等问题,开发接入便捷,大小只有253K,仅需几行代码,即可解决一切令开发者们头疼的问题,为用户提供最优秀的浏览体验。

实例

下面是一个简单的例子,通过拦截URL,实现在Native容器中页面跳转时动画效果。这也是HyBird开发中必须的一个功能吧。ヽ(゚∀゚*)ノ━━━ゥ♪

这里写图片描述

package com.notzuonotdied.webviewtest;

import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import android.widget.Toast;

import java.net.URL;

import static android.R.attr.host;

public class HyBridWebViewActivity extends AppCompatActivity {

    private HyBridWebView hybridwebview;
    private ProgressBar hybridprogressbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hy_brid_web_view);
        this.hybridprogressbar = (ProgressBar) findViewById(R.id.hybrid_progressbar);
        this.hybridwebview = (HyBridWebView) findViewById(R.id.hybrid_webview);
        initConfig(hybridwebview);
    }

    /**
     * 从intent传过来的参数获取到URL
     */
    protected String getUrl() {
        Uri data = getIntent().getData();
        String url;
        if (null == data) {
            url = getIntent().getStringExtra("URL");
            Log.i("Notzuonotdied","第二场");
        } else {
            url = data.toString();
            Log.i("Notzuonotdied", "进入->" + url);
        }
        return url;
    }

    /**
     * 需要设置webview的属性则重写此方法
     *
     * @param webView
     */
    protected void initConfig(WebView webView) {
        WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);
        webView.addJavascriptInterface(new HybridJsInterface(), "android");
        settings.setDomStorageEnabled(true);
        settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
        webView.setWebViewClient(new WebViewClient() {
            @Override// 拦截URL,方法内为拦截URL期间的自定义执行过程
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                //
                if (!TextUtils.isEmpty(url)) {
                    Log.i("Notzuonotdied", "shouldOverrideUrlLoading");
                    // 在这里可以通过解析url,如url中携带参数的话,可以通过截取来进行处理,响应自定义的Action。
                    Intent intent = new Intent(HyBridWebViewActivity.this, HyBridWebViewActivity.class);
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    intent.putExtra("URL", url);
                    startActivity(intent);
                    overridePendingTransition(R.anim.hybrid_right_in, R.anim.hybrid_left_out);
                    return true;
                }
                return false;
            }

            @Override// 当WebView中页面刚开始加载的时候
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                hybridprogressbar.setVisibility(View.VISIBLE);
                super.onPageStarted(view, url, favicon);
            }

            @Override// 当WebView中页面刚结束加载的时候
            public void onPageFinished(WebView view, String url) {
                hybridprogressbar.setVisibility(View.GONE);
                super.onPageFinished(view, url);
            }
        });
        settings.setJavaScriptCanOpenWindowsAutomatically(true);
        String url = getUrl();
        if (!TextUtils.isEmpty(url)) {
            webView.loadUrl(url);
        }
    }

    @Override
    public void onBackPressed() {
        // 判断webView中是否还有返回页面,如果有就返回webview历史缓存的页面,没有就直接退出当前Activity
        if (this.hybridwebview.canGoBack()) {
            // 返回webview历史缓存的页面
            this.hybridwebview.goBack();
        } else {
            // 退出当前Activity
            super.onBackPressed();
        }
    }

    @Override
    public void finish() {
        // Activity退出时动画
        overridePendingTransition(R.anim.hybrid_bottom_out, R.anim.hybrid_bottom_in);
        super.finish();
    }

    /**
     * JavaObject注入WebView
     * */
    public class HybridJsInterface {
        @JavascriptInterface
        public void goToNewPage(String message) {
            Toast.makeText(HyBridWebViewActivity.this, message, Toast.LENGTH_SHORT).show();
        }
    }

附录

源码地址:Github下载

转载。 https://blog.csdn.net/Notzuonotdied/article/details/54310571
收藏助手
不良信息举报
您举报文章:WebView-HyBird开发的桥梁
举报原因:
原因补充:

(最多只允许输入30个字)