HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

Mac版6.7.0真机联调控制台无法使用的解决办法

HBuilder

1.关闭HBuilder

  1. 对HBuilder.app点右键,显示包内容如下图

    3.拷贝jre到Contents目录下(注意是拷贝,不是剪切,这里的jre仍需要保留),我们会在今天晚上(2015-11-9)发布紧急更新
继续阅读 »

1.关闭HBuilder

  1. 对HBuilder.app点右键,显示包内容如下图

    3.拷贝jre到Contents目录下(注意是拷贝,不是剪切,这里的jre仍需要保留),我们会在今天晚上(2015-11-9)发布紧急更新
收起阅读 »

团队承接hbuilder,h5+,mui的外包

外包

如题:有需要外包的请联系我,前端,后台都可以全包,QQ1046373779

如题:有需要外包的请联系我,前端,后台都可以全包,QQ1046373779

iOS平台设置应用访问白名单(LSApplicationQueriesSchemes)

iOS 5+App开发

此文档不再维护,请参考新文档地址:https://uniapp.dcloud.io/tutorial/app-ios-schemewhitelist

从iOS9开始系统策略更新,加入对用户隐私以及禁止扫描系统信息的控制,限制了scheme协议的访问。需要将其它App注册的scheme添加到应用访问白名单(LSApplicationQueriesSchemes)中才能实现以下功能:

  • 通过scheme检查其它App是否安装,不添加到白名单则检测结果为未安装(即使应用已经安装)
  • 通过scheme协议调用其它App,不添加到白名单则会弹出提示框,用户确认后才能启动应用,添加到白名单列表后则无需用户确认直接启动应用。

设置应用跳转白名单列表

打开项目的manifest.json文件,切换到“代码视图”

  • 5+App项目
    在manifest.json文件的"plus"->"distribute"->"apple"下添加urlschemewhitelist节点数据如下:

    "plus": {    
    "distribute": {    
    "apple": {    
        "urlschemewhitelist": [    
                "BaiduSSO",  
                "qqmusic"  
        ],    
        //...    
    },    
    //...    
    },    
    //...    
    },    
    //... 
  • uni-app项目
    把上面的urlschemewhitelist节点数据放到manifest.json的"app-plus"->"distribute"->"ios"节点下

注意:保存后提交App云端打包后才能生效,列表最多可添加50个

App云端打包默认添加的白名单列表

为了方便开发者调用一些常用的第三方应用,云端打包默认已经添加以下白名单

lightsky  
shark.video  
bobo  
snssdk32  
pptv  
bilibili  
kugouURL  
gaeagj  
qqnews  
zhihu  
doubanradio  
openApp.jdMobile  
imeituan  
tmall  
dianping  
vipshop  
yanxuan  
wccbyihaodian  
taobao  
suning  
kaola  
ctrip  
kuaikanmanhua  
gugutouchmanga  
qrxs  
mailmaster  
jcnhers  
wbmain  
yixin  
ydcourse  
ntesopen  
yddict  
shanbay  
tencentweiboSdkv2  
weibosdk2.5  
sinaweibo  
sinaweibohd  
cydia  
weixin  
wechat  
weibosdk  
mqq  
mqqapi  
mqzone  
wtloginmqq2  
mqqopensdkapiV3  
mqqwpa  
mqqopensdkapiV2  
mqqOpensdkSSoL  
hbuilder  
streamapp  
baidumap  
iosamap  
qqmap

iOS平台设置UrlSchemes参考:https://ask.dcloud.net.cn/article/64

继续阅读 »

此文档不再维护,请参考新文档地址:https://uniapp.dcloud.io/tutorial/app-ios-schemewhitelist

从iOS9开始系统策略更新,加入对用户隐私以及禁止扫描系统信息的控制,限制了scheme协议的访问。需要将其它App注册的scheme添加到应用访问白名单(LSApplicationQueriesSchemes)中才能实现以下功能:

  • 通过scheme检查其它App是否安装,不添加到白名单则检测结果为未安装(即使应用已经安装)
  • 通过scheme协议调用其它App,不添加到白名单则会弹出提示框,用户确认后才能启动应用,添加到白名单列表后则无需用户确认直接启动应用。

设置应用跳转白名单列表

打开项目的manifest.json文件,切换到“代码视图”

  • 5+App项目
    在manifest.json文件的"plus"->"distribute"->"apple"下添加urlschemewhitelist节点数据如下:

    "plus": {    
    "distribute": {    
    "apple": {    
        "urlschemewhitelist": [    
                "BaiduSSO",  
                "qqmusic"  
        ],    
        //...    
    },    
    //...    
    },    
    //...    
    },    
    //... 
  • uni-app项目
    把上面的urlschemewhitelist节点数据放到manifest.json的"app-plus"->"distribute"->"ios"节点下

注意:保存后提交App云端打包后才能生效,列表最多可添加50个

App云端打包默认添加的白名单列表

为了方便开发者调用一些常用的第三方应用,云端打包默认已经添加以下白名单

lightsky  
shark.video  
bobo  
snssdk32  
pptv  
bilibili  
kugouURL  
gaeagj  
qqnews  
zhihu  
doubanradio  
openApp.jdMobile  
imeituan  
tmall  
dianping  
vipshop  
yanxuan  
wccbyihaodian  
taobao  
suning  
kaola  
ctrip  
kuaikanmanhua  
gugutouchmanga  
qrxs  
mailmaster  
jcnhers  
wbmain  
yixin  
ydcourse  
ntesopen  
yddict  
shanbay  
tencentweiboSdkv2  
weibosdk2.5  
sinaweibo  
sinaweibohd  
cydia  
weixin  
wechat  
weibosdk  
mqq  
mqqapi  
mqzone  
wtloginmqq2  
mqqopensdkapiV3  
mqqwpa  
mqqopensdkapiV2  
mqqOpensdkSSoL  
hbuilder  
streamapp  
baidumap  
iosamap  
qqmap

iOS平台设置UrlSchemes参考:https://ask.dcloud.net.cn/article/64

收起阅读 »

Android插件,TTS语音合成(文字转化语音)。

插件开发 插件

相关介绍

概念介绍

语音合成是实现人机语音交互,建立一个有听和讲能力的交互系统所必需的关键技术。随着语音技术的发展,百度自主研发了语音合成系统(TTS),功能是接受用户发送的文本,生成语音发送给用户。
对本文中将提到的概念约定如下:
语音合成:将文本合成为语音,即声音文件。
合成引擎:将文本合成为语音的核心模块。
TTS:Text To Speech,即“从文本到语音”。
BDTTSClient:语音合成 SDK 简称,详见下条。
语音合成 SDK:即本开发包,文中简称为 BDTTSClient。BDTTSClient 是一个封装了网络收发、音频播放功能的语音合成解决方案。借助 BDTTSClient 可以快速地在应用程序中集成语音合成功能。
百度语音

合成方式

1.离在线语音合成
2.在线语音合成(本文使用方式)

应用场景

我们团队的应用场景是在推送消息后,需要播放推送消息。

准备事项

1.下载Android 平台 HTML5+ SDK 。
2.注册百度开发者,创建应用,开通服务,请参考集成指南
3.下载在线语音合成SDK_Android版。

实现步骤

1.先导入5+SDK就不多说了,参考Android平台第三方插件开发指导
2.添加BDTTSClient到工程(将开发包中的 libs 目录整体拷贝到工程目录,libs 目录包括了各平台的 SO 库,开发者视应用需要可以进行删减。galaxy_lite.jar 是百度 Android 公共基础库。)
3.权限声明(需要在 AndroidManifest.xml 文件,增加以上三个权限)

<uses-permission android:name="android.permission.INTERNET" />  
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

4.集成java代码

import org.json.JSONArray;  
import com.baidu.speechsynthesizer.SpeechSynthesizer;  
import com.baidu.speechsynthesizer.SpeechSynthesizerListener;  
import com.baidu.speechsynthesizer.publicutility.SpeechError;  
import android.media.AudioManager;  
import android.util.Log;  
import io.dcloud.common.DHInterface.IWebview;  
import io.dcloud.common.DHInterface.StandardFeature;  
import io.dcloud.common.util.JSUtil;  
public class baidutts extends StandardFeature implements SpeechSynthesizerListener{  
    private IWebview iwv ;  
    private String text;  
    private SpeechSynthesizer speechSynthesizer;  
    private static final String TAG = "zlz";  
    public void speak(IWebview pWebview, JSONArray array)  
    {  
        iwv = pWebview;  
        String CallBackID = array.optString(0);  
        JSONArray newArray = new JSONArray();  
        newArray.put(array.optString(1));  
        //前台传过来的文本信息  
        text = array.optString(1);  
        //Log.i(TAG, text);  
        initialTts();  
        JSUtil.execCallback(pWebview, CallBackID, newArray, JSUtil.OK, false);  
    }  
    //百度TTS初始化  
    private void initialTts() {  
        speechSynthesizer = new SpeechSynthesizer(iwv.getContext(),  
                "holder", this);  
        // 此处需要将setApiKey方法的两个参数替换为你在百度开发者中心注册应用所得到的apiKey和secretKey  
        speechSynthesizer.setApiKey("your-apiKey", "your-secretKey");  
        speechSynthesizer.setAudioStreamType(AudioManager.STREAM_MUSIC);  
        //setVolumeControlStream(AudioManager.STREAM_MUSIC);  
        new Thread(new Runnable() {  
            @Override  
            public void run() {  
                setParams();  
                int ret = speechSynthesizer.speak(text);  
                if (ret != 0) {  
                    Log.i(TAG,"开始合成器失败" );  
                }  
            }  
        }).start();  

    }  
    private void setParams() {  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "0");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, "5");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, "5");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, "5");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_ENCODE, SpeechSynthesizer.AUDIO_ENCODE_AMR);  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_RATE, SpeechSynthesizer.AUDIO_BITRATE_AMR_15K85);  
    }  
    @Override  
    public void onBufferProgressChanged(SpeechSynthesizer arg0, int arg1) {}  
    @Override  
    public void onCancel(SpeechSynthesizer arg0) {}  
    @Override  
    public void onError(SpeechSynthesizer arg0, SpeechError arg1) {}  
    @Override  
    public void onNewDataArrive(SpeechSynthesizer arg0, byte[] arg1, boolean arg2) {}  
    @Override  
    public void onSpeechFinish(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSpeechPause(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSpeechProgressChanged(SpeechSynthesizer arg0, int arg1) {}  
    @Override  
    public void onSpeechResume(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSpeechStart(SpeechSynthesizer arg0) {}  
    @Override  
    public void onStartWorking(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSynthesizeFinish(SpeechSynthesizer arg0) {}  
}

5.前台javascript代码

document.addEventListener( "plusready",  function()  
{  
    var _BARCODE = 'kxdPlugins',  
        B = window.plus.bridge;  
    var kxdPlugins =   
    {  
        // 声明异步返回方法  
        speak : function (Argus, successCallback, errorCallback )   
        {  
            var success = typeof successCallback !== 'function' ? null : function(args)   
            {  
                successCallback(args);  
            },  
            fail = typeof errorCallback !== 'function' ? null : function(code)   
            {  
                errorCallback(code);  
            };  
            callbackID = B.callbackId(success, fail);  
            // 通知Native层plugintest扩展插件运行”speak”方法  
            return B.exec(_BARCODE, "speak", [callbackID, Argus]);  
        }  
    };  
    window.plus.kxdPlugins = kxdPlugins;  
}, true );

使用方式

和普通的API调用方式一样:plus.kxdPlugins.speak("这是语音播放的文字内容");

注意事项

1.在/assets/data/properties.xml中添加插件权限。
2.在/assets/apps/appid/www/manifest.json 中添加对应插件权限。
3.在/AndroidManifest.xml 文件中添加对应的百度语音合成权限。

继续阅读 »

相关介绍

概念介绍

语音合成是实现人机语音交互,建立一个有听和讲能力的交互系统所必需的关键技术。随着语音技术的发展,百度自主研发了语音合成系统(TTS),功能是接受用户发送的文本,生成语音发送给用户。
对本文中将提到的概念约定如下:
语音合成:将文本合成为语音,即声音文件。
合成引擎:将文本合成为语音的核心模块。
TTS:Text To Speech,即“从文本到语音”。
BDTTSClient:语音合成 SDK 简称,详见下条。
语音合成 SDK:即本开发包,文中简称为 BDTTSClient。BDTTSClient 是一个封装了网络收发、音频播放功能的语音合成解决方案。借助 BDTTSClient 可以快速地在应用程序中集成语音合成功能。
百度语音

合成方式

1.离在线语音合成
2.在线语音合成(本文使用方式)

应用场景

我们团队的应用场景是在推送消息后,需要播放推送消息。

准备事项

1.下载Android 平台 HTML5+ SDK 。
2.注册百度开发者,创建应用,开通服务,请参考集成指南
3.下载在线语音合成SDK_Android版。

实现步骤

1.先导入5+SDK就不多说了,参考Android平台第三方插件开发指导
2.添加BDTTSClient到工程(将开发包中的 libs 目录整体拷贝到工程目录,libs 目录包括了各平台的 SO 库,开发者视应用需要可以进行删减。galaxy_lite.jar 是百度 Android 公共基础库。)
3.权限声明(需要在 AndroidManifest.xml 文件,增加以上三个权限)

<uses-permission android:name="android.permission.INTERNET" />  
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

4.集成java代码

import org.json.JSONArray;  
import com.baidu.speechsynthesizer.SpeechSynthesizer;  
import com.baidu.speechsynthesizer.SpeechSynthesizerListener;  
import com.baidu.speechsynthesizer.publicutility.SpeechError;  
import android.media.AudioManager;  
import android.util.Log;  
import io.dcloud.common.DHInterface.IWebview;  
import io.dcloud.common.DHInterface.StandardFeature;  
import io.dcloud.common.util.JSUtil;  
public class baidutts extends StandardFeature implements SpeechSynthesizerListener{  
    private IWebview iwv ;  
    private String text;  
    private SpeechSynthesizer speechSynthesizer;  
    private static final String TAG = "zlz";  
    public void speak(IWebview pWebview, JSONArray array)  
    {  
        iwv = pWebview;  
        String CallBackID = array.optString(0);  
        JSONArray newArray = new JSONArray();  
        newArray.put(array.optString(1));  
        //前台传过来的文本信息  
        text = array.optString(1);  
        //Log.i(TAG, text);  
        initialTts();  
        JSUtil.execCallback(pWebview, CallBackID, newArray, JSUtil.OK, false);  
    }  
    //百度TTS初始化  
    private void initialTts() {  
        speechSynthesizer = new SpeechSynthesizer(iwv.getContext(),  
                "holder", this);  
        // 此处需要将setApiKey方法的两个参数替换为你在百度开发者中心注册应用所得到的apiKey和secretKey  
        speechSynthesizer.setApiKey("your-apiKey", "your-secretKey");  
        speechSynthesizer.setAudioStreamType(AudioManager.STREAM_MUSIC);  
        //setVolumeControlStream(AudioManager.STREAM_MUSIC);  
        new Thread(new Runnable() {  
            @Override  
            public void run() {  
                setParams();  
                int ret = speechSynthesizer.speak(text);  
                if (ret != 0) {  
                    Log.i(TAG,"开始合成器失败" );  
                }  
            }  
        }).start();  

    }  
    private void setParams() {  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "0");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, "5");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, "5");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, "5");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_ENCODE, SpeechSynthesizer.AUDIO_ENCODE_AMR);  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_RATE, SpeechSynthesizer.AUDIO_BITRATE_AMR_15K85);  
    }  
    @Override  
    public void onBufferProgressChanged(SpeechSynthesizer arg0, int arg1) {}  
    @Override  
    public void onCancel(SpeechSynthesizer arg0) {}  
    @Override  
    public void onError(SpeechSynthesizer arg0, SpeechError arg1) {}  
    @Override  
    public void onNewDataArrive(SpeechSynthesizer arg0, byte[] arg1, boolean arg2) {}  
    @Override  
    public void onSpeechFinish(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSpeechPause(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSpeechProgressChanged(SpeechSynthesizer arg0, int arg1) {}  
    @Override  
    public void onSpeechResume(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSpeechStart(SpeechSynthesizer arg0) {}  
    @Override  
    public void onStartWorking(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSynthesizeFinish(SpeechSynthesizer arg0) {}  
}

5.前台javascript代码

document.addEventListener( "plusready",  function()  
{  
    var _BARCODE = 'kxdPlugins',  
        B = window.plus.bridge;  
    var kxdPlugins =   
    {  
        // 声明异步返回方法  
        speak : function (Argus, successCallback, errorCallback )   
        {  
            var success = typeof successCallback !== 'function' ? null : function(args)   
            {  
                successCallback(args);  
            },  
            fail = typeof errorCallback !== 'function' ? null : function(code)   
            {  
                errorCallback(code);  
            };  
            callbackID = B.callbackId(success, fail);  
            // 通知Native层plugintest扩展插件运行”speak”方法  
            return B.exec(_BARCODE, "speak", [callbackID, Argus]);  
        }  
    };  
    window.plus.kxdPlugins = kxdPlugins;  
}, true );

使用方式

和普通的API调用方式一样:plus.kxdPlugins.speak("这是语音播放的文字内容");

注意事项

1.在/assets/data/properties.xml中添加插件权限。
2.在/assets/apps/appid/www/manifest.json 中添加对应插件权限。
3.在/AndroidManifest.xml 文件中添加对应的百度语音合成权限。

收起阅读 »

获取图片当前旋转状态

Orientation

今日开发过程中遇见从相册选择或者自拍照片,显示时为横向。故封装如下代码,希望对大家有所帮助。

function getImgOrientation (filePath) {  
            var ExifInterface = plus.android.importClass("android.media.ExifInterface");  
            var exifInterface = new ExifInterface(filePath.replace("file:///", ""));  
            var orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);  
            var degree = 0;  
            switch (orientation) {  
                case ExifInterface.ORIENTATION_ROTATE_90:  
                    degree = 90;  
                    break;  
                case ExifInterface.ORIENTATION_ROTATE_180:  
                    degree = 180;  
                    break;  
                case ExifInterface.ORIENTATION_ROTATE_270:  
                    degree = 270;  
                    break;  
            }  
            return degree;  
        }
继续阅读 »

今日开发过程中遇见从相册选择或者自拍照片,显示时为横向。故封装如下代码,希望对大家有所帮助。

function getImgOrientation (filePath) {  
            var ExifInterface = plus.android.importClass("android.media.ExifInterface");  
            var exifInterface = new ExifInterface(filePath.replace("file:///", ""));  
            var orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);  
            var degree = 0;  
            switch (orientation) {  
                case ExifInterface.ORIENTATION_ROTATE_90:  
                    degree = 90;  
                    break;  
                case ExifInterface.ORIENTATION_ROTATE_180:  
                    degree = 180;  
                    break;  
                case ExifInterface.ORIENTATION_ROTATE_270:  
                    degree = 270;  
                    break;  
            }  
            return degree;  
        }
收起阅读 »

整包(apk/ipa)升级

升级

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

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

  • 整包升级:适用于大版本更新,runtime发生变化时(模块、配置、版本等变化)必须使用此更新方法
  • 应用资源升级:适用于小版本更新 。runtime不变,前端页面整体压缩包更新
  • 应用资源差量升级:适用于小版本更新 。runtime不变,前端页面仅需要更新的部分更新。

本文重点描述5+应用整包升级,逻辑其实是非常简单,主要有三个步骤:

  1. 查询是否有新版本更新
  2. 下载新版本
  3. 安装新版本

而在实际应用的升级过程中则需要处理很多细节问题,下面就按照这个流程来简单说明在5+App中实现应用升级的逻辑。

查询是否有新版本更新

这个操作其实非常简单,就是客户端与升级服务器的一次交互操作,比较升级服务器上发布的最新客户端版本是否高于当前客户端版本号(5+ API中可以通过plus.runtime.version获取当前apk/ipa的版本号,注意打包方生效)?是的话则需要升级,否则无需升级。
从逻辑上来考虑有两种判断模式:

  1. 客户端判断是否有升级
    客户端从服务器获取最新的版本号,本地js判断是否需要升级。
  2. 服务器判断是否有升级
    客户端提交版本到服务器,有服务器判断返回是否需要升级。
    前者的优点是否服务器压力小,静态返回最新客户端版本即可,后者的优点则升级控制会更灵活,可以根据其它条件动态控制部分用户先升级(灰度发布)等。有条件的情况推荐采用第二种方式进行判断。

可以使用Javascript中的标准XHR请求,如果存在跨域问题则使用5+ API的XMLHttpRequest

下载新版本

如果判断到需要更新版本,则需要从服务器下载新版本,通常升级服务器应该返回下载新版本的地址(或者从固定的地址获取)。
有两种下载方法,一种调用Downloader API下载,示例如下:

var url=""; // 下载文件地址  
var dtask = plus.downloader.createDownload( url, {}, function ( d, status ) {  
    if ( status == 200 ) { // 下载成功  
        var path = d.filename;  
        console.log(d.filename);  
    } else {//下载失败  
        alert( "Download failed: " + status );   
    }    
});  
dtask.start(); 

安装新版本

下载原生安装包apk后,可调用[plus.runtime.install]()方法安装,示例如下:

    plus.runtime.install(path);  // 安装下载的apk文件

注意
iOS平台的ipa无法安装,此时需要跳转到appstore,提示用户自动点击升级更新,跳转到appstore的方法为打开应用的appstore地址,示例如下:

var url='itms-apps://itunes.apple.com/cn/app/hello-h5+/id682211190?l=zh&mt=8';// HelloH5应用在appstore的地址  
plus.runtime.openURL(url);

此处url是以"itms-apps://"开头,后面跟appstore上应用地址。

示例
在Hello H5+和Hello mui示例里,有2种不同的检查更新处理方式。
Hello H5+里的update.js比较复杂,在js里执行下载apk并安装的逻辑。
Hello mui里的update.js比较简单,弹出新包下载地址到浏览器,由浏览器执行下载逻辑。
如何选择看自己的要求。

继续阅读 »

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

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

  • 整包升级:适用于大版本更新,runtime发生变化时(模块、配置、版本等变化)必须使用此更新方法
  • 应用资源升级:适用于小版本更新 。runtime不变,前端页面整体压缩包更新
  • 应用资源差量升级:适用于小版本更新 。runtime不变,前端页面仅需要更新的部分更新。

本文重点描述5+应用整包升级,逻辑其实是非常简单,主要有三个步骤:

  1. 查询是否有新版本更新
  2. 下载新版本
  3. 安装新版本

而在实际应用的升级过程中则需要处理很多细节问题,下面就按照这个流程来简单说明在5+App中实现应用升级的逻辑。

查询是否有新版本更新

这个操作其实非常简单,就是客户端与升级服务器的一次交互操作,比较升级服务器上发布的最新客户端版本是否高于当前客户端版本号(5+ API中可以通过plus.runtime.version获取当前apk/ipa的版本号,注意打包方生效)?是的话则需要升级,否则无需升级。
从逻辑上来考虑有两种判断模式:

  1. 客户端判断是否有升级
    客户端从服务器获取最新的版本号,本地js判断是否需要升级。
  2. 服务器判断是否有升级
    客户端提交版本到服务器,有服务器判断返回是否需要升级。
    前者的优点是否服务器压力小,静态返回最新客户端版本即可,后者的优点则升级控制会更灵活,可以根据其它条件动态控制部分用户先升级(灰度发布)等。有条件的情况推荐采用第二种方式进行判断。

可以使用Javascript中的标准XHR请求,如果存在跨域问题则使用5+ API的XMLHttpRequest

下载新版本

如果判断到需要更新版本,则需要从服务器下载新版本,通常升级服务器应该返回下载新版本的地址(或者从固定的地址获取)。
有两种下载方法,一种调用Downloader API下载,示例如下:

var url=""; // 下载文件地址  
var dtask = plus.downloader.createDownload( url, {}, function ( d, status ) {  
    if ( status == 200 ) { // 下载成功  
        var path = d.filename;  
        console.log(d.filename);  
    } else {//下载失败  
        alert( "Download failed: " + status );   
    }    
});  
dtask.start(); 

安装新版本

下载原生安装包apk后,可调用[plus.runtime.install]()方法安装,示例如下:

    plus.runtime.install(path);  // 安装下载的apk文件

注意
iOS平台的ipa无法安装,此时需要跳转到appstore,提示用户自动点击升级更新,跳转到appstore的方法为打开应用的appstore地址,示例如下:

var url='itms-apps://itunes.apple.com/cn/app/hello-h5+/id682211190?l=zh&mt=8';// HelloH5应用在appstore的地址  
plus.runtime.openURL(url);

此处url是以"itms-apps://"开头,后面跟appstore上应用地址。

示例
在Hello H5+和Hello mui示例里,有2种不同的检查更新处理方式。
Hello H5+里的update.js比较复杂,在js里执行下载apk并安装的逻辑。
Hello mui里的update.js比较简单,弹出新包下载地址到浏览器,由浏览器执行下载逻辑。
如何选择看自己的要求。

收起阅读 »

低价外包H5移动端页面

外包

纯切图,css3动画

纯切图,css3动画

如何在本地进行微信公众号的开发调试

在进行微信公众号开发时,以前必须要有外网域名才能收到微信服务器的推送,这给开发和调试带来很大的不便。

现在方便了,QQ浏览器提供了微信公众号调试工具,开发者可以在自己的开发机上进行开发和调试,不再需要外网IP和域名。不仅可以直观看到已接收和已发送的消息内容和事件,方便开发和问题定位,还可以重复发送已接收的微信报文,避免调试时频繁操作手机

原理

调试工具内置了一个server,监听你本地运行的程序,并为你生成一个外网的url,你只需要在公众号的开发信息中配置上这个url,公众号收到消息后,就会通过这个url转发到调试工具上,调试工具再转给你的本地运行程序。

使用方法
(1)在本机启动运行自己程序的server
(2)打开qq浏览器,在应用中心中安装“微信调试工具”,安装完成后,选择“服务器端调试”
(3)填写本地server的ip和端口号,调试工具会返回一个外部URL
(4)到公众号后台开发者配置信息中填写这个URL
(5)用微信向公众号发送消息测试

继续阅读 »

在进行微信公众号开发时,以前必须要有外网域名才能收到微信服务器的推送,这给开发和调试带来很大的不便。

现在方便了,QQ浏览器提供了微信公众号调试工具,开发者可以在自己的开发机上进行开发和调试,不再需要外网IP和域名。不仅可以直观看到已接收和已发送的消息内容和事件,方便开发和问题定位,还可以重复发送已接收的微信报文,避免调试时频繁操作手机

原理

调试工具内置了一个server,监听你本地运行的程序,并为你生成一个外网的url,你只需要在公众号的开发信息中配置上这个url,公众号收到消息后,就会通过这个url转发到调试工具上,调试工具再转给你的本地运行程序。

使用方法
(1)在本机启动运行自己程序的server
(2)打开qq浏览器,在应用中心中安装“微信调试工具”,安装完成后,选择“服务器端调试”
(3)填写本地server的ip和端口号,调试工具会返回一个外部URL
(4)到公众号后台开发者配置信息中填写这个URL
(5)用微信向公众号发送消息测试

收起阅读 »

IOS平台:如何在离线打包时配置 3D Touch图标快捷菜单

App离线打包 iOS

Apple最新发布的iPhone 6s (plus) 设备支持3D touch功能,用户可通过重按屏幕上的图标,打开设置的菜单。H5+ SDK已经支持添加应用的快捷菜单,显示效果如下:

离线工程配置的方法

打开离线打包工程的info.plist文件,在文件中添加UIApplicationShortcutItems节点,并在节点下添加item。

各字段值规范如下表
各值的关系请参考上图
节点名 节点类型 可选性 节点说明 备注
UIApplicationShortcutItems Array 必选 图标快捷菜单节点声明
UIApplicationShortcutItemType String 必选 菜单类型
UIApplicationShortcutItemTitle String 必选 菜单项的标题
UIApplicationShortcutItemSubtitle String 可选 菜单项的副标题
UIApplicationShortcutItemIconFile String 可选 指定菜单项显示安装包内自带的图标,相对安装包的根目录位置 与UIApplicationShortcutItemIconType节点同时配置时优先级高于UIApplicationShortcutItemIconType
UIApplicationShortcutItemIconType String 可选 指定菜单项显示系统自带图标 取值可参考苹果官方文档
UIApplicationShortcutItemUserInfo String 可选 用户自定义的菜单项附加信息 附带信息可在应用内通过plus.runtime.arguments获取

App中处理快捷菜单项

请参考文档iOS平台支持3D Touch快捷菜单项

继续阅读 »

Apple最新发布的iPhone 6s (plus) 设备支持3D touch功能,用户可通过重按屏幕上的图标,打开设置的菜单。H5+ SDK已经支持添加应用的快捷菜单,显示效果如下:

离线工程配置的方法

打开离线打包工程的info.plist文件,在文件中添加UIApplicationShortcutItems节点,并在节点下添加item。

各字段值规范如下表
各值的关系请参考上图
节点名 节点类型 可选性 节点说明 备注
UIApplicationShortcutItems Array 必选 图标快捷菜单节点声明
UIApplicationShortcutItemType String 必选 菜单类型
UIApplicationShortcutItemTitle String 必选 菜单项的标题
UIApplicationShortcutItemSubtitle String 可选 菜单项的副标题
UIApplicationShortcutItemIconFile String 可选 指定菜单项显示安装包内自带的图标,相对安装包的根目录位置 与UIApplicationShortcutItemIconType节点同时配置时优先级高于UIApplicationShortcutItemIconType
UIApplicationShortcutItemIconType String 可选 指定菜单项显示系统自带图标 取值可参考苹果官方文档
UIApplicationShortcutItemUserInfo String 可选 用户自定义的菜单项附加信息 附带信息可在应用内通过plus.runtime.arguments获取

App中处理快捷菜单项

请参考文档iOS平台支持3D Touch快捷菜单项

收起阅读 »

iOS平台支持3D Touch快捷菜单项

3DTouch 5+App开发 iOS

iPhone6s(plus)设备已经支持3D Touch屏幕,HBuilder已支持添加应用的快捷菜单,效果如下:

配置快捷菜单项

打开应用的manifest.json文件,切换到代码视图,在plus -> distribute -> apple 下添加shortcuts节点,并配置各菜单项,数组中每项对应一个快捷菜单项:

    "shortcuts": [  
        {  
            "type": "share",  
            "title": "分 享",  
            "subtitle": "分享到微信、微博、QQ",  
            "icontype": "UIApplicationShortcutIconTypeShare"  
        },  
        {  
            "type": "about",  
            "title": "关 于",  
            "subtitle": "www.dcloud.io",  
            "iconfile": "sa.png",  
            "userinfo": {  
                "key3":"value3"  
            }  
        }  
    ],

uni-app项目将以上数据添加到"app-plus"->"distribute"->"ios"节点下
其中各字段值规范如下:

  • type: (必选)菜单项类型,字符串类型,用于标识菜单项
  • title: (必选)菜单项上显示的标题,字符串类型
  • subtitle: (可选)菜单项上显示的子标题,字符串类型
  • icontype: (可选)菜单项上显示的图标类型,字符串类型,取值参考iOS官方文档UIApplicationShortcutIconType
  • iconfile: (可选)菜单项上显示的图标文件,字符串类型,相对5+应用根目录路径,图标要求35x35分辨率,单色,参考Apple官方PS模板图
  • userinfo: (可选)菜单项上的自定义数据,JSON格式

App中处理快捷菜单项

判断应用是否通过快捷菜单项启动
plus.runtime.launcher的值为"shortcut"表示应用从快捷菜单项启动,示例如下:

if(plus.runtime.launcher=='shortcut'){  
     // ...  
}

判断快捷菜单项启动的参数
plus.runtime.arguments中保存所有快捷菜单项配置的值(JSON格式字符串),示例如下:

var cmd = JSON.parse(plus.runtime.arguments);  
console.log("Shortcut-plus.runtime.arguments: "+plus.runtime.arguments)  
var type=cmd&&cmd.type;  
switch(type){  
    case 'share':  
        // 用户点击了‘share'菜单项  
    break;  
    case 'about':  
        // 用户点击了’about'菜单项  
    break;  
    default:  
    break;  
}

plus.runtime.arguments的JSON格式字符串示例如下:
“{"type":"about","title":"关 于","subtitle": "www.dcloud.io","userinfo":{"key3":"value3"}}”
其中包括以下键值:

  • type: 菜单项类型,字符串类型
  • title: 菜单项上显示的标题,字符串类型
  • subtitle: 菜单项上显示的子标题,字符串类型
  • userinfo: 菜单项上的自定义数据

注:真机运行不生效,需提交App云端打包后才生效

实际用法参考HelloH5应用的“js/shortcut.js”
iOS平台5+SDK原生环境配置方法

uni-app网友经验分享:https://ask.dcloud.net.cn/article/36103

继续阅读 »

iPhone6s(plus)设备已经支持3D Touch屏幕,HBuilder已支持添加应用的快捷菜单,效果如下:

配置快捷菜单项

打开应用的manifest.json文件,切换到代码视图,在plus -> distribute -> apple 下添加shortcuts节点,并配置各菜单项,数组中每项对应一个快捷菜单项:

    "shortcuts": [  
        {  
            "type": "share",  
            "title": "分 享",  
            "subtitle": "分享到微信、微博、QQ",  
            "icontype": "UIApplicationShortcutIconTypeShare"  
        },  
        {  
            "type": "about",  
            "title": "关 于",  
            "subtitle": "www.dcloud.io",  
            "iconfile": "sa.png",  
            "userinfo": {  
                "key3":"value3"  
            }  
        }  
    ],

uni-app项目将以上数据添加到"app-plus"->"distribute"->"ios"节点下
其中各字段值规范如下:

  • type: (必选)菜单项类型,字符串类型,用于标识菜单项
  • title: (必选)菜单项上显示的标题,字符串类型
  • subtitle: (可选)菜单项上显示的子标题,字符串类型
  • icontype: (可选)菜单项上显示的图标类型,字符串类型,取值参考iOS官方文档UIApplicationShortcutIconType
  • iconfile: (可选)菜单项上显示的图标文件,字符串类型,相对5+应用根目录路径,图标要求35x35分辨率,单色,参考Apple官方PS模板图
  • userinfo: (可选)菜单项上的自定义数据,JSON格式

App中处理快捷菜单项

判断应用是否通过快捷菜单项启动
plus.runtime.launcher的值为"shortcut"表示应用从快捷菜单项启动,示例如下:

if(plus.runtime.launcher=='shortcut'){  
     // ...  
}

判断快捷菜单项启动的参数
plus.runtime.arguments中保存所有快捷菜单项配置的值(JSON格式字符串),示例如下:

var cmd = JSON.parse(plus.runtime.arguments);  
console.log("Shortcut-plus.runtime.arguments: "+plus.runtime.arguments)  
var type=cmd&&cmd.type;  
switch(type){  
    case 'share':  
        // 用户点击了‘share'菜单项  
    break;  
    case 'about':  
        // 用户点击了’about'菜单项  
    break;  
    default:  
    break;  
}

plus.runtime.arguments的JSON格式字符串示例如下:
“{"type":"about","title":"关 于","subtitle": "www.dcloud.io","userinfo":{"key3":"value3"}}”
其中包括以下键值:

  • type: 菜单项类型,字符串类型
  • title: 菜单项上显示的标题,字符串类型
  • subtitle: 菜单项上显示的子标题,字符串类型
  • userinfo: 菜单项上的自定义数据

注:真机运行不生效,需提交App云端打包后才生效

实际用法参考HelloH5应用的“js/shortcut.js”
iOS平台5+SDK原生环境配置方法

uni-app网友经验分享:https://ask.dcloud.net.cn/article/36103

收起阅读 »

如何动态兼容沉浸式状态栏模式

UserAgent navigator 沉浸式状态栏 5+App开发

HBuilder6.6.1版本已经完全支持沉浸式状态栏,可以下载最新版本HelloH5应用体验。
各平台配置参考:

  1. Android平台设置沉浸式状态栏显示效果
  2. iOS平台设置沉浸式状态栏显示效果

由于各系统版本的限制,沉浸式状态栏对系统有要求(Android4.4及以上、iOS7.0及以上),如果要兼容各系统版本,需要动态判断当前环境是否支持沉浸式状态栏以及系统状态栏的高度:

使用5+API

  • 判断当前环境是否支持沉浸式状态栏
    plus.navigator.isImmersedStatusbar()
    如果当前支持沉浸式状态栏则返回true,否则返回false。
  • 获取当前系统状态栏高度
    plus.navigator.getStatusbarHeight()
    获取系统状态栏高度,Number类型。
    其单位是逻辑像素值,即css中可直接使用的像素值,可能存在小数点。

实际用法参考HelloH5应用的“plus/doc.html”:

    // 创建加载内容窗口  
    var topoffset='45px';  
    if(plus.navigator.isImmersedStatusbar()){// 兼容immersed状态栏模式  
        // 获取状态栏高度并根据业务需求处理,这里重新计算了子窗口的偏移位置  
        topoffset=(Math.round(plus.navigator.getStatusbarHeight())+45)+'px';  
    }  
    // 使用偏移位置创建子窗口  
    wc=plus.webview.create(null,'doccontent',{top:topoffset,bottom:'0px',bounce:'vertical',bounceBackground:'#FFFFFF'});

通过userAgent判断
5+API需要在plusready事件后才能调用,通常此事件在DOM加载渲染后才会触发,无法再渲染前根据不同的状态来设置css。
为了解决此问题,在支持5+API运行环境的userAgent中特定字段Html5Plus/1.0后添加Immersed标识,如下:
"Html5Plus/1.0 (Immersed/30)"
其中Immersed/后的30表示状态栏的高度,单位为逻辑像素值。

可以使用正则表达式进行获取:

var immersed = 0;  
var ms=(/Html5Plus\/.+\s\(.*(Immersed\/(\d+\.?\d*).*)\)/gi).exec(navigator.userAgent);  
if(ms&&ms.length>=3){ // 当前环境为沉浸式状态栏模式  
    immersed=parseFloat(ms[2]);// 获取状态栏的高度  
}

immersed值如果大于0则表示当前环境支持沉浸式状态栏。
获取状态栏高度后,可以使用js动态修改DOM元素的css属性来设置样式,如设置界面头区域的顶部内边距为状态栏的高度(避免系统状态栏与界面头重叠),示例如下:

var t=document.getElementById('header');  
t&&t.style.paddingTop=immersed+'px';

具体项目中可根据界面设计,灵活使用immersed值来动态适配各种效果。
完整用法可参考HelloH5应用中的“js/immersed.js”

继续阅读 »

HBuilder6.6.1版本已经完全支持沉浸式状态栏,可以下载最新版本HelloH5应用体验。
各平台配置参考:

  1. Android平台设置沉浸式状态栏显示效果
  2. iOS平台设置沉浸式状态栏显示效果

由于各系统版本的限制,沉浸式状态栏对系统有要求(Android4.4及以上、iOS7.0及以上),如果要兼容各系统版本,需要动态判断当前环境是否支持沉浸式状态栏以及系统状态栏的高度:

使用5+API

  • 判断当前环境是否支持沉浸式状态栏
    plus.navigator.isImmersedStatusbar()
    如果当前支持沉浸式状态栏则返回true,否则返回false。
  • 获取当前系统状态栏高度
    plus.navigator.getStatusbarHeight()
    获取系统状态栏高度,Number类型。
    其单位是逻辑像素值,即css中可直接使用的像素值,可能存在小数点。

实际用法参考HelloH5应用的“plus/doc.html”:

    // 创建加载内容窗口  
    var topoffset='45px';  
    if(plus.navigator.isImmersedStatusbar()){// 兼容immersed状态栏模式  
        // 获取状态栏高度并根据业务需求处理,这里重新计算了子窗口的偏移位置  
        topoffset=(Math.round(plus.navigator.getStatusbarHeight())+45)+'px';  
    }  
    // 使用偏移位置创建子窗口  
    wc=plus.webview.create(null,'doccontent',{top:topoffset,bottom:'0px',bounce:'vertical',bounceBackground:'#FFFFFF'});

通过userAgent判断
5+API需要在plusready事件后才能调用,通常此事件在DOM加载渲染后才会触发,无法再渲染前根据不同的状态来设置css。
为了解决此问题,在支持5+API运行环境的userAgent中特定字段Html5Plus/1.0后添加Immersed标识,如下:
"Html5Plus/1.0 (Immersed/30)"
其中Immersed/后的30表示状态栏的高度,单位为逻辑像素值。

可以使用正则表达式进行获取:

var immersed = 0;  
var ms=(/Html5Plus\/.+\s\(.*(Immersed\/(\d+\.?\d*).*)\)/gi).exec(navigator.userAgent);  
if(ms&&ms.length>=3){ // 当前环境为沉浸式状态栏模式  
    immersed=parseFloat(ms[2]);// 获取状态栏的高度  
}

immersed值如果大于0则表示当前环境支持沉浸式状态栏。
获取状态栏高度后,可以使用js动态修改DOM元素的css属性来设置样式,如设置界面头区域的顶部内边距为状态栏的高度(避免系统状态栏与界面头重叠),示例如下:

var t=document.getElementById('header');  
t&&t.style.paddingTop=immersed+'px';

具体项目中可根据界面设计,灵活使用immersed值来动态适配各种效果。
完整用法可参考HelloH5应用中的“js/immersed.js”

收起阅读 »

Android平台设置沉浸式状态栏显示效果

沉浸式状态栏 5+App开发 Android navigator

系列文章导航:
状态栏大全-状态栏透明(沉浸式)、变色及全屏的区别
Android平台设置沉浸式状态栏显示效果
iOS平台设置沉浸式状态栏显示效果

正文:

应用可视区域到系统状态栏下透明显示效果,如下图所示:

此模式下应用占用全屏区域,而系统状态栏会拦截用户操作事件,此时需要预留出系统状态栏高度。
获取系统状态栏高度及沉浸式状态判断参考:如何动态判断沉浸式状态栏模式

HBuilder创建的应用默认不使用沉浸式状态栏样式,需要进行如下配置开启:
打开应用的manifest.json文件,切换到代码视图,在plus -> statusbar 下添加immersed节点并设置值为true。

"plus": {  
    "statusbar": {  
        "immersed": true  
    }  
}

如下图所示:

保存后提交App云端打包

注意:

  1. 真机运行不生效,需提交App云端打包后才生效;
  2. 此功能仅在Android4.4及以上系统有效。

状态栏大全-状态栏透明(沉浸式)、变色及全屏的区别
Android平台5+SDK原生环境配置方法
iOS平台设置沉浸式状态栏显示效果

继续阅读 »

系列文章导航:
状态栏大全-状态栏透明(沉浸式)、变色及全屏的区别
Android平台设置沉浸式状态栏显示效果
iOS平台设置沉浸式状态栏显示效果

正文:

应用可视区域到系统状态栏下透明显示效果,如下图所示:

此模式下应用占用全屏区域,而系统状态栏会拦截用户操作事件,此时需要预留出系统状态栏高度。
获取系统状态栏高度及沉浸式状态判断参考:如何动态判断沉浸式状态栏模式

HBuilder创建的应用默认不使用沉浸式状态栏样式,需要进行如下配置开启:
打开应用的manifest.json文件,切换到代码视图,在plus -> statusbar 下添加immersed节点并设置值为true。

"plus": {  
    "statusbar": {  
        "immersed": true  
    }  
}

如下图所示:

保存后提交App云端打包

注意:

  1. 真机运行不生效,需提交App云端打包后才生效;
  2. 此功能仅在Android4.4及以上系统有效。

状态栏大全-状态栏透明(沉浸式)、变色及全屏的区别
Android平台5+SDK原生环境配置方法
iOS平台设置沉浸式状态栏显示效果

收起阅读 »