因为H5页面在移动端的兼容性及扩展性方面体现出来的优势,又兼得APP中植入H5页面相应用的灵活性有大大的提升(如活动、游戏的更新等)。APP开发不可避免的须要载入一些H5页面。但安卓client对网页内容的排版、整理、交互等可能会出现一些不可预料的问题。本文将对安卓端载入网页写一些比較通用,可能避免问题的统一的解决方法总结。

背景

一般对前端知识有所了解的都清楚,解析网页主要是靠页面渲染引擎和JS解析引擎,前者负责取得网页的内容(HTML、XML、图象等等)、整理信息(比如加入CSS等),以及计算网页的显示方式然后会输出至显示器或打印机,后者负责网页的一些动态交互等。

安卓操作系统在前端页面载入方面针对开发人员提供了webView组件。并给予响应的API去使用。但其底层的内核在4.4曾经是webKit内核(对H5的支持还远远不够),4.4以后是chromium内核(JS解析引擎採用的便是现在最经常使用的V8引擎,对H5的支持也逐渐加大),随着安卓操作系统的不断迭代,5.0已解决通过网页打开本地文件选择器的问题。总之技术在不断的成熟,有兴趣了解细节的可參考http://blog.csdn.net/typename/article/details/40425275

疑难杂症

近期做了个项目,里面须要在APP中载入一个叫大转盘的活动。活动页面当然是用H5写的啦,于是就用webView进行载入代码例如以下:
package com.example.webviewtest;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.KeyEvent;
import android.webkit.JsResult;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient; @SuppressLint("SetJavaScriptEnabled")
public class WebViewActivity extends FragmentActivity {
private WebView webView;
private Context context;
private Activity activity; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
context = this;
activity = this;
initView();
} //
private void initView() {
webView = (WebView) findViewById(R.id.game_WV); initWebView();
} @SuppressLint("NewApi")
private void initWebView() { if (Build.VERSION.SDK_INT >= 19) {
webView.getSettings().setCacheMode(
WebSettings.LOAD_CACHE_ELSE_NETWORK);
}
webView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
activity.setTitle("Loading...");
activity.setProgress(newProgress * 100);
if (newProgress == 100) {
activity.setTitle(R.string.app_name);
}
} @Override
public boolean onJsAlert(WebView view, String url, String message,
JsResult result) {
return super.onJsAlert(view, url, message, result);
}
});
webView.setWebViewClient(new GameWebViewClient());
WebSettings s = webView.getSettings();
s.setJavaScriptEnabled(true);
webView.loadUrl(Constants.url);
} class GameWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view,
String url_Turntable) {
view.loadUrl(url_Turntable);
return true;
} @Override
public void onReceivedSslError(WebView view, SslErrorHandler handler,
SslError error) {
handler.proceed();
} @Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
} @Override
public void onPageFinished(WebView view, String url) {
} } @Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
webView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
} }

这里简介下。为何webView既要setWebClient,又要setWebChromeClient。这两者并非因为4.4以后webView採用了chromium的内核所以以后就舍弃了WebClient(之前有不少童鞋误解了)。事实上两者是相辅相成的:

WebViewClient主要帮助WebView处理各种通知、请求事件的,WebChromeClient主要辅助WebView处理Javascript的对话框、站点图标、站点title、载入进度,详细可重写的接口大家可自行查阅SDK文档。

回到正题,原本希望的效果是转盘能转动,各类信息包含用户信息、图片信息均能显示正常。可真正显示出来的竟是这样的效果:
转盘无法转动。用户是谁没有显示。可抽奖的次数亦没有显示,可是将链接放入浏览器内却毫无问题。显示正常

用Chrome浏览器调试

能够推断前端方面的代码应该是没有问题的,问题应该是出在webView的配置上面,因为我測试的手机是安卓4.4操作系统的。对H5页面及JS的交互的支持相对来说已经相对完好了,并且在手机自带的浏览器上输入同样的链接执行正常,那么初步判定应该是webView的webSettings的问题。也初步排除了是JS的问题,英文代码里已经设置了setJavaScriptEnabled(true)。前面说了安卓4.4以上webView的内核是chronium的。这给我们APP开发人员带来的还有一个优点是,能够用chorme浏览器调试、debug定位Webview载入h5页面时所遇到的问题,如图,打开chrome浏览器,插上手机,在此简要说明下前提条件(1、手机要是USB调试模式
 2、chromeclient需31以上  3、代码中加入
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
WebView.setWebContentsDebuggingEnabled(true);
}

)。另外可能须要翻墙(这点非常。。

。只是貌似仅仅要翻墙一次就可以)

手机装好程序,连接上电脑,打开相应页面(WebViewActivity)。
点击inspect(这里假设不行,可能须要翻墙)
出现例如以下界面
我们点击console
接着点进去看下发生了什么
咨询了下做前端的同事,这里是localStorage.getItem这里因为localStorage为空导致的。为什么会这样呢,到这里,须要补充一下 localStorage 的知识。
HTML5 Storage主要有:
sessionStorage: 会话 (session) 级别的数据存储,会话结束后,相关的数据就会被清除掉。

localStorage: 用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的。
作为 HTML5 标准的一部分,绝大多数的浏览器都是支持 localStorage 的,可是鉴于它的安全特性(不论什么人都能读取到它,虽然有相应的限制,将敏感数据存储在这里依旧不是明智之举)。Android 默认是关闭该功能的。
也就是说,针对写了这类代码的H5页面的解析,还须要在上述代码里加入:
s.setDomStorageEnabled(true);

经測试成功!

CrossWalk

上面的问题固然攻克了,可是假设出了问题,Chrome的DevTools都检測不出来呢?这是有可能的。前面说了,WebView解析网页最关键的是靠底层的解析引擎,WebView仅仅只是是通过JNI调用了引擎的解析功能并封装出来给咱们安卓的开发人员使用。面对4.4之前的手机终端怎么办?亦或是WebView所採用的轻量级的chronium内核本身就解析不出前端开发人员的牛逼脚本怎么办?虽然Google在版本号迭代中不断修复Bug,不可否认的是,眼下大家使用的安卓手机版本号參差不齐。高版本号的使用WebView的话都可能会遇到些奇葩问题,更别说低版本号的了。当然,我们队WebView的未来还是非常有信心的,高版本号的使用率的增高。以后这些问题会越来越少,但现现在。假设你对WebView作为富文本编辑器在全面性和稳定性还是不太惬意的话,能够尝试CrossWalk。

CrossWalk介绍

crooswalk的牛逼之处呢,不仅在于他高效、稳定,并且载入同一个界面,字体都会好看些,据说3D效果也是非常的理想,能够先来看看效果。
关于其牛逼之处。此处不做赘述,有兴趣的童鞋能够看看这篇文章http://dev.yesky.com/24/39285024.shtml,讲述了他的发展历史和优势。

CrossWalk安卓端使用

更高阶的使用及了解。能够參考crosswalk的官网。https://crosswalk-project.org/documentation/embedding_crosswalk.html
1)将下载下来的包,和你须要使用该包的工程放在同一个目录下,在eclipse中导入包的工程。看的出来其核心引擎来自于libxwalkcore.so库。
2)在主工程中,加入该库
3)源代码例如以下
<?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" >
<org.xwalk.core.XWalkView
android:id="@+id/game_WV"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

package com.example.webviewtest;

import org.xwalk.core.XWalkResourceClient;
import org.xwalk.core.XWalkView; import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity; @SuppressLint("SetJavaScriptEnabled")
public class CrossWalkActivity extends FragmentActivity {
private XWalkView webView;
private Context context; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crosswalk);
context = this;
initView();
} //
private void initView() {
webView = (XWalkView)findViewById(R.id.game_WV); initWebView();
} private void initWebView() {
webView.setResourceClient(new GameWebViewClient(webView));
webView.load(Constants.url,null);
} class GameWebViewClient extends XWalkResourceClient{ public GameWebViewClient(XWalkView view) {
super(view);
} @Override
public void onLoadStarted(XWalkView view, String url) {
super.onLoadStarted(view, url);
} @Override
public void onLoadFinished(XWalkView view, String url) {
super.onLoadFinished(view, url);
} } }

crosswalk弊端

要是说crosswalk有什么弊端,那就是太大了,一个.so动态库就足足有近20M。apk应用本身可能都不足他的1/4。

单这也看的出其代码量的巨大,效果全面那就毋庸置疑了。

Cordova

假设说你嫌crosswalk太大,又不愿意对webview在代码上进行各种设置以保障适应各种不同的H5页面。或许cordova是不错的选择,从本质上说,cordova事实上还是继承webview的,所以在他俩从本质上说是一样的,可是介于我之前并不知怎样去调试webview。老大又不想用crosswalk这个极度占空间的库。于是就找来了cordova的JAR包,总之把当时的问题攻克了吧,但现在回头看看。cordova在4.0后已经选择和crosswalk合作了,我用的jar包还是3.7的,建议以后无需再使用它。但既然做了,那也就把图和代码贴上来吧。

<?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" >
<org.apache.cordova.CordovaWebView
android:id="@+id/game_WV"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
package com.example.webviewtest;

import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import org.apache.cordova.CordovaChromeClient;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaPreferences;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.CordovaWebViewClient;
import org.apache.cordova.PluginEntry;
import org.apache.cordova.Whitelist; import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.KeyEvent; @SuppressLint("SetJavaScriptEnabled")
public class CordovaActivity extends FragmentActivity implements CordovaInterface {
private final ExecutorService threadPool = Executors.newCachedThreadPool();
private CordovaWebView webView;
private int activityRequestCode;
private CordovaPlugin activityResultCallBack;
private CordovaPreferences prefs = new CordovaPreferences();
private Whitelist internalWhitelist = new Whitelist();
private Whitelist externalWhitelist = new Whitelist();
private ArrayList<PluginEntry> pluginEntries;
private Context context; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cordova);
context = this;
initView();
} //
private void initView() {
webView = (CordovaWebView)findViewById(R.id.game_WV); initWebView();
} private void initWebView() {
internalWhitelist.addWhiteListEntry("*", false);
externalWhitelist.addWhiteListEntry("tel:*",false);
externalWhitelist.addWhiteListEntry("sms:*", false);
prefs.set("loglevel", "DEBUG");
webView.init(this, makeWebViewClient(webView), makeChromeClient(webView), pluginEntries, internalWhitelist, externalWhitelist, prefs); webView.loadUrlIntoView(Constants.url);
} private CordovaWebViewClient makeWebViewClient(CordovaWebView webView){
return webView.makeWebViewClient(this);
} private CordovaChromeClient makeChromeClient(CordovaWebView webView){
return webView.makeWebChromeClient(this);
} @Override
public Activity getActivity() {
// TODO Auto-generated method stub
return this;
} @Override
public ExecutorService getThreadPool() {
// TODO Auto-generated method stub
return threadPool;
} @Override
public Object onMessage(String arg0, Object arg1) {
if(arg0.equals("onPageStarted")){
}
if(arg0.equals("onPageFinished")){
}
return null;
} @Override
public void setActivityResultCallback(CordovaPlugin arg0) {
if(activityResultCallBack != null){
activityResultCallBack.onActivityResult(activityRequestCode, Activity.RESULT_CANCELED, null);
}
this.activityResultCallBack = arg0;
} @Override
public void startActivityForResult(CordovaPlugin arg0, Intent arg1, int arg2) {
setActivityResultCallback(arg0);
startActivityForResult(arg1, activityRequestCode);
} @Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK){
}
return super.onKeyDown(keyCode, event);
}
}

小结

说了这么多,我还是建议大家多使用安卓SDK提供的组件和功能,毕竟功能上能够自己封装,出了问题调试起来也方便,webview眼下在不断地更新迭代,大家还是要对它充满信心。
附件:http://download.csdn.net/detail/zcchange1025/9455185

安卓APP载入HTML5页面解决方式总结的更多相关文章

  1. Android Google Play app signing 最终完美解决方式

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/105561341 本文出自[赵彦军的博客] 在 GooglePlay 创建 App ...

  2. 移动端html5页面长按实现高亮全选文本内容的兼容解决方式

    近期须要给html5的WebAPP在页面上实现一个复制功能:用户点击长按文本会全选文字并弹出系统"复制"菜单.用户能够点击"复制"进行复制操作.然后粘贴到App ...

  3. https 页面中引入 http 资源的解决方式

    相对协议 应用场景 浏览器默认是不允许在 https 里面引用 http 资源的,一般都会弹出提示框. 用户确认后才会继续加载,用户体验非常差. 而且如果在一个 https 页面里动态的引入 http ...

  4. 用android studio创建第一个安卓程序加载html5 页面

    前言 软件版本:android studio v1.0正式版,由于v0.x以来软件变化一直比较大,很多问题搜索的解决方案也都是v0.x版本时代的,故首先声明一下版本. 动机:由于工作中需要对移动端软件 ...

  5. html5的postmessage实现js前端跨域訪问及调用解决方式

    关于跨域訪问.使用JSONP的方法.我前面已经demo过了.详细见http://supercharles888.blog.51cto.com/609344/856886,HTML5提供了一个很强大的A ...

  6. [转]html5: postMessage解决跨域和跨页面通信的问题

    [转]html5: postMessage解决跨域和跨页面通信的问题 平时做web开发的时候关于消息传递,除了客户端与服务器传值,还有几个经常会遇到的问题: 多窗口之间消息传递(newWin = wi ...

  7. android开发游记:SpringView 下拉刷新的高效解决方式,定制你自己风格的拖拽页面

    关于下拉刷新/上拉载入很多其它的解决方式网上已经有非常多了,浏览了眼下主流的下拉控件比方PullToRefresh库等.第一:大多数实现库都难以进行动画和样式的自己定义. 第二:不能非常好的兼容多种滚 ...

  8. VS2012执行Cocos2d-xTest案例载入失败解决方式

    编译cocos2d-x,一直提示载入失败 通过搜索,解决方式例如以下: 第一步,通过命令提示行工具进入VS安装文件夹下的%VS2012home%Common7\IDE 第二部,运行devenv.exe ...

  9. cocos2d移植到安卓引入第三方so文件时候编译会删除解决方式

    在游戏中对接支付的SDK的时候引入支付的so文件的时候在编译的时候总是被删除,后来经过查找资料自己整理出了一个解决方式 方案例如以下 在项目导入安卓中之后.在相应的jni目录中创建一个prebuilt ...

随机推荐

  1. 9patch生成图片

    private Bitmap get_ninepatch(int id,int x, int y, Context context){ // id is a resource id for a val ...

  2. 技术总结--android篇(一)--MVC模式

    先介绍下MVC模式:MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显 ...

  3. Highcharts数据表示(2)

    Highcharts数据表示(2) 数据节点是图表中最小的元素.每一个数据节点都是一个数据单元. 它确定了图表中一个图形元素的各种信息.一个数据节点通常包含下面三类信息: 1.坐标位置信息 因为Hig ...

  4. #定位系统性能瓶颈# perf

    perf是一个基于Linux 2.6+的调优工具,在liunx性能測量抽象出一套适应于各种不同CPU硬件的通用測量方法,其数据来源于比較新的linux内核提供的 perf_event 接口 系统事件: ...

  5. nginx的安装步骤

    nginx学习资料;https://zhuanlan.zhihu.com/p/34943332 1.下载nginx的安装包:https://nginx.org/en/download.html 2. ...

  6. NSURLSession简介

    NSURLSession是iOS7中新的网络接口,它与咱们熟悉的NSURLConnection是并列的.在程序在前台时,NSURLSession与NSURLConnection可以互为替代工作.注意, ...

  7. maven 项目加载本地JAR

     将jar安装到本地的maven仓库 1.首先确定本地有maven环境. 2.安装本地jar 模板: mvn install:install-file -Dfile=<path-to-file& ...

  8. 调试相关blogs收集

    Debug Diag官方blog  https://blogs.msdn.microsoft.com/debugdiag/ Tess  https://blogs.msdn.microsoft.com ...

  9. 移动端 AnyProxy HTTPS 配置

    参考:AnyProxy HTTPS相关教程 参考教程中,说明了 Windows 和 Mac 系统的配置,这里补充一下移动端(Android.iOS)的配置 生成RootCA > 下载 rootC ...

  10. Win8 Windows Defender default behaviour

    Problem Description ********************* Is it possible, to change the default behaviour when findi ...