HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

--

--

--

DCloud喜获CSDN、html5梦工厂两项大奖

流应用

2015开发工具及服务年度大奖评选,是全球最大的中文 IT 社区 CSDN 针对开发工具及服务的评选大奖,目的在于推动开发服务及工具质量的整体提升。国内领先的H5平台DCloud,凭借在流应用技术方面的仔细钻研和突破,从200多家参选企业中脱颖而出,获得最具含金量的「最佳技术创新奖」。

  “娜喊杯””由中国最大HTML5社区——HTML5梦工场举办,目的在于为快速推进HTML5在国内的发展和应用,整合行业优质资源促进上下游发展,打造健康的HTML5生态圈,DCloud凭借HBuilder完整的语法提示和dai码输入法、dai码块、大幅提升HTML、js、css的开发效率等诸多优势,一举获得 “娜喊杯”「2015 年度最佳 H5 开发者工具奖」。

  DCloud是领先的HTML5平台厂商,W3C会员,HTML5中国产业联盟的发起单位,在HTML5领域陆续推出开发工具HBuilder、手机端强化引擎5 runtime、跨平台的轻巧框架mui,流应用等四款产品。其中前三款工具帮助开发者快速的开发出优质的HTML5应用,如今已有几十万开发者使用DCloud的产品开发了几十万App。

  DCloud再接再厉又于2015年10月份发布国内首款流应用引擎。流应用的推出,让最终用户也感受到HTML5的好处,让不差钱的大型互联网公司也开始开发HTML5应用,它是一款基于HTM5 的技术的增强型js引擎,首先对H5进行了大幅强化,让js可以调用40万原生能力,并且大幅提升了H5的性能体验。经过强化过后,使得基于HTML5 开发的流应用可以达到原生应用的标准。

  过去业内也有HTML5强化引擎或hybrid方案,比如cordova,但其在功能和性能上还无法达到原生应用的水准。HTML5 做到了这一点,利用js的动态语言特点,把手机端App的安装包拆解,流式下载到手机端。类似流媒体边看边下一样,再辅以特殊的压缩解码技术,使得流应用可以在5秒内完成App的下载-安装-启动全过程。

  随着流应用引擎的发布,DCloud提供了从开发到发行的整体HTML5平台服务,一边服务开发商、一边服务手机用户,围绕HTML5建立一个更新颖的平台系统。

  DCloud,一直以用户体验至上为原则

  移动互联网一直是用户体验至上的原则,原来App强调使用体验好。经过几年的发展,在手机上使用App的过程已经不卡顿了,此时用户体验的痛点转移了,从使用App的体验不佳转移到获取、更新App的体验不佳了。现在获取传统原生App的过程,依然让用户等待过久。

  所有的不爽,总会有人来改变它。流应用可以在5秒内完成App的下载-安装-启动,可以无感知差量更新(一般一款流应用的版本更新消耗的流量只有十几K)。把安卓原生应用导致的手机会变卡,变得更费电等大多数问题流应用都解决了,如此一来,很可能出现用户卸载原生App改用流应用的局面。

  在2015开发工具及服务年度大奖评选的获奖理由里,专家们这样点评DCloud的流应用,“流应用引擎是一款基于HTM5 的技术的增强型js引擎,它首先对H5进行了大幅强化,让js可以调用40万原生能力,并且大幅提升了H5的性能体验,最让人期待的是这个流应用引擎是否可以颠覆原生App一统江山的局面。”

继续阅读 »

2015开发工具及服务年度大奖评选,是全球最大的中文 IT 社区 CSDN 针对开发工具及服务的评选大奖,目的在于推动开发服务及工具质量的整体提升。国内领先的H5平台DCloud,凭借在流应用技术方面的仔细钻研和突破,从200多家参选企业中脱颖而出,获得最具含金量的「最佳技术创新奖」。

  “娜喊杯””由中国最大HTML5社区——HTML5梦工场举办,目的在于为快速推进HTML5在国内的发展和应用,整合行业优质资源促进上下游发展,打造健康的HTML5生态圈,DCloud凭借HBuilder完整的语法提示和dai码输入法、dai码块、大幅提升HTML、js、css的开发效率等诸多优势,一举获得 “娜喊杯”「2015 年度最佳 H5 开发者工具奖」。

  DCloud是领先的HTML5平台厂商,W3C会员,HTML5中国产业联盟的发起单位,在HTML5领域陆续推出开发工具HBuilder、手机端强化引擎5 runtime、跨平台的轻巧框架mui,流应用等四款产品。其中前三款工具帮助开发者快速的开发出优质的HTML5应用,如今已有几十万开发者使用DCloud的产品开发了几十万App。

  DCloud再接再厉又于2015年10月份发布国内首款流应用引擎。流应用的推出,让最终用户也感受到HTML5的好处,让不差钱的大型互联网公司也开始开发HTML5应用,它是一款基于HTM5 的技术的增强型js引擎,首先对H5进行了大幅强化,让js可以调用40万原生能力,并且大幅提升了H5的性能体验。经过强化过后,使得基于HTML5 开发的流应用可以达到原生应用的标准。

  过去业内也有HTML5强化引擎或hybrid方案,比如cordova,但其在功能和性能上还无法达到原生应用的水准。HTML5 做到了这一点,利用js的动态语言特点,把手机端App的安装包拆解,流式下载到手机端。类似流媒体边看边下一样,再辅以特殊的压缩解码技术,使得流应用可以在5秒内完成App的下载-安装-启动全过程。

  随着流应用引擎的发布,DCloud提供了从开发到发行的整体HTML5平台服务,一边服务开发商、一边服务手机用户,围绕HTML5建立一个更新颖的平台系统。

  DCloud,一直以用户体验至上为原则

  移动互联网一直是用户体验至上的原则,原来App强调使用体验好。经过几年的发展,在手机上使用App的过程已经不卡顿了,此时用户体验的痛点转移了,从使用App的体验不佳转移到获取、更新App的体验不佳了。现在获取传统原生App的过程,依然让用户等待过久。

  所有的不爽,总会有人来改变它。流应用可以在5秒内完成App的下载-安装-启动,可以无感知差量更新(一般一款流应用的版本更新消耗的流量只有十几K)。把安卓原生应用导致的手机会变卡,变得更费电等大多数问题流应用都解决了,如此一来,很可能出现用户卸载原生App改用流应用的局面。

  在2015开发工具及服务年度大奖评选的获奖理由里,专家们这样点评DCloud的流应用,“流应用引擎是一款基于HTM5 的技术的增强型js引擎,它首先对H5进行了大幅强化,让js可以调用40万原生能力,并且大幅提升了H5的性能体验,最让人期待的是这个流应用引擎是否可以颠覆原生App一统江山的局面。”

收起阅读 »

真是醉了, 我想问问官方文档咋回事

下拉刷新

真是醉了, 我想问问官方文档咋回事

API 不想说啥了, 真的..., 各种找不到, 属性 参数 一个比一个难找

子页 不 支持 'slide-in-right' 也没看到个说明

独立一级页, 安卓机下拉刷新也是个大BUG, 哎, 刚用就发现这么多坑

不知道什么时候能趟完这些坑

继续阅读 »

真是醉了, 我想问问官方文档咋回事

API 不想说啥了, 真的..., 各种找不到, 属性 参数 一个比一个难找

子页 不 支持 'slide-in-right' 也没看到个说明

独立一级页, 安卓机下拉刷新也是个大BUG, 哎, 刚用就发现这么多坑

不知道什么时候能趟完这些坑

收起阅读 »

使用Native.js实现打开页面默认弹出软键盘

弹出软键盘 Native.JS focus

先来体验下这个神奇的功能(兼容iOS和Android):


此功能需要在模块权限中配置Native.js

var nativeWebview, imm, InputMethodManager;  
var initNativeObjects = function() {  
    if (mui.os.android) {  
        var main = plus.android.runtimeMainActivity();  
        var Context = plus.android.importClass("android.content.Context");  
        InputMethodManager = plus.android.importClass("android.view.inputmethod.InputMethodManager");  
        imm = main.getSystemService(Context.INPUT_METHOD_SERVICE);  
    } else {  
        nativeWebview = plus.webview.currentWebview().nativeInstanceObject();  
    }  
};  
var showSoftInput = function() {  
    if (mui.os.android) {  
        imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);  
    } else {  
        nativeWebview.plusCallMethod({  
            "setKeyboardDisplayRequiresUserAction": false  
        });  
    }  
    setTimeout(function() {  
       //此处可写具体逻辑设置获取焦点的input  
       var inputElem = document.querySelector('input');  
              inputElem.focus();   
    }, 200);  
};  
mui.plusReady(function() {  
    initNativeObjects();  
    showSoftInput();  
});

补充更新:

调用plus.webview.create()创建新的webview,会导致当前webview失去焦点,因此可能出现键盘闪一下又消失的情况。

解决方案:将创建webview的代码放到显示键盘之前(initNativeObjects方法之前)。

另外,为了强制当前webview获得焦点,可像如下方式修改showSoftInput方法:

var showSoftInput = function() {  
    var nativeWebview = plus.webview.currentWebview().nativeInstanceObject();  
    if (mui.os.android) {  
        //强制当前webview获得焦点  
        plus.android.importClass(nativeWebview);  
        nativeWebview.requestFocus();  
        imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);  
    } else {  
        nativeWebview.plusCallMethod({  
            "setKeyboardDisplayRequiresUserAction": false  
        });  
    }  
    setTimeout(function() {  
       //此处可写具体逻辑设置获取焦点的input  
       var inputElem = document.querySelector('input');  
              inputElem.focus();   
    }, 200);  
};
继续阅读 »

先来体验下这个神奇的功能(兼容iOS和Android):


此功能需要在模块权限中配置Native.js

var nativeWebview, imm, InputMethodManager;  
var initNativeObjects = function() {  
    if (mui.os.android) {  
        var main = plus.android.runtimeMainActivity();  
        var Context = plus.android.importClass("android.content.Context");  
        InputMethodManager = plus.android.importClass("android.view.inputmethod.InputMethodManager");  
        imm = main.getSystemService(Context.INPUT_METHOD_SERVICE);  
    } else {  
        nativeWebview = plus.webview.currentWebview().nativeInstanceObject();  
    }  
};  
var showSoftInput = function() {  
    if (mui.os.android) {  
        imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);  
    } else {  
        nativeWebview.plusCallMethod({  
            "setKeyboardDisplayRequiresUserAction": false  
        });  
    }  
    setTimeout(function() {  
       //此处可写具体逻辑设置获取焦点的input  
       var inputElem = document.querySelector('input');  
              inputElem.focus();   
    }, 200);  
};  
mui.plusReady(function() {  
    initNativeObjects();  
    showSoftInput();  
});

补充更新:

调用plus.webview.create()创建新的webview,会导致当前webview失去焦点,因此可能出现键盘闪一下又消失的情况。

解决方案:将创建webview的代码放到显示键盘之前(initNativeObjects方法之前)。

另外,为了强制当前webview获得焦点,可像如下方式修改showSoftInput方法:

var showSoftInput = function() {  
    var nativeWebview = plus.webview.currentWebview().nativeInstanceObject();  
    if (mui.os.android) {  
        //强制当前webview获得焦点  
        plus.android.importClass(nativeWebview);  
        nativeWebview.requestFocus();  
        imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);  
    } else {  
        nativeWebview.plusCallMethod({  
            "setKeyboardDisplayRequiresUserAction": false  
        });  
    }  
    setTimeout(function() {  
       //此处可写具体逻辑设置获取焦点的input  
       var inputElem = document.querySelector('input');  
              inputElem.focus();   
    }, 200);  
};
收起阅读 »

h5+实时定位问题 setInterval中只执行一次getCurrentPosition

Geolocation

参考问题:http://ask.dcloud.net.cn/question/10078
刚开始学习html5+App 要实现定时发送位置信息功能,看了官方示例如下,确实简单明了
官网示例
<!DOCTYPE html>
<html>****
<head>
<meta charset="utf-8">
<title>Geolocation Example</title>
<script type="text/javascript" >
// 扩展API加载完毕后调用onPlusReady回调函数
document.addEventListener( "plusready", onPlusReady, false );
// 扩展API加载完毕,现在可以正常调用扩展API
function onPlusReady() {
plus.geolocation.getCurrentPosition( function ( p ) {
alert( "Geolocation\nLatitude:" + p.coords.latitude + "\nLongitude:" + p.coords.longitude + "\nAltitude:" + p.coords.altitude );
}, function ( e ) {
alert( "Geolocation error: " + e.message );
} );
}
</script>
</head>
<body >
</body>
</html>

不过问题来了,现在要自动隔段时间就发送一下位置了,采用上面的方法getPosBaidu,加上下面这句,期间可能遇到其他问题plus未定义啊什么的,不过不是关键
setInterval('getPosBaidu()', 10000);
调试结果:
执行了一次getPosBaidu
然后就over了
原因可能是阻塞啊 什么的,具体我也不太懂了,就觉得跟setInterval有关

怎么办?
看了一堆dcloud定位相关问题的贴子,特别混乱。
总结下可能的方法,
1、自己开发5+sdk插件替换getCurrentPosition
2、Native.js 调用本地替换getCurrentPosition
3、换掉setInterval 用别的方法定时触发
4、曲线利用 plus.geolocation.watchPosition

结果当然是1,2太复杂,还没动。
4 方法,看了半天,watchPosition 这个方法也没太懂,继续setInterval 只用plus.geolocation.watchPosition 直接取代getCurrentPosition :开始监视触发一次,然后清楚监视就是一次啦,不过依然没用。
3方法目前算是成功了。

有其他方法的同学,请告知,毕竟这个办法也太曲线了。

html5+ 官方需要继续改进,要加快进度。 控制变量法:自己做到最好,历史才能选择究竟谁才是未来的主流,希望html5+应用火起来。
End。。。

继续阅读 »

参考问题:http://ask.dcloud.net.cn/question/10078
刚开始学习html5+App 要实现定时发送位置信息功能,看了官方示例如下,确实简单明了
官网示例
<!DOCTYPE html>
<html>****
<head>
<meta charset="utf-8">
<title>Geolocation Example</title>
<script type="text/javascript" >
// 扩展API加载完毕后调用onPlusReady回调函数
document.addEventListener( "plusready", onPlusReady, false );
// 扩展API加载完毕,现在可以正常调用扩展API
function onPlusReady() {
plus.geolocation.getCurrentPosition( function ( p ) {
alert( "Geolocation\nLatitude:" + p.coords.latitude + "\nLongitude:" + p.coords.longitude + "\nAltitude:" + p.coords.altitude );
}, function ( e ) {
alert( "Geolocation error: " + e.message );
} );
}
</script>
</head>
<body >
</body>
</html>

不过问题来了,现在要自动隔段时间就发送一下位置了,采用上面的方法getPosBaidu,加上下面这句,期间可能遇到其他问题plus未定义啊什么的,不过不是关键
setInterval('getPosBaidu()', 10000);
调试结果:
执行了一次getPosBaidu
然后就over了
原因可能是阻塞啊 什么的,具体我也不太懂了,就觉得跟setInterval有关

怎么办?
看了一堆dcloud定位相关问题的贴子,特别混乱。
总结下可能的方法,
1、自己开发5+sdk插件替换getCurrentPosition
2、Native.js 调用本地替换getCurrentPosition
3、换掉setInterval 用别的方法定时触发
4、曲线利用 plus.geolocation.watchPosition

结果当然是1,2太复杂,还没动。
4 方法,看了半天,watchPosition 这个方法也没太懂,继续setInterval 只用plus.geolocation.watchPosition 直接取代getCurrentPosition :开始监视触发一次,然后清楚监视就是一次啦,不过依然没用。
3方法目前算是成功了。

有其他方法的同学,请告知,毕竟这个办法也太曲线了。

html5+ 官方需要继续改进,要加快进度。 控制变量法:自己做到最好,历史才能选择究竟谁才是未来的主流,希望html5+应用火起来。
End。。。

收起阅读 »

【分享】图片下载本地缓存时间戳显示图片方法

技术分享 downloader 图片加工

参考文章
http://ask.dcloud.net.cn/article/256
http://ask.dcloud.net.cn/article/397

说明:为了方便,里面使用的图片来源是从上面文章中的源码项目获取的.
说明:参考了上面文章中的思路,然后自己重新写了一个较为完整的图片本地缓存显示工具.
功能
1.第一次显示图片时下载到本地,然后之后如果本地存在缓存(根据url),则显示本地缓存的图片

  1. 基于plus的storage,对每一个图片的缓存进行控制管理,增加时间戳参数,来控制本地缓存的有效时间
  2. 将所有的下载任务放入队列中,进行统一下载管理,增加最大并发请求数,防止一次性请求过多损耗性能
  3. 加入了图片缓存池机制,对于同一个下载路径的图片不会多次下载,而是填入缓存池,下载后统一回调
  4. 修改了图片本地路径的获取方法,摆脱第三方依赖
  5. 重构了代码,并入开发框架中

使用方法

 * 外部API:注意,需要显示的图片需要有data-img-localcache这个属性(用来放目标url),dom需要是原生对象  
 * 清除所有图片缓存:MobileFrame.ImageUtil.ImageLoaderFactory.clearLoaderImgsCache(successCb,errorCb)  
 * 采取本地缓存显示所有图片:MobileFrame.ImageUtil.ImageLoaderFactory.setAllNetImgsWithLocalCache();  
 * 清除某一张图片的本地缓存:MobileFrame.ImageUtil.ImageLoaderFactory.clearNetUrlImgCache(src);  
 * 显示某一张图片:MobileFrame.ImageUtil.ImageLoaderFactory.setImgWidthLocalCache(dom,src);

源码

/**  
 * @description   移动开发框架  
 * @author dailc  dailc   
 * @version 1.0  
 * @time 2016-01-11 16:57:57  
 * 功能模块:只依赖于plus系统  
 * @see http://ask.dcloud.net.cn/people/%E6%92%92%E7%BD%91%E8%A6%81%E8%A7%81%E9%B1%BC  
 * 图片本地缓存模块********************************  
 * 1.本地缓存显示图片  
 * 2.增加storage,增加每一个本地缓存的有效时间戳  
 * 3.增加自定义设置方法,可以根据不同需求,对参数进行修改  
 * 4.采用下载队列进行下载管理,增加最大并发请求数,防止一次性请求过多损耗性能  
 * 5.修改了图片本地路径的获取方法,摆脱第三方依赖  
 * 6.重构代码  
 * 外部API:注意,需要显示的图片需要有data-img-localcache这个属性(用来放目标url)  
 * 清除所有图片缓存:MobileFrame.ImageUtil.ImageLoaderFactory.clearLoaderImgsCache(successCb,errorCb)  
 * 采取本地缓存显示所有图片:MobileFrame.ImageUtil.ImageLoaderFactory.setAllNetImgsWithLocalCache();  
 * 清除某一张图片的本地缓存:MobileFrame.ImageUtil.ImageLoaderFactory.clearNetUrlImgCache(src);  
 * 显示某一张图片:MobileFrame.ImageUtil.ImageLoaderFactory.setImgWidthLocalCache(dom,src);  
 * 图片本地缓存模块完毕********************************  
 */  
(function(global) {  
    /**  
     * 定义全局函数对象 define出来的  
     */  
    var mapping = {};  
    /**  
     * 缓存,正在用到的对象,函数中return 出来的,这样就不需要重复执行函数  
     */  
    var cache = {};  
    /**  
     * @description 模块定义  
     * @param {String} id id  
     * @param {Function} func 对应的函数对象  
     */  
    global.define = function(id, func) {  
        mapping[id] = func;  
    };  
    /**  
     * @description 生成模块对象,并采用本地缓存  
     * @param {String} id  
     */  
    global.require = function(id) {  
        if (!/\.js$/.test(id)) {  
            id += '.js';  
        }  
        if (cache[id]) {  
            return cache[id];  
        } else {  
            return cache[id] = mapping[id]({});  
        }  
    };  
    /**  
     * @description 配置全局工具类以及一些需要用到的全局函数  
     */  
    (function() {  
        global.MobileFrame = {};  
        /**  
         * 空函数  
         */  
        MobileFrame.noop = function() {};  
        /**  
         * @description each遍历操作  
         * @param {type} elements  
         * @param {type} callback  
         * @returns {global}  
         */  
        MobileFrame.each = function(elements, callback, hasOwnProperty) {  
            if (!elements) {  
                return this;  
            }  
            if (typeof elements.length === 'number') {  
                [].every.call(elements, function(el, idx) {  
                    return callback.call(el, idx, el) !== false;  
                });  
            } else {  
                for (var key in elements) {  
                    if (hasOwnProperty) {  
                        if (elements.hasOwnProperty(key)) {  
                            if (callback.call(elements[key], key, elements[key]) === false) return elements;  
                        }  
                    } else {  
                        if (callback.call(elements[key], key, elements[key]) === false) return elements;  
                    }  
                }  
            }  
            return global;  
        };  
        /**  
         * @description plusReady  
         * @param {Function} callback  
         * @returns {global} 返回的是global  
         */  
        MobileFrame.plusReady = function(callback) {  
            if (window.plus) {  
                setTimeout(function() { //解决callback与plusready事件的执行时机问题(典型案例:showWaiting,closeWaiting)  
                    callback();  
                }, 0);  
            } else {  
                document.addEventListener("plusready", function() {  
                    callback();  
                }, false);  
            }  
            return global;  
        };  
        /**  
         * @description 得到相对路径对应的key,这个key可以使缓存池的或者是本地缓存键值  
         * 主要作用是去除非法字符  
         * @param {String} relativePath  
         */  
        MobileFrame.getRelativePathKey = function(relativePath) {  
            var finalKey =  
                //                  relativePath.replace('\/', '').replace('.', '').replace('\/', '')  
                //                  .replace('_download', '').replace('jpg', '');  
                relativePath.replace(/[&\|\\\*^%$#@\-]/g, "");  
            return finalKey;  
        };  
        /**  
         * @description 更改url类型,去除cache,因为cache会造成一些困扰  
         * @param {String} url 传入的url  
         */  
        MobileFrame.changImgUrlTypeNoCache = function(url) {  
            url = url || '';  
            if (url.indexOf('?') != -1) {  
                url += '&timeRandKey=' + Math.random();  
            } else {  
                url += '?timeRandKey=' + Math.random();  
            }  
            return url;  
        };  
        /**  
         * @description 删除指定路径的文件  
         * @param {String} relativePath  绝对路径或相对路径例如:  _downloads/imgs/test.jpg  
         * @param {Function} successCallback  删除成功回调  
         * @param {Function} errorCallback  失败回调  
         */  
        MobileFrame.delFile = function(relativePath, successCallback, errorCallback) {  
            if (!relativePath) {  
                return;  
            }  
            MobileFrame.plusReady(function() {  
                plus.io.resolveLocalFileSystemURL(relativePath, function(entry) {  
                    entry.remove(function(entry) {  
                        if (successCallback && typeof(successCallback) == 'function') {  
                            successCallback(true);  
                        }  
                    }, function(e) {  
                        if (errorCallback && typeof(errorCallback) == 'function') {  
                            errorCallback('删除文件失败!');  
                        }  
                    });  
                }, function() {  
                    if (errorCallback && typeof(errorCallback) == 'function') {  
                        errorCallback('打开文件路径失败!');  
                    }  
                });  
            });  
        };  
        /**  
         * @description 判断网络状态  
         */  
        function GetNetWorkState() {  
            var NetStateStr = '未知';  
            var types = {};  
            types[plus.networkinfo.CONNECTION_UNKNOW] = "未知";  
            types[plus.networkinfo.CONNECTION_NONE] = "未连接网络";  
            types[plus.networkinfo.CONNECTION_ETHERNET] = "有线网络";  
            types[plus.networkinfo.CONNECTION_WIFI] = "WiFi网络";  
            types[plus.networkinfo.CONNECTION_CELL2G] = "2G蜂窝网络";  
            types[plus.networkinfo.CONNECTION_CELL3G] = "3G蜂窝网络";  
            types[plus.networkinfo.CONNECTION_CELL4G] = "4G蜂窝网络";  
            NetStateStr = types[plus.networkinfo.getCurrentType()];  

            return NetStateStr;  
        };  
        /**  
         * @description 判断是否有网络  
         */  
        MobileFrame.IsNetWorkCanUse = function() {  
            var IsCanUseNetWork = false;  
            if (GetNetWorkState() == '未知' || GetNetWorkState() == '未连接网络') {  
                IsCanUseNetWork = false;  
            } else {  
                IsCanUseNetWork = true;  
            }  
            return IsCanUseNetWork;  
        };  
    })();  
    /**  
     * @description 定义模块功能-图片工具类  
     */  
    define('scripts/Core/ImageUtil.js', function(exports) {  
        /**  
         * @description 图片加载工厂,包含图片加载方法  
         * 例如:时间戳控制缓存,下载队列批次下载,默认图片,下载loading图片,下载失败图片  
         * 注意相对路径在Android:/sdcard/Android/data/io.dcloud.HBuilder/.HBuilder/...  
         * iOS:/Library/Pandora/...  
         */  
        (function(ImgLoaderFactory) {  
            /**  
             * 默认的options  
             */  
            var defaultSettingOptions = {  
                //默认的图片缓存目录-存到应用的downloads/imgs下  
                'imgStoragePath': "_downloads/imgs/",  
                //默认图片的基座路径  
                'defaultImgBase': '../../img/mobileFrame/',  
                //loading图片的名称  
                'loadingImgName': 'img_loading.jpg',  
                //error图片名称  
                'errorImgName': 'img_error.jpg',  
                //图片缓存的时间戳,毫秒单位,默认为1天  
                'imgsTimeStamp': 1000 * 60 * 60 * 24 * 1,  
                //同时最多的downloader 并发下载数目,默认为3个  
                'concurrentDownloadCount': 3,  
                //单个下载任务最大的请求时间,防止一些机型上无法触发错误回调,单位毫秒,默认10秒  
                'maxTimeSingleDownloadTaskSpend': 1000 * 10  
            };  
            //默认的下载图片临时变量  
            var defaultLoadingImg = defaultSettingOptions['defaultImgBase'] + defaultSettingOptions['loadingImgName'];  
            //默认的显示图片临时变量  
            var defaultImg = defaultSettingOptions['defaultImgBase'] + defaultSettingOptions['errorImgName'];  
            /**  
             * 图片缓存的session头部  
             */  
            var imageSessionKey_header = 'imageSessionKey_util_imgs_';  
            /**  
             * 图片缓存的session的管理者  
             */  
            var imageSessionManagerKey = 'imageSessionKey_util_Manager';  
            /**  
             * 图片缓存池,用来解决多张图片并发请求问题  
             * 默认是空的,当有多张图片是同一个请求时,缓存池子中会有数据  
             * 格式  {'url1':[dom1,dom2]}  
             */  
            var requestImgsPool = {};  
            /**  
             * 并发下载任务,包括下载队列,处理最大并发数下载  
             */  
            var concurrentDownloadTask = {  
                //任务池-还没有下载的任务  
                Queue: [],  
                //当前正在下载的任务数量  
                CurrentTaskCount: 0  
            };  
            /**  
             * 当前的任务队列,包含任务的名称,以及时间戳-用来控制最大的超时时间,防止不能正常触发回调  
             * 包含:  
             * taskObj,timeBegin  
             * 格式:{url1:{task1,time1}}  
             */  
            var currentDownloadTasks = {};  
            /**  
             * @description 将对应的图片缓存键值添加进入图片缓存管理中  
             * @param {String} key 图片路径对应的key  
             */  
            function addImageSessionKeyToManager(key) {  
                //获取管理者  
                var manager = plus.storage.getItem(imageSessionManagerKey);  
                if (manager == null) {  
                    //如果以前的缓存为空,生成缓存  
                    manager = [];  
                } else {  
                    try {  
                        manager = JSON.parse(manager);  
                    } catch (e) {}  
                }  
                if (manager.indexOf(key) == -1) {  
                    manager.push(key);  
                }  
                plus.storage.setItem(imageSessionManagerKey, JSON.stringify(manager));  
            };  
            /**  
             * @description 从缓存管理中移除相应的图片缓存  
             * @param {String} key 图片路径对应的key  
             */  
            function removeImageSessionKeyFromManager(key) {  
                //获取管理者  
                var manager = plus.storage.getItem(imageSessionManagerKey);  
                if (manager == null) {  
                    //这时候肯定没有离线缓存  
                    return;  
                }  
                try {  
                    manager = JSON.parse(manager);  
                } catch (e) {}  
                var index = -1;  
                for (var i = 0; i < manager.length || 0; i++) {  
                    if (manager[i] == key) {  
                        index = i;  
                    }  
                }  
                if (index != -1) {  
                    //删除对应的index位置  
                    manager.splice(index, 1);  
                    //重新存储  
                    plus.storage.setItem(imageSessionManagerKey, JSON.stringify(manager));  
                }  
            };  

            /**  
             * 设置图片缓存key  
             * @param {String} url  
             * @param {JSON} value 存进去的是图片相关的所有属性,包括时间戳,本地路径等  
             */  
            function setImageSessionItem(url, value) {  
                if (url == null) {  
                    return;  
                }  
                //然后添加进入缓存管理者中  
                addImageSessionKeyToManager(url);  
                url = imageSessionKey_header + MobileFrame.getRelativePathKey(url);  
                value = (value != null) ? value : '';  
                value = (typeof(value) == 'string') ? value : JSON.stringify(value);  
                plus.storage.setItem(url, value);  

            };  
            /**  
             * 获取图片缓存key  
             * @param {String} url  
             * @return {JSON} item 返回的是一个json对象,包括图片相关的所有属性,包括时间戳,本地路径等  
             * @example 包含属性:time localPath  
             */  
            function getImageSessionItem(url) {  
                if (url == null) {  
                    return null;  
                }  
                //去除非法字符  
                url = imageSessionKey_header + MobileFrame.getRelativePathKey(url);  
                var item = plus.storage.getItem(url);  
                try {  
                    if (item != null) {  
                        item = JSON.parse(item);  
                    }  
                } catch (e) {}  
                return item;  
            };  
            /**  
             * 移除图片缓存key  
             * @param {String} url  
             */  
            function removeImageSessionItem(url) {  
                if (url == null) {  
                    return null;  
                }  
                removeImageSessionKeyFromManager(url);  
                //去除非法字符  
                url = imageSessionKey_header + MobileFrame.getRelativePathKey(url);  
                var items = plus.storage.removeItem(url);  
            };  
            /**  
             * @description 设置图片的src地址,根据一个url,为所有需要的图片赋值  
             * @param {HTMLElement} $img 图片的dom,这里为原生dom对象  
             * @param {String} srcUrl 图片的路径  
             * @param {String} relativePath 相对路径,用来判断缓存池的  
             */  
            function setImgSrc($img, srcUrl, relativePath) {  
                if (!srcUrl) {  
                    return;  
                }  
                setImgSrcByDom($img, srcUrl);  
                //如果缓存池子中存在图片  
                if (!relativePath) {  
                    return;  
                }  
                var relativePathKey = MobileFrame.getRelativePathKey(relativePath);  
                if (requestImgsPool && requestImgsPool[relativePathKey]) {  
                    var imgsData = requestImgsPool[relativePathKey];  
                    //如果是数组  
                    if (Array.isArray(imgsData)) {  
                        for (var i = 0; i < imgsData.length; i++) {  
                            setImgSrcByDom(imgsData[i], srcUrl);  
                        }  
                    } else {  
                        //单条数据--单个dom对象  
                        setImgSrcByDom(imgsData, srcUrl);  
                    }  
                    if (srcUrl != defaultLoadingImg) {  
                        //如果不是loading图片就清空  
                        //清空图片池子中的该条键值  
                        requestImgsPool[relativePathKey] = null;  
                    }  
                }  
            };  
            /**  
             * @description 设置图片的src地址,一个dom和一个 src一一对应  
             * @param {HTMLElement} $img 图片的dom,原生dom对象  
             * @param {String} srcUrl 图片的路径  
             */  
            function setImgSrcByDom($img, srcUrl) {  
                if (!$img || !($img instanceof HTMLElement)) {  
                    //console.log('该dom不是原生对象,url:' + srcUrl);  
                    return;  
                }  
                //暂时去除loading图片的独特样式  
//              if (srcUrl == defaultLoadingImg) {  
//                  //默认的loading图片,修改css  
//                  $img.style.maxWidth = '30px';  
//                  $img.style.maxHeight = '30px';  
//              } else {  
//                  //恢复普通高度  
//                  $img.style.maxWidth = '100%';  
//                  $img.style.maxHeight = '100%';  
//              }  
                srcUrl = MobileFrame.changImgUrlTypeNoCache(srcUrl);  
                $img.setAttribute('src', srcUrl);  
            };  
            /**  
             * @description 移除所有的图片缓存键  
             */  
            function clearAllImageSessionKey() {  
                MobileFrame.plusReady(function() {  
                    var manager = plus.storage.getItem(imageSessionManagerKey);  
                    if (manager == null) {  
                        //这时候肯定没有离线缓存  
                        return;  
                    }  
                    try {  
                        manager = JSON.parse(manager);  
                    } catch (e) {}  
                    if (Array.isArray(manager)) {  
                        for (var i = 0; i < manager.length; i++) {  
                            removeImageSessionItem(manager[i]);  
                        }  
                    }  
                });  

            };  
            /**  
             * @description 设置图片加载工具的一些基本参数  
             * @param {JSON} options 参数  
             * @example 参数没传代表使用默认值,包括:  
             * imgStoragePath,string型,图片的默认路径  
             * defaultImgBase,string型,默认图片的基座路径  
             */  
            ImgLoaderFactory.setOptions = function(options) {  
                if (!options) {  
                    return;  
                }  
                //设置参数  
                for (var key in defaultSettingOptions) {  
                    //如果设置的是有效的  
                    if (options[key] != null) {  
                        defaultSettingOptions[key] = options[key];  
                    }  
                }  
                //默认的下载图片临时变量  
                defaultLoadingImg = defaultSettingOptions['defaultImgBase'] + defaultSettingOptions['loadingImgName'];  
                //默认的显示图片临时变量  
                defaultImg = defaultSettingOptions['defaultImgBase'] + defaultSettingOptions['errorImgName'];  
            };  
            /**  
             * @description 清除图片加载工厂的所有图片缓存  
             * @param {Function} successCallback 成功回调  
             * @param {Function} errorCallback 失败回调  
             */  
            ImgLoaderFactory.clearLoaderImgsCache = function(successCallback, errorCallback) {  
                MobileFrame.plusReady(function() {  
                    //遍历目录文件夹下的所有文件,然后删除  
                    var tmpUrl = plus.io.convertLocalFileSystemURL(defaultSettingOptions['imgStoragePath']);  
                    //需要手动加上 file://  
                    tmpUrl = 'file://' + tmpUrl;  
                    //同时清除所有的缓存键值  
                    clearAllImageSessionKey();  
                    plus.io.resolveLocalFileSystemURL(tmpUrl, function(entry) {  
                        entry.removeRecursively(function() {  
                            if (successCallback && typeof(successCallback) == 'function') {  
                                successCallback('清除图片缓存成功!');  
                            }  
                        }, function() {  
                            if (errorCallback && typeof(errorCallback) == 'function') {  
                                errorCallback('清除图片缓存失败!');  
                            }  
                        });  
                    }, function(e) {  
                        if (errorCallback && typeof(errorCallback) == 'function') {  
                            errorCallback('打开图片缓存目录失败!');  
                        }  
                    });  
                });  
            };  
            /**  
             * @description 从一个网络URL中,获取本地图片缓存相对路径  
             * @param {String} loadUrl 图片的网络路径,如果为null,则返回一个null  
             * @return {String} 返回路径或者是 ***outOfTime***表示缓存时间戳过去  
             * @example 获取相对路径可以有很多种方法  
             * 比如可以用md5将url加密,或者其它字符串操作等等  
             * 我这里也是根据项目而进行自适应的  
             */  
            ImgLoaderFactory.getRelativePathFromLoadUrl = function(loadUrl) {  
                if (loadUrl == null) return null;  
                //如果loadUrl以../开头,代表是相对路径,采用本地相对路径,这里就直接返回本地路径,这样就是直接赋值了  
                if (loadUrl.substring(0, 5).indexOf('../') != -1) {  
                    return loadUrl;  
                }  
                //图片缓存如果存在,判断是否过期,默认为''  
                var isOutOfTimeHeader = '';  
                //如果存在本地缓存,并且没有过期,采用本地缓存中的图片  
                var loacalImgSessionItem = getImageSessionItem(loadUrl);  
                if (loacalImgSessionItem != null) {  
                    //判断是否过期  time localPath  
                    if (loacalImgSessionItem.time) {  
                        loacalImgSessionItem.time = parseInt(loacalImgSessionItem.time, 10);  
                        if ((new Date()).valueOf() - loacalImgSessionItem.time > defaultSettingOptions['imgsTimeStamp']) {  
                            //console.log('当前缓存已经过期')  
                            //返回一个特殊字符,代表过期   
                            isOutOfTimeHeader = '***outOfTime***';  
                        } else {  
                            //console.log('缓存未过期');  
                        }  
                    }  
                    if (loacalImgSessionItem.localPath) {  
                        return loacalImgSessionItem.localPath;  
                    }  
                }  
                //获取图片后缀,如果没有获取到后缀,默认是jpg  
                var imgSuffix = loadUrl.substring(loadUrl.lastIndexOf(".") + 1, loadUrl.length);  
                if (  
                    imgSuffix.toLocaleLowerCase() != ("jpg") &&  
                    imgSuffix.toLocaleLowerCase() != ("jpeg") &&  
                    imgSuffix.toLocaleLowerCase() != ("png") &&  
                    imgSuffix.toLocaleLowerCase() != ("bmp") &&  
                    imgSuffix.toLocaleLowerCase() != ("svg") &&  
                    imgSuffix.toLocaleLowerCase() != ("gif")  
                ) {  
                    //如果后缀没有包含以上图片,将后缀改为jpg  
                    imgSuffix = 'jpg';  
                }  
                //更换存储方式,变为将整个路径存储下来,然后去除非法字符  
                var regIllegal = /[&\|\\\*^%$#@\-:.?\/=!]/g;  
                //获取图片名字  
                var imgName = loadUrl.replace(regIllegal, '');  
                //最终的名字  
                var filename = imgName + '.' + imgSuffix;  
                //console.log('loadurl:'+loadUrl+',fileName:'+filename);  
                var relativePath = defaultSettingOptions['imgStoragePath'] + filename;  
                setImageSessionItem(loadUrl, {  
                    'localPath': relativePath,  
                    'time': (new Date()).valueOf()  
                });  
                //将是否过期标识传出  
                return isOutOfTimeHeader + relativePath;  
            };  

            /**  
             * @description 删除某一张网络图片的本地缓存,同时也会删除缓存键值  
             */  
            ImgLoaderFactory.clearNetUrlImgCache = function(netImgUrl, successCallback, errorCallback) {  
                MobileFrame.plusReady(function() {  
                    //删除该键值对应的缓存  
                    removeImageSessionItem(netImgUrl);  
                    MobileFrame.delFile(ImgLoaderFactory.getRelativePathFromLoadUrl(netImgUrl), successCallback, errorCallback);  
                });  
            };  

            /**  
             * @description 给指定的图片dom 设置本地图片属性  
             * @param {HTMLElement} $img 目标图片dom,这里为原生的dom对象  
             * @param {String} relativePath 本地图片路径  
             */  
            function setImgFromLocalCache($img, relativePath) {  
                /*例如:  
                 * 本地相对路径("downloads/imgs/logo.jpg")转成SD卡绝对路径  
                 * 例如相对路径:downloads/imgs/logo.jpg  
                 * ("/storage/emulated/0/Android/data/io.dcloud.HBuilder/.HBuilder/downloads/imgs/logo.jpg");  
                 * */  
                if (!relativePath) {  
                    return;  
                }  
                MobileFrame.plusReady(function() {  
                    var sd_path = plus.io.convertLocalFileSystemURL(relativePath);  
                    setImgSrc($img, sd_path, relativePath);  
                });  
            };  
            /**  
             * @description 通过本地缓存的方法显示网络图片  
             * @param {HTMLElement} $img 原生dom对象  
             * @param {String} loadUrl loadurl  
             */  
            ImgLoaderFactory.setImgWidthLocalCache = function($img, loadUrl) {  
                if ($img == null || loadUrl == null) return;  
                MobileFrame.plusReady(function() {  
                    var relativePath = ImgLoaderFactory.getRelativePathFromLoadUrl(loadUrl);  
                    //判断需不需要将路径进行编码,如果是中文路径,需要编码后才能下载  
                    var regChinese = /[\u4E00-\u9FA5]/g;  
                    var tmpLoadUrl = loadUrl.replace(regChinese, 'chineseRemoveAfter');  
                    if (tmpLoadUrl.indexOf('chineseRemoveAfter') != -1) {  
                        loadUrl = encodeURI(loadUrl);  
                    }  
                    //判断是否已经缓存过期  
                    var isCacheOutOfTime = false;  
                    if (relativePath.indexOf('***outOfTime***') != -1) {  
                        relativePath.replace('***outOfTime***', '');  
                        isCacheOutOfTime = true;  
                    }  
                    if (relativePath == 'default.jpg') {  
                        //设置默认图片  
                        setImgSrc($img, defaultImg);  
                    } else {  
                        if (isCacheOutOfTime == false) {  
                            //检查图片是否已存在  
                            plus.io.resolveLocalFileSystemURL(relativePath, function(entry) {  
                                //如果文件存在,则直接设置本地图片  
                                setImgFromLocalCache($img, relativePath);  
                            }, function(e) {  
                                readyToGetNetImg($img, loadUrl, relativePath);  
                            });  
                        } else {  
                            //否则,本地缓存已经过期,直接网络获取  
                            readyToGetNetImg($img, loadUrl, relativePath);  
                        }  
                    }  
                });  
            };  
            /**  
             * @description 准备通过网络获取图片  
             * @param {HTMLElement} $img 原生dom对象  
             * @param {String} loadUrl loadurl  
             * @param {String} relativePath 本地相对路径  
             */  
            function readyToGetNetImg($img, loadUrl, relativePath) {  
                //如果文件不存在,上网下载  
                if (MobileFrame.IsNetWorkCanUse() == true) {  
                    //添加进入图片缓存池中  
                    var relativePathKey = MobileFrame.getRelativePathKey(relativePath);  
                    if (requestImgsPool && requestImgsPool[relativePathKey] && Array.isArray(requestImgsPool[relativePathKey])) {  
                        //如果已经存在该条图片缓存池,代表这条资源已经进行请求了,只需要填进响应池子即可  
                        //console.log('已经存在缓存池:'+loadUrl);  
                        requestImgsPool[relativePathKey].push($img);  
                        //设置一张加载中图片,这只是假象,其实并没有下载  
                        setImgSrc($img, defaultLoadingImg);  
                        return;  
                    } else {  
                        //新建缓存池  
                        //console.log('新建缓存池:'+loadUrl);  
                        requestImgsPool[relativePathKey] = [];  
                        requestImgsPool[relativePathKey].push($img);  
                    }  
                    //如果网络状态能用,联网下载图片  
                    setImgFromNet($img, loadUrl, relativePath);  
                } else {  
                    //采用本地默认图片  
                    setImgSrc($img, defaultImg);  
                }  
            };  
            /**  
             * @description 从网络下载图片,并给指定的img dom赋值  
             * @param {HTMLElement} $img 目标img 原生dom  
             * @param {String} loadUrl 网络图片路径  
             * @param {String} relativePath 图片下载后的本地路径,如果不指定,采用默认值 在_downloads/imgs/下  
             */  
            function setImgFromNet($img, loadUrl, relativePath) {  
                relativePath = (typeof(relativePath) == 'string' && relativePath != '') ? relativePath : ImgLoaderFactory.getRelativePathFromLoadUrl(loadUrl);  
                //下载参数  
                var options = {  
                    filename: relativePath,  
                    timeout: 3,  
                    retryInterval: 3  
                };  
                //解决ios的网络缓存问题  
                loadUrl = MobileFrame.changImgUrlTypeNoCache(loadUrl);  
                //1.将图片 设为默认的下载图片  
                setImgSrc($img, defaultLoadingImg);  
                //2.创建下载任务  
                var dtask = plus.downloader.createDownload(loadUrl,  
                    options,  
                    function(d, status) {  
                        if (status == 200) {  
                            //下载成功  
                            //console.log('绝对路径:'+d.filename);  
                            //这里传入的是相对路径,方便缓存显示  
                            setImgFromLocalCache($img, relativePath);  
                        } else {  
                            //下载失败,需删除本地临时文件,否则下次进来时会检查到图片已存在  
                            //console.log("下载失败=" + status + "==" + relativePath);  
                            //dtask.abort();//文档描述:取消下载,删除临时文件;(但经测试临时文件没有删除,故使用delFile()方法删除);  
                            if (relativePath != null) {  
                                MobileFrame.delFile(relativePath);  
                            }  
                            setImgSrc($img, defaultImg);  
                        }  
                        //下载完成,当前任务数-1,并重新检查下载队列  
                        concurrentDownloadTask['CurrentTaskCount']--;  
                        //下载完成,从当前下载队列中去除  
                        currentDownloadTasks[dtask.url] = null;  
                        executeDownloadTasks();  
                    });  
                //3.启动下载任务,添加进入下载队列中  
                concurrentDownloadTask['Queue'].push(dtask);  
                //执行并发下载队列  
                executeDownloadTasks();  
            };  
            /**  
             * @description 执行下载任务,通过队列中一个一个的进行  
             */  
            function executeDownloadTasks() {  
                //console.log('检查下载队列');  
                //先检查是否存在任务超时的  
                for (var taskItem in currentDownloadTasks) {  
                    if (currentDownloadTasks[taskItem] &&  
                        currentDownloadTasks[taskItem].timeBegin && (new Date()).valueOf() - currentDownloadTasks[taskItem].timeBegin > defaultSettingOptions['maxTimeSingleDownloadTaskSpend']) {  
                        //如果当前下载人已经超时,并且没有自动触发回调  
                        //终止任务下载  
                        currentDownloadTasks[taskItem].taskObj && currentDownloadTasks[taskItem].taskObj.abort&&currentDownloadTasks[taskItem].taskObj.abort();  
                        concurrentDownloadTask['CurrentTaskCount']--;  
                        //从当前任务队列中去除  
                        currentDownloadTasks[taskItem] = null;  
                        //console.log('存在超时的任务,手动剔除');  
                    }  
                }  
                //如果当前下载任务小于并发下载数         
                if (concurrentDownloadTask['CurrentTaskCount'] < defaultSettingOptions['concurrentDownloadCount']) {  
                    if (concurrentDownloadTask['Queue'].length > 0) {  
                        //开启一个下载任务  
                        var nowTask = concurrentDownloadTask['Queue'].shift();  
                        nowTask.start()  
                            //当前任务数++  
                        concurrentDownloadTask['CurrentTaskCount']++;  
                        currentDownloadTasks[nowTask.url] = {  
                            taskObj: nowTask,  
                            timeBegin: (new Date()).valueOf()  
                        }  
                        //console.log('添加一个下载任务');  
                    }else{  
                        //console.log('已经没有了下载任务');  
                    }  
                }else{  
                    //console.log('已经达到最大下载数量,延迟下载');  
                }  
            };  
            /**  
             * @description 设置页面中的所有图片(本地缓存方式)  
             * 注意,只有存在data-img-localcache 标签的图片才会有效果  
             */  
            ImgLoaderFactory.setAllNetImgsWithLocalCache = function() {  
                //获取页面中所有的图片  
                var imgs = document.querySelectorAll('img');  
                MobileFrame.each(imgs, function(key, value) {  
                    var src = this.getAttribute('data-img-localcache');  
                    //console.log('显示图片:' + src);  
                    if (src != null && src != '') {  
                        ImgLoaderFactory.setImgWidthLocalCache(this, src);  
                    }  
                });  
            };  
        })(exports.ImageLoaderFactory = {});  

        return exports;  
    });  
    /**  
     * 生成模块  
     */  
    {  
        MobileFrame.ImageUtil = require('scripts/Core/ImageUtil.js');  
    }  

})(this);


为了方便,就直接将源码都并入了自己写的框架类中了,
另外感谢@wenju 提供的思路!
欢迎大家留言,讨论!

源码见附件

继续阅读 »

参考文章
http://ask.dcloud.net.cn/article/256
http://ask.dcloud.net.cn/article/397

说明:为了方便,里面使用的图片来源是从上面文章中的源码项目获取的.
说明:参考了上面文章中的思路,然后自己重新写了一个较为完整的图片本地缓存显示工具.
功能
1.第一次显示图片时下载到本地,然后之后如果本地存在缓存(根据url),则显示本地缓存的图片

  1. 基于plus的storage,对每一个图片的缓存进行控制管理,增加时间戳参数,来控制本地缓存的有效时间
  2. 将所有的下载任务放入队列中,进行统一下载管理,增加最大并发请求数,防止一次性请求过多损耗性能
  3. 加入了图片缓存池机制,对于同一个下载路径的图片不会多次下载,而是填入缓存池,下载后统一回调
  4. 修改了图片本地路径的获取方法,摆脱第三方依赖
  5. 重构了代码,并入开发框架中

使用方法

 * 外部API:注意,需要显示的图片需要有data-img-localcache这个属性(用来放目标url),dom需要是原生对象  
 * 清除所有图片缓存:MobileFrame.ImageUtil.ImageLoaderFactory.clearLoaderImgsCache(successCb,errorCb)  
 * 采取本地缓存显示所有图片:MobileFrame.ImageUtil.ImageLoaderFactory.setAllNetImgsWithLocalCache();  
 * 清除某一张图片的本地缓存:MobileFrame.ImageUtil.ImageLoaderFactory.clearNetUrlImgCache(src);  
 * 显示某一张图片:MobileFrame.ImageUtil.ImageLoaderFactory.setImgWidthLocalCache(dom,src);

源码

/**  
 * @description   移动开发框架  
 * @author dailc  dailc   
 * @version 1.0  
 * @time 2016-01-11 16:57:57  
 * 功能模块:只依赖于plus系统  
 * @see http://ask.dcloud.net.cn/people/%E6%92%92%E7%BD%91%E8%A6%81%E8%A7%81%E9%B1%BC  
 * 图片本地缓存模块********************************  
 * 1.本地缓存显示图片  
 * 2.增加storage,增加每一个本地缓存的有效时间戳  
 * 3.增加自定义设置方法,可以根据不同需求,对参数进行修改  
 * 4.采用下载队列进行下载管理,增加最大并发请求数,防止一次性请求过多损耗性能  
 * 5.修改了图片本地路径的获取方法,摆脱第三方依赖  
 * 6.重构代码  
 * 外部API:注意,需要显示的图片需要有data-img-localcache这个属性(用来放目标url)  
 * 清除所有图片缓存:MobileFrame.ImageUtil.ImageLoaderFactory.clearLoaderImgsCache(successCb,errorCb)  
 * 采取本地缓存显示所有图片:MobileFrame.ImageUtil.ImageLoaderFactory.setAllNetImgsWithLocalCache();  
 * 清除某一张图片的本地缓存:MobileFrame.ImageUtil.ImageLoaderFactory.clearNetUrlImgCache(src);  
 * 显示某一张图片:MobileFrame.ImageUtil.ImageLoaderFactory.setImgWidthLocalCache(dom,src);  
 * 图片本地缓存模块完毕********************************  
 */  
(function(global) {  
    /**  
     * 定义全局函数对象 define出来的  
     */  
    var mapping = {};  
    /**  
     * 缓存,正在用到的对象,函数中return 出来的,这样就不需要重复执行函数  
     */  
    var cache = {};  
    /**  
     * @description 模块定义  
     * @param {String} id id  
     * @param {Function} func 对应的函数对象  
     */  
    global.define = function(id, func) {  
        mapping[id] = func;  
    };  
    /**  
     * @description 生成模块对象,并采用本地缓存  
     * @param {String} id  
     */  
    global.require = function(id) {  
        if (!/\.js$/.test(id)) {  
            id += '.js';  
        }  
        if (cache[id]) {  
            return cache[id];  
        } else {  
            return cache[id] = mapping[id]({});  
        }  
    };  
    /**  
     * @description 配置全局工具类以及一些需要用到的全局函数  
     */  
    (function() {  
        global.MobileFrame = {};  
        /**  
         * 空函数  
         */  
        MobileFrame.noop = function() {};  
        /**  
         * @description each遍历操作  
         * @param {type} elements  
         * @param {type} callback  
         * @returns {global}  
         */  
        MobileFrame.each = function(elements, callback, hasOwnProperty) {  
            if (!elements) {  
                return this;  
            }  
            if (typeof elements.length === 'number') {  
                [].every.call(elements, function(el, idx) {  
                    return callback.call(el, idx, el) !== false;  
                });  
            } else {  
                for (var key in elements) {  
                    if (hasOwnProperty) {  
                        if (elements.hasOwnProperty(key)) {  
                            if (callback.call(elements[key], key, elements[key]) === false) return elements;  
                        }  
                    } else {  
                        if (callback.call(elements[key], key, elements[key]) === false) return elements;  
                    }  
                }  
            }  
            return global;  
        };  
        /**  
         * @description plusReady  
         * @param {Function} callback  
         * @returns {global} 返回的是global  
         */  
        MobileFrame.plusReady = function(callback) {  
            if (window.plus) {  
                setTimeout(function() { //解决callback与plusready事件的执行时机问题(典型案例:showWaiting,closeWaiting)  
                    callback();  
                }, 0);  
            } else {  
                document.addEventListener("plusready", function() {  
                    callback();  
                }, false);  
            }  
            return global;  
        };  
        /**  
         * @description 得到相对路径对应的key,这个key可以使缓存池的或者是本地缓存键值  
         * 主要作用是去除非法字符  
         * @param {String} relativePath  
         */  
        MobileFrame.getRelativePathKey = function(relativePath) {  
            var finalKey =  
                //                  relativePath.replace('\/', '').replace('.', '').replace('\/', '')  
                //                  .replace('_download', '').replace('jpg', '');  
                relativePath.replace(/[&\|\\\*^%$#@\-]/g, "");  
            return finalKey;  
        };  
        /**  
         * @description 更改url类型,去除cache,因为cache会造成一些困扰  
         * @param {String} url 传入的url  
         */  
        MobileFrame.changImgUrlTypeNoCache = function(url) {  
            url = url || '';  
            if (url.indexOf('?') != -1) {  
                url += '&timeRandKey=' + Math.random();  
            } else {  
                url += '?timeRandKey=' + Math.random();  
            }  
            return url;  
        };  
        /**  
         * @description 删除指定路径的文件  
         * @param {String} relativePath  绝对路径或相对路径例如:  _downloads/imgs/test.jpg  
         * @param {Function} successCallback  删除成功回调  
         * @param {Function} errorCallback  失败回调  
         */  
        MobileFrame.delFile = function(relativePath, successCallback, errorCallback) {  
            if (!relativePath) {  
                return;  
            }  
            MobileFrame.plusReady(function() {  
                plus.io.resolveLocalFileSystemURL(relativePath, function(entry) {  
                    entry.remove(function(entry) {  
                        if (successCallback && typeof(successCallback) == 'function') {  
                            successCallback(true);  
                        }  
                    }, function(e) {  
                        if (errorCallback && typeof(errorCallback) == 'function') {  
                            errorCallback('删除文件失败!');  
                        }  
                    });  
                }, function() {  
                    if (errorCallback && typeof(errorCallback) == 'function') {  
                        errorCallback('打开文件路径失败!');  
                    }  
                });  
            });  
        };  
        /**  
         * @description 判断网络状态  
         */  
        function GetNetWorkState() {  
            var NetStateStr = '未知';  
            var types = {};  
            types[plus.networkinfo.CONNECTION_UNKNOW] = "未知";  
            types[plus.networkinfo.CONNECTION_NONE] = "未连接网络";  
            types[plus.networkinfo.CONNECTION_ETHERNET] = "有线网络";  
            types[plus.networkinfo.CONNECTION_WIFI] = "WiFi网络";  
            types[plus.networkinfo.CONNECTION_CELL2G] = "2G蜂窝网络";  
            types[plus.networkinfo.CONNECTION_CELL3G] = "3G蜂窝网络";  
            types[plus.networkinfo.CONNECTION_CELL4G] = "4G蜂窝网络";  
            NetStateStr = types[plus.networkinfo.getCurrentType()];  

            return NetStateStr;  
        };  
        /**  
         * @description 判断是否有网络  
         */  
        MobileFrame.IsNetWorkCanUse = function() {  
            var IsCanUseNetWork = false;  
            if (GetNetWorkState() == '未知' || GetNetWorkState() == '未连接网络') {  
                IsCanUseNetWork = false;  
            } else {  
                IsCanUseNetWork = true;  
            }  
            return IsCanUseNetWork;  
        };  
    })();  
    /**  
     * @description 定义模块功能-图片工具类  
     */  
    define('scripts/Core/ImageUtil.js', function(exports) {  
        /**  
         * @description 图片加载工厂,包含图片加载方法  
         * 例如:时间戳控制缓存,下载队列批次下载,默认图片,下载loading图片,下载失败图片  
         * 注意相对路径在Android:/sdcard/Android/data/io.dcloud.HBuilder/.HBuilder/...  
         * iOS:/Library/Pandora/...  
         */  
        (function(ImgLoaderFactory) {  
            /**  
             * 默认的options  
             */  
            var defaultSettingOptions = {  
                //默认的图片缓存目录-存到应用的downloads/imgs下  
                'imgStoragePath': "_downloads/imgs/",  
                //默认图片的基座路径  
                'defaultImgBase': '../../img/mobileFrame/',  
                //loading图片的名称  
                'loadingImgName': 'img_loading.jpg',  
                //error图片名称  
                'errorImgName': 'img_error.jpg',  
                //图片缓存的时间戳,毫秒单位,默认为1天  
                'imgsTimeStamp': 1000 * 60 * 60 * 24 * 1,  
                //同时最多的downloader 并发下载数目,默认为3个  
                'concurrentDownloadCount': 3,  
                //单个下载任务最大的请求时间,防止一些机型上无法触发错误回调,单位毫秒,默认10秒  
                'maxTimeSingleDownloadTaskSpend': 1000 * 10  
            };  
            //默认的下载图片临时变量  
            var defaultLoadingImg = defaultSettingOptions['defaultImgBase'] + defaultSettingOptions['loadingImgName'];  
            //默认的显示图片临时变量  
            var defaultImg = defaultSettingOptions['defaultImgBase'] + defaultSettingOptions['errorImgName'];  
            /**  
             * 图片缓存的session头部  
             */  
            var imageSessionKey_header = 'imageSessionKey_util_imgs_';  
            /**  
             * 图片缓存的session的管理者  
             */  
            var imageSessionManagerKey = 'imageSessionKey_util_Manager';  
            /**  
             * 图片缓存池,用来解决多张图片并发请求问题  
             * 默认是空的,当有多张图片是同一个请求时,缓存池子中会有数据  
             * 格式  {'url1':[dom1,dom2]}  
             */  
            var requestImgsPool = {};  
            /**  
             * 并发下载任务,包括下载队列,处理最大并发数下载  
             */  
            var concurrentDownloadTask = {  
                //任务池-还没有下载的任务  
                Queue: [],  
                //当前正在下载的任务数量  
                CurrentTaskCount: 0  
            };  
            /**  
             * 当前的任务队列,包含任务的名称,以及时间戳-用来控制最大的超时时间,防止不能正常触发回调  
             * 包含:  
             * taskObj,timeBegin  
             * 格式:{url1:{task1,time1}}  
             */  
            var currentDownloadTasks = {};  
            /**  
             * @description 将对应的图片缓存键值添加进入图片缓存管理中  
             * @param {String} key 图片路径对应的key  
             */  
            function addImageSessionKeyToManager(key) {  
                //获取管理者  
                var manager = plus.storage.getItem(imageSessionManagerKey);  
                if (manager == null) {  
                    //如果以前的缓存为空,生成缓存  
                    manager = [];  
                } else {  
                    try {  
                        manager = JSON.parse(manager);  
                    } catch (e) {}  
                }  
                if (manager.indexOf(key) == -1) {  
                    manager.push(key);  
                }  
                plus.storage.setItem(imageSessionManagerKey, JSON.stringify(manager));  
            };  
            /**  
             * @description 从缓存管理中移除相应的图片缓存  
             * @param {String} key 图片路径对应的key  
             */  
            function removeImageSessionKeyFromManager(key) {  
                //获取管理者  
                var manager = plus.storage.getItem(imageSessionManagerKey);  
                if (manager == null) {  
                    //这时候肯定没有离线缓存  
                    return;  
                }  
                try {  
                    manager = JSON.parse(manager);  
                } catch (e) {}  
                var index = -1;  
                for (var i = 0; i < manager.length || 0; i++) {  
                    if (manager[i] == key) {  
                        index = i;  
                    }  
                }  
                if (index != -1) {  
                    //删除对应的index位置  
                    manager.splice(index, 1);  
                    //重新存储  
                    plus.storage.setItem(imageSessionManagerKey, JSON.stringify(manager));  
                }  
            };  

            /**  
             * 设置图片缓存key  
             * @param {String} url  
             * @param {JSON} value 存进去的是图片相关的所有属性,包括时间戳,本地路径等  
             */  
            function setImageSessionItem(url, value) {  
                if (url == null) {  
                    return;  
                }  
                //然后添加进入缓存管理者中  
                addImageSessionKeyToManager(url);  
                url = imageSessionKey_header + MobileFrame.getRelativePathKey(url);  
                value = (value != null) ? value : '';  
                value = (typeof(value) == 'string') ? value : JSON.stringify(value);  
                plus.storage.setItem(url, value);  

            };  
            /**  
             * 获取图片缓存key  
             * @param {String} url  
             * @return {JSON} item 返回的是一个json对象,包括图片相关的所有属性,包括时间戳,本地路径等  
             * @example 包含属性:time localPath  
             */  
            function getImageSessionItem(url) {  
                if (url == null) {  
                    return null;  
                }  
                //去除非法字符  
                url = imageSessionKey_header + MobileFrame.getRelativePathKey(url);  
                var item = plus.storage.getItem(url);  
                try {  
                    if (item != null) {  
                        item = JSON.parse(item);  
                    }  
                } catch (e) {}  
                return item;  
            };  
            /**  
             * 移除图片缓存key  
             * @param {String} url  
             */  
            function removeImageSessionItem(url) {  
                if (url == null) {  
                    return null;  
                }  
                removeImageSessionKeyFromManager(url);  
                //去除非法字符  
                url = imageSessionKey_header + MobileFrame.getRelativePathKey(url);  
                var items = plus.storage.removeItem(url);  
            };  
            /**  
             * @description 设置图片的src地址,根据一个url,为所有需要的图片赋值  
             * @param {HTMLElement} $img 图片的dom,这里为原生dom对象  
             * @param {String} srcUrl 图片的路径  
             * @param {String} relativePath 相对路径,用来判断缓存池的  
             */  
            function setImgSrc($img, srcUrl, relativePath) {  
                if (!srcUrl) {  
                    return;  
                }  
                setImgSrcByDom($img, srcUrl);  
                //如果缓存池子中存在图片  
                if (!relativePath) {  
                    return;  
                }  
                var relativePathKey = MobileFrame.getRelativePathKey(relativePath);  
                if (requestImgsPool && requestImgsPool[relativePathKey]) {  
                    var imgsData = requestImgsPool[relativePathKey];  
                    //如果是数组  
                    if (Array.isArray(imgsData)) {  
                        for (var i = 0; i < imgsData.length; i++) {  
                            setImgSrcByDom(imgsData[i], srcUrl);  
                        }  
                    } else {  
                        //单条数据--单个dom对象  
                        setImgSrcByDom(imgsData, srcUrl);  
                    }  
                    if (srcUrl != defaultLoadingImg) {  
                        //如果不是loading图片就清空  
                        //清空图片池子中的该条键值  
                        requestImgsPool[relativePathKey] = null;  
                    }  
                }  
            };  
            /**  
             * @description 设置图片的src地址,一个dom和一个 src一一对应  
             * @param {HTMLElement} $img 图片的dom,原生dom对象  
             * @param {String} srcUrl 图片的路径  
             */  
            function setImgSrcByDom($img, srcUrl) {  
                if (!$img || !($img instanceof HTMLElement)) {  
                    //console.log('该dom不是原生对象,url:' + srcUrl);  
                    return;  
                }  
                //暂时去除loading图片的独特样式  
//              if (srcUrl == defaultLoadingImg) {  
//                  //默认的loading图片,修改css  
//                  $img.style.maxWidth = '30px';  
//                  $img.style.maxHeight = '30px';  
//              } else {  
//                  //恢复普通高度  
//                  $img.style.maxWidth = '100%';  
//                  $img.style.maxHeight = '100%';  
//              }  
                srcUrl = MobileFrame.changImgUrlTypeNoCache(srcUrl);  
                $img.setAttribute('src', srcUrl);  
            };  
            /**  
             * @description 移除所有的图片缓存键  
             */  
            function clearAllImageSessionKey() {  
                MobileFrame.plusReady(function() {  
                    var manager = plus.storage.getItem(imageSessionManagerKey);  
                    if (manager == null) {  
                        //这时候肯定没有离线缓存  
                        return;  
                    }  
                    try {  
                        manager = JSON.parse(manager);  
                    } catch (e) {}  
                    if (Array.isArray(manager)) {  
                        for (var i = 0; i < manager.length; i++) {  
                            removeImageSessionItem(manager[i]);  
                        }  
                    }  
                });  

            };  
            /**  
             * @description 设置图片加载工具的一些基本参数  
             * @param {JSON} options 参数  
             * @example 参数没传代表使用默认值,包括:  
             * imgStoragePath,string型,图片的默认路径  
             * defaultImgBase,string型,默认图片的基座路径  
             */  
            ImgLoaderFactory.setOptions = function(options) {  
                if (!options) {  
                    return;  
                }  
                //设置参数  
                for (var key in defaultSettingOptions) {  
                    //如果设置的是有效的  
                    if (options[key] != null) {  
                        defaultSettingOptions[key] = options[key];  
                    }  
                }  
                //默认的下载图片临时变量  
                defaultLoadingImg = defaultSettingOptions['defaultImgBase'] + defaultSettingOptions['loadingImgName'];  
                //默认的显示图片临时变量  
                defaultImg = defaultSettingOptions['defaultImgBase'] + defaultSettingOptions['errorImgName'];  
            };  
            /**  
             * @description 清除图片加载工厂的所有图片缓存  
             * @param {Function} successCallback 成功回调  
             * @param {Function} errorCallback 失败回调  
             */  
            ImgLoaderFactory.clearLoaderImgsCache = function(successCallback, errorCallback) {  
                MobileFrame.plusReady(function() {  
                    //遍历目录文件夹下的所有文件,然后删除  
                    var tmpUrl = plus.io.convertLocalFileSystemURL(defaultSettingOptions['imgStoragePath']);  
                    //需要手动加上 file://  
                    tmpUrl = 'file://' + tmpUrl;  
                    //同时清除所有的缓存键值  
                    clearAllImageSessionKey();  
                    plus.io.resolveLocalFileSystemURL(tmpUrl, function(entry) {  
                        entry.removeRecursively(function() {  
                            if (successCallback && typeof(successCallback) == 'function') {  
                                successCallback('清除图片缓存成功!');  
                            }  
                        }, function() {  
                            if (errorCallback && typeof(errorCallback) == 'function') {  
                                errorCallback('清除图片缓存失败!');  
                            }  
                        });  
                    }, function(e) {  
                        if (errorCallback && typeof(errorCallback) == 'function') {  
                            errorCallback('打开图片缓存目录失败!');  
                        }  
                    });  
                });  
            };  
            /**  
             * @description 从一个网络URL中,获取本地图片缓存相对路径  
             * @param {String} loadUrl 图片的网络路径,如果为null,则返回一个null  
             * @return {String} 返回路径或者是 ***outOfTime***表示缓存时间戳过去  
             * @example 获取相对路径可以有很多种方法  
             * 比如可以用md5将url加密,或者其它字符串操作等等  
             * 我这里也是根据项目而进行自适应的  
             */  
            ImgLoaderFactory.getRelativePathFromLoadUrl = function(loadUrl) {  
                if (loadUrl == null) return null;  
                //如果loadUrl以../开头,代表是相对路径,采用本地相对路径,这里就直接返回本地路径,这样就是直接赋值了  
                if (loadUrl.substring(0, 5).indexOf('../') != -1) {  
                    return loadUrl;  
                }  
                //图片缓存如果存在,判断是否过期,默认为''  
                var isOutOfTimeHeader = '';  
                //如果存在本地缓存,并且没有过期,采用本地缓存中的图片  
                var loacalImgSessionItem = getImageSessionItem(loadUrl);  
                if (loacalImgSessionItem != null) {  
                    //判断是否过期  time localPath  
                    if (loacalImgSessionItem.time) {  
                        loacalImgSessionItem.time = parseInt(loacalImgSessionItem.time, 10);  
                        if ((new Date()).valueOf() - loacalImgSessionItem.time > defaultSettingOptions['imgsTimeStamp']) {  
                            //console.log('当前缓存已经过期')  
                            //返回一个特殊字符,代表过期   
                            isOutOfTimeHeader = '***outOfTime***';  
                        } else {  
                            //console.log('缓存未过期');  
                        }  
                    }  
                    if (loacalImgSessionItem.localPath) {  
                        return loacalImgSessionItem.localPath;  
                    }  
                }  
                //获取图片后缀,如果没有获取到后缀,默认是jpg  
                var imgSuffix = loadUrl.substring(loadUrl.lastIndexOf(".") + 1, loadUrl.length);  
                if (  
                    imgSuffix.toLocaleLowerCase() != ("jpg") &&  
                    imgSuffix.toLocaleLowerCase() != ("jpeg") &&  
                    imgSuffix.toLocaleLowerCase() != ("png") &&  
                    imgSuffix.toLocaleLowerCase() != ("bmp") &&  
                    imgSuffix.toLocaleLowerCase() != ("svg") &&  
                    imgSuffix.toLocaleLowerCase() != ("gif")  
                ) {  
                    //如果后缀没有包含以上图片,将后缀改为jpg  
                    imgSuffix = 'jpg';  
                }  
                //更换存储方式,变为将整个路径存储下来,然后去除非法字符  
                var regIllegal = /[&\|\\\*^%$#@\-:.?\/=!]/g;  
                //获取图片名字  
                var imgName = loadUrl.replace(regIllegal, '');  
                //最终的名字  
                var filename = imgName + '.' + imgSuffix;  
                //console.log('loadurl:'+loadUrl+',fileName:'+filename);  
                var relativePath = defaultSettingOptions['imgStoragePath'] + filename;  
                setImageSessionItem(loadUrl, {  
                    'localPath': relativePath,  
                    'time': (new Date()).valueOf()  
                });  
                //将是否过期标识传出  
                return isOutOfTimeHeader + relativePath;  
            };  

            /**  
             * @description 删除某一张网络图片的本地缓存,同时也会删除缓存键值  
             */  
            ImgLoaderFactory.clearNetUrlImgCache = function(netImgUrl, successCallback, errorCallback) {  
                MobileFrame.plusReady(function() {  
                    //删除该键值对应的缓存  
                    removeImageSessionItem(netImgUrl);  
                    MobileFrame.delFile(ImgLoaderFactory.getRelativePathFromLoadUrl(netImgUrl), successCallback, errorCallback);  
                });  
            };  

            /**  
             * @description 给指定的图片dom 设置本地图片属性  
             * @param {HTMLElement} $img 目标图片dom,这里为原生的dom对象  
             * @param {String} relativePath 本地图片路径  
             */  
            function setImgFromLocalCache($img, relativePath) {  
                /*例如:  
                 * 本地相对路径("downloads/imgs/logo.jpg")转成SD卡绝对路径  
                 * 例如相对路径:downloads/imgs/logo.jpg  
                 * ("/storage/emulated/0/Android/data/io.dcloud.HBuilder/.HBuilder/downloads/imgs/logo.jpg");  
                 * */  
                if (!relativePath) {  
                    return;  
                }  
                MobileFrame.plusReady(function() {  
                    var sd_path = plus.io.convertLocalFileSystemURL(relativePath);  
                    setImgSrc($img, sd_path, relativePath);  
                });  
            };  
            /**  
             * @description 通过本地缓存的方法显示网络图片  
             * @param {HTMLElement} $img 原生dom对象  
             * @param {String} loadUrl loadurl  
             */  
            ImgLoaderFactory.setImgWidthLocalCache = function($img, loadUrl) {  
                if ($img == null || loadUrl == null) return;  
                MobileFrame.plusReady(function() {  
                    var relativePath = ImgLoaderFactory.getRelativePathFromLoadUrl(loadUrl);  
                    //判断需不需要将路径进行编码,如果是中文路径,需要编码后才能下载  
                    var regChinese = /[\u4E00-\u9FA5]/g;  
                    var tmpLoadUrl = loadUrl.replace(regChinese, 'chineseRemoveAfter');  
                    if (tmpLoadUrl.indexOf('chineseRemoveAfter') != -1) {  
                        loadUrl = encodeURI(loadUrl);  
                    }  
                    //判断是否已经缓存过期  
                    var isCacheOutOfTime = false;  
                    if (relativePath.indexOf('***outOfTime***') != -1) {  
                        relativePath.replace('***outOfTime***', '');  
                        isCacheOutOfTime = true;  
                    }  
                    if (relativePath == 'default.jpg') {  
                        //设置默认图片  
                        setImgSrc($img, defaultImg);  
                    } else {  
                        if (isCacheOutOfTime == false) {  
                            //检查图片是否已存在  
                            plus.io.resolveLocalFileSystemURL(relativePath, function(entry) {  
                                //如果文件存在,则直接设置本地图片  
                                setImgFromLocalCache($img, relativePath);  
                            }, function(e) {  
                                readyToGetNetImg($img, loadUrl, relativePath);  
                            });  
                        } else {  
                            //否则,本地缓存已经过期,直接网络获取  
                            readyToGetNetImg($img, loadUrl, relativePath);  
                        }  
                    }  
                });  
            };  
            /**  
             * @description 准备通过网络获取图片  
             * @param {HTMLElement} $img 原生dom对象  
             * @param {String} loadUrl loadurl  
             * @param {String} relativePath 本地相对路径  
             */  
            function readyToGetNetImg($img, loadUrl, relativePath) {  
                //如果文件不存在,上网下载  
                if (MobileFrame.IsNetWorkCanUse() == true) {  
                    //添加进入图片缓存池中  
                    var relativePathKey = MobileFrame.getRelativePathKey(relativePath);  
                    if (requestImgsPool && requestImgsPool[relativePathKey] && Array.isArray(requestImgsPool[relativePathKey])) {  
                        //如果已经存在该条图片缓存池,代表这条资源已经进行请求了,只需要填进响应池子即可  
                        //console.log('已经存在缓存池:'+loadUrl);  
                        requestImgsPool[relativePathKey].push($img);  
                        //设置一张加载中图片,这只是假象,其实并没有下载  
                        setImgSrc($img, defaultLoadingImg);  
                        return;  
                    } else {  
                        //新建缓存池  
                        //console.log('新建缓存池:'+loadUrl);  
                        requestImgsPool[relativePathKey] = [];  
                        requestImgsPool[relativePathKey].push($img);  
                    }  
                    //如果网络状态能用,联网下载图片  
                    setImgFromNet($img, loadUrl, relativePath);  
                } else {  
                    //采用本地默认图片  
                    setImgSrc($img, defaultImg);  
                }  
            };  
            /**  
             * @description 从网络下载图片,并给指定的img dom赋值  
             * @param {HTMLElement} $img 目标img 原生dom  
             * @param {String} loadUrl 网络图片路径  
             * @param {String} relativePath 图片下载后的本地路径,如果不指定,采用默认值 在_downloads/imgs/下  
             */  
            function setImgFromNet($img, loadUrl, relativePath) {  
                relativePath = (typeof(relativePath) == 'string' && relativePath != '') ? relativePath : ImgLoaderFactory.getRelativePathFromLoadUrl(loadUrl);  
                //下载参数  
                var options = {  
                    filename: relativePath,  
                    timeout: 3,  
                    retryInterval: 3  
                };  
                //解决ios的网络缓存问题  
                loadUrl = MobileFrame.changImgUrlTypeNoCache(loadUrl);  
                //1.将图片 设为默认的下载图片  
                setImgSrc($img, defaultLoadingImg);  
                //2.创建下载任务  
                var dtask = plus.downloader.createDownload(loadUrl,  
                    options,  
                    function(d, status) {  
                        if (status == 200) {  
                            //下载成功  
                            //console.log('绝对路径:'+d.filename);  
                            //这里传入的是相对路径,方便缓存显示  
                            setImgFromLocalCache($img, relativePath);  
                        } else {  
                            //下载失败,需删除本地临时文件,否则下次进来时会检查到图片已存在  
                            //console.log("下载失败=" + status + "==" + relativePath);  
                            //dtask.abort();//文档描述:取消下载,删除临时文件;(但经测试临时文件没有删除,故使用delFile()方法删除);  
                            if (relativePath != null) {  
                                MobileFrame.delFile(relativePath);  
                            }  
                            setImgSrc($img, defaultImg);  
                        }  
                        //下载完成,当前任务数-1,并重新检查下载队列  
                        concurrentDownloadTask['CurrentTaskCount']--;  
                        //下载完成,从当前下载队列中去除  
                        currentDownloadTasks[dtask.url] = null;  
                        executeDownloadTasks();  
                    });  
                //3.启动下载任务,添加进入下载队列中  
                concurrentDownloadTask['Queue'].push(dtask);  
                //执行并发下载队列  
                executeDownloadTasks();  
            };  
            /**  
             * @description 执行下载任务,通过队列中一个一个的进行  
             */  
            function executeDownloadTasks() {  
                //console.log('检查下载队列');  
                //先检查是否存在任务超时的  
                for (var taskItem in currentDownloadTasks) {  
                    if (currentDownloadTasks[taskItem] &&  
                        currentDownloadTasks[taskItem].timeBegin && (new Date()).valueOf() - currentDownloadTasks[taskItem].timeBegin > defaultSettingOptions['maxTimeSingleDownloadTaskSpend']) {  
                        //如果当前下载人已经超时,并且没有自动触发回调  
                        //终止任务下载  
                        currentDownloadTasks[taskItem].taskObj && currentDownloadTasks[taskItem].taskObj.abort&&currentDownloadTasks[taskItem].taskObj.abort();  
                        concurrentDownloadTask['CurrentTaskCount']--;  
                        //从当前任务队列中去除  
                        currentDownloadTasks[taskItem] = null;  
                        //console.log('存在超时的任务,手动剔除');  
                    }  
                }  
                //如果当前下载任务小于并发下载数         
                if (concurrentDownloadTask['CurrentTaskCount'] < defaultSettingOptions['concurrentDownloadCount']) {  
                    if (concurrentDownloadTask['Queue'].length > 0) {  
                        //开启一个下载任务  
                        var nowTask = concurrentDownloadTask['Queue'].shift();  
                        nowTask.start()  
                            //当前任务数++  
                        concurrentDownloadTask['CurrentTaskCount']++;  
                        currentDownloadTasks[nowTask.url] = {  
                            taskObj: nowTask,  
                            timeBegin: (new Date()).valueOf()  
                        }  
                        //console.log('添加一个下载任务');  
                    }else{  
                        //console.log('已经没有了下载任务');  
                    }  
                }else{  
                    //console.log('已经达到最大下载数量,延迟下载');  
                }  
            };  
            /**  
             * @description 设置页面中的所有图片(本地缓存方式)  
             * 注意,只有存在data-img-localcache 标签的图片才会有效果  
             */  
            ImgLoaderFactory.setAllNetImgsWithLocalCache = function() {  
                //获取页面中所有的图片  
                var imgs = document.querySelectorAll('img');  
                MobileFrame.each(imgs, function(key, value) {  
                    var src = this.getAttribute('data-img-localcache');  
                    //console.log('显示图片:' + src);  
                    if (src != null && src != '') {  
                        ImgLoaderFactory.setImgWidthLocalCache(this, src);  
                    }  
                });  
            };  
        })(exports.ImageLoaderFactory = {});  

        return exports;  
    });  
    /**  
     * 生成模块  
     */  
    {  
        MobileFrame.ImageUtil = require('scripts/Core/ImageUtil.js');  
    }  

})(this);


为了方便,就直接将源码都并入了自己写的框架类中了,
另外感谢@wenju 提供的思路!
欢迎大家留言,讨论!

源码见附件

收起阅读 »

地图定位问题终于搞清楚了。。

定位 高德地图 HTML5+

不得不吐槽下mui的社区,问问题基本没人答复。小白经常被一个问题卡死,严重拖累进度和心情。

自己在qq群里反复询问,在高德社区发帖,一步步搜索问题。终于搞懂了大部分。下面进入正题:

自己的APP需要定位模块,类似滴滴打车的需求。

所以找了高德的JS API,用浏览器定位(小白注意:我们用mui开发的Hybrid APP不能用高德的原生Android API,下面会讲到怎么使用系统的原生能力)

所谓的浏览器定位实际上都是使用了HTML5的Geolocation功能。关于定位,分为GPS定位和网络定位2种。GPS定位,精度较高,可达到10米,但室内不可用,且超级费电。网络定位,分为wifi定位和基站定位,都是通过获取wifi或者基站信息,然后查询对应的wifi或者基站位置数据库,得到的定位地点。定位数据库可以不断完善不断补充,所以,越定位越准确。我们可以先判断浏览器是否支持geolocation, 如果不支持,可以提示错误,或者进入其他逻辑处理流程,现在移动端的智能手机浏览器绝大部分都是支持的。代码如下:

if( navigator.geolocation ){  
    alert('恭喜,您的浏览器支持HTML5 Geolocation API');  
    navigator.geolocation.getCurrentPosition( successCallback, handleLocationError,{maximumAge:15000, timeout:10000, enableHighAccuracy:true});  
}else{  
    alert( "对不起,您的浏览器不支持html5定位");  
}  

function successCallback (){  
    alert('成功获取到位置信息');  
};  

function handleLocationError(error) {  
    console.log(error);  
    switch(error.code){  
        case 0:  
          alert("获取位置信息出错!");  
          break;  
        case 1:  
          alert("您设置了阻止该页面获取位置信息!");  
          break;  
        case 2:  
          alert("浏览器无法确定您的位置!");  
          break;  
        case 3:  
          alert("获取位置信息超时!");  
          break;  
        default:  
          alert('不明原因');  
          break;  
    }  
}

当然浏览器定位不是那么好用,由于各种其他原因和我们的天朝的社会主义特色政策,国行Android手机“阉割”了谷歌GMS服务包,导致HTML5的geolocation无法使用wifi和基站定位服务。经常会定位失败。但是提供地图和定位相关服务的不止是google一家,还有百度、高德、搜狗啊。

咋一看,百度js api也有自己的geolocation(http://developer.baidu.com/map/reference/index.php?title=Class:%E6%9C%8D%E5%8A%A1%E7%B1%BB/Geolocation) 这个取代html5自带的不就可以了么,经过实验,真是图样图森破了,原来所有的javascript API都还是调用的浏览器自身的geolocation进行封装实现的,也就是说如果原本不能wifi+基站定位,用百度、高德的javascript api的效果是一样的。

由于我人在北京,每次都把我定位到了一个叫“军诚水果超市”的地方。开始因为我听说高德的浏览器有很大偏差,以为是定位偏差的问题。但直到我注意到这里:(http://lbs.amap.com/api/javascript-api/reference/plugin/) 在AMap.Geolocation 插件中有这样一句话:当浏览器原生定位接口定位失败后,系统会调用IP定位,返回定位点所在城市中心点。此时,定位精度范围返回“null”。使用高德JS API浏览器定位,精度如果返回null就说明定位失败了,而不是定位偏差。(国内的文档太不规范,着实把我骗到了)

最后要说明的是,如果是开发Android原生软件的话,可以在APP里封装百度地图定位的SDK,这个是可以解决没有谷歌GMS服务包无法定位的问题的,因为百度地图定位SDK实际上是起到和谷歌服务包里面的定位模块一样的作用,由这里也可以看到HTML5 Webapp和原生APP的一个差别,不是一个层面的解决方案。

记得项目目录里的menifest.json文件里可以配置百度和高德的SDK么?这就是HTML5+API赋予你的强大能力!如果你配置正确的话,即可使用系统原生的定位,不需要坑爹的浏览器定位了。这就是Hybrid APP相对Web APP的一个优势。

最终解决方案是:配置好百度或高德的SDK,使用HTML5+API的plus.geolocation.getCurrentPosition()利用原生定位获取到坐标点后,再用百度或高德的JS API去实现其他的地图模块功能。

参考的文章:

http://www.html5col.com/getcurrentposition/

http://blog.csdn.net/albert528108/article/details/39213419

继续阅读 »

不得不吐槽下mui的社区,问问题基本没人答复。小白经常被一个问题卡死,严重拖累进度和心情。

自己在qq群里反复询问,在高德社区发帖,一步步搜索问题。终于搞懂了大部分。下面进入正题:

自己的APP需要定位模块,类似滴滴打车的需求。

所以找了高德的JS API,用浏览器定位(小白注意:我们用mui开发的Hybrid APP不能用高德的原生Android API,下面会讲到怎么使用系统的原生能力)

所谓的浏览器定位实际上都是使用了HTML5的Geolocation功能。关于定位,分为GPS定位和网络定位2种。GPS定位,精度较高,可达到10米,但室内不可用,且超级费电。网络定位,分为wifi定位和基站定位,都是通过获取wifi或者基站信息,然后查询对应的wifi或者基站位置数据库,得到的定位地点。定位数据库可以不断完善不断补充,所以,越定位越准确。我们可以先判断浏览器是否支持geolocation, 如果不支持,可以提示错误,或者进入其他逻辑处理流程,现在移动端的智能手机浏览器绝大部分都是支持的。代码如下:

if( navigator.geolocation ){  
    alert('恭喜,您的浏览器支持HTML5 Geolocation API');  
    navigator.geolocation.getCurrentPosition( successCallback, handleLocationError,{maximumAge:15000, timeout:10000, enableHighAccuracy:true});  
}else{  
    alert( "对不起,您的浏览器不支持html5定位");  
}  

function successCallback (){  
    alert('成功获取到位置信息');  
};  

function handleLocationError(error) {  
    console.log(error);  
    switch(error.code){  
        case 0:  
          alert("获取位置信息出错!");  
          break;  
        case 1:  
          alert("您设置了阻止该页面获取位置信息!");  
          break;  
        case 2:  
          alert("浏览器无法确定您的位置!");  
          break;  
        case 3:  
          alert("获取位置信息超时!");  
          break;  
        default:  
          alert('不明原因');  
          break;  
    }  
}

当然浏览器定位不是那么好用,由于各种其他原因和我们的天朝的社会主义特色政策,国行Android手机“阉割”了谷歌GMS服务包,导致HTML5的geolocation无法使用wifi和基站定位服务。经常会定位失败。但是提供地图和定位相关服务的不止是google一家,还有百度、高德、搜狗啊。

咋一看,百度js api也有自己的geolocation(http://developer.baidu.com/map/reference/index.php?title=Class:%E6%9C%8D%E5%8A%A1%E7%B1%BB/Geolocation) 这个取代html5自带的不就可以了么,经过实验,真是图样图森破了,原来所有的javascript API都还是调用的浏览器自身的geolocation进行封装实现的,也就是说如果原本不能wifi+基站定位,用百度、高德的javascript api的效果是一样的。

由于我人在北京,每次都把我定位到了一个叫“军诚水果超市”的地方。开始因为我听说高德的浏览器有很大偏差,以为是定位偏差的问题。但直到我注意到这里:(http://lbs.amap.com/api/javascript-api/reference/plugin/) 在AMap.Geolocation 插件中有这样一句话:当浏览器原生定位接口定位失败后,系统会调用IP定位,返回定位点所在城市中心点。此时,定位精度范围返回“null”。使用高德JS API浏览器定位,精度如果返回null就说明定位失败了,而不是定位偏差。(国内的文档太不规范,着实把我骗到了)

最后要说明的是,如果是开发Android原生软件的话,可以在APP里封装百度地图定位的SDK,这个是可以解决没有谷歌GMS服务包无法定位的问题的,因为百度地图定位SDK实际上是起到和谷歌服务包里面的定位模块一样的作用,由这里也可以看到HTML5 Webapp和原生APP的一个差别,不是一个层面的解决方案。

记得项目目录里的menifest.json文件里可以配置百度和高德的SDK么?这就是HTML5+API赋予你的强大能力!如果你配置正确的话,即可使用系统原生的定位,不需要坑爹的浏览器定位了。这就是Hybrid APP相对Web APP的一个优势。

最终解决方案是:配置好百度或高德的SDK,使用HTML5+API的plus.geolocation.getCurrentPosition()利用原生定位获取到坐标点后,再用百度或高德的JS API去实现其他的地图模块功能。

参考的文章:

http://www.html5col.com/getcurrentposition/

http://blog.csdn.net/albert528108/article/details/39213419

收起阅读 »

解决使用Mui索引列表“indexed-list-select” 动态加载数据后搜索失效问题

记得前不久刚用上Mui,感觉很赞,后来在开发中遇到一个问题,是关于使用Mui的索引列表的一个问题,因为官方示例数据都是纯静态演示数据,而我实际项目中的数据是需要根据不同的条件来动态加载数据,开发过程中发现动态加载进来的数据在搜索中失效,后来也在官网上提问过,当时问题的链接mui.indexedlist搜索功能失效问题,也有很多热心的网友给了回复,但都没有解决这个问题,后来自己利用空闲的时间去捣腾了一下,然后达到了效果,现在把他分享出来,希望能给遇到同样问题的人一点帮助,下载附件打开即可

继续阅读 »

记得前不久刚用上Mui,感觉很赞,后来在开发中遇到一个问题,是关于使用Mui的索引列表的一个问题,因为官方示例数据都是纯静态演示数据,而我实际项目中的数据是需要根据不同的条件来动态加载数据,开发过程中发现动态加载进来的数据在搜索中失效,后来也在官网上提问过,当时问题的链接mui.indexedlist搜索功能失效问题,也有很多热心的网友给了回复,但都没有解决这个问题,后来自己利用空闲的时间去捣腾了一下,然后达到了效果,现在把他分享出来,希望能给遇到同样问题的人一点帮助,下载附件打开即可

收起阅读 »

Android平台本地(离线)打包指南 - Android Studio

Android SDK App离线打包

新版离线sdk文档已发布,离线打包请参考最新文档

<!--

预备环境

  • AndroidStudio开发环境,要求安装Android4.0或以上(API 14)SDK。
  • 下载HBuilder离线打包Android版SDK(5+ SDK下载)。

离线打包SDK目录说明

  • HBuilder-Hello:离线打包演示应用;
  • HBuilder-Integrate: 5+ SDK 集成和插件开发示例
  • libs:SDK库文件目录;
  • Feature列表.xls:Android平台各扩展Feature API对应的permission;
  • Readme.txt:版本说明文件。

配置编译工程

  • 导入演示应用HBuilder-Hello工程
    启动AndroidStudio 点击导入工程“import project”


    选中工程所在目录点击“OK”

    选择生成的AndroidStudio工程的保存路径,然后点击“next”,

    在演示工程中已经配置好HelloH5应用相关资源,完成导入后,可Shift F10直接编译运行:

    从SDK目录中拷贝要使用的feature库到工程app/libs目录
    参考“Feature列表.xls”文档确定应用中使用到的扩展API,拷贝libs目录中对应的“jar”文件拷贝到工程的app->libs目录下,如果有引用“so”文件需要拷贝到工程的app->src->main->jniLibs下对应目录中。如使用分享功能(新浪微博分享、腾讯微博分享,微信分享),则需要拷贝以下文件:

    拷贝完成后在工程中刷新可在工程的libs目录下显示:

  • 配置应用的权限
    参考“Feature列表.xls”文档确定应用中使用到的扩展API,在AndroidManifest.xml文件中删除不用到API的权限。如不使用音频(plus.audio.*)相关5+ API,则可删除Audio相关的权限:

  • 配置其它第三方库的数据
    由于第三方库需要使用一些特定的数据,所以在使用时需要将相关参数配置到AndroidManifest.xml文件中。如使用分享功能,则需要配置各分享平台的申请应用的相关参数:


    如果不使用此类API,则可将此数据删除。
    插件的配置方法请参考以下文档
    Android离线打包插件配置
    推送插件配置
    分享插件配置
    登陆鉴权插件配置
    地图插件配置
    支付插件配置
    定位插件配置

  • <a id="version">配置应用的包名及版本号</a>
    打开AndroidManifest.xml文件,在代码视图中修改根节点的package属性值,如下:


    其中package为应用的包名,采用反向域名格式,为应用的标识;versionCode为应用的版本号(整数值),用于各应用市场的升级判断,建议与manifest.json中version -> code值一致;versionName为应用的版本名称(字符串),在系统应用管理程序中显示的版本号,建议与manifest.json中version -> name值一致。

  • <a id="name">配置应用名称</a>
    打开app->res -> values -> strings.xml文件,修改“app_name”字段值,该值为安装到手机上桌面显示的应用名称,建议与manifest.json中name(基础配置中的应用名称)对应:

  • <a id="channel">配置离线打包广告支持及渠道打包配置</a>
    新版本SDK简化了离线打包支持DCloud广告联盟功能。
    AndroidManifest.xml文件中添加如下节点到<application>节点中,并配置替换其中5+应用appid、广告联盟会员adid的值和渠道标识,因为涉及到开屏广告业务,项目工程必须以io.dcloud.PandoraEntry作为5+应用的入口Activity。该activity已包含在lib.5plus.base-release中,开发者无需实现

    <meta-data android:name="DCLOUD_AD_ID" android:value="广告标识"/>  
    <meta-data android:name="DCLOUD_AD_SPLASH" android:value="true"/><!--如果不开启开屏广告则不设置此字段或者值设置为false  
    <meta-data android:name="DCLOUD_STREAMAPP_CHANNEL" android:value="包名|应用标识|广告标识|渠道,如io.dcloud.appid|appid|adid|google" /><!--为了保证广告统计的有效性,请正确设置此值
    • 包名:对应Android项目中build.gradle中的applicationId,如io.dcloud.HBuilder
    • 应用标识:对应对应5+或uni-app项目manifest.json中appid
    • 广告标识:DCloud的广告标识,开通广告后可在dev.dcloud.net.cn获取,如果没有开通广告,设置值为空即可
    • 渠道:渠道包制作指南
      注意:提交谷歌应用市场(Google Play)时一定要将渠道标识设置为google!!!
      注意:以上操作只是配置5+应用具备广告能力,实际开通需要在manifest.json配置开关,具体参考DCloud广告联盟
      如果不需要支持广告,仍然可以使用WidgetWebView方式集成。
  • 配置应用图标和启动界面
    将应用的图标(文件名为icon.png)启动图片按照对应的尺寸拷贝到工程的app->src->main->res -> drawable-XXX目录下:

  • 更新应用资源
    打开app->src->main->assets -> apps 目录,将下面“HelloH5”目录名称修改为应用manifest.json中的id名称(这步非常重要,否则会导致应用无法正常启动),并将所有应用资源拷贝到其下的www目录中:

  • 配置应用信息
    打开app->src->main->assets -> data下的control.xml文件,修改appid和appver的值:


    其中appid值为HBuilder应用的appid,必须与应用manifest.json中的id值完全一致;appver为应用的版本号,用于应用资源的升级,必须保持与manifest.json中的version -> name值完全一致;version值为应用基座版本号(plus.runtime.innerVersion返回的值),不要随意修改。

  • 配置完成编译运行
    应用配置完毕,按Shift F10编译运行应用
    在弹出的设备列表对话框中选择要运行的设备:


    点击OK后启动应用:

常见问题

Q: 打包后页面中无法调用5+API
A: 修改app->build.gradle文件defaultConfig节点下miniSdkVersion和targetSdkVersion属性指定版本号为8

注意(新版已不需要添加):打包时如果修改APK的包名,则同时需要修改Rinfomation.java文件import的包名,
同时需要修改AndroidManifest.xml文件中引用包名的插件有
推送插件
微信(登陆,分享 ,支付),微信同时需要修改引入的WXEntryActivity.java和WXPayEntryActivity.java文件所在的包名

Q :使用Android Studio编译时如果提示如下错误

A:
下载附件并解压

点击下载
点击 Android Studio 菜单 File->Settings->Build,Execution,Development->Build Tools->Gradle选择Use local gradle distribution,选择附件解压后的目录点击OK即可

-->

继续阅读 »

新版离线sdk文档已发布,离线打包请参考最新文档

<!--

预备环境

  • AndroidStudio开发环境,要求安装Android4.0或以上(API 14)SDK。
  • 下载HBuilder离线打包Android版SDK(5+ SDK下载)。

离线打包SDK目录说明

  • HBuilder-Hello:离线打包演示应用;
  • HBuilder-Integrate: 5+ SDK 集成和插件开发示例
  • libs:SDK库文件目录;
  • Feature列表.xls:Android平台各扩展Feature API对应的permission;
  • Readme.txt:版本说明文件。

配置编译工程

  • 导入演示应用HBuilder-Hello工程
    启动AndroidStudio 点击导入工程“import project”


    选中工程所在目录点击“OK”

    选择生成的AndroidStudio工程的保存路径,然后点击“next”,

    在演示工程中已经配置好HelloH5应用相关资源,完成导入后,可Shift F10直接编译运行:

    从SDK目录中拷贝要使用的feature库到工程app/libs目录
    参考“Feature列表.xls”文档确定应用中使用到的扩展API,拷贝libs目录中对应的“jar”文件拷贝到工程的app->libs目录下,如果有引用“so”文件需要拷贝到工程的app->src->main->jniLibs下对应目录中。如使用分享功能(新浪微博分享、腾讯微博分享,微信分享),则需要拷贝以下文件:

    拷贝完成后在工程中刷新可在工程的libs目录下显示:

  • 配置应用的权限
    参考“Feature列表.xls”文档确定应用中使用到的扩展API,在AndroidManifest.xml文件中删除不用到API的权限。如不使用音频(plus.audio.*)相关5+ API,则可删除Audio相关的权限:

  • 配置其它第三方库的数据
    由于第三方库需要使用一些特定的数据,所以在使用时需要将相关参数配置到AndroidManifest.xml文件中。如使用分享功能,则需要配置各分享平台的申请应用的相关参数:


    如果不使用此类API,则可将此数据删除。
    插件的配置方法请参考以下文档
    Android离线打包插件配置
    推送插件配置
    分享插件配置
    登陆鉴权插件配置
    地图插件配置
    支付插件配置
    定位插件配置

  • <a id="version">配置应用的包名及版本号</a>
    打开AndroidManifest.xml文件,在代码视图中修改根节点的package属性值,如下:


    其中package为应用的包名,采用反向域名格式,为应用的标识;versionCode为应用的版本号(整数值),用于各应用市场的升级判断,建议与manifest.json中version -> code值一致;versionName为应用的版本名称(字符串),在系统应用管理程序中显示的版本号,建议与manifest.json中version -> name值一致。

  • <a id="name">配置应用名称</a>
    打开app->res -> values -> strings.xml文件,修改“app_name”字段值,该值为安装到手机上桌面显示的应用名称,建议与manifest.json中name(基础配置中的应用名称)对应:

  • <a id="channel">配置离线打包广告支持及渠道打包配置</a>
    新版本SDK简化了离线打包支持DCloud广告联盟功能。
    AndroidManifest.xml文件中添加如下节点到<application>节点中,并配置替换其中5+应用appid、广告联盟会员adid的值和渠道标识,因为涉及到开屏广告业务,项目工程必须以io.dcloud.PandoraEntry作为5+应用的入口Activity。该activity已包含在lib.5plus.base-release中,开发者无需实现

    <meta-data android:name="DCLOUD_AD_ID" android:value="广告标识"/>  
    <meta-data android:name="DCLOUD_AD_SPLASH" android:value="true"/><!--如果不开启开屏广告则不设置此字段或者值设置为false  
    <meta-data android:name="DCLOUD_STREAMAPP_CHANNEL" android:value="包名|应用标识|广告标识|渠道,如io.dcloud.appid|appid|adid|google" /><!--为了保证广告统计的有效性,请正确设置此值
    • 包名:对应Android项目中build.gradle中的applicationId,如io.dcloud.HBuilder
    • 应用标识:对应对应5+或uni-app项目manifest.json中appid
    • 广告标识:DCloud的广告标识,开通广告后可在dev.dcloud.net.cn获取,如果没有开通广告,设置值为空即可
    • 渠道:渠道包制作指南
      注意:提交谷歌应用市场(Google Play)时一定要将渠道标识设置为google!!!
      注意:以上操作只是配置5+应用具备广告能力,实际开通需要在manifest.json配置开关,具体参考DCloud广告联盟
      如果不需要支持广告,仍然可以使用WidgetWebView方式集成。
  • 配置应用图标和启动界面
    将应用的图标(文件名为icon.png)启动图片按照对应的尺寸拷贝到工程的app->src->main->res -> drawable-XXX目录下:

  • 更新应用资源
    打开app->src->main->assets -> apps 目录,将下面“HelloH5”目录名称修改为应用manifest.json中的id名称(这步非常重要,否则会导致应用无法正常启动),并将所有应用资源拷贝到其下的www目录中:

  • 配置应用信息
    打开app->src->main->assets -> data下的control.xml文件,修改appid和appver的值:


    其中appid值为HBuilder应用的appid,必须与应用manifest.json中的id值完全一致;appver为应用的版本号,用于应用资源的升级,必须保持与manifest.json中的version -> name值完全一致;version值为应用基座版本号(plus.runtime.innerVersion返回的值),不要随意修改。

  • 配置完成编译运行
    应用配置完毕,按Shift F10编译运行应用
    在弹出的设备列表对话框中选择要运行的设备:


    点击OK后启动应用:

常见问题

Q: 打包后页面中无法调用5+API
A: 修改app->build.gradle文件defaultConfig节点下miniSdkVersion和targetSdkVersion属性指定版本号为8

注意(新版已不需要添加):打包时如果修改APK的包名,则同时需要修改Rinfomation.java文件import的包名,
同时需要修改AndroidManifest.xml文件中引用包名的插件有
推送插件
微信(登陆,分享 ,支付),微信同时需要修改引入的WXEntryActivity.java和WXPayEntryActivity.java文件所在的包名

Q :使用Android Studio编译时如果提示如下错误

A:
下载附件并解压

点击下载
点击 Android Studio 菜单 File->Settings->Build,Execution,Development->Build Tools->Gradle选择Use local gradle distribution,选择附件解压后的目录点击OK即可

-->

收起阅读 »

首届流应用开发大赛名单揭晓,大众点评、京东、有道等获奖

流应用

由DCloud、36氪、CSDN联合举办的首届中国流应用开发大赛,经过一个月的选拨和评选,获奖名单正式揭晓。活动期间,数百人报名参赛,无论是一线互联网公司如大众点评、京东、网易,还是创业公司,如挑食火锅、艺人捧场,都积极开发流应用,流应用在营销、用户和订单获取、开发成本节约等方面的价值成功的吸引了App厂商。

本次大赛的中奖名单如下:

一、优秀应用奖
获奖名单(4名):
大众点评外卖、大众点评爱美丽、有道词典、京东秒杀
奖励:应用商店价值5万元的推广资源

二、优秀创业奖
获奖名单(4名):
挑食火锅、宝贝租车、艺人捧场,弈客围棋
奖励:知名创业媒体36氪提供的融资平台三天banner展示位广告资源,知名投资人约见机会。

提示:体验获奖流应用的方式:使用安装360手机助手的安卓手机,点击下面的图片链接,根据提示在外部浏览器中打开,即可体验。

1.大众点评外卖

流应用体验二维码

2.大众点评爱美丽

流应用体验二维码

3.有道词典

流应用体验二维码

4.京东秒杀

流应用体验二维码

5.挑食火锅

流应用体验二维码

6.宝贝租车

流应用体验二维码

7.艺人捧场

流应用体验二维码

8.弈客围棋

除了上述获奖的App,还有很多创业者开发了体验优质的流应用,如HiMall、枫桥居花卉等。

优秀创业者获得36Kr融资频道Banner宣传:

关于流应用:流应用是DCloud公司开发的一种可以让手机App安装包实现边用边下的技术。利用js的动态语言特点,把手机端App的安装包拆解,流式下载到手机端。类似流媒体边看边下一样,流应用也可以边用边下。再辅以特殊的压缩解码技术,使得流应用可以在5秒内完成App的下载-安装-启动全过程。流应用使用的js经过了强化,可以调用原生40多万API,使得流应用的功能和体验都达到了原生水准。
本次流应用大赛之所以受到众多开发者如此关注,主要得益于流应用具有的开发者优势和用户优势。

在开发者优势方面:

1.降低App的开发成本和人员数量,原来App厂商需要iOS、Android、前端等三个开发团队,流应用只需要1个前端开发即可完成,并且还能打包成原生App以及发布为wap网站和公众号。高效率本就是世界进步发展趋势,在资本寒冬的今天,尤其需要这种高效率开发方案。

2.降低推广成本提高转化率,推广成本降低50-80%。原生App由于安装包比较大,从下载、到安装、到启动,使得推广成本非常高,而流应用从下载到安装启动一步到位,5秒内即可完成,使得App的推广成本降低,转化率提升50%-80%。

3.多端发布,方便测试。流应用基于mui开发App,生成流应用的同时,iOS、Android原生版、浏览器版、微信App和百度直达号版也随之生成,开发一次,多端发布,效率大幅提升,成本大幅下降。

在用户优势方面,流应用具有如下五大特点:

1.秒装秒开:流应用具有秒装秒开的功能,使得用户在下载安装的时候,5秒内即可安装启动,速度非常快。
2.差量实时更新:流应用采用自动差量更新技术,每次更新只需要2-3K流量,使得用户永远使用的都是最新版本的流应用,和原生应用更新相比,每次更新都需要重新下载安装包,启动一次来说,用户体验非常好。
3.省流量:流应用具有很多缩小应用包的专利技术,能大幅减少应用包体积,但毫不影响应用功能体验。。

  1. 省空间:流应用的安装包只有几百K,和原生App几十M,几百M的App相比,非常小,装一个原生App的空间可以装几十个流应用,流应用更加节省空间。
    5.省电更流畅:流应用采用了包体压缩技术、差量更新技术,使得流应用对手机的内存、空间占用率非常少,非常省电,同时也更加流畅。

虽然本次大赛结束了,但流应用发展还在继续。后续提交流应用的开发者还有机会获得由DCloud提供的应用商店推广资源和机械键盘等奖励礼品,欢迎大家继续提交流应用。流应用交流QQ群:471285299。

继续阅读 »

由DCloud、36氪、CSDN联合举办的首届中国流应用开发大赛,经过一个月的选拨和评选,获奖名单正式揭晓。活动期间,数百人报名参赛,无论是一线互联网公司如大众点评、京东、网易,还是创业公司,如挑食火锅、艺人捧场,都积极开发流应用,流应用在营销、用户和订单获取、开发成本节约等方面的价值成功的吸引了App厂商。

本次大赛的中奖名单如下:

一、优秀应用奖
获奖名单(4名):
大众点评外卖、大众点评爱美丽、有道词典、京东秒杀
奖励:应用商店价值5万元的推广资源

二、优秀创业奖
获奖名单(4名):
挑食火锅、宝贝租车、艺人捧场,弈客围棋
奖励:知名创业媒体36氪提供的融资平台三天banner展示位广告资源,知名投资人约见机会。

提示:体验获奖流应用的方式:使用安装360手机助手的安卓手机,点击下面的图片链接,根据提示在外部浏览器中打开,即可体验。

1.大众点评外卖

流应用体验二维码

2.大众点评爱美丽

流应用体验二维码

3.有道词典

流应用体验二维码

4.京东秒杀

流应用体验二维码

5.挑食火锅

流应用体验二维码

6.宝贝租车

流应用体验二维码

7.艺人捧场

流应用体验二维码

8.弈客围棋

除了上述获奖的App,还有很多创业者开发了体验优质的流应用,如HiMall、枫桥居花卉等。

优秀创业者获得36Kr融资频道Banner宣传:

关于流应用:流应用是DCloud公司开发的一种可以让手机App安装包实现边用边下的技术。利用js的动态语言特点,把手机端App的安装包拆解,流式下载到手机端。类似流媒体边看边下一样,流应用也可以边用边下。再辅以特殊的压缩解码技术,使得流应用可以在5秒内完成App的下载-安装-启动全过程。流应用使用的js经过了强化,可以调用原生40多万API,使得流应用的功能和体验都达到了原生水准。
本次流应用大赛之所以受到众多开发者如此关注,主要得益于流应用具有的开发者优势和用户优势。

在开发者优势方面:

1.降低App的开发成本和人员数量,原来App厂商需要iOS、Android、前端等三个开发团队,流应用只需要1个前端开发即可完成,并且还能打包成原生App以及发布为wap网站和公众号。高效率本就是世界进步发展趋势,在资本寒冬的今天,尤其需要这种高效率开发方案。

2.降低推广成本提高转化率,推广成本降低50-80%。原生App由于安装包比较大,从下载、到安装、到启动,使得推广成本非常高,而流应用从下载到安装启动一步到位,5秒内即可完成,使得App的推广成本降低,转化率提升50%-80%。

3.多端发布,方便测试。流应用基于mui开发App,生成流应用的同时,iOS、Android原生版、浏览器版、微信App和百度直达号版也随之生成,开发一次,多端发布,效率大幅提升,成本大幅下降。

在用户优势方面,流应用具有如下五大特点:

1.秒装秒开:流应用具有秒装秒开的功能,使得用户在下载安装的时候,5秒内即可安装启动,速度非常快。
2.差量实时更新:流应用采用自动差量更新技术,每次更新只需要2-3K流量,使得用户永远使用的都是最新版本的流应用,和原生应用更新相比,每次更新都需要重新下载安装包,启动一次来说,用户体验非常好。
3.省流量:流应用具有很多缩小应用包的专利技术,能大幅减少应用包体积,但毫不影响应用功能体验。。

  1. 省空间:流应用的安装包只有几百K,和原生App几十M,几百M的App相比,非常小,装一个原生App的空间可以装几十个流应用,流应用更加节省空间。
    5.省电更流畅:流应用采用了包体压缩技术、差量更新技术,使得流应用对手机的内存、空间占用率非常少,非常省电,同时也更加流畅。

虽然本次大赛结束了,但流应用发展还在继续。后续提交流应用的开发者还有机会获得由DCloud提供的应用商店推广资源和机械键盘等奖励礼品,欢迎大家继续提交流应用。流应用交流QQ群:471285299。

收起阅读 »

Android 平台5+SDK接口说明

集成 Android SDK

查看Android平台5+SDK API参考手册请点击

初始化SDK

initSDK(ICore core)

说明:
初始化SDK, SDK所有接口必须在初始化之后才可以调用
参数:

  • core(ICore ) 5+内核对象

示例

    @Override  
    public void onCoreReady(ICore coreHandler) {  
        try {  
            SDK.initSDK(coreHandler);  
            SDK.requestAllFeature();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }

SDK集成使用方法

IApp startWebApp(Activity activity,String appBasePath,String startArgs,IWebviewStateListener listener,IOnCreateSplashView ocs)

说明:
创建并启动5+WebApp对象
参数:

  • activity(Activity) Activity对象
  • appBasePath(String) 移动应用所在的目录
  • startArgs(String) 应用启动时传递的参数
  • listener(IWebviewStateListener) Webview事件监听对象,用来监听应用的首页面加载状态
  • ocs(IOnCreateSplashView) Splash事件监听对象,用来监听splash页面的加载和关闭事件

示例

    public void onCoreInitEnd(ICore coreHandler) {  

        //创建默认webapp,  
        String appBasePath = "/apps/HelloH5";//表示 file:///android_asset/apps/HBuilder  
        String args = "{url:'http://www.baidu.com'}";//设置启动参数  

        app = SDK.startWebApp(activity,appBasePath, args, new IWebviewStateListener() {  
            @Override  
            public Object onCallBack(int pType, Object pArgs) {  

                switch (pType) {  
                case IWebviewStateListener.ON_WEBVIEW_READY:  
                    //准备完毕之后添加webview到显示父View中,设置排版不显示状态,避免显示webview时,html内容排版错乱问题  
                    View view = ((IWebview)pArgs).obtainApp().obtainWebAppRootView().obtainMainView();  
                    view.setVisibility(View.INVISIBLE);  
                    rootView.addView(view, 0);  
                    break;  
                case IWebviewStateListener.ON_PAGE_STARTED:  
                    break;  
                case IWebviewStateListener.ON_PROGRESS_CHANGED:  
                    break;  
                case IWebviewStateListener.ON_PAGE_FINISHED:  
                    //页面加载完毕,设置显示webview  
                    app.obtainWebAppRootView().obtainMainView().setVisibility(View.VISIBLE);  
                    break;  
                }  
                return null;  
            }  
        },this);  
    }

IWebview createWebview(Activity activity,String pagePath,String basePath,String docPath,String appid, String appName,IWebviewStateListener listener)

说明:
创建一个支持5+API的Webview对象
参数:

  • activity(Activity) Activity对象
  • pagePath(String) 页面路径
  • basePath(String) 应用的默认路径
  • docPath(String) 应用的doc目录路径
  • appid(String) 应用的appid
  • appName(String) 应用名称
  • listener(IWebviewStateListener) 加载事件监听器

示例:

public void onCoreInitEnd(ICore coreHandler) {  
        //设置单页面集成的appid  
        String appid = "test1";  
        // 单页面集成时要加载页面的路径,可以是本地文件路径也可以是网络路径  
        String url = "file:///android_asset/apps/H5Plugin/www/index.html";  
        webview = SDK.createWebview(activity, url, appid, new IWebviewStateListener() {  
            @Override  
            public Object onCallBack(int pType, Object pArgs) {  
                switch (pType) {  
                case IWebviewStateListener.ON_WEBVIEW_READY:  
                    // 准备完毕之后添加webview到显示父View中,设置排版不显示状态,避免显示webview时,html内容排版错乱问题  
                    ((IWebview) pArgs).obtainFrameView().obtainMainView().setVisibility(View.INVISIBLE);  
                    SDK.attach(mRootView, ((IWebview) pArgs));  
                    break;  
                case IWebviewStateListener.ON_PAGE_STARTED:                   
                    break;  
                case IWebviewStateListener.ON_PROGRESS_CHANGED:                   
                    break;  
                case IWebviewStateListener.ON_PAGE_FINISHED:                      
                    // 页面加载完毕,设置显示webview  
                    webview.obtainFrameView().obtainMainView().setVisibility(View.VISIBLE);  
                    break;  
                }  
                return null;  
            }  
        });  

    }

IWebview createWebview(Activity activity,String pagePath,String appid,IWebviewStateListener listener)

说明:
创建一个支持5+API的Webview对象
参数:

  • activity(Activity) Activity对象
  • pagePath(String) HTML页面的路径
  • appid(String) 默认应用的APPID
  • listener(IWebviewStateListener) 页面加载事件监听器

WebApp相关方法

void stopWebApp(IApp app)

说明:
停止一个正在运行的5+WebApp
参数:
app(IApp) 正在运行的WebAPP对象

String obtainCurrentRunnbingAppId()

说明:
获取当前正在运行的5+WebApp的APPID

IApp obtainCurrentApp ()

说明:
获取到当前在前台运行的5+Webapp对象

页面相关方法

ArrayList<IWebview> obtainAllIWebview()

说明:
获取运行应用的Webview对象的集合

IWebview obtainWebview(String appid,String webviewId)

说明:
根据APPID和WebviewID获取到指定的Webview对象
参数:

  • appid(String) 要获取应用的APPID
  • webviewId(String) 获取应用内页面的ID

IWebview obatinFirstPage(IApp app,IWebviewStateListener listener)

说明:
获取指定应用app的首页面Iwebview对象
参数:

  • app(IApp) IApp对象
  • listener(IWebviewStateListener) 应用首页面的加载状态的监听

void closeWebView(IWebview webview)

说明:
关闭指定IWebview页面
参数:

  • webview(IWebview)

设置可使用的5+API权限

void requestAllFeature()

说明:
向5+基座申请全部扩展API调用能力
示例

public void onCoreReady(ICore coreHandler) {  
        try {  
            SDK.initSDK(coreHandler);  
            SDK.requestAllFeature();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }

void requestFeature(String mainFeatureName,String subFeatrureName,boolean autoBoot)

说明
单独设置可以使用的API特征名
参数

  • mainFeatureName(String) JS 特征名称
  • subFeatrureName(String) JS特征二级名称
  • autoBoot(boolean) 是否基座自动启动

示例

requestFeature("Share","share-weixin",false);

注册新扩展插件

void registerJsApi(String featureName,String className,String jsContent)

说明:
向5+SDK注册一个新的扩展API
参数:

  • featureName(String) 扩展插件的JS功能名称
  • className(String) 扩展插件的原生类名
  • jsContent(String) 扩展插件的JS代码

示例

    // 注册新的5+扩展API  
    private void regNewJsAPI() {  
        // 扩展插件的JS特征名称  
        String featureName = "T";  
        // 为处理扩展Feature的接收类全名称  
        String className = "com.HBuilder.integrate.webview.WebViewMode_FeatureImpl";  
        // content 为扩展Feature而创建的js代码  
        String content = "(function(plus){function test(){return plus.bridge.execSync('T','test',[arguments]);}plus.T = {test:test};})(window.plus);";  
        // 注册新的扩展方法  
        SDK.registerJsApi(featureName, className, content);  
    }  
继续阅读 »

查看Android平台5+SDK API参考手册请点击

初始化SDK

initSDK(ICore core)

说明:
初始化SDK, SDK所有接口必须在初始化之后才可以调用
参数:

  • core(ICore ) 5+内核对象

示例

    @Override  
    public void onCoreReady(ICore coreHandler) {  
        try {  
            SDK.initSDK(coreHandler);  
            SDK.requestAllFeature();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }

SDK集成使用方法

IApp startWebApp(Activity activity,String appBasePath,String startArgs,IWebviewStateListener listener,IOnCreateSplashView ocs)

说明:
创建并启动5+WebApp对象
参数:

  • activity(Activity) Activity对象
  • appBasePath(String) 移动应用所在的目录
  • startArgs(String) 应用启动时传递的参数
  • listener(IWebviewStateListener) Webview事件监听对象,用来监听应用的首页面加载状态
  • ocs(IOnCreateSplashView) Splash事件监听对象,用来监听splash页面的加载和关闭事件

示例

    public void onCoreInitEnd(ICore coreHandler) {  

        //创建默认webapp,  
        String appBasePath = "/apps/HelloH5";//表示 file:///android_asset/apps/HBuilder  
        String args = "{url:'http://www.baidu.com'}";//设置启动参数  

        app = SDK.startWebApp(activity,appBasePath, args, new IWebviewStateListener() {  
            @Override  
            public Object onCallBack(int pType, Object pArgs) {  

                switch (pType) {  
                case IWebviewStateListener.ON_WEBVIEW_READY:  
                    //准备完毕之后添加webview到显示父View中,设置排版不显示状态,避免显示webview时,html内容排版错乱问题  
                    View view = ((IWebview)pArgs).obtainApp().obtainWebAppRootView().obtainMainView();  
                    view.setVisibility(View.INVISIBLE);  
                    rootView.addView(view, 0);  
                    break;  
                case IWebviewStateListener.ON_PAGE_STARTED:  
                    break;  
                case IWebviewStateListener.ON_PROGRESS_CHANGED:  
                    break;  
                case IWebviewStateListener.ON_PAGE_FINISHED:  
                    //页面加载完毕,设置显示webview  
                    app.obtainWebAppRootView().obtainMainView().setVisibility(View.VISIBLE);  
                    break;  
                }  
                return null;  
            }  
        },this);  
    }

IWebview createWebview(Activity activity,String pagePath,String basePath,String docPath,String appid, String appName,IWebviewStateListener listener)

说明:
创建一个支持5+API的Webview对象
参数:

  • activity(Activity) Activity对象
  • pagePath(String) 页面路径
  • basePath(String) 应用的默认路径
  • docPath(String) 应用的doc目录路径
  • appid(String) 应用的appid
  • appName(String) 应用名称
  • listener(IWebviewStateListener) 加载事件监听器

示例:

public void onCoreInitEnd(ICore coreHandler) {  
        //设置单页面集成的appid  
        String appid = "test1";  
        // 单页面集成时要加载页面的路径,可以是本地文件路径也可以是网络路径  
        String url = "file:///android_asset/apps/H5Plugin/www/index.html";  
        webview = SDK.createWebview(activity, url, appid, new IWebviewStateListener() {  
            @Override  
            public Object onCallBack(int pType, Object pArgs) {  
                switch (pType) {  
                case IWebviewStateListener.ON_WEBVIEW_READY:  
                    // 准备完毕之后添加webview到显示父View中,设置排版不显示状态,避免显示webview时,html内容排版错乱问题  
                    ((IWebview) pArgs).obtainFrameView().obtainMainView().setVisibility(View.INVISIBLE);  
                    SDK.attach(mRootView, ((IWebview) pArgs));  
                    break;  
                case IWebviewStateListener.ON_PAGE_STARTED:                   
                    break;  
                case IWebviewStateListener.ON_PROGRESS_CHANGED:                   
                    break;  
                case IWebviewStateListener.ON_PAGE_FINISHED:                      
                    // 页面加载完毕,设置显示webview  
                    webview.obtainFrameView().obtainMainView().setVisibility(View.VISIBLE);  
                    break;  
                }  
                return null;  
            }  
        });  

    }

IWebview createWebview(Activity activity,String pagePath,String appid,IWebviewStateListener listener)

说明:
创建一个支持5+API的Webview对象
参数:

  • activity(Activity) Activity对象
  • pagePath(String) HTML页面的路径
  • appid(String) 默认应用的APPID
  • listener(IWebviewStateListener) 页面加载事件监听器

WebApp相关方法

void stopWebApp(IApp app)

说明:
停止一个正在运行的5+WebApp
参数:
app(IApp) 正在运行的WebAPP对象

String obtainCurrentRunnbingAppId()

说明:
获取当前正在运行的5+WebApp的APPID

IApp obtainCurrentApp ()

说明:
获取到当前在前台运行的5+Webapp对象

页面相关方法

ArrayList<IWebview> obtainAllIWebview()

说明:
获取运行应用的Webview对象的集合

IWebview obtainWebview(String appid,String webviewId)

说明:
根据APPID和WebviewID获取到指定的Webview对象
参数:

  • appid(String) 要获取应用的APPID
  • webviewId(String) 获取应用内页面的ID

IWebview obatinFirstPage(IApp app,IWebviewStateListener listener)

说明:
获取指定应用app的首页面Iwebview对象
参数:

  • app(IApp) IApp对象
  • listener(IWebviewStateListener) 应用首页面的加载状态的监听

void closeWebView(IWebview webview)

说明:
关闭指定IWebview页面
参数:

  • webview(IWebview)

设置可使用的5+API权限

void requestAllFeature()

说明:
向5+基座申请全部扩展API调用能力
示例

public void onCoreReady(ICore coreHandler) {  
        try {  
            SDK.initSDK(coreHandler);  
            SDK.requestAllFeature();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }

void requestFeature(String mainFeatureName,String subFeatrureName,boolean autoBoot)

说明
单独设置可以使用的API特征名
参数

  • mainFeatureName(String) JS 特征名称
  • subFeatrureName(String) JS特征二级名称
  • autoBoot(boolean) 是否基座自动启动

示例

requestFeature("Share","share-weixin",false);

注册新扩展插件

void registerJsApi(String featureName,String className,String jsContent)

说明:
向5+SDK注册一个新的扩展API
参数:

  • featureName(String) 扩展插件的JS功能名称
  • className(String) 扩展插件的原生类名
  • jsContent(String) 扩展插件的JS代码

示例

    // 注册新的5+扩展API  
    private void regNewJsAPI() {  
        // 扩展插件的JS特征名称  
        String featureName = "T";  
        // 为处理扩展Feature的接收类全名称  
        String className = "com.HBuilder.integrate.webview.WebViewMode_FeatureImpl";  
        // content 为扩展Feature而创建的js代码  
        String content = "(function(plus){function test(){return plus.bridge.execSync('T','test',[arguments]);}plus.T = {test:test};})(window.plus);";  
        // 注册新的扩展方法  
        SDK.registerJsApi(featureName, className, content);  
    }  
收起阅读 »

chrome://inspect调试html页面空白,DOM无法加载的解决方案

控制台调试 chrome

chrome://inspect调试html页面空白,DOM无法加载的解决方案

先描述一下问题

有一段时间没碰huilder hybird app 开发了,今天调试的时候  
chrome://inspect/#devices  
可以发现页面,但是打开后空白  

解决方案

翻墙后就可以了,下次就不需要翻墙了  

估计原因

1.chrome升级后原有配置失效  
2.浏览器缓存被软件管家清掉了
继续阅读 »

chrome://inspect调试html页面空白,DOM无法加载的解决方案

先描述一下问题

有一段时间没碰huilder hybird app 开发了,今天调试的时候  
chrome://inspect/#devices  
可以发现页面,但是打开后空白  

解决方案

翻墙后就可以了,下次就不需要翻墙了  

估计原因

1.chrome升级后原有配置失效  
2.浏览器缓存被软件管家清掉了
收起阅读 »