Hybrid混合开发知识点(一)

Hybrid是半Native半Web开发模式,充分利用H5的跨平台、快速迭代能力以及Native的流畅性、系统API调用能力,具有可复用性高、开发成本低、跨平台开发的特点。

在阐述Hybrid混合开发知识点之前,我们先梳理下WebView加载H5页面及H5与Android的交互等方面的知识点。

WebView常用API及属性配置

WebViewClient类:处理各种通知&请求事件。常用方法如下:

  • shouldOverrideUrlLoading():在本WebView中打开网页,只在重定向和点击网页内链接时触发,且先于onPageStarted()。
    返回false表示默认由webview加载URL,返回true表示不加载URL,用户自行处理。
  • onPageStarted():载入页面时调用,加载页面、点击链接、返回上一个网页都会触发。
  • onLoadResource():加载页面资源时回调,如每张图片加载都会调用。
  • onReceivedSslError():webview默认不处理https请求,此方法针对https请求做处理。如handler.proceed();
  • onReceivedError():页面加载异常时会触发,可根据errorCode替换assets目录下预配置的对应加载反馈页面。

WebChromeClient类:辅助webview处理javaScript对话框,加载进度,网站图标,网站标题等。

WebSettings常用属性:

  • setJavaScriptEnabled(true):设置允许执行javaScript脚本,默认false.
  • setSupportZoom(true):设置是否支持缩放,默认true.
  • setBlockNetworkImage(false):把图片加载放在最后来加载渲染.
  • setAllowFileAccess(true):设置可以访问文件.
  • setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN):支持内容重新布局.
  • setUseWideViewPort(true):扩大比例的缩放.
  • setLoadWithOverviewMode(true):自适应屏幕,配合setUseWideViewPort一起使用.
  • setLoadsImagesAutomatically(true):是否下载图片资源,默认true.
  • setCacheMode:设置浏览器缓存模式:
    • LOAD_DEFAULT:默认值。当存在缓存且未过期时加载缓存数据,否则通过网络加载数据。
    • LOAD_NO_CACHE:不使用缓存。仅通过网络加载数据。
    • LOAD_CACHE_ONLY:只使用缓存。仅加载缓存数据,不通过网络加载数据。
    • LOAD_CACHE_ELSE_NETWORK:只要有缓存,是否过期都加载缓存,否则加载网络数据。

WebView常用API调用:

  • getOriginalUrl:获取当前网页的原始url,针对某些执行重定向操作的网页。
  • loadData:以字符串形式加载html片段(若包含中文,会乱码,须设置mimeType类型)。
  • postUrl:以post请求加载url(post携带的数据须是application/x-www-form-urlencoded编码)。
  • reload:刷新页面,重载资源。
  • stopLoading:停止加载。
  • pageUp:向上翻页至页面顶部。
  • clearCache(includeDiskFiles):清除缓存,传参false仅清除内存缓存,传参true同时清除内存和磁盘缓存
  • clearFormData:清除表单自动填充数据(不含已存储本地的表单数据)。
  • onPause:尽可能暂停webview行为,如动画、定位,不暂停js脚本执行。onResume恢复被暂停的行为。
  • pauseTimers:暂停所有webview全部行为,resumeTimers恢复被pauseTimers暂停的行为。
  • destroy:销毁webview,须先从viewGroup中移除webview才有效。
  • getContentHight:获取网页内容高度。
  • loadUrl:加载远程或本地的url。
    • assets资源:file:///android_asset/
    • res资源:file:///android_res/
    • raw资源:file:///android_res/raw/
    • sdcard资源:file:///sdcard/

Android调用H5

WebView需要设置setJavaScriptEnabled(true);使WebView支持执行JavaScript脚本。

//支持Android 4.4以前的版本,支持页面刷新,但无返回值。
webview.post(new Runnable()){
    @Override
    public void run(){
        webview.loadUrl("javaScript:callJs('params')");
    }   
}

//支持Android 4.4+版本,能接收返回值,不支持页面刷新,调用及回调均在UI线程中。
webview.evaluateJavascipt("javascript:callJS('params')",
    new ValueCallback<String>(){
        @Override
        public void onReceiveValue(String value){
            //处理从js返回的值value
        }   
    });

以上两种Android调用H5方法,均在onPageFinished()方法回调后执行,即页面加载完毕后。

H5调用Android

//为js添加Java映射对象。
webview.addJavascriptInterface(new JsCallAndroid(),"jscallandroid");

public class JsCallAndroid {
    //需添加@JavascriptInterface注解,避免webview漏洞。
    @JavascriptInterface
    public void hello(String msg) {
         System.out.println("JS调用了Android的hello方法");
    }
}

js通过脚本映射addJavascriptInterface()中的Object对象,实现H5调用Android方法。前提须设置settings.setJavaScriptEnabled(true)。

Scheme协议

Android中的Scheme是一种页面跳转协议,常处理以下场景交互:

  • App端不同页面跳转;
  • 通过shouldOverrideUrlLoading()回调拦截URL,实现H5页面跳转App页面;
  • 根据URI跳转到另一个App指定页面;
  • 接收服务端下发URI跳转App对应页面;

在使用Scheme前,须在AndroidManifest.xml中对Activity添加< intent-filter/>过滤器注册。

<activity
    android:name=".ui.activity.TestActvity"
    android:screenOrientation="portrait"
    android:windowSoftInputMode="stateAlwaysHidden|adjustPan">
        <!--Scheme协议配置,intent-filter过滤器支持页面跳转-->
        <intent-filter>
        <!--协议部分,path前须加/,除了sheme外其他选填-->
            <data
                android:host="book"
                android:path="/bookDetail"
                android:port="8888"
                android:scheme="app" />

            <!--下面也要设置-->
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />

            <action android:name="android.intent.action.VIEW" />
        </intent-filter>
</activity>

常规的URL Scheme格式为:[scheme]://[host]/[path]?[query],如 app://book:8888/bookDetail?bookId=10011002

app代表Scheme协议名称,book代表作用域,8888代表端口号(如Http协议的默认端口号为80),bookDetail代表页面路径,bookId代表拼接参数。

App内页面跳转,示例1:

  • 页面A跳转及结果回调;
String uri = "app://book:8888/bookDetail?bookId=10011002";
schemeToActivity(uri, 100);

/**
 * 采用Scheme协议页面跳转,校验scheme协议是否有效
 * @param uri
 * @param requestCode
 */
private void schemeToActivity(String uri, int requestCode) {
    if (TextUtils.isEmpty(uri)) return;
    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
    List<ResolveInfo> activities = getPackageManager().queryIntentActivities(intent, 0);
    if (activities != null && !activities.isEmpty()) {
        if (requestCode > 0) {
            startActivityForResult(intent, requestCode);
        } else {
            startActivity(intent);
        }
    } else {
        throw new RuntimeException("can not find the activity's URL Scheme");
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    String string = data.getStringExtra("result");
    if (requestCode == 100) {
        Log.d("test", string);
    }
}
  • 页面B解析Scheme及setResult;
/**
 * 解析Scheme协议的uri数据
 */
private void getSchemeIntent() {
    Intent intent = getIntent();
    Uri uri = intent.getData();
    if (uri == null)return;
    String scheme = uri.getScheme();
    String host = uri.getHost();
    int port = uri.getPort();
    String path = uri.getPath();
    List<String> pathSegments = uri.getPathSegments();
    String query = uri.getQuery();
    String bookId = uri.getQueryParameter("bookId");
    Log.d("test",bookId);
}


//退回页面A回传值
Intent intent = new Intent();
intent.putExtra("result","hello world!");
setResult(RESULT_OK,intent);
finish();

通过debug模式,可获得从Scheme中解析出的参数值,如下:


H5页面跳转App页面,示例2:

若要跳转AndroidManifest.xml中定义Scheme的Activity,H5端配置如下:

<a href="app://book:8888/bookDetail?bookId=10011002">图书详情页</a>

在App内点击该WebView页面链接会触发WebViewClient#shouldOverrideUrlLoading()方法,解析如下:

@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
       if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
                //判定重定向
                WebView.HitTestResult testResult = view.getHitTestResult();
                if (testResult == null ||
                        testResult.getType() == WebView.HitTestResult.UNKNOWN_TYPE) {
                    return false;
                }

                //解析URL Scheme
                Uri uri = request.getUrl();
                //根据schemeToActivity(uri.toString(),0);方案处理跳转
                return true;
        }
        return false;
}

如上所示,和常规的URL Scheme解析同理,注意shouldOverrideUrlLoading()的版本兼容。

重定向问题的处理方案:WebView的getHitTestResult()的函数可以获取点击页面元素的类型,若获取的HitTestResult为null或UNKNOWN_TYPE,则认定为重定向URL,对此情况直接return false。

shouldOverrideUrlLoading()处理H5与Android交互的存在的问题:

  • 重定向在低版本上返回问题,成熟方案不多;
  • 拦截URI处理页面跳转或点击事件,硬编码导致的臃肿问题;
  • shouldOverrideUrlLoading()本身调用时机的局限性;

硬编码问题虽然借鉴了ARouter路由跳转思想能缓解页面跳转,但是在交互方面addJavascriptInterface映射对象的方案更优。

URL Scheme更适合处理App页面之间的跳转。

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

推荐阅读更多精彩内容

  • 一、前言: 1. 概述: Hybrid是半Native半Web开发模式,充分利用H5的跨平台、快速迭代能力以及Na...
    因为我的心阅读 1,099评论 1 2
  • 链接:https://www.jianshu.com/p/fd61e8f4049e 一、简介 这部分主要介绍下 W...
    柒黍阅读 1,778评论 0 4
  • WebView·开车指南 2016-08-31BugDev 北京市东城区首席Bug布道师开山之作,一整月交通事故血...
    53c021c38a1d阅读 822评论 0 1
  • 前言 Android项目中WebView是必不可少的,越来越开的迭代节奏导致越来越多的App采用混合开发,接着我们...
    半罐子晃阅读 1,762评论 0 4
  • 最基础的使用方法 最简单的布局: 在Activity中使用WebView: 但只是这样的话,在模拟器上是会直接调到...
    HolenZhou阅读 7,766评论 12 33