
Facebook登录开通指南
开通条件
1.海外网络环境
2.Facebook账号(Facebook登录页面)
创建应用
1.打开Facebook开发者中心
2.点击右上角"我的应用"

3.进入应用管理界面,点击"创建应用"
4.根据需要选择应用产品的类型(应用类型详见"详细了解应用类型"),然后点击继续
5.填写应用信息
6.创建完成后即可获取应用的应用编号(即appID)
7.为应用添加登录功能
设置登录-iOS
1.我的应用--设置--基本,选择添加平台,选择iOS
2.填写信息保存即可
设置登录-Android
我的应用--设置--基本,选择添加平台
选择android平台,应用商店选择Google Play
填写必要的包名和散列信息,类名是固定的。如图
散列的获取方法,参考文档:
https://developers.facebook.com/docs/facebook-login/android 第六小节
如果获取到的散列位数不对,需要找台linux/mac 计算机。
使用下面的命令获取
keytool -exportcert -alias hbuilder -keystore ./HBuilder.keystore | openssl dgst -sha1 -binary | openssl base64
应用权限
使用Facebook登录需开启"public_profile"以及"email"的访问权限
点击"应用审核"-"权限和功能",开启"public_profile"以及"email"的高级访问权限
开通条件
1.海外网络环境
2.Facebook账号(Facebook登录页面)
创建应用
1.打开Facebook开发者中心
2.点击右上角"我的应用"
3.进入应用管理界面,点击"创建应用"
4.根据需要选择应用产品的类型(应用类型详见"详细了解应用类型"),然后点击继续
5.填写应用信息
6.创建完成后即可获取应用的应用编号(即appID)
7.为应用添加登录功能
设置登录-iOS
1.我的应用--设置--基本,选择添加平台,选择iOS
2.填写信息保存即可
设置登录-Android
我的应用--设置--基本,选择添加平台
选择android平台,应用商店选择Google Play
填写必要的包名和散列信息,类名是固定的。如图
散列的获取方法,参考文档:
https://developers.facebook.com/docs/facebook-login/android 第六小节
如果获取到的散列位数不对,需要找台linux/mac 计算机。
使用下面的命令获取
keytool -exportcert -alias hbuilder -keystore ./HBuilder.keystore | openssl dgst -sha1 -binary | openssl base64
应用权限
使用Facebook登录需开启"public_profile"以及"email"的访问权限
点击"应用审核"-"权限和功能",开启"public_profile"以及"email"的高级访问权限

Google登录开通指南
暂未发布
前置条件
1 海外网络环境
2 Google账号
Android开通步骤
2.1 打开Google 登录引导页
网址: https://developers.google.com/identity/sign-in/android/sign-in?hl=zh-cn
2.2 选择项目配置
点击后出现项目与应用选择界面,
如果你有已创建过的Firebase项目,可以直接选择。
如果没有,可以选择新建一个Google Api 项目。
选择项目后,在该项目下新建一个应用
选择应用平台 android
需要填写应用的包名和sha1指纹
指纹的获取方法在界面上有提示。按照提示操作即可。
点击创建,即可完成开通步骤。
iOS开通步骤
3.1 打开Google登录iOS引导页
3.2 点击创建OAuth客户端ID,填写项目名称
3.3 选择iOS平台、填写BundleID后,点击CREATE,即可获取Client ID
暂未发布
前置条件
1 海外网络环境
2 Google账号
Android开通步骤
2.1 打开Google 登录引导页
网址: https://developers.google.com/identity/sign-in/android/sign-in?hl=zh-cn
2.2 选择项目配置
点击后出现项目与应用选择界面,
如果你有已创建过的Firebase项目,可以直接选择。
如果没有,可以选择新建一个Google Api 项目。
选择项目后,在该项目下新建一个应用
选择应用平台 android
需要填写应用的包名和sha1指纹
指纹的获取方法在界面上有提示。按照提示操作即可。
点击创建,即可完成开通步骤。
iOS开通步骤
3.1 打开Google登录iOS引导页
3.2 点击创建OAuth客户端ID,填写项目名称
3.3 选择iOS平台、填写BundleID后,点击CREATE,即可获取Client ID

关于隐私政策弹窗经验分享
首先来说按文档配置相关隐私协议 https://ask.dcloud.net.cn/article/39073
我是用的HX3.2.3进行打包的,点击manifest.json=》APP启动页面配置+》使用原生隐私政策提示框,勾选后会在项目中自动添加androidPrivacy.json文件
注意!androidPrivacy.json不要添加注释,会影响隐私政策提示框的显示!!
androidPrivacy.json文件如下:
{
"version": "1", (这里个人建议和应用同步更新版本号,因为每次你更新应用后都需要更新版本号)
"prompt": "template",
"title": "服务协议和隐私政策",
"message": " 请你务必审慎阅读、充分理解“服务协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。<br/> 你可阅读<a href=\"\">《服务协议》</a>和<a href=\"\">《隐私政策》</a>了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。",
"buttonAccept": "同意并接受",
"buttonRefuse": "暂不同意",
"second": {
"title": "确认提示",
"message": " 进入应用前,你需先同意<a href=\"\">《服务协议》</a>和<a href=\"\">《隐私政策》</a>,否则将退出应用。",
"buttonAccept": "同意并继续",
"buttonRefuse": "退出应用"
},
"styles": {
"backgroundColor": "#00FF00",
"borderRadius":"5px",
"title": {
"color": "#ff00ff"
},
"buttonAccept": {
"color": "#ffff00"
},
"buttonRefuse": {
"color": "#00ffff"
}
}
}
message中的巨坑来了!!!划重点,一定一定一定(重要的事情说三遍)要注意这里,不然坑到你哭。
一定一定一定要将“/”做转义,例如:<a href=\"https:\/\/www.baidu.com\/\">(简直坑死人不偿命)普通的“/”就需要转义成“\/”,如果最后是以.html结束的后面不需要转义了,等下看截图。
系统给的背景颜色和按钮颜色,自己想改就改。
最后如果自定义基座调试运行没得弹窗,不要紧!!!安心的打个包,试一下就好了。
欢迎大家给与建议和更好的体验,我也是第一次用HX写APP。如果对你有帮助,记得一键三连。
首先来说按文档配置相关隐私协议 https://ask.dcloud.net.cn/article/39073
我是用的HX3.2.3进行打包的,点击manifest.json=》APP启动页面配置+》使用原生隐私政策提示框,勾选后会在项目中自动添加androidPrivacy.json文件
注意!androidPrivacy.json不要添加注释,会影响隐私政策提示框的显示!!
androidPrivacy.json文件如下:
{
"version": "1", (这里个人建议和应用同步更新版本号,因为每次你更新应用后都需要更新版本号)
"prompt": "template",
"title": "服务协议和隐私政策",
"message": " 请你务必审慎阅读、充分理解“服务协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。<br/> 你可阅读<a href=\"\">《服务协议》</a>和<a href=\"\">《隐私政策》</a>了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。",
"buttonAccept": "同意并接受",
"buttonRefuse": "暂不同意",
"second": {
"title": "确认提示",
"message": " 进入应用前,你需先同意<a href=\"\">《服务协议》</a>和<a href=\"\">《隐私政策》</a>,否则将退出应用。",
"buttonAccept": "同意并继续",
"buttonRefuse": "退出应用"
},
"styles": {
"backgroundColor": "#00FF00",
"borderRadius":"5px",
"title": {
"color": "#ff00ff"
},
"buttonAccept": {
"color": "#ffff00"
},
"buttonRefuse": {
"color": "#00ffff"
}
}
}
message中的巨坑来了!!!划重点,一定一定一定(重要的事情说三遍)要注意这里,不然坑到你哭。
一定一定一定要将“/”做转义,例如:<a href=\"https:\/\/www.baidu.com\/\">(简直坑死人不偿命)普通的“/”就需要转义成“\/”,如果最后是以.html结束的后面不需要转义了,等下看截图。
系统给的背景颜色和按钮颜色,自己想改就改。
最后如果自定义基座调试运行没得弹窗,不要紧!!!安心的打个包,试一下就好了。
欢迎大家给与建议和更好的体验,我也是第一次用HX写APP。如果对你有帮助,记得一键三连。

uniapp获取某段时间的通话记录
plusReady(){
// 在这里调用plus api
var CallLog = plus.android.importClass("android.provider.CallLog");
var main = plus.android.runtimeMainActivity();
var obj = main.getContentResolver();
plus.android.importClass(obj);
var selection = {
mobile: '',
startTime: new Date('2021-9-1').getTime(),
endTime: new Date('2021-9-16').getTime(),
type: ''
}
//查询
var sql = '';
var keyword = '';
for (let item of Object.keys(selection)) {
if (selection[item] != '') {
switch (item) {
case 'mobile':
sql = sql == '' ? sql : sql + ' and ';
sql = sql + CallLog.Calls.NUMBER + " like '%" + selection[item] + "%'";
keyword = selection[item];
break;
case 'startTime':
sql = sql == '' ? sql : sql + ' and ';
sql = sql + CallLog.Calls.DATE + '>=' + selection[item];
break;
case 'endTime':
sql = sql == '' ? sql : sql + ' and ';
sql = sql + CallLog.Calls.DATE + '<=' + selection[item];
break;
case 'type':
sql = sql == '' ? sql : sql + ' and ';
sql = sql + CallLog.Calls.TYPE + '=' + selection[item];
break;
}
}
}
sql = sql != '' ? sql : null;
var cursor = obj.query(
CallLog.Calls.CONTENT_URI,
null,
sql, //传入条件
null,
null
);
plus.android.importClass(cursor);
var content = []; // 用来存储数据
var count = 0; // 记录多少条
if (cursor.moveToFirst()) {
while (cursor.moveToNext()) {
count++;
//号码
var number = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER));
//呼叫类型
var type;
switch (
parseInt(cursor.getString(cursor.getColumnIndex(CallLog.Calls.TYPE))))
// 判断通话类型
{
case CallLog.Calls.INCOMING_TYPE:
type = "呼入";
break;
case CallLog.Calls.OUTGOING_TYPE:
type = "呼出";
break;
case CallLog.Calls.MISSED_TYPE:
type = "未接";
break;
default:
type = "挂断"; //应该是挂断.根据我手机类型判断出的
break;
}
// 获取时间
var date = new Date(parseInt(
cursor.getString(cursor.getColumnIndexOrThrow(CallLog.Calls.DATE))));
// 联系人
var Name_Col = cursor.getColumnIndexOrThrow(CallLog.Calls.CACHED_NAME);
var name = cursor.getString(Name_Col);
// 号码归属地 返回:北京 联通
var numberLocation = cursor.getString(
cursor.getColumnIndex(CallLog.Calls.GEOCODED_LOCATION)
);
//通话时间,单位:s
var Duration_Col = cursor.getColumnIndexOrThrow(CallLog.Calls.DURATION);
var duration = cursor.getString(Duration_Col);
// 存入数组
content.push({
name: name, // 联系人的姓名
mobile: number, // 联系人电话
numberLocation: numberLocation, // 号码的归属地
// callTime: new Date().getTime(date), // 呼入或呼出时间
callTime: this.formatDate(date), // 呼入或呼出时间
talkTime: duration, // 通话时长
type: type
});
// 查询50条 就跳出
// if (count > 50) {
// break;
// }
}
}
console.log(JSON.stringify(content));
this.tapeList = content
},
plusReady(){
// 在这里调用plus api
var CallLog = plus.android.importClass("android.provider.CallLog");
var main = plus.android.runtimeMainActivity();
var obj = main.getContentResolver();
plus.android.importClass(obj);
var selection = {
mobile: '',
startTime: new Date('2021-9-1').getTime(),
endTime: new Date('2021-9-16').getTime(),
type: ''
}
//查询
var sql = '';
var keyword = '';
for (let item of Object.keys(selection)) {
if (selection[item] != '') {
switch (item) {
case 'mobile':
sql = sql == '' ? sql : sql + ' and ';
sql = sql + CallLog.Calls.NUMBER + " like '%" + selection[item] + "%'";
keyword = selection[item];
break;
case 'startTime':
sql = sql == '' ? sql : sql + ' and ';
sql = sql + CallLog.Calls.DATE + '>=' + selection[item];
break;
case 'endTime':
sql = sql == '' ? sql : sql + ' and ';
sql = sql + CallLog.Calls.DATE + '<=' + selection[item];
break;
case 'type':
sql = sql == '' ? sql : sql + ' and ';
sql = sql + CallLog.Calls.TYPE + '=' + selection[item];
break;
}
}
}
sql = sql != '' ? sql : null;
var cursor = obj.query(
CallLog.Calls.CONTENT_URI,
null,
sql, //传入条件
null,
null
);
plus.android.importClass(cursor);
var content = []; // 用来存储数据
var count = 0; // 记录多少条
if (cursor.moveToFirst()) {
while (cursor.moveToNext()) {
count++;
//号码
var number = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER));
//呼叫类型
var type;
switch (
parseInt(cursor.getString(cursor.getColumnIndex(CallLog.Calls.TYPE))))
// 判断通话类型
{
case CallLog.Calls.INCOMING_TYPE:
type = "呼入";
break;
case CallLog.Calls.OUTGOING_TYPE:
type = "呼出";
break;
case CallLog.Calls.MISSED_TYPE:
type = "未接";
break;
default:
type = "挂断"; //应该是挂断.根据我手机类型判断出的
break;
}
// 获取时间
var date = new Date(parseInt(
cursor.getString(cursor.getColumnIndexOrThrow(CallLog.Calls.DATE))));
// 联系人
var Name_Col = cursor.getColumnIndexOrThrow(CallLog.Calls.CACHED_NAME);
var name = cursor.getString(Name_Col);
// 号码归属地 返回:北京 联通
var numberLocation = cursor.getString(
cursor.getColumnIndex(CallLog.Calls.GEOCODED_LOCATION)
);
//通话时间,单位:s
var Duration_Col = cursor.getColumnIndexOrThrow(CallLog.Calls.DURATION);
var duration = cursor.getString(Duration_Col);
// 存入数组
content.push({
name: name, // 联系人的姓名
mobile: number, // 联系人电话
numberLocation: numberLocation, // 号码的归属地
// callTime: new Date().getTime(date), // 呼入或呼出时间
callTime: this.formatDate(date), // 呼入或呼出时间
talkTime: duration, // 通话时长
type: type
});
// 查询50条 就跳出
// if (count > 50) {
// break;
// }
}
}
console.log(JSON.stringify(content));
this.tapeList = content
},
收起阅读 »

Unipush后台推送使用指南
亲爱的开发者:
Unipush推送服务于2021年9月14日晚重磅升级,优化了整体页面布局,全方位提升消息推送用户体验。
【消息推送】简介
一、 功能介绍
-
推送的消息主要分为三种:通知消息、透传消息以及分组对比。
-
通知消息即是通知发送后会直接在手机通知栏(状态栏)展示一条消息,Unipush帮助将消息送达到客户端并处理消息的展示方式为通知栏消息。
-
透传消息,即是自定义消息,Unipush只负责将消息送达到客户端,而客户端接收到消息后需要自己处理消息的展示方式或后续动作。
-
分组对比,支持对比通知文案及对比用户群体,小规模测试最佳文案及最佳用户群。
二、 使用场景
-
推送活动通知,如促销、活动、红包、优惠券等
-
推送资讯热点,如资讯、新闻、热点、赛事等
-
推送关注动态,如关注、订阅、收藏、加购物车等
-
推送系统通知,如账户状态、安全、支付、物流等
三、 操作步骤
【消息推送】通知消息
功能介绍:
通知消息即是通知发送后会直接在手机通知栏(状态栏)展示一条消息,Unipush帮助将消息送达到客户端并处理消息的展示方式为通知栏消息,并且支持启动应用、打开第三方链接、打开应用内页面、附加消息四种后续动作形式。
操作步骤:
Step 1:点击【创建推送】-【通知消息】,根据小图标的解释说明填写好目标平台、通知标题、通知内容、目标选择、消息有效时长、短信补量、广告投放等信息。
Step 2:设置通知消息在各个平台的通知展示形式。在Android平台,可通过【通知渠道模板】设置响铃、震动、浮动、唤醒屏幕等通知提醒方式;通过【后续动作】设置后续动作为启动应用、打开第三方链接、打开应用内特定页面及附加消息,附加消息的形式为透传消息;通过【展开式通知】设置通知消息下拉展开的形式为文本、大图;通过【通知栏图标】设置通知消息到达通知栏的图标为默认图标、APP本地图标、上传图片、指定图片路径;通过【角标数字】设置通知消息到达客户端后,角标数字的增量。在iOS平台,也可进行相应设置。
Step 3:查看推送预览,检查确认各项设置是否正确,也可以选择发送测试推送到手机上进行测试预览。确认无误后,点击【确定】,正式发送推送。
【消息推送】透传消息
功能介绍:
透传消息,即是自定义消息,Unipush只负责将消息送达到客户端,而客户端接收到消息后需要自己处理消息的展示方式或后续动作。
操作步骤:
Step 1:点击【创建推送】-【透传消息】,根据小图标的解释说明填写好目标平台、通知标题、通知内容、目标选择、消息有效时长等信息以及在各个平台的通知展示方式及后续动作。然后点击【预览】
Step 2:查看推送预览,检查确认各项设置是否正确,也可以选择发送测试推送到手机上进行测试预览。确认无误后,点击【确定】,正式发送推送。
【消息推送】分组对比
功能介绍:
分组对比功能支持在同一个推送计划中,添加2-5条文案/人群进行对比测试,通过对各测试组的到达、展示、点击数据的监测分析,筛选出最优文案/人群,并支持实时及定时地按最优测试组补发剩余用户,提高通知消息点击率,助力运营提效。
操作步骤:
Step 1:点击【创建推送】-【分组对比】,填写测试名称、选择测试模式、目标平台、测试人群、补发策略、推送设置等 ,点击【测试预览】
Step 2:查看推送预览,检查确认各项设置是否正确。确认无误后,点击【确定】,正式发送推送。
Step 3:点击【数据统计】-【推送记录】,管理测试任务,可选择停止测试、取消补发、手动补发等
【数据统计】推送数据
功能介绍:
【推送数据】记录所有推送任务的记录(包括推送通知记录、透传记录、分组对比记录、API数据记录),显示每个推送任务的详情、目标用户、后续动作、状态等相关信息,也能够查看某次推送的详细数据及转化情况。
【折损原因分析】针对单个推送任务,提供消息从下发-到达-展示-点击各阶段,消息未成功触达的折损原因分析,比如:卸载、关闭通知、推送超限、参数超限、参数无效等。快速诊断定位折损原因,并给出针对性的建议解决方案。同时支持开发者导出未收到消息的用户折损列表,便于针对这部分用户做后续触达转化,提高运营效率。
【大盘分析】针对单个推送任务,提供每个任务的消息到达率和点击率,与应用自身的平均值及APP所属分类Top20个推应用的均值做比较,高效评估该推送任务的转化水平,调整推送运营策略。
名词解释:
-
百日内联网用户数(可推送用户数):3个月内活跃的用户数,即连网过Unipush平台的用户数。
-
实际下发数:本次推送任务中,由Unipush服务器实际发出的消息总数。该数据实时更新。
-
到达数:通知到达用户手机后,UnipushSDK上报给Unipush服务器的到达回执总数。该数据实时更新。
-
展示数:通知到达用户手机后由UnipushSDK正确解析并展示在用户手机通知栏的展示回执总数。该数据实时更新。对于透传消息由于是APP客户端自行处理展示,所以Unipush无法统计其展示数,可以用自定义事件来统计,先定义好自定义事件的名称及编号,在需要埋点的地方调用对应方法来提交统计(详情咨询Unipush技术支持)。
-
点击数:通知展示在用户手机通知栏后,点击该通知的用户总数。该数据实时更新。对于透传消息由于是APP客户端自行处理展示,所以Unipush无法统计其点击数,可以用自定义事件来统计,先定义好自定义事件的名称及编号,在需要埋点的地方调用对应方法来提交统计(详情咨询Unipush技术支持)。
【数据统计】日推送数
功能介绍:
您所有推送任务(包括推送通知、透传消息、AB测试以及API 推送)的所有数据总和(过滤无效用户后)。
名词解释:
-
推送趋势:过去24小时内,所有推送任务的总体下发情况。每半小时刷新一次。
-
到达趋势:过去24小时内,所有推送任务的总体到达情况。每半小时刷新一次。
-
点击趋势:过去24小时内,所有推送任务的总体点击情况。每半小时刷新一次。
【数据统计】用户数据
功能介绍:
用于展示当前所选择应用的用户数据,包括新增用户数(峰值)、次日留存率、在线用户数(峰值)、日联网用户数、关闭通知率、卸载用户数的数据。通过用户数据可以更加了解您应用的在线情况。
名词解释:
- 新增用户数:当日新增与Unipush服务器保持连接的用户数。
- 次日留存率:新用户在第二天的使用情况比例。
- 在线用户数(峰值):当前与Unipush服务器保持连接的用户数的峰值,每10分钟统计一次。
- 活跃用户数:当日内Unipush推送SDK与Unipush服务器建立过长链接的用户总数量
- 日联网用户数:当日所有连接过Unipush服务器的用户总数。
- 关闭通知率:活跃用户中,通知栏消息提示关闭的用户数占比。
- 卸载用户数:每周卸载该应用的用户数(此处统计的为iOS用户的卸载数)。
【应用概览】
提供用户数据和应用维度的推送数据的总览
【配置管理】故障排查
功能介绍:
您可以查询CID是否在线,CID绑定的devicetoken等信息,还可以进行推送测试,推送路径查询
状态查询:检测当前应用下的CID是否在线、包名、devicetoken等信息
推送测试:在集成过程中进行单推测试,可推送通知和透传消息
消息链路查询:查询某任务下某cid的具体实时推送路径情况
【配置管理】用户分组
功能介绍:
用户分组指利用Unipush提供的大数据能力,根据用户性别、偏好、所在区域等属性将用户进行分组,形成分组后,在创建推送消息时,可以将推送消息发给该分组,免去每次筛选的麻烦。
操作步骤:
Step 1:点击【配置管理】-【用户分组】-【添加用户分组】
Step 2:填写用户分组名称,选择用户属性设置,点击【确定】
【配置管理】别名管理
功能介绍:
Unipush使用ClientID来标识每个独立的用户,但ClientID不等于开发者应用上的用户名。别名是开发者根据自身需求为每个用户设定的标识,建议对不同用户设定不同别名,保证可通过别名来唯一确认某特定用户。适用于和APP内部数据打通,例如可将用户的邮箱、昵称、手机号等设为别名,即可通过邮箱、昵称、手机号指定目标用户。Unipush支持单个添加或批量导入别名。
别名规则:
-
有效的别名组成:字母(区分大小写)、数字、下划线、汉字。
-
任务备注名长度限制为 40 字节。( UTF-8 )
-
一个别名最多允许绑定10个ClientID。
-
批量上传文件格式如下,每行一条,前面为别名,后面为ClientID,使用英文逗号分隔。(示例:别名1,14c3a47a37461ssass7384e88de32d8)
操作步骤:
点击【配置管理】-【别名管理】-【添加别名】或者【批量导入】-【确定】
【配置管理】应用标签
功能介绍:
支持APP应用通过UnipushSDK接口自行设置应用标签,每个用户(通过CID来标识 )最多支持100个标签,例如“喜爱足球”,“喜爱动漫”。推送时指定某一标签推送,即可推送给所有打上此标签的用户。
【配置管理】渠道模板
功能介绍:
渠道模板用于设置消息下发的提醒方式:响铃、震动、浮动、唤醒屏幕等
操作步骤:
点击【配置管理】-【渠道模板】-【添加渠道模板】,填写模板名称、渠道ID、渠道名称、通知优先级、提醒方式,点击【确定】
【配置管理】短信配置
功能介绍:
Unipush有提供短信补量服务(在创建推送设置里面可以设置选择),针对 App 侧一段时间内无法收到推送的消息(可能由于断网、后台禁止运行、消息延迟等原因)的用户,进行短信通道信息补充。而针对提供的短信通道补充服务,则支持对短信签名以及短信模板进行设置。
操作步骤:
Step 1:配置短信签名
点击【配置管理】-【短信配置】-【签名配置】-【新增签名】,然后填写签名内容,点击【确定】
Step 2:配置短信模板
点击【配置管理】-【短信配置】-【模板配置】-【新增模板】,填写好签名内容、模板类型、模板名称、模板内容等信息,点击【确定】
【自定义事件】
功能介绍:
Unipush透传及部分厂商返回的推送报表数据不包含展示数和点击数。可通过自定义事件来统计,先定义好自定义事件的名称及编号,在需要埋点的地方,调用对应方法来提交统计,补全相应数据。也可用于应用内部的某个事件统计,如点击某个按钮、跳转动作等。
亲爱的开发者:
Unipush推送服务于2021年9月14日晚重磅升级,优化了整体页面布局,全方位提升消息推送用户体验。
【消息推送】简介
一、 功能介绍
-
推送的消息主要分为三种:通知消息、透传消息以及分组对比。
-
通知消息即是通知发送后会直接在手机通知栏(状态栏)展示一条消息,Unipush帮助将消息送达到客户端并处理消息的展示方式为通知栏消息。
-
透传消息,即是自定义消息,Unipush只负责将消息送达到客户端,而客户端接收到消息后需要自己处理消息的展示方式或后续动作。
-
分组对比,支持对比通知文案及对比用户群体,小规模测试最佳文案及最佳用户群。
二、 使用场景
-
推送活动通知,如促销、活动、红包、优惠券等
-
推送资讯热点,如资讯、新闻、热点、赛事等
-
推送关注动态,如关注、订阅、收藏、加购物车等
-
推送系统通知,如账户状态、安全、支付、物流等
三、 操作步骤
【消息推送】通知消息
功能介绍:
通知消息即是通知发送后会直接在手机通知栏(状态栏)展示一条消息,Unipush帮助将消息送达到客户端并处理消息的展示方式为通知栏消息,并且支持启动应用、打开第三方链接、打开应用内页面、附加消息四种后续动作形式。
操作步骤:
Step 1:点击【创建推送】-【通知消息】,根据小图标的解释说明填写好目标平台、通知标题、通知内容、目标选择、消息有效时长、短信补量、广告投放等信息。
Step 2:设置通知消息在各个平台的通知展示形式。在Android平台,可通过【通知渠道模板】设置响铃、震动、浮动、唤醒屏幕等通知提醒方式;通过【后续动作】设置后续动作为启动应用、打开第三方链接、打开应用内特定页面及附加消息,附加消息的形式为透传消息;通过【展开式通知】设置通知消息下拉展开的形式为文本、大图;通过【通知栏图标】设置通知消息到达通知栏的图标为默认图标、APP本地图标、上传图片、指定图片路径;通过【角标数字】设置通知消息到达客户端后,角标数字的增量。在iOS平台,也可进行相应设置。
Step 3:查看推送预览,检查确认各项设置是否正确,也可以选择发送测试推送到手机上进行测试预览。确认无误后,点击【确定】,正式发送推送。
【消息推送】透传消息
功能介绍:
透传消息,即是自定义消息,Unipush只负责将消息送达到客户端,而客户端接收到消息后需要自己处理消息的展示方式或后续动作。
操作步骤:
Step 1:点击【创建推送】-【透传消息】,根据小图标的解释说明填写好目标平台、通知标题、通知内容、目标选择、消息有效时长等信息以及在各个平台的通知展示方式及后续动作。然后点击【预览】
Step 2:查看推送预览,检查确认各项设置是否正确,也可以选择发送测试推送到手机上进行测试预览。确认无误后,点击【确定】,正式发送推送。
【消息推送】分组对比
功能介绍:
分组对比功能支持在同一个推送计划中,添加2-5条文案/人群进行对比测试,通过对各测试组的到达、展示、点击数据的监测分析,筛选出最优文案/人群,并支持实时及定时地按最优测试组补发剩余用户,提高通知消息点击率,助力运营提效。
操作步骤:
Step 1:点击【创建推送】-【分组对比】,填写测试名称、选择测试模式、目标平台、测试人群、补发策略、推送设置等 ,点击【测试预览】
Step 2:查看推送预览,检查确认各项设置是否正确。确认无误后,点击【确定】,正式发送推送。
Step 3:点击【数据统计】-【推送记录】,管理测试任务,可选择停止测试、取消补发、手动补发等
【数据统计】推送数据
功能介绍:
【推送数据】记录所有推送任务的记录(包括推送通知记录、透传记录、分组对比记录、API数据记录),显示每个推送任务的详情、目标用户、后续动作、状态等相关信息,也能够查看某次推送的详细数据及转化情况。
【折损原因分析】针对单个推送任务,提供消息从下发-到达-展示-点击各阶段,消息未成功触达的折损原因分析,比如:卸载、关闭通知、推送超限、参数超限、参数无效等。快速诊断定位折损原因,并给出针对性的建议解决方案。同时支持开发者导出未收到消息的用户折损列表,便于针对这部分用户做后续触达转化,提高运营效率。
【大盘分析】针对单个推送任务,提供每个任务的消息到达率和点击率,与应用自身的平均值及APP所属分类Top20个推应用的均值做比较,高效评估该推送任务的转化水平,调整推送运营策略。
名词解释:
-
百日内联网用户数(可推送用户数):3个月内活跃的用户数,即连网过Unipush平台的用户数。
-
实际下发数:本次推送任务中,由Unipush服务器实际发出的消息总数。该数据实时更新。
-
到达数:通知到达用户手机后,UnipushSDK上报给Unipush服务器的到达回执总数。该数据实时更新。
-
展示数:通知到达用户手机后由UnipushSDK正确解析并展示在用户手机通知栏的展示回执总数。该数据实时更新。对于透传消息由于是APP客户端自行处理展示,所以Unipush无法统计其展示数,可以用自定义事件来统计,先定义好自定义事件的名称及编号,在需要埋点的地方调用对应方法来提交统计(详情咨询Unipush技术支持)。
-
点击数:通知展示在用户手机通知栏后,点击该通知的用户总数。该数据实时更新。对于透传消息由于是APP客户端自行处理展示,所以Unipush无法统计其点击数,可以用自定义事件来统计,先定义好自定义事件的名称及编号,在需要埋点的地方调用对应方法来提交统计(详情咨询Unipush技术支持)。
【数据统计】日推送数
功能介绍:
您所有推送任务(包括推送通知、透传消息、AB测试以及API 推送)的所有数据总和(过滤无效用户后)。
名词解释:
-
推送趋势:过去24小时内,所有推送任务的总体下发情况。每半小时刷新一次。
-
到达趋势:过去24小时内,所有推送任务的总体到达情况。每半小时刷新一次。
-
点击趋势:过去24小时内,所有推送任务的总体点击情况。每半小时刷新一次。
【数据统计】用户数据
功能介绍:
用于展示当前所选择应用的用户数据,包括新增用户数(峰值)、次日留存率、在线用户数(峰值)、日联网用户数、关闭通知率、卸载用户数的数据。通过用户数据可以更加了解您应用的在线情况。
名词解释:
- 新增用户数:当日新增与Unipush服务器保持连接的用户数。
- 次日留存率:新用户在第二天的使用情况比例。
- 在线用户数(峰值):当前与Unipush服务器保持连接的用户数的峰值,每10分钟统计一次。
- 活跃用户数:当日内Unipush推送SDK与Unipush服务器建立过长链接的用户总数量
- 日联网用户数:当日所有连接过Unipush服务器的用户总数。
- 关闭通知率:活跃用户中,通知栏消息提示关闭的用户数占比。
- 卸载用户数:每周卸载该应用的用户数(此处统计的为iOS用户的卸载数)。
【应用概览】
提供用户数据和应用维度的推送数据的总览
【配置管理】故障排查
功能介绍:
您可以查询CID是否在线,CID绑定的devicetoken等信息,还可以进行推送测试,推送路径查询
状态查询:检测当前应用下的CID是否在线、包名、devicetoken等信息
推送测试:在集成过程中进行单推测试,可推送通知和透传消息
消息链路查询:查询某任务下某cid的具体实时推送路径情况
【配置管理】用户分组
功能介绍:
用户分组指利用Unipush提供的大数据能力,根据用户性别、偏好、所在区域等属性将用户进行分组,形成分组后,在创建推送消息时,可以将推送消息发给该分组,免去每次筛选的麻烦。
操作步骤:
Step 1:点击【配置管理】-【用户分组】-【添加用户分组】
Step 2:填写用户分组名称,选择用户属性设置,点击【确定】
【配置管理】别名管理
功能介绍:
Unipush使用ClientID来标识每个独立的用户,但ClientID不等于开发者应用上的用户名。别名是开发者根据自身需求为每个用户设定的标识,建议对不同用户设定不同别名,保证可通过别名来唯一确认某特定用户。适用于和APP内部数据打通,例如可将用户的邮箱、昵称、手机号等设为别名,即可通过邮箱、昵称、手机号指定目标用户。Unipush支持单个添加或批量导入别名。
别名规则:
-
有效的别名组成:字母(区分大小写)、数字、下划线、汉字。
-
任务备注名长度限制为 40 字节。( UTF-8 )
-
一个别名最多允许绑定10个ClientID。
-
批量上传文件格式如下,每行一条,前面为别名,后面为ClientID,使用英文逗号分隔。(示例:别名1,14c3a47a37461ssass7384e88de32d8)
操作步骤:
点击【配置管理】-【别名管理】-【添加别名】或者【批量导入】-【确定】
【配置管理】应用标签
功能介绍:
支持APP应用通过UnipushSDK接口自行设置应用标签,每个用户(通过CID来标识 )最多支持100个标签,例如“喜爱足球”,“喜爱动漫”。推送时指定某一标签推送,即可推送给所有打上此标签的用户。
【配置管理】渠道模板
功能介绍:
渠道模板用于设置消息下发的提醒方式:响铃、震动、浮动、唤醒屏幕等
操作步骤:
点击【配置管理】-【渠道模板】-【添加渠道模板】,填写模板名称、渠道ID、渠道名称、通知优先级、提醒方式,点击【确定】
【配置管理】短信配置
功能介绍:
Unipush有提供短信补量服务(在创建推送设置里面可以设置选择),针对 App 侧一段时间内无法收到推送的消息(可能由于断网、后台禁止运行、消息延迟等原因)的用户,进行短信通道信息补充。而针对提供的短信通道补充服务,则支持对短信签名以及短信模板进行设置。
操作步骤:
Step 1:配置短信签名
点击【配置管理】-【短信配置】-【签名配置】-【新增签名】,然后填写签名内容,点击【确定】
Step 2:配置短信模板
点击【配置管理】-【短信配置】-【模板配置】-【新增模板】,填写好签名内容、模板类型、模板名称、模板内容等信息,点击【确定】
【自定义事件】
功能介绍:
Unipush透传及部分厂商返回的推送报表数据不包含展示数和点击数。可通过自定义事件来统计,先定义好自定义事件的名称及编号,在需要埋点的地方,调用对应方法来提交统计,补全相应数据。也可用于应用内部的某个事件统计,如点击某个按钮、跳转动作等。
收起阅读 »

解决ios14以上DLNA投屏搜索不到设备问题~
iOS开发申请组播广播权限
iOS14以后,使用组播广播功能需要申请权限。
1、申请地址:https://developer.apple.com/contact/request/networking-multicast;
2、填写相应的App Information,提交了等待审核;
3、等待几天,审核通过了会给你发邮件:Your request to use Multicast Networking was approved. You can now add the Multicast Networking entitlement with your Provisioning Profile.
4、登录开发者账号,证书管理界面,在AppID的配置界面,增加对应权限:Additional Capabilities中,把Multicast Networking给勾上;
5、重新生成Profiles里对应的证书;
6、在工程里配置.entitlements文件,增加一项。Key:com.apple.developer.networking.multicast type:boolean Value:YES
7、在Info.plist文件中,添加本地网络使用权限:Privacy - Local Network Usage Description
权限问题解决完毕,组播广播功能可以正常开发使用了。
uniapp没有这个entitlements文件,那这个时候要怎么办?请加QQ:320675819(备注:ios14投屏搜索不到设备问题)
iOS开发申请组播广播权限
iOS14以后,使用组播广播功能需要申请权限。
1、申请地址:https://developer.apple.com/contact/request/networking-multicast;
2、填写相应的App Information,提交了等待审核;
3、等待几天,审核通过了会给你发邮件:Your request to use Multicast Networking was approved. You can now add the Multicast Networking entitlement with your Provisioning Profile.
4、登录开发者账号,证书管理界面,在AppID的配置界面,增加对应权限:Additional Capabilities中,把Multicast Networking给勾上;
5、重新生成Profiles里对应的证书;
6、在工程里配置.entitlements文件,增加一项。Key:com.apple.developer.networking.multicast type:boolean Value:YES
7、在Info.plist文件中,添加本地网络使用权限:Privacy - Local Network Usage Description
权限问题解决完毕,组播广播功能可以正常开发使用了。
uniapp没有这个entitlements文件,那这个时候要怎么办?请加QQ:320675819(备注:ios14投屏搜索不到设备问题)
收起阅读 »
如何实现 iOS 短视频跨页面的无痕续播?
在一切皆可视频化的今天,短视频内容作为移动端产品新的促活点,受到了越来越多的重视与投入。盒马在秒播、卡顿率、播放成功率等基础优化之外,在用户使用体验上引入了无痕续播能力,提升用户观看视频内容的延续性。本篇将分享盒马在 iOS 短视频方面的实践干货。
作者|神捕
审校|泰一
跨页面续播是除秒播外另一个可以从体感上增加用户体验的能力。由于一些业务场景需要在不同页面上播放同一个视频内容的场景,而这些场景页面切换往往是连续的,这就要求短视频的播放也是连续。这样才能使得体验上会有连贯性,让用户在进入沉浸式页面时,能流畅的过度,且无感知的继续播放,从而产生连续不间断的感受。下面我们开始介绍盒马短视频的跨页面续播能力和流畅的动画切换效果的流畅性。
!
https://v.youku.com/v_show/id_XNTgwNTk4NzQ4MA==.html
如上视频所示,视频在列表页预览观看后,用户很可能继续点击跳到下一个全屏页面,进入沉浸式体验。在这过程中,视频窗口平滑变大至全屏,视频进度是延续的,中间没有感觉到视频或音频的停顿感。在页面返回后,视频窗口也有相应的还原效果。
目标
接入简单,只需要关心并加一个参数,其它逻辑内聚。
适配性好,支持裁剪模式的切换。
视频、音频无缝衔接,不能有任何停顿感。
页面间播放状态隔离,互不干扰。
实现方案
在方案选择上,主要考虑了以下三种:
目前盒马采用的是第 3 种 ——playerView 的复用方式,具体来说,无痕续播的实现,至少需要以下几个步骤:
- 用户点击,从 A 页面跳转到 B 页面,如:domain/path?reusedPlayerView=0xyyyyyy, 在原有业务参数的基础上,添加一个 reusedPlayerView 参数,把 playerView 传给下个页面 。
- B 页面 HMTBPlayerView 的实例化:内部实例化一个或复用 A 页面的 reusedPlayerView。
- playerView 的大小位置换算,实现切换动画。
- 从 B 页面返回 A 时,实现退出动画并返还 playerView。
以上步骤不多,但具体实现起来是比较复杂的,下面我们将围绕 4 个主要问题的解决过程,来说明具体实现方式。
尺寸变化的动画
正常来说,只要计算好 playerView 的原始 Rect,以及最终 Rect,基于 UIView 做 frame 动画就可以简单实现窗口变大效果。但实现时发现,手淘播放器内部重写了 setFrame 方法,只要修改了 frame,playerView 将直接显示为终态,动画没有效果。
于是,这里采用了 CGAffineTransform 的 scale 实现:先把 playerView 的 frame 设置为终态,计算好变化前后的尺寸比例 ratio,设置 playerView.transform = CGAffineTransformMakeScale (ratio, ratio)
,将其尺寸等比缩小为初始位置大小,而后就可以执行 transform 的动画实现从起点到终点的变换。
需要注意的是,此处 ratio 的计算方式,是以 playerView 内真实渲染的视频尺寸计算,而不是 playerView 本身大小。
渲染 mode 的切换
视频渲染本身可以设置为 ScaleAspectFit 或 ScaleAspectFill,目前在盒马的场景中,存在一种 A 页面的播放器为 fill mode,且 playerView 固定正方形,但跳转到 B 页面时,变成 fit mode,这样就出现了一个在尺寸变化动画进行时的 mode 切换的问题。
上述通过 setFrame 并修改 transform 的方式,可以实现把 playerView 大小变换成与动画前的初始大小一致,但是,如果此时存在 mode 切换需求就有可能出现计算后的大小不一致,比如从一个 9:16 长方形的 playerview 变成一个 1 : 1 且 mode 为 fill 的正方形 playerView,此时宽度一致,但高度明显多出了,直接做动画会导致初始状态闪动。
这里的解决方式,我们使用了 maskView 进行 mode 切换过渡:首先,计算 maskView 分别在宽高上的 scale,然后设置 playerView.maskView.transform
。计算方式如下:
CGAffineTransformMakeScale(originalRect.size.width/(destRect.size.width*ratio), originalRect.size.height/(destRect.size.height*ratio))
这样就实现利用 maskView,把 9:16 的长方形显示成 1:1 的可见区域,实现动画的起始位置重合。最后,结合上述 playerView.transform 动画,再添加一个 maskView.transform 动画,二者配合,模拟出带 mode 切换场景下的动画过渡效果。
主动回收与主动归还策略
在实现了进场动画之后,最重要的是需要考虑 playerView 复用逻辑,其中比较重要的一点就是 playerView 什么时候归还给 A 页面。
目前我们采用的是租借思路:
- 有可借 playerView 时,进行借用;
- 复用的 playerview 不再使用时,及时主动归还;
- 当出租方自己要使用时,发现租方还未返还,此时进行主动回收。
具体场景来说:进场时,判断有 reusePlayerView,则进行复用;当沉浸式视频(B 页面,类似抖音)翻到下一个视频时,上一个视频进行主动归还操作,如果用户又划回到第 1 个视频,此时是 new 的 playerView 了;另外,当用户点击页面关闭时,主动归还(如果还未还的话);特别要注意的是,这里还增加了一个主动回收机制,场景比如用户通过一些我们未知的方式,回到了页面 A,此时 reusePlayerView 是没有主动归还的,但页面 A 自己又需要 play,此时就触发了主动回收机制,保证当前页面可用。
有一点需要提一下的是,在页面返回时也有动画,实现方面与上述类似,唯一区别是,返回时页面可能 dealloc 了,动画会有问题,所以我们做法是先把 playerView 从 B 页面,添加到 window, 做好缩放动画,结束后,再主动归还给页面 A。
状态隔离
在使用播放器复用时,需要考虑一个重要的问题,就是复用后,播放器状态、设置的隔离。比如,在页面 A 进入页面 B 后,播放器无痕续播,但播放器的状态对 A 来说是暂停,对于 B 来说必须是播放状态,虽然二者使用的是同一个 playerView。
这种隔离是很有必要的,比如业务想要引导用户进入页面 A 的业务,在这里观看视频可以得积分,那么在他进入页面 B 时,就不应该继续结算积分(业务依赖了播放状态通知)。还有,A 与 B 页面的播放设置可能不同,A 可能是静音,B 是有声音,设置不同,也需要隔离。我们是这样做的,如下图(视图层级):
图中,最外层的 view 是盒马自己封装的播放器 HMTBPlayerView,内部有一个手淘的 TBMPBPlayerView,大小一样。我们拿来做复用的其实是 TBMPBPlayerView 这一层,而把业务层的所有设置放在 HMTBPlayerView,这样的话,在 TBMPBPlayerView 被移走时,重新根据新的 HMTBPlayerView 设置它,做好关联,而旧的 HMTBPlayerView 设置不受影响,包括播放器回调。
总结
综上,我们实现了一种播放器复用方式,在播放器内部实现了窗口切换、状态隔离等逻辑,对 App 使用方来说是几乎无感的。该方案不仅可用在无痕续播场景上,今后也可以用在 App 内全局播放器实例复用优化方向。
「视频云技术」你最值得关注的音视频技术公众号,每周推送来自阿里云一线的实践技术文章,在这里与音视频领域一流工程师交流切磋。公众号后台回复【技术】可加入阿里云视频云产品技术交流群,和业内大咖一起探讨音视频技术,获取更多行业最新信息。
在一切皆可视频化的今天,短视频内容作为移动端产品新的促活点,受到了越来越多的重视与投入。盒马在秒播、卡顿率、播放成功率等基础优化之外,在用户使用体验上引入了无痕续播能力,提升用户观看视频内容的延续性。本篇将分享盒马在 iOS 短视频方面的实践干货。
作者|神捕
审校|泰一
跨页面续播是除秒播外另一个可以从体感上增加用户体验的能力。由于一些业务场景需要在不同页面上播放同一个视频内容的场景,而这些场景页面切换往往是连续的,这就要求短视频的播放也是连续。这样才能使得体验上会有连贯性,让用户在进入沉浸式页面时,能流畅的过度,且无感知的继续播放,从而产生连续不间断的感受。下面我们开始介绍盒马短视频的跨页面续播能力和流畅的动画切换效果的流畅性。
!
https://v.youku.com/v_show/id_XNTgwNTk4NzQ4MA==.html
如上视频所示,视频在列表页预览观看后,用户很可能继续点击跳到下一个全屏页面,进入沉浸式体验。在这过程中,视频窗口平滑变大至全屏,视频进度是延续的,中间没有感觉到视频或音频的停顿感。在页面返回后,视频窗口也有相应的还原效果。
目标
接入简单,只需要关心并加一个参数,其它逻辑内聚。
适配性好,支持裁剪模式的切换。
视频、音频无缝衔接,不能有任何停顿感。
页面间播放状态隔离,互不干扰。
实现方案
在方案选择上,主要考虑了以下三种:
目前盒马采用的是第 3 种 ——playerView 的复用方式,具体来说,无痕续播的实现,至少需要以下几个步骤:
- 用户点击,从 A 页面跳转到 B 页面,如:domain/path?reusedPlayerView=0xyyyyyy, 在原有业务参数的基础上,添加一个 reusedPlayerView 参数,把 playerView 传给下个页面 。
- B 页面 HMTBPlayerView 的实例化:内部实例化一个或复用 A 页面的 reusedPlayerView。
- playerView 的大小位置换算,实现切换动画。
- 从 B 页面返回 A 时,实现退出动画并返还 playerView。
以上步骤不多,但具体实现起来是比较复杂的,下面我们将围绕 4 个主要问题的解决过程,来说明具体实现方式。
尺寸变化的动画
正常来说,只要计算好 playerView 的原始 Rect,以及最终 Rect,基于 UIView 做 frame 动画就可以简单实现窗口变大效果。但实现时发现,手淘播放器内部重写了 setFrame 方法,只要修改了 frame,playerView 将直接显示为终态,动画没有效果。
于是,这里采用了 CGAffineTransform 的 scale 实现:先把 playerView 的 frame 设置为终态,计算好变化前后的尺寸比例 ratio,设置 playerView.transform = CGAffineTransformMakeScale (ratio, ratio)
,将其尺寸等比缩小为初始位置大小,而后就可以执行 transform 的动画实现从起点到终点的变换。
需要注意的是,此处 ratio 的计算方式,是以 playerView 内真实渲染的视频尺寸计算,而不是 playerView 本身大小。
渲染 mode 的切换
视频渲染本身可以设置为 ScaleAspectFit 或 ScaleAspectFill,目前在盒马的场景中,存在一种 A 页面的播放器为 fill mode,且 playerView 固定正方形,但跳转到 B 页面时,变成 fit mode,这样就出现了一个在尺寸变化动画进行时的 mode 切换的问题。
上述通过 setFrame 并修改 transform 的方式,可以实现把 playerView 大小变换成与动画前的初始大小一致,但是,如果此时存在 mode 切换需求就有可能出现计算后的大小不一致,比如从一个 9:16 长方形的 playerview 变成一个 1 : 1 且 mode 为 fill 的正方形 playerView,此时宽度一致,但高度明显多出了,直接做动画会导致初始状态闪动。
这里的解决方式,我们使用了 maskView 进行 mode 切换过渡:首先,计算 maskView 分别在宽高上的 scale,然后设置 playerView.maskView.transform
。计算方式如下:
CGAffineTransformMakeScale(originalRect.size.width/(destRect.size.width*ratio), originalRect.size.height/(destRect.size.height*ratio))
这样就实现利用 maskView,把 9:16 的长方形显示成 1:1 的可见区域,实现动画的起始位置重合。最后,结合上述 playerView.transform 动画,再添加一个 maskView.transform 动画,二者配合,模拟出带 mode 切换场景下的动画过渡效果。
主动回收与主动归还策略
在实现了进场动画之后,最重要的是需要考虑 playerView 复用逻辑,其中比较重要的一点就是 playerView 什么时候归还给 A 页面。
目前我们采用的是租借思路:
- 有可借 playerView 时,进行借用;
- 复用的 playerview 不再使用时,及时主动归还;
- 当出租方自己要使用时,发现租方还未返还,此时进行主动回收。
具体场景来说:进场时,判断有 reusePlayerView,则进行复用;当沉浸式视频(B 页面,类似抖音)翻到下一个视频时,上一个视频进行主动归还操作,如果用户又划回到第 1 个视频,此时是 new 的 playerView 了;另外,当用户点击页面关闭时,主动归还(如果还未还的话);特别要注意的是,这里还增加了一个主动回收机制,场景比如用户通过一些我们未知的方式,回到了页面 A,此时 reusePlayerView 是没有主动归还的,但页面 A 自己又需要 play,此时就触发了主动回收机制,保证当前页面可用。
有一点需要提一下的是,在页面返回时也有动画,实现方面与上述类似,唯一区别是,返回时页面可能 dealloc 了,动画会有问题,所以我们做法是先把 playerView 从 B 页面,添加到 window, 做好缩放动画,结束后,再主动归还给页面 A。
状态隔离
在使用播放器复用时,需要考虑一个重要的问题,就是复用后,播放器状态、设置的隔离。比如,在页面 A 进入页面 B 后,播放器无痕续播,但播放器的状态对 A 来说是暂停,对于 B 来说必须是播放状态,虽然二者使用的是同一个 playerView。
这种隔离是很有必要的,比如业务想要引导用户进入页面 A 的业务,在这里观看视频可以得积分,那么在他进入页面 B 时,就不应该继续结算积分(业务依赖了播放状态通知)。还有,A 与 B 页面的播放设置可能不同,A 可能是静音,B 是有声音,设置不同,也需要隔离。我们是这样做的,如下图(视图层级):
图中,最外层的 view 是盒马自己封装的播放器 HMTBPlayerView,内部有一个手淘的 TBMPBPlayerView,大小一样。我们拿来做复用的其实是 TBMPBPlayerView 这一层,而把业务层的所有设置放在 HMTBPlayerView,这样的话,在 TBMPBPlayerView 被移走时,重新根据新的 HMTBPlayerView 设置它,做好关联,而旧的 HMTBPlayerView 设置不受影响,包括播放器回调。
总结
综上,我们实现了一种播放器复用方式,在播放器内部实现了窗口切换、状态隔离等逻辑,对 App 使用方来说是几乎无感的。该方案不仅可用在无痕续播场景上,今后也可以用在 App 内全局播放器实例复用优化方向。
收起阅读 »「视频云技术」你最值得关注的音视频技术公众号,每周推送来自阿里云一线的实践技术文章,在这里与音视频领域一流工程师交流切磋。公众号后台回复【技术】可加入阿里云视频云产品技术交流群,和业内大咖一起探讨音视频技术,获取更多行业最新信息。

dcloud官方应该没有很擅长写样式的人,要弄暗黑模式的话要好好整理整理uni.scss和组件
很多组件样式颜色背景都写死了还是不同的默认样式,而且可能因为是不同的人写的,
虽然有uni.scss,但是都没怎么引用各写各的。
这也导致了ui框架很难直接写一套暗黑模式。
如果分类管理的好,uni.scss写一套浅色,一套暗黑,引用那里批量变一下就可以。
我用过quasar framework,组件样式方面写的很好,都不需要自己写css,根组件一个mode="dark"直接全部子组件跟着变色
hello-uniapp中组件演示也写的很乱,不应该写乱七八糟的其他的padding,margin,display,color,backgroud
flex就应该只用uni-flex的class
一组一组就是uni-group
一个个小标题就应该是uni-section
ui这个活吃力不讨好,不知道dcloud会花多大力气继续在uni-ui上面
很多组件样式颜色背景都写死了还是不同的默认样式,而且可能因为是不同的人写的,
虽然有uni.scss,但是都没怎么引用各写各的。
这也导致了ui框架很难直接写一套暗黑模式。
如果分类管理的好,uni.scss写一套浅色,一套暗黑,引用那里批量变一下就可以。
我用过quasar framework,组件样式方面写的很好,都不需要自己写css,根组件一个mode="dark"直接全部子组件跟着变色
hello-uniapp中组件演示也写的很乱,不应该写乱七八糟的其他的padding,margin,display,color,backgroud
flex就应该只用uni-flex的class
一组一组就是uni-group
一个个小标题就应该是uni-section
ui这个活吃力不讨好,不知道dcloud会花多大力气继续在uni-ui上面
收起阅读 »
uni.showToast方法设置icon属性值为error时在微信开发者工具中图标失效,显示属性值为success时的图标
uni.showToast方法设置icon属性值为error时在微信开发者工具中图标失效,显示属性值为success时的图标
uni.showToast方法设置icon属性值为error时在微信开发者工具中图标失效,显示属性值为success时的图标

解决 uni-admin 左侧菜单折叠的问题
uni-nav-menu.vue
约190行
// 关闭所有
closeAll() {
let url = this.active.substring(1);
this.subChildrens.forEach((item) => {
let childrens = item.index.children;
let num = 0;
for (let i=0; i<childrens.length; i++){
let obj = childrens[i];
console.log(obj.value);
if (obj.value == url){
num ++;
}
}
if (item.isOpen && num==0) {
item.isOpen = false
}
})
}
uni-nav-menu.vue
约190行
// 关闭所有
closeAll() {
let url = this.active.substring(1);
this.subChildrens.forEach((item) => {
let childrens = item.index.children;
let num = 0;
for (let i=0; i<childrens.length; i++){
let obj = childrens[i];
console.log(obj.value);
if (obj.value == url){
num ++;
}
}
if (item.isOpen && num==0) {
item.isOpen = false
}
})
}