HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

授权登录插件配置

OAuth

此文档将不再维护,请参考新文档:https://uniapp.dcloud.io/tutorial/app-oauth

云端打包登录鉴权功能需要到第三方开发平台申请应用后获取相关配置参数,目前支持的平台包括:

  1. 微信:微信开放平台
  2. QQ:腾讯开放平台
  3. 微博:新浪微博开放平台
  4. 苹果登录:iOS 苹果授权登录(Sign in with Apple)教程
  5. Google登录:HBuilderX3.2.7+版本支持
  6. Facebook登录:HBuilder3.2.7+版本支持

配置参数需要提交云端打包后才能生效,如果需要真机运行生效请使用自定义基座

HBuilderX中配置登录鉴权

从微信/QQ/微博开放平台申请获取配置参数后,需在HBuilderX中配置并提交云端打包才能生效。
老版本HBuilder配置界面有差异,逻辑是一样的,建议更新使用HBuilderX

配置使用登录鉴权模块

打开应用的manifest.json文件,在“App模块配置”项中勾选“OAuth(登录鉴权)”:

配置登录鉴权参数

微信

在manifest.json文件“App模块配置”项的“OAuth(登录鉴权)”下,勾选“微信登录”项,并输入从微信开放平台申请的参数:

QQ

在manifest.json文件“App模块配置”项的“OAuth(登录鉴权)”下,勾选“QQ登录”项,并输入从腾讯QQ开放平台申请的参数:

  • appid:腾讯QQ开放平台申请应用的AppID值。

微博

在manifest.json文件“App模块配置”项的“OAuth(登录鉴权)”下,勾选“新浪微博登录”项,并输入从新浪微博开放平台申请的参数:

  • appkey: "新浪微博平台应用appkey";
  • appsecret: "新浪微博平台应用appsecret";
  • redirect_uri: "新浪微博平台应用授权回调页地址"。

配置完成后Ctrl+S保存提交App云端打包生效。

<a id="secret"></a>

配置参数安全性问题

在HBuilder|HBuilderX中配置的参数云端打包后会保存在apk/ipa中,对于安全性要求高的开发者可能担心存在参数泄露的风险,可以采取以下方式处理。

  • JS代码中动态传参数
    如登录服务AuthService的请求授权认证方法authorize,可以通过第三个参数options动态传入appid、appSecret等。
    这些参数可以加密保存到js代码中或连网从服务器获取(避免保存在本地引起泄露风险,当然需要考虑网络传输过程的安全问题)。

  • 通过服务器完成授权认证
    根据OAuth规范,实际客户端授权只是为了获取授权临时票据(code),这时候可以仅在客户端配置appid参数调用登录服务AuthService的请求授权认证方法authorize获取临时票据,将票据提交到服务器完成后续的操作。
    这种情况授权的参数(如appsecret)仅保存服务器即可,安全性更高。
    在服务器的对接流程需要根据各开放平台的规范要求进行处理,如微信参考:授权后接口调用

这时在HBuilder|HBuilderX中可以填入任意值提交云端打包

继续阅读 »

此文档将不再维护,请参考新文档:https://uniapp.dcloud.io/tutorial/app-oauth

云端打包登录鉴权功能需要到第三方开发平台申请应用后获取相关配置参数,目前支持的平台包括:

  1. 微信:微信开放平台
  2. QQ:腾讯开放平台
  3. 微博:新浪微博开放平台
  4. 苹果登录:iOS 苹果授权登录(Sign in with Apple)教程
  5. Google登录:HBuilderX3.2.7+版本支持
  6. Facebook登录:HBuilder3.2.7+版本支持

配置参数需要提交云端打包后才能生效,如果需要真机运行生效请使用自定义基座

HBuilderX中配置登录鉴权

从微信/QQ/微博开放平台申请获取配置参数后,需在HBuilderX中配置并提交云端打包才能生效。
老版本HBuilder配置界面有差异,逻辑是一样的,建议更新使用HBuilderX

配置使用登录鉴权模块

打开应用的manifest.json文件,在“App模块配置”项中勾选“OAuth(登录鉴权)”:

配置登录鉴权参数

微信

在manifest.json文件“App模块配置”项的“OAuth(登录鉴权)”下,勾选“微信登录”项,并输入从微信开放平台申请的参数:

QQ

在manifest.json文件“App模块配置”项的“OAuth(登录鉴权)”下,勾选“QQ登录”项,并输入从腾讯QQ开放平台申请的参数:

  • appid:腾讯QQ开放平台申请应用的AppID值。

微博

在manifest.json文件“App模块配置”项的“OAuth(登录鉴权)”下,勾选“新浪微博登录”项,并输入从新浪微博开放平台申请的参数:

  • appkey: "新浪微博平台应用appkey";
  • appsecret: "新浪微博平台应用appsecret";
  • redirect_uri: "新浪微博平台应用授权回调页地址"。

配置完成后Ctrl+S保存提交App云端打包生效。

<a id="secret"></a>

配置参数安全性问题

在HBuilder|HBuilderX中配置的参数云端打包后会保存在apk/ipa中,对于安全性要求高的开发者可能担心存在参数泄露的风险,可以采取以下方式处理。

  • JS代码中动态传参数
    如登录服务AuthService的请求授权认证方法authorize,可以通过第三个参数options动态传入appid、appSecret等。
    这些参数可以加密保存到js代码中或连网从服务器获取(避免保存在本地引起泄露风险,当然需要考虑网络传输过程的安全问题)。

  • 通过服务器完成授权认证
    根据OAuth规范,实际客户端授权只是为了获取授权临时票据(code),这时候可以仅在客户端配置appid参数调用登录服务AuthService的请求授权认证方法authorize获取临时票据,将票据提交到服务器完成后续的操作。
    这种情况授权的参数(如appsecret)仅保存服务器即可,安全性更高。
    在服务器的对接流程需要根据各开放平台的规范要求进行处理,如微信参考:授权后接口调用

这时在HBuilder|HBuilderX中可以填入任意值提交云端打包

收起阅读 »

Android证书的生成和指纹获取

可能iOS下各种证书的繁杂,所以官方只给出了iOS系统下证书的生成说明,Android证书的生成的确很简单,我简单说明一下:
1、安装JDK
2、在cmd下,进入到JDK的bin目录,输入:
keytool -genkey -alias yourapp -keyalg RSA -validity 20000 -keystore yourapp.keystore
说明:yourapp就是证书的别名,20000是证书的有效天数,yourapp.keystore就是生成的证书名字。
3、一路根据指示设置密码,组织等,注意密码是不会显示或者以***代替,但是其实已经输入了。确认后选择Y,生成的证书会在bin目录下。
4、获取证书的指纹,输入:
keytool -list -v -keystore "D:\Program Files\Java\jdk1.8.0_40\bin\yourapp.keystore" -alias yourapp
路径请使用自己安装JDK的路径代替。
这样就可以获取MD5、SHA1的证书指纹。

的确很简单,但是的确有人不会,譬如说我以前就不会,囧。
列出来,以供大家查阅。

继续阅读 »

可能iOS下各种证书的繁杂,所以官方只给出了iOS系统下证书的生成说明,Android证书的生成的确很简单,我简单说明一下:
1、安装JDK
2、在cmd下,进入到JDK的bin目录,输入:
keytool -genkey -alias yourapp -keyalg RSA -validity 20000 -keystore yourapp.keystore
说明:yourapp就是证书的别名,20000是证书的有效天数,yourapp.keystore就是生成的证书名字。
3、一路根据指示设置密码,组织等,注意密码是不会显示或者以***代替,但是其实已经输入了。确认后选择Y,生成的证书会在bin目录下。
4、获取证书的指纹,输入:
keytool -list -v -keystore "D:\Program Files\Java\jdk1.8.0_40\bin\yourapp.keystore" -alias yourapp
路径请使用自己安装JDK的路径代替。
这样就可以获取MD5、SHA1的证书指纹。

的确很简单,但是的确有人不会,譬如说我以前就不会,囧。
列出来,以供大家查阅。

收起阅读 »

【分享】头像类裁剪上传

头像裁剪

夜深了,分享个头像类的裁剪上传,代码写的比较啰嗦。
虽然说用jquery渲染力差,没办法,还是用了。然后借助cropper来完成裁剪

<script src="plugin/avatar/js/jquery.min.js"></script>  
<script src="plugin/avatar/js/cropper.js"></script>  
<link href="plugin/avatar/css/cropper.css" rel="stylesheet">

照片容器

<a href="#picture"><div id="changeAvatar"></div></a>

裁剪照片的容器(样式就自己写了)

<div id="showEdit">  
        <header class="mui-bar mui-bar-nav mui-nav-bg" style="background:none; position:absolute; top:0;">  
            <a class="mui-icon iconfont icon-roundclosefill mui-pull-left" style="color:#ffffff; font-size:36px;" onclick="closeEdit();"></a>  
            <a class="mui-icon iconfont icon-roundcheckfill mui-pull-right" style="color:#ffffff; font-size:36px;" onclick="confirm();"></a>  
        </header>  
        <div id="report"></div>  
    </div>

下面就很简单了,调用下相机和相册选择图片。然后加入cropper的几行代码就搞定了

//拍照  
function getImage() {  
    var cmr = plus.camera.getCamera();  
    cmr.captureImage( function (p) {  
        plus.io.resolveLocalFileSystemURL( p, function ( entry ) {      
            var localurl = entry.toLocalURL();//  
            $("#report").html('<img src="'+localurl+'">');  
            cutImg();  
            mui('#picture').popover('toggle');  
        });  
    });  
}  
//相册选取  
function galleryImgs(){  
    plus.gallery.pick( function(e){  
        $("#report").html('<img src="'+e.files[0]+'">');  
        cutImg();  
        mui('#picture').popover('toggle');  
    }, function ( e ) {  
        //outSet( "取消选择图片" );  
    },{filter:"image",multiple:true});  
}

照片裁剪类:

function cutImg(){    
    $("#showEdit").fadeIn();  
    var $image = $('#report > img');  
    $image.cropper({  
      aspectRatio: 1 / 1,  
      autoCropArea: 0.8  
    });  
}

确定裁剪并上传(base64)

function confirm(){  

    $("#showEdit").fadeOut();  
    var $image = $('#report > img');  
    var dataURL = $image.cropper("getDataURL");  
    $("#changeAvatar").html('<img src="'+dataURL+'" />');  
}  

function postAvatar() {  

    var $image = $('#report > img');  
    var dataURL = $image.cropper("getDataURL");  
    var data = {  
            base64: dataURL  
    };  
    $.post(url,data,function(data){  
        //这里就自己写了哈  
    });  
};

效果

有高手可以补充下的谢谢啦哈

继续阅读 »

夜深了,分享个头像类的裁剪上传,代码写的比较啰嗦。
虽然说用jquery渲染力差,没办法,还是用了。然后借助cropper来完成裁剪

<script src="plugin/avatar/js/jquery.min.js"></script>  
<script src="plugin/avatar/js/cropper.js"></script>  
<link href="plugin/avatar/css/cropper.css" rel="stylesheet">

照片容器

<a href="#picture"><div id="changeAvatar"></div></a>

裁剪照片的容器(样式就自己写了)

<div id="showEdit">  
        <header class="mui-bar mui-bar-nav mui-nav-bg" style="background:none; position:absolute; top:0;">  
            <a class="mui-icon iconfont icon-roundclosefill mui-pull-left" style="color:#ffffff; font-size:36px;" onclick="closeEdit();"></a>  
            <a class="mui-icon iconfont icon-roundcheckfill mui-pull-right" style="color:#ffffff; font-size:36px;" onclick="confirm();"></a>  
        </header>  
        <div id="report"></div>  
    </div>

下面就很简单了,调用下相机和相册选择图片。然后加入cropper的几行代码就搞定了

//拍照  
function getImage() {  
    var cmr = plus.camera.getCamera();  
    cmr.captureImage( function (p) {  
        plus.io.resolveLocalFileSystemURL( p, function ( entry ) {      
            var localurl = entry.toLocalURL();//  
            $("#report").html('<img src="'+localurl+'">');  
            cutImg();  
            mui('#picture').popover('toggle');  
        });  
    });  
}  
//相册选取  
function galleryImgs(){  
    plus.gallery.pick( function(e){  
        $("#report").html('<img src="'+e.files[0]+'">');  
        cutImg();  
        mui('#picture').popover('toggle');  
    }, function ( e ) {  
        //outSet( "取消选择图片" );  
    },{filter:"image",multiple:true});  
}

照片裁剪类:

function cutImg(){    
    $("#showEdit").fadeIn();  
    var $image = $('#report > img');  
    $image.cropper({  
      aspectRatio: 1 / 1,  
      autoCropArea: 0.8  
    });  
}

确定裁剪并上传(base64)

function confirm(){  

    $("#showEdit").fadeOut();  
    var $image = $('#report > img');  
    var dataURL = $image.cropper("getDataURL");  
    $("#changeAvatar").html('<img src="'+dataURL+'" />');  
}  

function postAvatar() {  

    var $image = $('#report > img');  
    var dataURL = $image.cropper("getDataURL");  
    var data = {  
            base64: dataURL  
    };  
    $.post(url,data,function(data){  
        //这里就自己写了哈  
    });  
};

效果

有高手可以补充下的谢谢啦哈

收起阅读 »

夜深了,分享个思路,解决图片“表面”上秒传效果

接触5+有一个月了,也是接触APP开发有一个月了。今天先分享个思路出来,供大家参考。
图片上传传统解决方案:
1.选择图片

  1. 可能还要经过canvas的压缩处理
  2. 传到服务器返回图片url,或者点击发布同时上传
    这样的流程无疑会造成发布时间慢,用户体验性差。
    解决思路,现在还是个思路,明天开始敲
    1.选择图片
  3. 返回图片本地地址
  4. 点击发布写入本地存储(websql),列表页就调用这个本地数据了
  5. 写个js在后台开始慢慢处理数据与服务器的同步
    表面上用户会以为自己发布的图片已经秒速发布成功,还能在列表页看到刚刚发布的内容。实际还在后面慢慢的同步。

有高手可以帮忙完善下思路或者敲两下代码的感激不尽~

继续阅读 »

接触5+有一个月了,也是接触APP开发有一个月了。今天先分享个思路出来,供大家参考。
图片上传传统解决方案:
1.选择图片

  1. 可能还要经过canvas的压缩处理
  2. 传到服务器返回图片url,或者点击发布同时上传
    这样的流程无疑会造成发布时间慢,用户体验性差。
    解决思路,现在还是个思路,明天开始敲
    1.选择图片
  3. 返回图片本地地址
  4. 点击发布写入本地存储(websql),列表页就调用这个本地数据了
  5. 写个js在后台开始慢慢处理数据与服务器的同步
    表面上用户会以为自己发布的图片已经秒速发布成功,还能在列表页看到刚刚发布的内容。实际还在后面慢慢的同步。

有高手可以帮忙完善下思路或者敲两下代码的感激不尽~

收起阅读 »

分享一个检测iOS是否允许使用相机的Native.js

var AVCaptureDevice = plus.ios.importClass("AVCaptureDevice");  
var Status = AVCaptureDevice.authorizationStatusForMediaType("vide");  
if (3 != Status) {  
    var btnArray = ['确定'];  
    mui.confirm(' ','请在设置中允许使用相机',btnArray,function(e) {});  
}

注意此功能在iOS7.0以上设备才支持!

继续阅读 »
var AVCaptureDevice = plus.ios.importClass("AVCaptureDevice");  
var Status = AVCaptureDevice.authorizationStatusForMediaType("vide");  
if (3 != Status) {  
    var btnArray = ['确定'];  
    mui.confirm(' ','请在设置中允许使用相机',btnArray,function(e) {});  
}

注意此功能在iOS7.0以上设备才支持!

收起阅读 »

Native.js可以获取安卓的短信内容吗

Native.JS

想做个验证的功能,客户编辑了内容发到我的手机上,然后获取发过来的短信类容,POST到服务器然后进行绑定操作,不要问为啥不用短信接口给客户发送验证码,特么的太穷了,耗不起啊!

想做个验证的功能,客户编辑了内容发到我的手机上,然后获取发过来的短信类容,POST到服务器然后进行绑定操作,不要问为啥不用短信接口给客户发送验证码,特么的太穷了,耗不起啊!

关于使用中过程中的jQuery代码提示建议

真心很喜欢hbuilder,现在在努力的从sublime往hbuilder转;但是很多时候觉得hbuilder的很多地方还有待完善;不得不得在使用的过程中再打开sublime;例如jQuery的代码提示;
同样是打hover、click、post;回车,生成的代码如下;
hbuilder:
$(fucntion(){
$('#test').hover();
$('#test').click();
$.post()

})
sublime:
$(function(){
$('#test').hover(function() {
/ Stuff to do when the mouse enters the element /
}, function() {
/ Stuff to do when the mouse leaves the element /
});
$('#test').click(function(event) {
/ Act on the event /
});
$.post('/path/to/file', param1: 'value1', function(data, textStatus, xhr) {
/optional stuff to do after success /
});
})
还有就是直接打开远程服务器端的文件;hbuilder不能使用less和jQuery提示;
渴望hbuilder的代码提示功能可以加强;
愿hbuilder越来越完善;

继续阅读 »

真心很喜欢hbuilder,现在在努力的从sublime往hbuilder转;但是很多时候觉得hbuilder的很多地方还有待完善;不得不得在使用的过程中再打开sublime;例如jQuery的代码提示;
同样是打hover、click、post;回车,生成的代码如下;
hbuilder:
$(fucntion(){
$('#test').hover();
$('#test').click();
$.post()

})
sublime:
$(function(){
$('#test').hover(function() {
/ Stuff to do when the mouse enters the element /
}, function() {
/ Stuff to do when the mouse leaves the element /
});
$('#test').click(function(event) {
/ Act on the event /
});
$.post('/path/to/file', param1: 'value1', function(data, textStatus, xhr) {
/optional stuff to do after success /
});
})
还有就是直接打开远程服务器端的文件;hbuilder不能使用less和jQuery提示;
渴望hbuilder的代码提示功能可以加强;
愿hbuilder越来越完善;

收起阅读 »

MUI+HTML5+集成 IOS Weibo APP例子

HBuilder HTML5+ mui

最近闲得蛋痛,无意间发现了这么个东东,看看觉得还行,于是乎准备写个demo来玩玩,这一写不要紧,看到官方的文档,立马蛋都碎了~这~~
想查个API真是翻山越岭,走遍大江南北,什么百度、Google、360翻了个底朝天,就连个简单的demo都没人放出来,真是心碎了,好吧!事以至此还是看官方的组件例子吧,这个CSS啊,JS啊看得是头晕眼花,而且还不带任何关联的,这要写个成型的东东出来逼得死人哇。好在我这个人是个打不死的小强,我就不信了爷还征服不了你,立马菊花一紧,饭也不用吃了,水也不要喝了,烟也不要抽了,盯着个HBuilder码呀码呀码,码到一半都想砸电脑了,低头一看,得了还是别砸了,这尼玛是公司的Mac!认命吧,接着码,突然灵光一闪,醍醐灌顶,我艹,原来是这么回事哇,原来晚上才是IT男的天下,这句话真是一点都没错呀。好吧,经过一天的努力终于有个demo的样子了,收工了,打豆豆!!!!

继续阅读 »

最近闲得蛋痛,无意间发现了这么个东东,看看觉得还行,于是乎准备写个demo来玩玩,这一写不要紧,看到官方的文档,立马蛋都碎了~这~~
想查个API真是翻山越岭,走遍大江南北,什么百度、Google、360翻了个底朝天,就连个简单的demo都没人放出来,真是心碎了,好吧!事以至此还是看官方的组件例子吧,这个CSS啊,JS啊看得是头晕眼花,而且还不带任何关联的,这要写个成型的东东出来逼得死人哇。好在我这个人是个打不死的小强,我就不信了爷还征服不了你,立马菊花一紧,饭也不用吃了,水也不要喝了,烟也不要抽了,盯着个HBuilder码呀码呀码,码到一半都想砸电脑了,低头一看,得了还是别砸了,这尼玛是公司的Mac!认命吧,接着码,突然灵光一闪,醍醐灌顶,我艹,原来是这么回事哇,原来晚上才是IT男的天下,这句话真是一点都没错呀。好吧,经过一天的努力终于有个demo的样子了,收工了,打豆豆!!!!

收起阅读 »

关于AVG AntVirus Business Edition会误杀HBuilder关键文件的说明

启动 杀毒软件 误报

第一步 双击误报了的组件 主要有 两个如图


接下来继续看图









排除无效的话 就可能需要勾选 任意位置 和可更改所有内容

继续阅读 »

第一步 双击误报了的组件 主要有 两个如图


接下来继续看图









排除无效的话 就可能需要勾选 任意位置 和可更改所有内容

收起阅读 »

安卓分享9宫格图片到微信

分享链接 分享 Native.JS

首先感谢@DCloud_Android_磊子,test_weixin_share_mul_pic这个是他写给我的,我只做了一点小改动
用法,先setShareMsg把当前页面的商品名称、相册存到全局变量msg,然后调用shareToWechat弹出actionSheet选择分享到朋友圈或者发送给好友。
函数代码如下:

  function setShareMsg(goodsdata){  
            msg.content = msg.title = goodsdata.goods_name;  
            /* 分享图片 */  
            mui.each(goodsdata.pictures, function(i, n){  
                msg.pictures.push( n.url );  
            });  
            /* 分享链接 */  
            msg.href = mui.constMap.SERVER_HOST + "/mobile/goods.php?id=" + goodsdata.id ;  
            msg.thumbs.push("_www/72.png");  
            var userdata = getCachedUser();  
            if(userdata ){  
                msg.href += "&father=" + userdata.user.wx.wxid;  
            }  
            msg.brief = goodsdata.goods_brief;  
            getShortUrl(msg.href, function(result){  
                if(! result.error_code){  
                    msg.url_short = result[0].url_short;  
                }  
            });  
        }  
                  /**  
         * 发送分享消息  
         * @param {plus.share.ShareService} s  
         */  
        function shareMessage(ex) {  
            var pictures = [];  
            var pic_unloaded = msg.pictures.length ;  
            function onStateChanged( d, status ) {  
                switch(d.state){  
                    case 4:  
                        if(status == 200){  
                            console.log("图片下载完成:" + d.filename);  
                            pictures.push( plus.io.convertLocalFileSystemURL(d.filename) );  
                            pic_unloaded --;  
                            if(pic_unloaded == 0){  
                                copyToClip( msg.title + (msg.url_short || msg.href) + "\r\n" + msg.brief );  
                                mui.toast("标题已复制,请长按黏贴");  
                                console.log(JSON.stringify(pictures));  
                                test_weixin_share_mul_pic(ex, pictures, msg.title + (msg.url_short || msg.href) + "\r\n" + msg.brief);  
                            }  
                        } else {  
                            mui.toast("下载图片失败");  
                        }  
                        break;  
                    case 1:  
                        console.log("下载开始");  
                        break;  
                    case 2:  
                        console.log("请求已响应");   
                        break;  
                    case 3:  
                        console.log("下载进行中");  
                        break;  
                    default:  
                        console.log("state: " + d.state);  
                        break;  
                }  
            }  
            mui.toast("下载图片中,即将打开微信分享...");  
            mui.each(msg.pictures, function(i, n){  
                // 创建下载任务  
                var dtask = plus.downloader.createDownload( n );  
                dtask.addEventListener( "statechanged", onStateChanged, false );  
                dtask.start();  
            });  
        }  
        function shareToWechat(){  
            var shareto = ['com.tencent.mm.ui.tools.ShareToTimeLineUI', 'com.tencent.mm.ui.tools.ShareImgUI'];  
            plus.nativeUI.actionSheet( {title:"分享到微信",cancel:"取消",buttons:[{title:"分享到微信朋友圈"},{title:"发送给微信好友"}]}, function(e){  
                console.log( "User pressed: "+ e.index);  
                shareMessage(shareto[e.index - 1]);  
            } );  
        }  
        function test_weixin_share_mul_pic(ex, pics, description){  
            var Intent = plus.android.importClass('android.content.Intent');  
            var ComponentName = plus.android.importClass('android.content.ComponentName');  
            var ArrayList = plus.android.importClass('java.util.ArrayList');  
            var Uri = plus.android.importClass('android.net.Uri');  
            var Environment = plus.android.importClass('android.os.Environment');  
            var File = plus.android.importClass('java.io.File');  
            //var sdcardDir = plus.android.invoke(Environment.getExternalStorageDirectory(),'getAbsolutePath');  
            var intent = new Intent();  
            var localComponentName = new ComponentName("com.tencent.mm", ex);  
            intent.setComponent(localComponentName);  
            intent.setAction("android.intent.action.SEND_MULTIPLE");  
            intent.setType("image/*");  
            var localArrayList = new ArrayList();  
            for(var i =0;i< pics.length;i++){  
                var filePath = pics[i]; //sdcardDir +   
                //console.log('filePath=' + filePath);  
                localArrayList.add(Uri.fromFile(new File(filePath)));  
            }  
            intent.putParcelableArrayListExtra("android.intent.extra.STREAM", localArrayList);  
            intent.putExtra(Intent.EXTRA_TEXT, description);     
            var act = plus.android.runtimeMainActivity();  
            act.startActivity(intent);  
        }
继续阅读 »

首先感谢@DCloud_Android_磊子,test_weixin_share_mul_pic这个是他写给我的,我只做了一点小改动
用法,先setShareMsg把当前页面的商品名称、相册存到全局变量msg,然后调用shareToWechat弹出actionSheet选择分享到朋友圈或者发送给好友。
函数代码如下:

  function setShareMsg(goodsdata){  
            msg.content = msg.title = goodsdata.goods_name;  
            /* 分享图片 */  
            mui.each(goodsdata.pictures, function(i, n){  
                msg.pictures.push( n.url );  
            });  
            /* 分享链接 */  
            msg.href = mui.constMap.SERVER_HOST + "/mobile/goods.php?id=" + goodsdata.id ;  
            msg.thumbs.push("_www/72.png");  
            var userdata = getCachedUser();  
            if(userdata ){  
                msg.href += "&father=" + userdata.user.wx.wxid;  
            }  
            msg.brief = goodsdata.goods_brief;  
            getShortUrl(msg.href, function(result){  
                if(! result.error_code){  
                    msg.url_short = result[0].url_short;  
                }  
            });  
        }  
                  /**  
         * 发送分享消息  
         * @param {plus.share.ShareService} s  
         */  
        function shareMessage(ex) {  
            var pictures = [];  
            var pic_unloaded = msg.pictures.length ;  
            function onStateChanged( d, status ) {  
                switch(d.state){  
                    case 4:  
                        if(status == 200){  
                            console.log("图片下载完成:" + d.filename);  
                            pictures.push( plus.io.convertLocalFileSystemURL(d.filename) );  
                            pic_unloaded --;  
                            if(pic_unloaded == 0){  
                                copyToClip( msg.title + (msg.url_short || msg.href) + "\r\n" + msg.brief );  
                                mui.toast("标题已复制,请长按黏贴");  
                                console.log(JSON.stringify(pictures));  
                                test_weixin_share_mul_pic(ex, pictures, msg.title + (msg.url_short || msg.href) + "\r\n" + msg.brief);  
                            }  
                        } else {  
                            mui.toast("下载图片失败");  
                        }  
                        break;  
                    case 1:  
                        console.log("下载开始");  
                        break;  
                    case 2:  
                        console.log("请求已响应");   
                        break;  
                    case 3:  
                        console.log("下载进行中");  
                        break;  
                    default:  
                        console.log("state: " + d.state);  
                        break;  
                }  
            }  
            mui.toast("下载图片中,即将打开微信分享...");  
            mui.each(msg.pictures, function(i, n){  
                // 创建下载任务  
                var dtask = plus.downloader.createDownload( n );  
                dtask.addEventListener( "statechanged", onStateChanged, false );  
                dtask.start();  
            });  
        }  
        function shareToWechat(){  
            var shareto = ['com.tencent.mm.ui.tools.ShareToTimeLineUI', 'com.tencent.mm.ui.tools.ShareImgUI'];  
            plus.nativeUI.actionSheet( {title:"分享到微信",cancel:"取消",buttons:[{title:"分享到微信朋友圈"},{title:"发送给微信好友"}]}, function(e){  
                console.log( "User pressed: "+ e.index);  
                shareMessage(shareto[e.index - 1]);  
            } );  
        }  
        function test_weixin_share_mul_pic(ex, pics, description){  
            var Intent = plus.android.importClass('android.content.Intent');  
            var ComponentName = plus.android.importClass('android.content.ComponentName');  
            var ArrayList = plus.android.importClass('java.util.ArrayList');  
            var Uri = plus.android.importClass('android.net.Uri');  
            var Environment = plus.android.importClass('android.os.Environment');  
            var File = plus.android.importClass('java.io.File');  
            //var sdcardDir = plus.android.invoke(Environment.getExternalStorageDirectory(),'getAbsolutePath');  
            var intent = new Intent();  
            var localComponentName = new ComponentName("com.tencent.mm", ex);  
            intent.setComponent(localComponentName);  
            intent.setAction("android.intent.action.SEND_MULTIPLE");  
            intent.setType("image/*");  
            var localArrayList = new ArrayList();  
            for(var i =0;i< pics.length;i++){  
                var filePath = pics[i]; //sdcardDir +   
                //console.log('filePath=' + filePath);  
                localArrayList.add(Uri.fromFile(new File(filePath)));  
            }  
            intent.putParcelableArrayListExtra("android.intent.extra.STREAM", localArrayList);  
            intent.putExtra(Intent.EXTRA_TEXT, description);     
            var act = plus.android.runtimeMainActivity();  
            act.startActivity(intent);  
        }
收起阅读 »

App资源在线升级更新

更新 升级 wgt widget

官方已发布APP升级中心,支持原生APP整包升级和wgt资源包升级。详见

注意:uni-app的热更新另见文档https://ask.dcloud.net.cn/article/35667

5+应用可使用以下方式进行升级

本文重点介绍5+应用资源独立升级,相对整包升级有以下优势:

  1. 无需重新提交应用市场审核更新,降低更新周期;
  2. 无需用户手动点击操作安装更新,优化用户体验;
  3. App资源相对整包体积更小,升级速度更快。

生成移动App资源升级包

  • 在HBuilder中编辑好新的移动App资源后,更新manifest.json的版本号
    原来版本是1.0,新版本修改为2.0:

  • 在HBuilder中生成升级包文件(wgt)
    菜单“发行” -> “制作移动App资源升级包”:

    在以下界面中通过“浏览”按钮选择保存路径,点击“确定”保存wgt文件:

  • 生成wgt后提交到手机可访问的网络地址
    App资源升级包下载地址:
    http://www.dcloud.io/docs/a/update/H5EF3C469.wgt
    为了模拟正常的升级检测流程,添加以下检测升级地址(返回最新版本号):
    http://demo.dcloud.net.cn/test/update/check.php

应用中检测更新资源

检测服务器上是否有新版本

  • 获取当前应用的版本号

    var wgtVer=null;  
    function plusReady(){  
    // ......  
    // 获取本地应用资源版本号  
    plus.runtime.getProperty(plus.runtime.appid,function(inf){  
        wgtVer=inf.version;  
        console.log("当前应用版本:"+wgtVer);  
    });  
    }  
    if(window.plus){  
    plusReady();  
    }else{  
    document.addEventListener('plusready',plusReady,false);  
    }
  • 发起ajax请求检测是否有新版本

    // 检测更新  
    var checkUrl="http://demo.dcloud.net.cn/test/update/check.php";  
    function checkUpdate(){  
    plus.nativeUI.showWaiting("检测更新...");  
    var xhr=new XMLHttpRequest();  
    xhr.onreadystatechange=function(){  
        switch(xhr.readyState){  
            case 4:  
            plus.nativeUI.closeWaiting();  
            if(xhr.status==200){  
                console.log("检测更新成功:"+xhr.responseText);  
                var newVer=xhr.responseText;  
                if(wgtVer&&newVer&&(wgtVer!=newVer)){  
                    downWgt();  // 下载升级包  
                }else{  
                    plus.nativeUI.alert("无新版本可更新!");  
                }  
            }else{  
                console.log("检测更新失败!");  
                plus.nativeUI.alert("检测更新失败!");  
            }  
            break;  
            default:  
            break;  
        }  
    }  
    xhr.open('GET',checkUrl);  
    xhr.send();  
    }

更新应用资源

  • 从服务器下载应用资源包(wgt文件)

    // 下载wgt文件  
    var wgtUrl="http://demo.dcloud.net.cn/test/update/H5EF3C469.wgt";  
    function downWgt(){  
    plus.nativeUI.showWaiting("下载wgt文件...");  
    plus.downloader.createDownload( wgtUrl, {filename:"_doc/update/"}, function(d,status){  
        if ( status == 200 ) {   
            console.log("下载wgt成功:"+d.filename);  
            installWgt(d.filename); // 安装wgt包  
        } else {  
            console.log("下载wgt失败!");  
            plus.nativeUI.alert("下载wgt失败!");  
        }  
        plus.nativeUI.closeWaiting();  
    }).start();  
    }
  • 更新应用资源包(wgt文件)

    // 更新应用资源  
    function installWgt(path){  
    plus.nativeUI.showWaiting("安装wgt文件...");  
    plus.runtime.install(path,{},function(){  
        plus.nativeUI.closeWaiting();  
        console.log("安装wgt文件成功!");  
        plus.nativeUI.alert("应用资源更新完成!",function(){  
            plus.runtime.restart();  
        });  
    },function(e){  
        plus.nativeUI.closeWaiting();  
        console.log("安装wgt文件失败["+e.code+"]:"+e.message);  
        plus.nativeUI.alert("安装wgt文件失败["+e.code+"]:"+e.message);  
    });  
    }

    wgt更新原生层是通过文件夹重命名方式实现,要么全部更新成功,要么更新失败,不会出现仅部分文件更新的情况

其它可改进的思路

  • 检测更新更好的模式应该是客户端提交本地应用资源版本号到升级服务器,由升级服务器判断是否可更新并且返回App升级资源包下载地址,避免在客户端写资源下载地址;
  • 更新时可以在后台静默下载,下次启动是直接更新,避免更新时打断用户操作。
  • 差量升级更新App资源,参考App资源在线差量升级更新

附件testUpdate.zip为测试移动App包

App store应用更新说明
应用资源更新肯定是违反apple政策的,但目前看起来,如果你不是很大的公司,apple不会理睬你。如果你是大公司,建议不要做整体更新,每次更新几个页面,也不要提示更新后需要重启,这样会安全点。

继续阅读 »

官方已发布APP升级中心,支持原生APP整包升级和wgt资源包升级。详见

注意:uni-app的热更新另见文档https://ask.dcloud.net.cn/article/35667

5+应用可使用以下方式进行升级

本文重点介绍5+应用资源独立升级,相对整包升级有以下优势:

  1. 无需重新提交应用市场审核更新,降低更新周期;
  2. 无需用户手动点击操作安装更新,优化用户体验;
  3. App资源相对整包体积更小,升级速度更快。

生成移动App资源升级包

  • 在HBuilder中编辑好新的移动App资源后,更新manifest.json的版本号
    原来版本是1.0,新版本修改为2.0:

  • 在HBuilder中生成升级包文件(wgt)
    菜单“发行” -> “制作移动App资源升级包”:

    在以下界面中通过“浏览”按钮选择保存路径,点击“确定”保存wgt文件:

  • 生成wgt后提交到手机可访问的网络地址
    App资源升级包下载地址:
    http://www.dcloud.io/docs/a/update/H5EF3C469.wgt
    为了模拟正常的升级检测流程,添加以下检测升级地址(返回最新版本号):
    http://demo.dcloud.net.cn/test/update/check.php

应用中检测更新资源

检测服务器上是否有新版本

  • 获取当前应用的版本号

    var wgtVer=null;  
    function plusReady(){  
    // ......  
    // 获取本地应用资源版本号  
    plus.runtime.getProperty(plus.runtime.appid,function(inf){  
        wgtVer=inf.version;  
        console.log("当前应用版本:"+wgtVer);  
    });  
    }  
    if(window.plus){  
    plusReady();  
    }else{  
    document.addEventListener('plusready',plusReady,false);  
    }
  • 发起ajax请求检测是否有新版本

    // 检测更新  
    var checkUrl="http://demo.dcloud.net.cn/test/update/check.php";  
    function checkUpdate(){  
    plus.nativeUI.showWaiting("检测更新...");  
    var xhr=new XMLHttpRequest();  
    xhr.onreadystatechange=function(){  
        switch(xhr.readyState){  
            case 4:  
            plus.nativeUI.closeWaiting();  
            if(xhr.status==200){  
                console.log("检测更新成功:"+xhr.responseText);  
                var newVer=xhr.responseText;  
                if(wgtVer&&newVer&&(wgtVer!=newVer)){  
                    downWgt();  // 下载升级包  
                }else{  
                    plus.nativeUI.alert("无新版本可更新!");  
                }  
            }else{  
                console.log("检测更新失败!");  
                plus.nativeUI.alert("检测更新失败!");  
            }  
            break;  
            default:  
            break;  
        }  
    }  
    xhr.open('GET',checkUrl);  
    xhr.send();  
    }

更新应用资源

  • 从服务器下载应用资源包(wgt文件)

    // 下载wgt文件  
    var wgtUrl="http://demo.dcloud.net.cn/test/update/H5EF3C469.wgt";  
    function downWgt(){  
    plus.nativeUI.showWaiting("下载wgt文件...");  
    plus.downloader.createDownload( wgtUrl, {filename:"_doc/update/"}, function(d,status){  
        if ( status == 200 ) {   
            console.log("下载wgt成功:"+d.filename);  
            installWgt(d.filename); // 安装wgt包  
        } else {  
            console.log("下载wgt失败!");  
            plus.nativeUI.alert("下载wgt失败!");  
        }  
        plus.nativeUI.closeWaiting();  
    }).start();  
    }
  • 更新应用资源包(wgt文件)

    // 更新应用资源  
    function installWgt(path){  
    plus.nativeUI.showWaiting("安装wgt文件...");  
    plus.runtime.install(path,{},function(){  
        plus.nativeUI.closeWaiting();  
        console.log("安装wgt文件成功!");  
        plus.nativeUI.alert("应用资源更新完成!",function(){  
            plus.runtime.restart();  
        });  
    },function(e){  
        plus.nativeUI.closeWaiting();  
        console.log("安装wgt文件失败["+e.code+"]:"+e.message);  
        plus.nativeUI.alert("安装wgt文件失败["+e.code+"]:"+e.message);  
    });  
    }

    wgt更新原生层是通过文件夹重命名方式实现,要么全部更新成功,要么更新失败,不会出现仅部分文件更新的情况

其它可改进的思路

  • 检测更新更好的模式应该是客户端提交本地应用资源版本号到升级服务器,由升级服务器判断是否可更新并且返回App升级资源包下载地址,避免在客户端写资源下载地址;
  • 更新时可以在后台静默下载,下次启动是直接更新,避免更新时打断用户操作。
  • 差量升级更新App资源,参考App资源在线差量升级更新

附件testUpdate.zip为测试移动App包

App store应用更新说明
应用资源更新肯定是违反apple政策的,但目前看起来,如果你不是很大的公司,apple不会理睬你。如果你是大公司,建议不要做整体更新,每次更新几个页面,也不要提示更新后需要重启,这样会安全点。

收起阅读 »