HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

打安卓包,打包失败,包错误日志https://service.dcloud.net.cn/build/errorLog/f1452b50-92b2-11eb-a333-d744a281b21a

不是bug

首先,更新 manifest.json 中的版本号。
比如之前是 1.0.0,那么新版本应该是 1.0.1 或 1.1.0 这样。

你要的解决打包失败原因来了:

然后,在 HBuilderX 中生成升级包(wgt)。
菜单->发行->原生App-制作移动App资源升级包
生成结束会在控制台告知升级包的输出位置。

就成功了!希望能帮助到你。

继续阅读 »

首先,更新 manifest.json 中的版本号。
比如之前是 1.0.0,那么新版本应该是 1.0.1 或 1.1.0 这样。

你要的解决打包失败原因来了:

然后,在 HBuilderX 中生成升级包(wgt)。
菜单->发行->原生App-制作移动App资源升级包
生成结束会在控制台告知升级包的输出位置。

就成功了!希望能帮助到你。

收起阅读 »

uniapp H5端不能够定位当前位置的问题

uniapp

H5 端获取定位信息,需要部署在 https 服务上,本地预览(localhost)仍然可以使用 http 协议。(照搬官方文档)

参考文章链接
这篇文章里面提到需要配置腾讯地图的key,我配置上之后地图列表加载不出来,不知道是不是申请的步骤不对,我只用了https服务就ok了。

继续阅读 »

H5 端获取定位信息,需要部署在 https 服务上,本地预览(localhost)仍然可以使用 http 协议。(照搬官方文档)

参考文章链接
这篇文章里面提到需要配置腾讯地图的key,我配置上之后地图列表加载不出来,不知道是不是申请的步骤不对,我只用了https服务就ok了。

收起阅读 »

关于个推getui,服务端发送推送,在线可以收到,离线永远收不到的问题解决。

个推SDK

1.检查参数是否正确。

  1. 可以先不要使用透传方式。
  2. 是否设置了厂商通道消息内容
  3. 设置为打开应用内特定页面就是intent参数。
  4. intent参数是否正确 intent:#Intent;component=包名/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title=".urlencode(标题).";S.content=".urlencode($内容).";S.payload=".json_encode(你的自定义参数).";end

6.PHP端参考代码。

 $api = new \GTClient("https://restapi.getui.com/","", "","");  
        $stt = ['default'=>1];  
        //设置推送参数  
        $push = new \GTPushRequest();  
        $push->setRequestId(time());  
        $message = new \GTPushMessage();  
        $notify = new \GTNotification();  
        $channel = new \GTPushChannel();  

        //配置推送条件  
        $str = new \GTStrategy();  
        $str->setDefault(1);  
        $str->setHw(1);  
        $setting = new \GTSettings();  //定时推送暂无  
        $setting->setStrategy($str);  
        $push->setSettings($setting);  
        $setting->setTtl(3600000); ////消息有效期,走厂商消息需要设置该值  

       //推送苹果离线通知标题内容  
        $alert = new \GTAlert();  
         $alert->setTitle($title);  
       $alert->setBody($content);  
        $aps = new \GTAps();  
        //1表示静默推送(无通知栏消息),静默推送时不需要填写其他参数。  
        //苹果建议1小时最多推送3条静默消息  
        $aps->setContentAvailable(0);  
       $aps->setSound("default");  
       $aps->setAlert($alert);  
       $iosDto = new \GTIos();  
       $iosDto->setAps($aps);  
        $iosDto->setType("notify");  
        $pushChannel = new \GTPushChannel();  
        $pushChannel->setIos($iosDto);  

        //安卓离线厂商通道推送消息体  
        $pushChannel = new \GTPushChannel();  
        $androidDTO = new \GTAndroid();  
        $ups = new \GTUps();  
        $notification1 = new \GTThirdNotification();;  
        $notification1->setTitle($title);  
        $notification1->setBody($content);  
        $ups->setNotification($notification1);  
        if($clickType == 'none')  //设置推送类型  
        {  
            $notification1->setClickType($clickType);  
        }else if($clickType == 'payload' || $clickType == 'payload_custom'){ //自定义消息 打开APP和不打开APP  
            $notification1->setClickType($clickType);  
            $notification1->setPayload(json_encode($data));  
        }else if($clickType == 'url'){  //打开URL  
            $notification1->setClickType($clickType);  
            $notification1->setUrl($url);  
        }else if($clickType == 'intent'){  //打开特定页面  
            $notification1->setClickType($clickType);  
            $notification1->setIntent("intent:#Intent;component=com.hqtwtq.y8/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title=".urlencode($title).";S.content=".urlencode($content).";S.payload=".json_encode($data).";end");  
        }else{  
            $notification1->setClickType($clickType);  
        }  
            //各厂商自有功能单项设置  
        if($time){  
            $message->setDuration($time);  //设置推送时间  
            $data = json_encode(array('title'=>$title,'content'=>$content,'duration'=>$time,'importance'=>'HIGH','payload'=>json_encode($data)));  
        }else{  
            $data = json_encode(array('title'=>$title,'content'=>$content,'payload'=>json_encode($data)));  
        }  
        //$ups->setTransmission($data);  
        $androidDTO->setUps($ups);  
        $pushChannel->setAndroid($androidDTO);  
        $push->setPushChannel($pushChannel);  

        //个推在线通道  
        $notify->setTitle($title);  
        $notify->setBody($content);  
        if($img) $notify->setBigImage($img); //推送图片  
        //1、intent:打开应用内特定页面 2、url:打开网页地址。3、payload:自定义消息内容启动应用。4、payload_custom:自定义消息内容不启动应用。5、startapp:打开应用首页。6、none:纯通知,无后续动作  
        if($clickType == 'none')  //设置推送类型  
        {  
            $notify->setClickType($clickType);  
        }else if($clickType == 'payload' || $clickType == 'payload_custom'){ //自定义消息 打开APP和不打开APP  
            $notify->setClickType($clickType);  
            $notify->setPayload(json_encode($data));  
        }else if($clickType == 'url'){  //打开URL  
            $notify->setClickType($clickType);  
            $notify->setUrl($url);  
        }else if($clickType == 'intent'){  //打开特定页面  
            $notify->setClickType($clickType);  
            $notify->setIntent("intent:#Intent;component=com.hqtwtq.y8/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title=".urlencode($title).";S.content=".urlencode($content).";S.payload=".json_encode($data).";end");  
        }else{  
            $notify->setClickType($clickType);  
        }  
        $message->setNotification($notify);  
        $push->setPushMessage($message);  
      /*  if($time){  
            $message->setDuration($time);  //设置推送时间  
            $data = json_encode(array('title'=>$title,'body'=>$content,'duration'=>$time,'importance'=>'HIGH','payload'=>json_encode($data)));  
        }else{  
            $data = json_encode(array('title'=>$title,'body'=>$content,'payload'=>json_encode($data)));  
        }  
        $ups->setTransmission($data);  //  
        $android->setUps($ups);  
        $channel->setAndroid($android);  
        $push->setPushChannel($channel);  
        $push->setPushMessage($message);*/  
        if($cid)  //推送给某个用户  
        {  
            $push->setCid($cid);  
            //处理返回结果  
            $result = $api->pushApi()->pushToSingleByCid($push);  
        }else{  
            $result = $api->pushApi()->pushAll($push);  
        }  
        Log::info('推送数据:【推送】'.json_encode($result)); //写入日志  
        return $result;
继续阅读 »

1.检查参数是否正确。

  1. 可以先不要使用透传方式。
  2. 是否设置了厂商通道消息内容
  3. 设置为打开应用内特定页面就是intent参数。
  4. intent参数是否正确 intent:#Intent;component=包名/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title=".urlencode(标题).";S.content=".urlencode($内容).";S.payload=".json_encode(你的自定义参数).";end

6.PHP端参考代码。

 $api = new \GTClient("https://restapi.getui.com/","", "","");  
        $stt = ['default'=>1];  
        //设置推送参数  
        $push = new \GTPushRequest();  
        $push->setRequestId(time());  
        $message = new \GTPushMessage();  
        $notify = new \GTNotification();  
        $channel = new \GTPushChannel();  

        //配置推送条件  
        $str = new \GTStrategy();  
        $str->setDefault(1);  
        $str->setHw(1);  
        $setting = new \GTSettings();  //定时推送暂无  
        $setting->setStrategy($str);  
        $push->setSettings($setting);  
        $setting->setTtl(3600000); ////消息有效期,走厂商消息需要设置该值  

       //推送苹果离线通知标题内容  
        $alert = new \GTAlert();  
         $alert->setTitle($title);  
       $alert->setBody($content);  
        $aps = new \GTAps();  
        //1表示静默推送(无通知栏消息),静默推送时不需要填写其他参数。  
        //苹果建议1小时最多推送3条静默消息  
        $aps->setContentAvailable(0);  
       $aps->setSound("default");  
       $aps->setAlert($alert);  
       $iosDto = new \GTIos();  
       $iosDto->setAps($aps);  
        $iosDto->setType("notify");  
        $pushChannel = new \GTPushChannel();  
        $pushChannel->setIos($iosDto);  

        //安卓离线厂商通道推送消息体  
        $pushChannel = new \GTPushChannel();  
        $androidDTO = new \GTAndroid();  
        $ups = new \GTUps();  
        $notification1 = new \GTThirdNotification();;  
        $notification1->setTitle($title);  
        $notification1->setBody($content);  
        $ups->setNotification($notification1);  
        if($clickType == 'none')  //设置推送类型  
        {  
            $notification1->setClickType($clickType);  
        }else if($clickType == 'payload' || $clickType == 'payload_custom'){ //自定义消息 打开APP和不打开APP  
            $notification1->setClickType($clickType);  
            $notification1->setPayload(json_encode($data));  
        }else if($clickType == 'url'){  //打开URL  
            $notification1->setClickType($clickType);  
            $notification1->setUrl($url);  
        }else if($clickType == 'intent'){  //打开特定页面  
            $notification1->setClickType($clickType);  
            $notification1->setIntent("intent:#Intent;component=com.hqtwtq.y8/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title=".urlencode($title).";S.content=".urlencode($content).";S.payload=".json_encode($data).";end");  
        }else{  
            $notification1->setClickType($clickType);  
        }  
            //各厂商自有功能单项设置  
        if($time){  
            $message->setDuration($time);  //设置推送时间  
            $data = json_encode(array('title'=>$title,'content'=>$content,'duration'=>$time,'importance'=>'HIGH','payload'=>json_encode($data)));  
        }else{  
            $data = json_encode(array('title'=>$title,'content'=>$content,'payload'=>json_encode($data)));  
        }  
        //$ups->setTransmission($data);  
        $androidDTO->setUps($ups);  
        $pushChannel->setAndroid($androidDTO);  
        $push->setPushChannel($pushChannel);  

        //个推在线通道  
        $notify->setTitle($title);  
        $notify->setBody($content);  
        if($img) $notify->setBigImage($img); //推送图片  
        //1、intent:打开应用内特定页面 2、url:打开网页地址。3、payload:自定义消息内容启动应用。4、payload_custom:自定义消息内容不启动应用。5、startapp:打开应用首页。6、none:纯通知,无后续动作  
        if($clickType == 'none')  //设置推送类型  
        {  
            $notify->setClickType($clickType);  
        }else if($clickType == 'payload' || $clickType == 'payload_custom'){ //自定义消息 打开APP和不打开APP  
            $notify->setClickType($clickType);  
            $notify->setPayload(json_encode($data));  
        }else if($clickType == 'url'){  //打开URL  
            $notify->setClickType($clickType);  
            $notify->setUrl($url);  
        }else if($clickType == 'intent'){  //打开特定页面  
            $notify->setClickType($clickType);  
            $notify->setIntent("intent:#Intent;component=com.hqtwtq.y8/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title=".urlencode($title).";S.content=".urlencode($content).";S.payload=".json_encode($data).";end");  
        }else{  
            $notify->setClickType($clickType);  
        }  
        $message->setNotification($notify);  
        $push->setPushMessage($message);  
      /*  if($time){  
            $message->setDuration($time);  //设置推送时间  
            $data = json_encode(array('title'=>$title,'body'=>$content,'duration'=>$time,'importance'=>'HIGH','payload'=>json_encode($data)));  
        }else{  
            $data = json_encode(array('title'=>$title,'body'=>$content,'payload'=>json_encode($data)));  
        }  
        $ups->setTransmission($data);  //  
        $android->setUps($ups);  
        $channel->setAndroid($android);  
        $push->setPushChannel($channel);  
        $push->setPushMessage($message);*/  
        if($cid)  //推送给某个用户  
        {  
            $push->setCid($cid);  
            //处理返回结果  
            $result = $api->pushApi()->pushToSingleByCid($push);  
        }else{  
            $result = $api->pushApi()->pushAll($push);  
        }  
        Log::info('推送数据:【推送】'.json_encode($result)); //写入日志  
        return $result;
收起阅读 »

unipush,APP离线推送,无法触发click问题原因。

unipush

原因:厂商通道的intent配置遗漏导致。

错误示例(按?号提示上写的格式):intent:#Intent;component=你的包名/io.dcloud.PandoraEntry;S.title=标题10;S.content=内容10;S.payload=%7B%22type%22%3A%222%22%2C%22url%22%3A%22pages%2Factivity%2Ftakeaway%2Findex%22%7D;end

正确示例:intent:#Intent;component=你的包名/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title=标题10;S.content=内容10;S.payload=%7B%22type%22%3A%222%22%2C%22url%22%3A%22pages%2Factivity%2Ftakeaway%2Findex%22%7D;end

总结:S.UP-OL-SU=true; 是必须要写的,不写会无法触发click的监听。

继续阅读 »

原因:厂商通道的intent配置遗漏导致。

错误示例(按?号提示上写的格式):intent:#Intent;component=你的包名/io.dcloud.PandoraEntry;S.title=标题10;S.content=内容10;S.payload=%7B%22type%22%3A%222%22%2C%22url%22%3A%22pages%2Factivity%2Ftakeaway%2Findex%22%7D;end

正确示例:intent:#Intent;component=你的包名/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title=标题10;S.content=内容10;S.payload=%7B%22type%22%3A%222%22%2C%22url%22%3A%22pages%2Factivity%2Ftakeaway%2Findex%22%7D;end

总结:S.UP-OL-SU=true; 是必须要写的,不写会无法触发click的监听。

收起阅读 »

uni-app App打包(基础配置)

uniapp 移动APP 小程序 公众号 HTML5

线上案例
1,模块配置
打开项目点击 manifest.json

image.png
模块配置勾选 支付、登录、分享、 ViderPlayer和自己需要用的模块填写相关配置 Ctrl+s 保存
APP自动生成图标选择一个1024x1024的图片传上去,生成一下就好了

2,权限配置
安卓的已经配置好了,ios的必须开发者填写相关的描述信息,需要用那些就填写哪些,ios必须填写详细,不然上架容易被拒

image.png
安卓的权限中文说明地址:http://www.kjson.com/files/androidmanifest

3,模块配置

1.定位选择高德地图,App端只支持高德地图,所以尽量选择高德地图,key在高德开发平台就可以申请到  
2.OAuth 授权登录  
    一键登录是uni-app官方自带的,详情请看官方文档  
    地址:https://uniapp.dcloud.io/univerify  
3.自定义第三方登录比如:微信登录  
  请填写相关的配置就可以了  
4注意:  
ios端如果包含了任何第三方登录,那必须也得把苹果登录也带上,不然苹果上架审核不通过  
根据苹果审核指南要求,如果 app 使用第三方或社交登录服务 (例如,Facebook 登录、Google 登录、通过 Twitter 登录、通过 LinkedIn 登录、通过 Amazon 登录或微信登录) 来对其进行设置或验证这个 app 的用户主帐户,则该 app 必须同时提供“通过 Apple 登录”作为同等选项。详情参考:App Store 审核指南 - 通过 Apple 登录  
说明:https://ask.dcloud.net.cn/article/36651  
5. 支付、分享填写相关的配置就可以了  
6. 如果有视频,需要勾选VideoPlayer(视频播放)  

4,App常用其它设置

image.png

为了方便测试选择了全部支持,测试的时候由于各种模拟器支持的不一样所以都勾选了,打包上线的时候选择第一个就可以,不然ios容易出现以下问题

image.png

具体说明可以看dcloud的文档
https://ask.dcloud.net.cn/article/36195

也可访问线上案例

继续阅读 »

线上案例
1,模块配置
打开项目点击 manifest.json

image.png
模块配置勾选 支付、登录、分享、 ViderPlayer和自己需要用的模块填写相关配置 Ctrl+s 保存
APP自动生成图标选择一个1024x1024的图片传上去,生成一下就好了

2,权限配置
安卓的已经配置好了,ios的必须开发者填写相关的描述信息,需要用那些就填写哪些,ios必须填写详细,不然上架容易被拒

image.png
安卓的权限中文说明地址:http://www.kjson.com/files/androidmanifest

3,模块配置

1.定位选择高德地图,App端只支持高德地图,所以尽量选择高德地图,key在高德开发平台就可以申请到  
2.OAuth 授权登录  
    一键登录是uni-app官方自带的,详情请看官方文档  
    地址:https://uniapp.dcloud.io/univerify  
3.自定义第三方登录比如:微信登录  
  请填写相关的配置就可以了  
4注意:  
ios端如果包含了任何第三方登录,那必须也得把苹果登录也带上,不然苹果上架审核不通过  
根据苹果审核指南要求,如果 app 使用第三方或社交登录服务 (例如,Facebook 登录、Google 登录、通过 Twitter 登录、通过 LinkedIn 登录、通过 Amazon 登录或微信登录) 来对其进行设置或验证这个 app 的用户主帐户,则该 app 必须同时提供“通过 Apple 登录”作为同等选项。详情参考:App Store 审核指南 - 通过 Apple 登录  
说明:https://ask.dcloud.net.cn/article/36651  
5. 支付、分享填写相关的配置就可以了  
6. 如果有视频,需要勾选VideoPlayer(视频播放)  

4,App常用其它设置

image.png

为了方便测试选择了全部支持,测试的时候由于各种模拟器支持的不一样所以都勾选了,打包上线的时候选择第一个就可以,不然ios容易出现以下问题

image.png

具体说明可以看dcloud的文档
https://ask.dcloud.net.cn/article/36195

也可访问线上案例

收起阅读 »

uni-app商城源码 h5+小程序+公众号+app

h5+ 公众号 移动APP 源码 小程序

线上案例

1:有详细的代码注释,有完整系统手册。
2:基于 SpringBoot 框架开发业界主流。
3:【前端】Web PC 管理端 vue + element UI。
4:【前端】移动端使用 UniApp 框架,前后端分离开发。
5:标准RESTful 接口、标准数据传输,逻辑层次更明确,更多的提高api复用。
6:支持Redis队列,降低流量高峰,解除耦合,高可用。
7:无缝事件机制,行为扩展更方便,方便二次开发。
8:数据导出,方便个性化分析。
9:数据统计分析,使用ECharts图表统计,实现用户、产品、订单、资金等统计分析。
10:权限管理,后台多种角色,多重身份权限管理,权限可以控制到按钮级别的操作。
11:Vue表单生成控件,拖拽配置表单,减少前端重复表单工作量,提高前端开发效率。



线上案例

继续阅读 »

线上案例

1:有详细的代码注释,有完整系统手册。
2:基于 SpringBoot 框架开发业界主流。
3:【前端】Web PC 管理端 vue + element UI。
4:【前端】移动端使用 UniApp 框架,前后端分离开发。
5:标准RESTful 接口、标准数据传输,逻辑层次更明确,更多的提高api复用。
6:支持Redis队列,降低流量高峰,解除耦合,高可用。
7:无缝事件机制,行为扩展更方便,方便二次开发。
8:数据导出,方便个性化分析。
9:数据统计分析,使用ECharts图表统计,实现用户、产品、订单、资金等统计分析。
10:权限管理,后台多种角色,多重身份权限管理,权限可以控制到按钮级别的操作。
11:Vue表单生成控件,拖拽配置表单,减少前端重复表单工作量,提高前端开发效率。



线上案例

收起阅读 »

微信小程序顶部导航栏自定义组件

小程序

navHeader.vue,已经计算好高度,调用: <navHeader></navHeader>

<template>  
    <view>  
        <view class="bg-orange" :style="'height:'+statusBarHeight+'px'"></view>  
        <view class="bg-orange flex align-center justify-center" :style="'height:'+navBarHeight+'px'">  
            <view class="text-lg">  
                {{title}}  
            </view>  
        </view>  
    </view>  
</template>  

<script>  
    export default {  
        name:"navHeader",  
        data() {  
            return {  
                statusBarHeight: 0,  
                navBarHeight: 0,  
                title:'测试'  
            };  
        },  
        created() {  
            this.statusBarHeight = uni.getSystemInfoSync().statusBarHeight;  
             let plat = uni.getSystemInfoSync().platform;  
             this.navBarHeight = 44;  
             if(plat=='android'){  
                  this.navBarHeight = 48;  
             }  
        }  
    }  
</script>  

<style>  

</style>  
继续阅读 »

navHeader.vue,已经计算好高度,调用: <navHeader></navHeader>

<template>  
    <view>  
        <view class="bg-orange" :style="'height:'+statusBarHeight+'px'"></view>  
        <view class="bg-orange flex align-center justify-center" :style="'height:'+navBarHeight+'px'">  
            <view class="text-lg">  
                {{title}}  
            </view>  
        </view>  
    </view>  
</template>  

<script>  
    export default {  
        name:"navHeader",  
        data() {  
            return {  
                statusBarHeight: 0,  
                navBarHeight: 0,  
                title:'测试'  
            };  
        },  
        created() {  
            this.statusBarHeight = uni.getSystemInfoSync().statusBarHeight;  
             let plat = uni.getSystemInfoSync().platform;  
             this.navBarHeight = 44;  
             if(plat=='android'){  
                  this.navBarHeight = 48;  
             }  
        }  
    }  
</script>  

<style>  

</style>  
收起阅读 »

老开发的心声: 官方何时能真正的去解决BUG,留给UNI的时间不多了。一个bug 反应了,永远是等待,那生态做来干什么

uni其实很适合新手入门,但是,一旦永久了,总是有很多的bug,而有些bug 就是致命性的, u 软件开发了很久,才发现是官方系统性bug, excase me 我怎么修? 反馈问题,提交BUG,运气不好,毛人都找不到一个。运气好,有个官方的人跟你说上几句,然后,说上几句就是让你等,等等,我T吗都工作都要等掉了,还等,一个BUG几个月不修复,我也是醉了, 这样的生态,有什么意思,,
uni 本是国产之光,如今,感觉越来越混, 社区生态不维护好,搞什么 都是暖用。
BUGS:

1 .uni各种闪退(代码正确)
2 录音限制10分钟, 期间,一旦APP进入后台模式隐藏超过60秒,录音就听不到了。。。神bug。。
3.官方蓝牙链接,获取链接上时好时坏, 代码都没问题哈,

  1. 关键 这些Bug,不是轻易复现的,都是软件开发后了,实际使用才能发现,然后怎么办??找官方?? 官方除了让你等,就是让你等。。。玩鸡毛哦

准备弃坑去RN了。。。
一位使用hubildx 4年的老开发了,真的伤心。

继续阅读 »

uni其实很适合新手入门,但是,一旦永久了,总是有很多的bug,而有些bug 就是致命性的, u 软件开发了很久,才发现是官方系统性bug, excase me 我怎么修? 反馈问题,提交BUG,运气不好,毛人都找不到一个。运气好,有个官方的人跟你说上几句,然后,说上几句就是让你等,等等,我T吗都工作都要等掉了,还等,一个BUG几个月不修复,我也是醉了, 这样的生态,有什么意思,,
uni 本是国产之光,如今,感觉越来越混, 社区生态不维护好,搞什么 都是暖用。
BUGS:

1 .uni各种闪退(代码正确)
2 录音限制10分钟, 期间,一旦APP进入后台模式隐藏超过60秒,录音就听不到了。。。神bug。。
3.官方蓝牙链接,获取链接上时好时坏, 代码都没问题哈,

  1. 关键 这些Bug,不是轻易复现的,都是软件开发后了,实际使用才能发现,然后怎么办??找官方?? 官方除了让你等,就是让你等。。。玩鸡毛哦

准备弃坑去RN了。。。
一位使用hubildx 4年的老开发了,真的伤心。

收起阅读 »

语法错误

数据库int项值,不能有双引号

数据库int项值,不能有双引号

JS 和 Native 互相调用方式

subNViews

  

  一、网页JS调用Native中java代码

  哥斯拉大战金刚下载地址:duan.kim

  网页和本地代码的交互主要是通过webview这个桥梁来进行的,webview中的如下接口:

  接口中的第一个参数object,为注入webview的java对象

  第二个参数name,为暴露给javaScript使用的名字。

  即通过该接口给webview注入一个object对象,然后javaScript可以通过name来引用object对象。

  Webview的设置如下,打开javaScript功能,加载本地网页,注入对象:

  //启用javascript

  mWebView.getSettings().setJavaScriptEnabled(true);

  //从assets目录下面的加载html

  mWebView.loadUrl("file:///android_asset/wx.html");

  mWebView.addJavascriptInterface(this,"wx");

  native本地定义的java接口:

  分别定义了带有参数和不带参数的两个接口,当JS调用的时候,本地会弹出Toast显示文本,并且更新native界面中的textview内容。

  网页端调用Native端代码方式如下:

  调用方式主要是通过window来引用webview中注入的对象。

  需要注意的是在Android4.2之前addJavascriptInterface接口存在注入漏洞,即JS可以通过反射获取到native端的其他接口,进行其他非法操作,所以4.2之后升级增加了JS只能访问带有JavascriptInterface注解的Java函数的限制,在本地定义的提供给JS调用的接口都需要增加android.webkit.JavascriptInterface声明。

  二、Native端java调用网页JS方法

  网页JS端提供给Java端的代码定义如下,主要用来改变标签内容:

  Java端的调用方法如下:

  Webview对象通过loadUrl接口来加载以javascript协议头的方式就可以调用JS中定义的接口了。

继续阅读 »

  

  一、网页JS调用Native中java代码

  哥斯拉大战金刚下载地址:duan.kim

  网页和本地代码的交互主要是通过webview这个桥梁来进行的,webview中的如下接口:

  接口中的第一个参数object,为注入webview的java对象

  第二个参数name,为暴露给javaScript使用的名字。

  即通过该接口给webview注入一个object对象,然后javaScript可以通过name来引用object对象。

  Webview的设置如下,打开javaScript功能,加载本地网页,注入对象:

  //启用javascript

  mWebView.getSettings().setJavaScriptEnabled(true);

  //从assets目录下面的加载html

  mWebView.loadUrl("file:///android_asset/wx.html");

  mWebView.addJavascriptInterface(this,"wx");

  native本地定义的java接口:

  分别定义了带有参数和不带参数的两个接口,当JS调用的时候,本地会弹出Toast显示文本,并且更新native界面中的textview内容。

  网页端调用Native端代码方式如下:

  调用方式主要是通过window来引用webview中注入的对象。

  需要注意的是在Android4.2之前addJavascriptInterface接口存在注入漏洞,即JS可以通过反射获取到native端的其他接口,进行其他非法操作,所以4.2之后升级增加了JS只能访问带有JavascriptInterface注解的Java函数的限制,在本地定义的提供给JS调用的接口都需要增加android.webkit.JavascriptInterface声明。

  二、Native端java调用网页JS方法

  网页JS端提供给Java端的代码定义如下,主要用来改变标签内容:

  Java端的调用方法如下:

  Webview对象通过loadUrl接口来加载以javascript协议头的方式就可以调用JS中定义的接口了。

收起阅读 »

html5 5+sdk启动白屏

WEBSOCKET

  目前两个项目是html加原生一起开发,主要是html,html运用的是hbulider,里面有一个套成型的mui,原生这边主要是做一些插件之类的,html不能满足的功能,不知道是这个sdk适配的不怎么完美还是什么原因,总有一些问题,下面是我遇到的问题:
  
  标题
  
  ==启动白屏
  
  这个应该是android原生的问题,原生好处理,加载html,利用这个5+sdk的框架,就会有一些问题,
  
  处理:添加一个启动界面,用原生,设置背景为透明,设置一个主题
  
  <style name="Login" parent="DCloudTheme" >
  
  <!--<item name="android:windowIsTranslucent">true</item>-->
  
  <item name="android:windowBackground">@drawable/splash</item>
  
  <item name="android:windowNoTitle">true</item>
  
  </style>
  
  进来不做任何处理,直接跳转sdkWebApp,我是用这个方式直接
  
  加载的项目。这个sdkwebapp的主题必须设置为dcloud,
  
  加载地图会出现黑屏
  
  这个项目里面有一个考勤界面,需要百度地图
  
  ,目前
  
  ios的不会出现跳转时黑屏的问题,android的不行,不知道后期更新会不会解决这个问题

继续阅读 »

  目前两个项目是html加原生一起开发,主要是html,html运用的是hbulider,里面有一个套成型的mui,原生这边主要是做一些插件之类的,html不能满足的功能,不知道是这个sdk适配的不怎么完美还是什么原因,总有一些问题,下面是我遇到的问题:
  
  标题
  
  ==启动白屏
  
  这个应该是android原生的问题,原生好处理,加载html,利用这个5+sdk的框架,就会有一些问题,
  
  处理:添加一个启动界面,用原生,设置背景为透明,设置一个主题
  
  <style name="Login" parent="DCloudTheme" >
  
  <!--<item name="android:windowIsTranslucent">true</item>-->
  
  <item name="android:windowBackground">@drawable/splash</item>
  
  <item name="android:windowNoTitle">true</item>
  
  </style>
  
  进来不做任何处理,直接跳转sdkWebApp,我是用这个方式直接
  
  加载的项目。这个sdkwebapp的主题必须设置为dcloud,
  
  加载地图会出现黑屏
  
  这个项目里面有一个考勤界面,需要百度地图
  
  ,目前
  
  ios的不会出现跳转时黑屏的问题,android的不行,不知道后期更新会不会解决这个问题

收起阅读 »

WebSocket介绍和Socket的区别

chrome

  WebSocket介绍与原理

  WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成。

  ——百度百科

  目的:即时通讯,替代轮询

  应用场景:网站上的即时通讯是很常见的,比如网页的QQ,聊天系统等。按照以往的技术能力通常是采用轮询、Comet技术解决。

  HTTP协议是非持久化的,单向的网络协议,在建立连接后只允许浏览器向服务器发出请求后,服务器才能返回相应的数据。当需要即时通讯时,通过轮询在特定的时间间隔(如1秒),由浏览器向服务器发送Request请求,然后将最新的数据返回给浏览器。这样的方法最明显的缺点就是需要不断的发送请求,而且通常HTTP request的Header是非常长的,为了传输一个很小的数据需要付出巨大的代价,是很不合算的,占用了很多的宽带。

  缺点:会导致过多不必要的请求,浪费流量和服务器资源,每一次请求、应答,都浪费了一定流量在相同的头部信息上

  然而WebSocket的出现可以弥补这一缺点。在WebSocket中,只需要服务器和浏览器通过HTTP协议进行一个握手的动作,然后单独建立一条TCP的通信通道进行数据的传送。

  原理:WebSocket同HTTP一样也是应用层的协议,但是它是一种双向通信协议,是建立在TCP之上的。

  连接过程——握手过程

  1.浏览器、服务器建立TCP连接,三次握手。这是通信的基础,传输控制层,若失败后续都不执行。

  2.TCP连接成功后,浏览器通过HTTP协议向服务器传送WebSocket支持的版本号等信息。(开始前的HTTP握手)

  3.服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据。

  4.当收到了连接成功的消息后,通过TCP通道进行传输通信。

  WebSocket与HTTP的关系

  相同点

  都是一样基于TCP的,都是可靠性传输协议。

  都是应用层协议。

  不同点

  WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息。HTTP是单向的。

  WebSocket是需要握手进行建立连接的。

  联系

  WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的。

  WebSocket与Socket的关系

  Socket其实并不是一个协议,而是为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口。Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。当两台主机通信时,必须通过Socket连接,Socket则利用TCP/IP协议建立TCP连接。TCP连接则更依靠于底层的IP协议,IP协议的连接则依赖于链路层等更低层次。WebSocket则是一个典型的应用层协议。Socket是传输控制层协议,WebSocket是应用层协议。

  HTML5与WebSocket的关系

  WebSocket API是HTML5标准的一部分,但这并不代表WebSocket一定要用在HTML中,或者只能在基于浏览器的应用程序中使用。实际上,许多语言、框架和服务器都提供了WebSocket支持,例如:

  *基于C的libwebsocket.org

  *基于Node.js的Socket.io

  *基于Python的ws4py

  *基于C++的WebSocket++

  *Apache对WebSocket的支持:Apache Module mod_proxy_wstunnel

  *Nginx对WebSockets的支持:NGINX as a WebSockets Proxy、NGINX Announces Support for WebSocket Protocol、WebSocket proxying

  *lighttpd对WebSocket的支持:mod_websocket

  WebSocket机制

  WebSocket是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在TCP之上,同HTTP一样通过TCP来传输数据,但是它和HTTP最大不同是:

  WebSocket是一种双向通信协议,在建立连接后,WebSocket服务器和Browser/Client Agent都能主动的向对方发送或接收数据,就像Socket一样;WebSocket需要类似TCP的客户端和服务器端通过握手连接,连接成功后才能相互通信。

  非WebSocket模式传统HTTP客户端与服务器的交互如下图所示:

  图1.传统HTTP请求响应客户端服务器交互图

  使用WebSocket模式客户端与服务器的交互如下图:

  图2.WebSocket请求响应客户端服务器交互图

  上图对比可以看出,相对于传统HTTP每次请求-应答都需要客户端与服务端建立连接的模式,WebSocket是类似Socket的TCP长连接的通讯模式,一旦WebSocket连接建立后,后续数据都以帧序列的形式传输。在客户端断开WebSocket连接或Server端断掉连接前,不需要客户端和服务端重新发起连接请求。在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。

  我们再通过客户端和服务端交互的报文看一下WebSocket通讯与传统HTTP的不同:

  在客户端,new WebSocket实例化一个新的WebSocket客户端对象,连接类似ws://yourdomain:port/path的服务端WebSocket URL,WebSocket客户端对象会自动解析并识别为WebSocket请求,从而连接服务端端口,执行双方握手过程,客户端发送数据格式类似:

  清单1.WebSocket客户端连接报文

  GET/webfin/websocket/HTTP/1.1

  Host:localhost

  Upgrade:websocket

  Connection:Upgrade

  Sec-WebSocket-Key:xqBt3ImNzJbYqRINxEFlkg==

  Origin:

  http://localhost

  :8080

  Sec-WebSocket-Version:13

  http://localhost

  :8080

  Sec-WebSocket-Version:13

  可以看到,客户端发起的WebSocket连接报文类似传统HTTP报文,”Upgrade:websocket”参数值表明这是WebSocket类型请求,“Sec-WebSocket-Key”是WebSocket客户端发送的一个base64编码的密文,要求服务端必须返回一个对应加密的“Sec-WebSocket-Accept”应答,否则客户端会抛出“Error during WebSocket handshake”错误,并关闭连接。

  服务端收到报文后返回的数据格式类似:

  清单2.WebSocket服务端响应报文

  HTTP/1.1 101 Switching Protocols

  Upgrade:websocket

  Connection:Upgrade

  Sec-WebSocket-Accept:K7DJLdLooIwIG/MOpvWFB3y3FE8=

  “Sec-WebSocket-Accept”的值是服务端采用与客户端一致的密钥计算出来后返回客户端的,“HTTP/1.1 101 Switching Protocols”表示服务端接受WebSocket协议的客户端连接,经过这样的请求-响应处理后,客户端服务端的WebSocket连接握手成功,后续就可以进行TCP通讯了。

  在开发方面,WebSocket API也十分简单,我们只需要实例化WebSocket,创建连接,然后服务端和客户端就可以相互发送和响应消息,在下文WebSocket实现及案例分析部分,可以看到详细的WebSocket API及代码实现。

  WebSocket实现

  如上文所述,WebSocket的实现分为客户端和服务端两部分,客户端(通常为浏览器)发出WebSocket连接请求,服务端响应,实现类似TCP握手的动作,从而在浏览器客户端和WebSocket服务端之间形成一条HTTP长连接快速通道。两者之间后续进行直接的数据互相传送,不再需要发起连接和相应。

  以下简要描述WebSocket服务端API及客户端API。

  WebSocket服务端API

  WebSocket服务端在各个主流应用服务器厂商中已基本获得符合JEE JSR356标准规范API的支持,以下列举了部分常见的商用及开源应用服务器对WebSocket Server端的支持情况:

  表1.WebSocket服务端支持

  厂商应用服务器备注

  IBM WebSphere WebSphere 8.0以上版本支持,7.X之前版本结合MQTT支持类似的HTTP长连接

  甲骨文WebLogic WebLogic 12c支持,11g及10g版本通过HTTP Publish支持类似的HTTP长连接

  微软IIS IIS 7.0+支持

  Apache Tomcat Tomcat 7.0.5+支持,7.0.2X及7.0.3X通过自定义API支持

  Jetty Jetty 7.0+支持

  以下我们使用Tomcat7.0.5版本的服务端示例代码说明WebSocket服务端的实现:

  JSR356的WebSocket规范使用javax.websocket.*的API,可以将一个普通Java对象(POJO)使用 ServerEndpoint注释作为WebSocket服务器的端点,代码示例如下:

  清单3.WebSocket服务端API示例

   ServerEndpoint("/echo")

  public class EchoEndpoint{

   OnOpen

  public void onOpen(Session session)throws IOException{

  //以下代码省略...

  }

   OnMessage

  public String onMessage(String message){

  //以下代码省略...

  }

   Message(maxMessageSize=6)

  public void receiveMessage(String s){

  //以下代码省略...

  }

   OnError

  public void onError(Throwable t){

  //以下代码省略...

  }

   OnClose

  public void onClose(Session session,CloseReason reason){

  //以下代码省略...

  }

  }

  代码解释:

  上文的简洁代码即建立了一个WebSocket的服务端, ServerEndpoint("/echo")的annotation注释端点表示将WebSocket服务端运行在ws://[Server端IP或域名]:[Server端口]/websockets/echo的访问端点,客户端浏览器已经可以对WebSocket客户端API发起HTTP长连接了。

  使用ServerEndpoint注释的类必须有一个公共的无参数构造函数, onMessage注解的Java方法用于接收传入的WebSocket信息,这个信息可以是文本格式,也可以是二进制格式。

  OnOpen在这个端点一个新的连接建立时被调用。参数提供了连接的另一端的更多细节。Session表明两个WebSocket端点对话连接的另一端,可以理解为类似HTTPSession的概念。

  OnClose在连接被终止时调用。参数closeReason可封装更多细节,如为什么一个WebSocket连接关闭。

  更高级的定制如 Message注释,MaxMessageSize属性可以被用来定义消息字节最大限制,在示例程序中,如果超过6个字节的信息被接收,就报告错误和连接关闭。

  注意:早期不同应用服务器支持的WebSocket方式不尽相同,即使同一厂商,不同版本也有细微差别,如Tomcat服务器7.0.5以上的版本都是标准JSR356规范实现,而7.0.2x/7.0.3X的版本使用自定义API(WebSocketServlet和StreamInbound,前者是一个容器,用来初始化WebSocket环境;后者是用来具体处理WebSocket请求和响应,详见案例分析部分),且Tomcat7.0.3x与7.0.2x的createWebSocketInbound方法的定义不同,增加了一个HttpServletRequest参数,使得可以从request参数中获取更多WebSocket客户端的信息,如下代码所示:

  清单4.Tomcat7.0.3X版本WebSocket API

  public class EchoServlet extends WebSocketServlet{

   Override

  protected StreamInbound createWebSocketInbound(String subProtocol,

  HttpServletRequest request){

  //以下代码省略....

  return new MessageInbound(){

  //以下代码省略....

  }

  protected void onBinaryMessage(ByteBuffer buffer)

  throws IOException{

  //以下代码省略...

  }

  protected void onTextMessage(CharBuffer buffer)throws IOException{

  getWsOutbound().writeTextMessage(buffer);

  //以下代码省略...

  }

  };

  }

  }

  因此选择WebSocket的Server端重点需要选择其版本,通常情况下,更新的版本对WebSocket的支持是标准JSR规范API,但也要考虑开发易用性及老版本程序移植性等方面的问题,如下文所述的客户案例,就是因为客户要求统一应用服务器版本所以使用的Tomcat 7.0.3X版本的WebSocketServlet实现,而不是JSR356的 ServerEndpoint注释端点。

  WebSocket客户端API

  对于WebSocket客户端,主流的浏览器(包括PC和移动终端)现已都支持标准的HTML5的WebSocket API,这意味着客户端的WebSocket JavaScirpt脚本具备良好的一致性和跨平台特性,以下列举了常见的浏览器厂商对WebSocket的支持情况:

  表2.WebSocket客户端支持

  浏览器支持情况

  Chrome Chrome version 4+支持

  Firefox Firefox version 5+支持

  IE IE version 10+支持

  Safari IOS 5+支持

  Android Brower Android 4.5+支持

  客户端WebSocket API基本上已经在各个主流浏览器厂商中实现了统一,因此使用标准HTML5定义的WebSocket客户端的JavaScript API即可,当然也可以使用业界满足WebSocket标准规范的开源框架,如Socket.io。

  以下以一段代码示例说明WebSocket的客户端实现:

  清单5.WebSocket客户端API示例

  var ws=new WebSocket(“ws://echo.websocket.org”);

  ws.onopen=function(){ws.send(“Test!”);};

  ws.onmessage=function(evt){console.log(evt.data);ws.close();};

  ws.onclose=function(evt){console.log(“WebSocketClosed!”);};

  ws.onerror=function(evt){console.log(“WebSocketError!”);};

  第一行代码是在申请一个WebSocket对象,参数是需要连接的服务器端的地址,同HTTP协议开头一样,WebSocket协议的URL使用ws://开头,另外安全的WebSocket协议使用wss://开头。

  第二行到第五行为WebSocket对象注册消息的处理函数,WebSocket对象一共支持四个消息onopen,onmessage,onclose和onerror,有了这4个事件,我们就可以很容易很轻松的驾驭WebSocket。

  当Browser和WebSocketServer连接成功后,会触发onopen消息;如果连接失败,发送、接收数据失败或者处理数据出现错误,browser会触发onerror消息;当Browser接收到WebSocketServer发送过来的数据时,就会触发onmessage消息,参数evt中包含Server传输过来的数据;当Browser接收到WebSocketServer端发送的关闭连接请求时,就会触发onclose消息。我们可以看出所有的操作都是采用异步回调的方式触发,这样不会阻塞UI,可以获得更快的响应时间,更好的用户体验。

继续阅读 »

  WebSocket介绍与原理

  WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成。

  ——百度百科

  目的:即时通讯,替代轮询

  应用场景:网站上的即时通讯是很常见的,比如网页的QQ,聊天系统等。按照以往的技术能力通常是采用轮询、Comet技术解决。

  HTTP协议是非持久化的,单向的网络协议,在建立连接后只允许浏览器向服务器发出请求后,服务器才能返回相应的数据。当需要即时通讯时,通过轮询在特定的时间间隔(如1秒),由浏览器向服务器发送Request请求,然后将最新的数据返回给浏览器。这样的方法最明显的缺点就是需要不断的发送请求,而且通常HTTP request的Header是非常长的,为了传输一个很小的数据需要付出巨大的代价,是很不合算的,占用了很多的宽带。

  缺点:会导致过多不必要的请求,浪费流量和服务器资源,每一次请求、应答,都浪费了一定流量在相同的头部信息上

  然而WebSocket的出现可以弥补这一缺点。在WebSocket中,只需要服务器和浏览器通过HTTP协议进行一个握手的动作,然后单独建立一条TCP的通信通道进行数据的传送。

  原理:WebSocket同HTTP一样也是应用层的协议,但是它是一种双向通信协议,是建立在TCP之上的。

  连接过程——握手过程

  1.浏览器、服务器建立TCP连接,三次握手。这是通信的基础,传输控制层,若失败后续都不执行。

  2.TCP连接成功后,浏览器通过HTTP协议向服务器传送WebSocket支持的版本号等信息。(开始前的HTTP握手)

  3.服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据。

  4.当收到了连接成功的消息后,通过TCP通道进行传输通信。

  WebSocket与HTTP的关系

  相同点

  都是一样基于TCP的,都是可靠性传输协议。

  都是应用层协议。

  不同点

  WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息。HTTP是单向的。

  WebSocket是需要握手进行建立连接的。

  联系

  WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的。

  WebSocket与Socket的关系

  Socket其实并不是一个协议,而是为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口。Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。当两台主机通信时,必须通过Socket连接,Socket则利用TCP/IP协议建立TCP连接。TCP连接则更依靠于底层的IP协议,IP协议的连接则依赖于链路层等更低层次。WebSocket则是一个典型的应用层协议。Socket是传输控制层协议,WebSocket是应用层协议。

  HTML5与WebSocket的关系

  WebSocket API是HTML5标准的一部分,但这并不代表WebSocket一定要用在HTML中,或者只能在基于浏览器的应用程序中使用。实际上,许多语言、框架和服务器都提供了WebSocket支持,例如:

  *基于C的libwebsocket.org

  *基于Node.js的Socket.io

  *基于Python的ws4py

  *基于C++的WebSocket++

  *Apache对WebSocket的支持:Apache Module mod_proxy_wstunnel

  *Nginx对WebSockets的支持:NGINX as a WebSockets Proxy、NGINX Announces Support for WebSocket Protocol、WebSocket proxying

  *lighttpd对WebSocket的支持:mod_websocket

  WebSocket机制

  WebSocket是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在TCP之上,同HTTP一样通过TCP来传输数据,但是它和HTTP最大不同是:

  WebSocket是一种双向通信协议,在建立连接后,WebSocket服务器和Browser/Client Agent都能主动的向对方发送或接收数据,就像Socket一样;WebSocket需要类似TCP的客户端和服务器端通过握手连接,连接成功后才能相互通信。

  非WebSocket模式传统HTTP客户端与服务器的交互如下图所示:

  图1.传统HTTP请求响应客户端服务器交互图

  使用WebSocket模式客户端与服务器的交互如下图:

  图2.WebSocket请求响应客户端服务器交互图

  上图对比可以看出,相对于传统HTTP每次请求-应答都需要客户端与服务端建立连接的模式,WebSocket是类似Socket的TCP长连接的通讯模式,一旦WebSocket连接建立后,后续数据都以帧序列的形式传输。在客户端断开WebSocket连接或Server端断掉连接前,不需要客户端和服务端重新发起连接请求。在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。

  我们再通过客户端和服务端交互的报文看一下WebSocket通讯与传统HTTP的不同:

  在客户端,new WebSocket实例化一个新的WebSocket客户端对象,连接类似ws://yourdomain:port/path的服务端WebSocket URL,WebSocket客户端对象会自动解析并识别为WebSocket请求,从而连接服务端端口,执行双方握手过程,客户端发送数据格式类似:

  清单1.WebSocket客户端连接报文

  GET/webfin/websocket/HTTP/1.1

  Host:localhost

  Upgrade:websocket

  Connection:Upgrade

  Sec-WebSocket-Key:xqBt3ImNzJbYqRINxEFlkg==

  Origin:

  http://localhost

  :8080

  Sec-WebSocket-Version:13

  http://localhost

  :8080

  Sec-WebSocket-Version:13

  可以看到,客户端发起的WebSocket连接报文类似传统HTTP报文,”Upgrade:websocket”参数值表明这是WebSocket类型请求,“Sec-WebSocket-Key”是WebSocket客户端发送的一个base64编码的密文,要求服务端必须返回一个对应加密的“Sec-WebSocket-Accept”应答,否则客户端会抛出“Error during WebSocket handshake”错误,并关闭连接。

  服务端收到报文后返回的数据格式类似:

  清单2.WebSocket服务端响应报文

  HTTP/1.1 101 Switching Protocols

  Upgrade:websocket

  Connection:Upgrade

  Sec-WebSocket-Accept:K7DJLdLooIwIG/MOpvWFB3y3FE8=

  “Sec-WebSocket-Accept”的值是服务端采用与客户端一致的密钥计算出来后返回客户端的,“HTTP/1.1 101 Switching Protocols”表示服务端接受WebSocket协议的客户端连接,经过这样的请求-响应处理后,客户端服务端的WebSocket连接握手成功,后续就可以进行TCP通讯了。

  在开发方面,WebSocket API也十分简单,我们只需要实例化WebSocket,创建连接,然后服务端和客户端就可以相互发送和响应消息,在下文WebSocket实现及案例分析部分,可以看到详细的WebSocket API及代码实现。

  WebSocket实现

  如上文所述,WebSocket的实现分为客户端和服务端两部分,客户端(通常为浏览器)发出WebSocket连接请求,服务端响应,实现类似TCP握手的动作,从而在浏览器客户端和WebSocket服务端之间形成一条HTTP长连接快速通道。两者之间后续进行直接的数据互相传送,不再需要发起连接和相应。

  以下简要描述WebSocket服务端API及客户端API。

  WebSocket服务端API

  WebSocket服务端在各个主流应用服务器厂商中已基本获得符合JEE JSR356标准规范API的支持,以下列举了部分常见的商用及开源应用服务器对WebSocket Server端的支持情况:

  表1.WebSocket服务端支持

  厂商应用服务器备注

  IBM WebSphere WebSphere 8.0以上版本支持,7.X之前版本结合MQTT支持类似的HTTP长连接

  甲骨文WebLogic WebLogic 12c支持,11g及10g版本通过HTTP Publish支持类似的HTTP长连接

  微软IIS IIS 7.0+支持

  Apache Tomcat Tomcat 7.0.5+支持,7.0.2X及7.0.3X通过自定义API支持

  Jetty Jetty 7.0+支持

  以下我们使用Tomcat7.0.5版本的服务端示例代码说明WebSocket服务端的实现:

  JSR356的WebSocket规范使用javax.websocket.*的API,可以将一个普通Java对象(POJO)使用 ServerEndpoint注释作为WebSocket服务器的端点,代码示例如下:

  清单3.WebSocket服务端API示例

   ServerEndpoint("/echo")

  public class EchoEndpoint{

   OnOpen

  public void onOpen(Session session)throws IOException{

  //以下代码省略...

  }

   OnMessage

  public String onMessage(String message){

  //以下代码省略...

  }

   Message(maxMessageSize=6)

  public void receiveMessage(String s){

  //以下代码省略...

  }

   OnError

  public void onError(Throwable t){

  //以下代码省略...

  }

   OnClose

  public void onClose(Session session,CloseReason reason){

  //以下代码省略...

  }

  }

  代码解释:

  上文的简洁代码即建立了一个WebSocket的服务端, ServerEndpoint("/echo")的annotation注释端点表示将WebSocket服务端运行在ws://[Server端IP或域名]:[Server端口]/websockets/echo的访问端点,客户端浏览器已经可以对WebSocket客户端API发起HTTP长连接了。

  使用ServerEndpoint注释的类必须有一个公共的无参数构造函数, onMessage注解的Java方法用于接收传入的WebSocket信息,这个信息可以是文本格式,也可以是二进制格式。

  OnOpen在这个端点一个新的连接建立时被调用。参数提供了连接的另一端的更多细节。Session表明两个WebSocket端点对话连接的另一端,可以理解为类似HTTPSession的概念。

  OnClose在连接被终止时调用。参数closeReason可封装更多细节,如为什么一个WebSocket连接关闭。

  更高级的定制如 Message注释,MaxMessageSize属性可以被用来定义消息字节最大限制,在示例程序中,如果超过6个字节的信息被接收,就报告错误和连接关闭。

  注意:早期不同应用服务器支持的WebSocket方式不尽相同,即使同一厂商,不同版本也有细微差别,如Tomcat服务器7.0.5以上的版本都是标准JSR356规范实现,而7.0.2x/7.0.3X的版本使用自定义API(WebSocketServlet和StreamInbound,前者是一个容器,用来初始化WebSocket环境;后者是用来具体处理WebSocket请求和响应,详见案例分析部分),且Tomcat7.0.3x与7.0.2x的createWebSocketInbound方法的定义不同,增加了一个HttpServletRequest参数,使得可以从request参数中获取更多WebSocket客户端的信息,如下代码所示:

  清单4.Tomcat7.0.3X版本WebSocket API

  public class EchoServlet extends WebSocketServlet{

   Override

  protected StreamInbound createWebSocketInbound(String subProtocol,

  HttpServletRequest request){

  //以下代码省略....

  return new MessageInbound(){

  //以下代码省略....

  }

  protected void onBinaryMessage(ByteBuffer buffer)

  throws IOException{

  //以下代码省略...

  }

  protected void onTextMessage(CharBuffer buffer)throws IOException{

  getWsOutbound().writeTextMessage(buffer);

  //以下代码省略...

  }

  };

  }

  }

  因此选择WebSocket的Server端重点需要选择其版本,通常情况下,更新的版本对WebSocket的支持是标准JSR规范API,但也要考虑开发易用性及老版本程序移植性等方面的问题,如下文所述的客户案例,就是因为客户要求统一应用服务器版本所以使用的Tomcat 7.0.3X版本的WebSocketServlet实现,而不是JSR356的 ServerEndpoint注释端点。

  WebSocket客户端API

  对于WebSocket客户端,主流的浏览器(包括PC和移动终端)现已都支持标准的HTML5的WebSocket API,这意味着客户端的WebSocket JavaScirpt脚本具备良好的一致性和跨平台特性,以下列举了常见的浏览器厂商对WebSocket的支持情况:

  表2.WebSocket客户端支持

  浏览器支持情况

  Chrome Chrome version 4+支持

  Firefox Firefox version 5+支持

  IE IE version 10+支持

  Safari IOS 5+支持

  Android Brower Android 4.5+支持

  客户端WebSocket API基本上已经在各个主流浏览器厂商中实现了统一,因此使用标准HTML5定义的WebSocket客户端的JavaScript API即可,当然也可以使用业界满足WebSocket标准规范的开源框架,如Socket.io。

  以下以一段代码示例说明WebSocket的客户端实现:

  清单5.WebSocket客户端API示例

  var ws=new WebSocket(“ws://echo.websocket.org”);

  ws.onopen=function(){ws.send(“Test!”);};

  ws.onmessage=function(evt){console.log(evt.data);ws.close();};

  ws.onclose=function(evt){console.log(“WebSocketClosed!”);};

  ws.onerror=function(evt){console.log(“WebSocketError!”);};

  第一行代码是在申请一个WebSocket对象,参数是需要连接的服务器端的地址,同HTTP协议开头一样,WebSocket协议的URL使用ws://开头,另外安全的WebSocket协议使用wss://开头。

  第二行到第五行为WebSocket对象注册消息的处理函数,WebSocket对象一共支持四个消息onopen,onmessage,onclose和onerror,有了这4个事件,我们就可以很容易很轻松的驾驭WebSocket。

  当Browser和WebSocketServer连接成功后,会触发onopen消息;如果连接失败,发送、接收数据失败或者处理数据出现错误,browser会触发onerror消息;当Browser接收到WebSocketServer发送过来的数据时,就会触发onmessage消息,参数evt中包含Server传输过来的数据;当Browser接收到WebSocketServer端发送的关闭连接请求时,就会触发onclose消息。我们可以看出所有的操作都是采用异步回调的方式触发,这样不会阻塞UI,可以获得更快的响应时间,更好的用户体验。

收起阅读 »