zoukankan      html  css  js  c++  java
  • Wind.js在移动跨平台框架PhoneGap中的异步体验

    最近正在做一个移动跨平台项目的应用开发,包括在iphone,ipad,android,windows phone等手机设备中运行混合式客户端应用程序,这里选择了PhoneGap的移动跨平台框架,这里我先简单介绍下PhoneGap到底是什么东东:

    介绍

    PhoneGap是一款HTML5平台,通过它,开发商可以使用HTML、CSS及JavaScript来开发本地移动应用程序。因此,目前开发商可以只 编写一次应用程序,然后在6个主要的移动平台和应用程序商店(app store)里进行发布,这些移动平台和应用程序商店包括:iOS、Android、BlackBerry、webOS、bada以及Symbian。

    官方地址

    英文官方:http://phonegap.com/

    中文官方:http://www.phonegap.cn/

    在上面你可以找到它的入门使用说明,这里我就不具体描述了。

    关于PhoneGap开发的项目实践经验,我会在后面再另开文章来说明。

    本篇文章的重点在于对于老赵的Wind.js的使用体验,老赵是谁,相信也不用多说了,具体可以见他的博客:http://blog.zhaojie.me/

    这里就先介绍他的Wind.js:

    官方链接:http://windjs.org/

    开源链接:https://github.com/JeffreyZhao/wind

    对于它的定义,我就直接引用官方的一段话:

    Wind.js的前身为Jscex,即JavaScript Computation EXpressions的缩写,它为JavaScript语言提供了一个monadic扩展,能够显著提高一些常见场景下的编程体验(例如异步编程)。Wind.js完全使用JavaScript编写,能够在任意支持JavaScript的执行引擎里使用,包括各浏览器及服务器端JavaScript环境(例如Node.js)。

    实际上,它就是原先的Jscex。

    对于我来说,正在开始了解Wind.js的时候,是在今年的7月份,在阿里技术嘉年华(http://adc.taobao.com/)中听node.js专场时了解到的,当时现场火爆,大家对于Wind.js也是非常感兴趣。当然过程中也有一些人保持一种观望的态度。而对于我这种实战派的开发者来说,在一个有应用场景的情况下,Wind.js才更有说服力。

    于是,在我当时的理解当中,它应该是属于可以改变异步体验的一种绝佳的方式,可以让你的代码可读性大大提升,而你从此不再为了setTimeout,callback之类的写法而烦恼!

    好的,一些理论的东西我就先不多说了,Wind.js文档中已经写的很多了,今天就来用Wind.js来体验下如何应用到我的项目中去。

    请先看下面的一段代码:

    复制代码
    function login(userName, password, type, callback) {
        if (callback == null) {
            return;
        }

        callIfNetworkAvailable(function() {
            callNativeAPI(
                native_refreshUrl,
                { key: 'Login', username: userName, password: password, type: type },
                function (result) {
                    callback(result);
                }
            );
        });
    }
    function callIfNetworkAvailable(fn) {
        if (fn == null) {
            return;
        }
        getNetworkStatus(function (result) {
            if (result.status) {
                if (result.data) {
                    fn();
                }
                else {
                    hideLoadingMsg();
                    alert(lang.networkUnAvailable);
                }
            }
            else {
                hideLoadingMsg();
                alert(lang.getNetworkAvailableStatusFailed);
            }
        });
    }
    function getNetworkStatus(callback) {
        if (callback == null) {
            return;
        }
        callNativeAPI(
            native_getUrl,
            { key: 'GetNetworkStatus' },
            function (result) {
                callback(result);
            }
        );
    }
    function callNativeAPI(url, data, callback) {
        var items = url.split("/");
        var serviceName = items[0];
        var actionName = items[1].toLowerCase();
        //因为参数必须是数组,所以把参数放在一个数组中
        var params = [];
        params.push(data);
        log({ step: '调用Native接口前的参数信息', parameters: data });
        //调用Native接口
        Cordova.exec(
            function (result) {
                log({ step: '调用Native接口的返回值信息', returnValue: result });
                if (callback != null) {
                    callback(result);
            }
        },
        function () { },
        serviceName,
        actionName,
        params
        );
    }
    复制代码

    这里首先我先需要说的是,PhoneGap的javascript脚本与原生(iOS,android,wp等)的API的plugin交互,采用与浏览器webkit中的webview进行通信,而它的底层原理就是iframe的交互,它是以一种特定规范的通信协议来展开,而在传统的web上iframe的使用本身就是最原始的异步加载原理的使用。所以,没有办法异步方式在phonegap的开发中广泛使用。

    再回过头看上面的代码,它实际上实现的是一个登录的功能,在登录的过程中,首先我必须先调用callIfNetworkAvailable方法,而它的参数本身作为一个回调函数,getNetworkStatus会调用callNativeAPI,callNativeAPI函数里面的Cordova.exec方法实际上就是一个跟原生交互的一个方法入口,通过传递serviceName,actionName以及对应需要传递的数据参数来决定,而它总是需要一个回调方法(successCallback,failCallback)来接收返回的数据。

    最后的执行:

    复制代码
    $(document).delegate("#loginPage #loginButton", "click", function () {
        var userName = $("#username").val();
        var password = $("#password").val();
        if (isNullOrEmpty(userName)) {
            alert(lang.usernameCannotEmpty);
            return;
        }
        if (isNullOrEmpty(password)) {
            alert(lang.passwordCannotEmpty);
            return;
        }
        showLoading("登录中...");
        login(userName, password, "normal", function (result) {
            if (!result.status) {
                alert(result.message);
            }
            else {
                showPage("taskListPage");
                hideLoading();
            }
        });
    });
    复制代码

     我们得到的就是需要不断地写嵌套函数,不断地callback,这样的写法看起来还是很纠结的!

    于是,萌生了采用Wind.js的异步调用的独特方式:

    先来看看我是如何改造这段代码的:

    复制代码
    //用户登录
    var loginAsync = eval(Wind.compile('async', function (userName, password, type) {
        var result = $await(callIfNetworkAvailableAsync());
        if(result) {
            var result = $await(callNativeAPIAsync(native_refreshUrl,
            { key: 'Login', username: userName, password: password, type: type }));
            return result;
        }
        return null;
    }));

    //在当前网络可用的情况下调用指定函数
    var callIfNetworkAvailableAsync = eval(Wind.compile('async', function() {
        var result = $await(getNetworkStatusAsync());
        if (result.status) {
            if (result.data) {
                return true;
            }
            else {
                hideLoadingMsg();
                alert(lang.networkUnAvailable);
            }
        }
        else {
            hideLoadingMsg();
            alert(lang.getNetworkAvailableStatusFailed);
        }
        return false;
    }));

    //获取当前网络状态
    var getNetworkStatusAsync = eval(Wind.compile('async', function() {
        var result = $await(callNativeAPIAsync(native_getUrl, { key: 'GetNetworkStatus' }));
        return result;
    }));

    //JS端与PhoneGap Native API进行交互
    var callNativeAPIAsync = eval(Wind.compile('async', function(url, data) {
        var items = url.split("/");
        var serviceName = items[0];
        var actionName = items[1].toLowerCase();
        //因为参数必须是数组,所以把参数放在一个数组中
        var params = [];
        params.push(data);
        $await(logAsync({ step: '调用Native接口前的参数信息', parameters: data }));
        //调用Native接口
        var result = $await($.cordovaAsync(serviceName, actionName, params));
        if(result) {
            $await(logAsync({ step: '调用Native接口的返回值信息', returnValue: result }));
        }
        return result;
    }))
    复制代码

    从代码中,你不难发现,它采用一种顺序执行代替异步的方式很巧妙地绕开了一连串callback的写法,在感官上似乎更符合了一个开发者顺序执行模型的思想。

    而return result;就是一个回调的结果。

    到这里你可能还有个疑问,Cordova.exec是如何做到的,这里我定义了一个叫做$.cordovaAsync的函数:

    复制代码
    //phonegap异步调用插件
    $.cordovaAsync = function (serviceName, actionName, params) {
        return Task.create(function (t) {
            var success = function (result) {
                t.complete("success", result);
            };
            var fail = function(result) {
                t.complete("failure", result);
            }
            Cordova.exec(success, fail, serviceName, actionName, params);
        });
    }
    复制代码

    这里是Wind.js提供的一种任务式的插件绑定,例如它可以把一个jQuery中的$.ajax改装成一个$.ajaxAsync的Wind.js调用方式,这里我把它改装成$.cordovaAsync来调用Cordova.exec,而最终var result = $await($.cordovaAsync(serviceName, actionName, params));的返回值实际上就是一个回调函数的success,这样我就实现了JS与原生客户端的交互。

    最后的执行: 

    复制代码
    $(document).delegate("#loginPage #loginButton", "click", function () {
        var userName = $("#username").val();
        var password = $("#password").val();
        if (isNullOrEmpty(userName)) {
            alert(lang.usernameCannotEmpty);
            return;
        }
        if (isNullOrEmpty(password)) {
            alert(lang.passwordCannotEmpty);
            return;
        }
        showLoading("登录中...");
        startLoginAsync(userName, password, 'normal').start();
    });

    var startLoginAsync = eval(Wind.compile('async', function (userName, password, type) {
        var result = $await(loginAsync(userName, password, type));
        if(result) {
            if (!result.status) {
                alert(result.message);
            }
            else {
                hideLoading();
                showPage("taskListPage");
            }
        }
    }));
    复制代码

    理解上就相当简单了。

    实例图:

    4d555398gw1dw0v5lyas1j (1).jpg

    总结

    会持续关注Wind.js的发展,并且接下来也会了解下它的内部原理。另外,提出一个建议,eval(Wind.compile('async',…这样的写法还能够更加简易些吗,比如我觉得使用$.await(function(…){ … });就是挺好的方式。

    开源地址:https://github.com/sunleepy/cooper-mobi


    作者:Leepy
     
    邮箱:sunleepy(AT)gmail.com
     
        
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。
     
     
    分类: PhoneGapWindjs
  • 相关阅读:
    java中的数组长度的计算
    用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。C++实现
    c++中计算数组的长度。以及c++中向量的长度的计算的方式。
    3.mouseenter和mouseover事件的区别
    0.jQuery选择器
    2.点击隐藏盒子
    1.jQuery入口函数
    jquery选项卡效果
    %你考试2020.1
    二十七、rsync同步工具
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2667999.html
Copyright © 2011-2022 走看看