十六位互联网大咖,齐邀你共赴七麦【NextWorld2018】增长盛宴!
今年的 12 月 12 日,不仅有阿里的“双十二”购物日,还有一场属于你的互联网年末盛宴。
12 月 12 日 9:00 — 17:00。
北京 · 四季酒店 · 五层宴会厅。(朝阳区亮马桥路 48 号)
十六位互联网行业大咖,齐聚【NextWorld2018 新原力增长】峰会,邀你一起深度解锁用户增长新姿势!
最具价值峰会,齐聚大咖风采
清科集团联席总裁【袁润兵】,为你解读互联网资本浪潮下的新趋势。
数看全球:国内市场流量红利消失,全球市场近况如何?
AppsFlyer 大中华地区商务副总裁【徐宇】,带你揭秘全球移动应用市场现状和出海新机会。
OneSight CEO【李蕾】,解析出海营销新技术,助力移动产品新增长。
UMKA 俄罗斯电商平台中国区副总裁【Sofia Zhang】,分享如何实现用户本地化爆发式增长的宝贵经验。
游艺道副总裁【孙可】,评说出海新红利,快速崛起的跨境手游。
聚焦增长:产品增长乏力,如何为产品赋能,实现高速增长?
樊登读书会副总裁【孙向利】,分享关于知识付费时代,如何激发用户自传播力,助力产品增长。
十点读书 CTO【张峡】,解读十点读书如何通过矩阵式布局,一年涨粉上千万。
社群裂变:获客成本日益升高,产品如何通过裂变,零成本精准获客?
知乎商业市场经理【陈欣】,为你解析内容创意如何引爆产品病毒式增长。
创业家副总裁【易涛】,分享黑马营如何通过社群营销打造市值 50 亿的“创业基地”。
产业互联网:传统行业如何借力互联网实现新增长?
连咖啡合伙人【张洪基】,和你聊聊连咖啡的社交营销新模式。
Plum 平台创始人兼 CEO【徐薇】,解析 Plum 如何实现二手时尚平台交易量的几十倍增长。
拉勾招聘联合创始人&CMO【鲍艾乐】,带来拉勾年度大数据,俯瞰“增长er”的生存地图。
以为这就完事了?错!惊喜还远未结束!
新产品!
2018 年最值得期待的 App 出海推广平台全新重磅发布!七麦技术团队历时两年诚意巨献,让你出海有术!
加人脉!
峰会到场数十位移动互联网意见领袖,1000+ 精英创业、推广从业者到场,覆盖推广、营销、运营多领域从业人员,互联网最牛的人等你来撩~ 资源、人脉无压力!
有福利!
现场设“2018年度推广增长大礼包”,有料有趣高价值!
扫描下图二维码,马上报名参加吧!
和七麦一起,共赴年末增长盛宴!
NextWorld 2018新原力增长峰会了解详情 收起阅读 »
12 月 12 日 9:00 — 17:00。
北京 · 四季酒店 · 五层宴会厅。(朝阳区亮马桥路 48 号)
十六位互联网行业大咖,齐聚【NextWorld2018 新原力增长】峰会,邀你一起深度解锁用户增长新姿势!
最具价值峰会,齐聚大咖风采
清科集团联席总裁【袁润兵】,为你解读互联网资本浪潮下的新趋势。
数看全球:国内市场流量红利消失,全球市场近况如何?
AppsFlyer 大中华地区商务副总裁【徐宇】,带你揭秘全球移动应用市场现状和出海新机会。
OneSight CEO【李蕾】,解析出海营销新技术,助力移动产品新增长。
UMKA 俄罗斯电商平台中国区副总裁【Sofia Zhang】,分享如何实现用户本地化爆发式增长的宝贵经验。
游艺道副总裁【孙可】,评说出海新红利,快速崛起的跨境手游。
聚焦增长:产品增长乏力,如何为产品赋能,实现高速增长?
樊登读书会副总裁【孙向利】,分享关于知识付费时代,如何激发用户自传播力,助力产品增长。
十点读书 CTO【张峡】,解读十点读书如何通过矩阵式布局,一年涨粉上千万。
社群裂变:获客成本日益升高,产品如何通过裂变,零成本精准获客?
知乎商业市场经理【陈欣】,为你解析内容创意如何引爆产品病毒式增长。
创业家副总裁【易涛】,分享黑马营如何通过社群营销打造市值 50 亿的“创业基地”。
产业互联网:传统行业如何借力互联网实现新增长?
连咖啡合伙人【张洪基】,和你聊聊连咖啡的社交营销新模式。
Plum 平台创始人兼 CEO【徐薇】,解析 Plum 如何实现二手时尚平台交易量的几十倍增长。
拉勾招聘联合创始人&CMO【鲍艾乐】,带来拉勾年度大数据,俯瞰“增长er”的生存地图。
以为这就完事了?错!惊喜还远未结束!
新产品!
2018 年最值得期待的 App 出海推广平台全新重磅发布!七麦技术团队历时两年诚意巨献,让你出海有术!
加人脉!
峰会到场数十位移动互联网意见领袖,1000+ 精英创业、推广从业者到场,覆盖推广、营销、运营多领域从业人员,互联网最牛的人等你来撩~ 资源、人脉无压力!
有福利!
现场设“2018年度推广增长大礼包”,有料有趣高价值!
扫描下图二维码,马上报名参加吧!
和七麦一起,共赴年末增长盛宴!
NextWorld 2018新原力增长峰会了解详情 收起阅读 »
【活动推荐】ECUG Con 2018 拥抱下一个十年
国内云计算领域大咖 许式伟ECUG Con 2018
倾情发起的技术盛宴
引领国内云领域风向的高端峰会
ECUG Con 2018
2018 年 12 月 22-23 日 深圳
全新启程!
七牛云 CEO 许式伟
PingCAP CEO 刘奇
七牛云产品副总裁戴文军
Gopher 社区创始人 Asta Xie
阿里巴巴技术专家孙宏亮
《Kubernetes IN ACTION》作者 Marko Lukša
华为云 AI 推理平台&云搜索技术总监胡斐然
七牛云技术总监陈超
阿里云高级开发工程师严明明
京东云区块链实验室与数据库部负责人郭里靖
网易研究院云计算资深架构师朱剑峰
腾讯云高级工程师刘兆瑞
货拉拉数据分析负责人高遥
......
超豪华讲师阵容!
超有料精彩分享!
ECUG 历经十年蜕变
邀您共同开启下个十年
让我们坚持技术情怀,秉承技术精神
开启新的云计算布道篇章!
时 间
2018 年 12 月 22-23 日
地 点
深圳市南山区软件产业基地
更多详情请见下方海报~
扫描上方二维码 ,立即购买早鸟票
与大咖讲师共同探索云计算的下一个十年!
活动详情:了解更多 收起阅读 »
0 元福利手慢无 | 年尾盛会——2018 · 支付学院年会环信用户免费报名
文能提笔抒骚情
武能切图画交互
作为一个产品经理
学习不是忙里偷闲的充电
而是写进岗位职责里的日日功
公司一步步发展,业务一步步扩张
支付
一个熟悉又陌生的词映入眼帘
从 2014 年起,Ping++ 用 4 年的沉淀积累,化繁为简,实现「 7 行代码接入支付」的聚合支付、会员账户系统、多级商户系统等产品,首次提出「聚合支付」,在聚合支付领域树立标杆、建立规则。曾经踩过的无数坑,都成为了支付这一领域内宝贵的经验。所以说,不如为所有的产品经理们办一个「支付学院」吧!就这样,支付学院第一期款款而来。
「你们应该继续办下去啊!支持你们!」
「这次真的收获满满,老师讲得我还要再捋捋,太多了一下吸收不了。」
「可以私底下给秋秋老师打 Call 吗?请替我表白!」
「PPT 我打印出来学习,你们下次别用黑底白字了,打印起来反白很麻烦。」
「……」
受宠若惊,十分惶恐。因此,领域即便如此狭窄,架构即便大同小异,也要像宰相的女儿山鲁佐德一样,奉上个个迥异的故事,直到第一千零一夜。
于是就又举办了第二期、第三期,以及现在如约而至的 2018 年会。
❤ 作为一名严肃活泼认真可爱的支付从业者,你一定知道支付的前提是合规,监管即是这开头的 1 ;而后作为一个产品商业化的核心,支付系统与交易系统应为业务敞开怀抱,随着需求的变化而迭代以支撑业务十倍百倍的增长,0 是谦虚也是包容;在当今经济下行的处境下,很多企业为了寻求进一步的发展,纷纷选择了出海,去海外寻求新的增长点,因此跨境就像尾部的 1,代表了新的起点和开始。
❤ 1001 个支付故事,其实也是 4 个支付故事。2018 支付学院年会来为大家讲解关于交易系统、支付系统、监管和跨境支付的故事。
年会不同以往的是,大家热热闹闹开开心心相聚才是最重要的,因此这次人数应该远超以往,如果想要 0 元听老师讲课,那你就需要手快一些了。
0 元票兑换码
feqylt1
有道是,相逢即是有缘,有缘自会相逢。
我觉得我们已经很有缘了,故事也有了,来年会一起聊聊吧。
活动详情:免费报名 收起阅读 »
【我最喜爱的 Cloud Studio 插件评选大赛】终于开始了!
由 环信、腾讯云和 CODING 共同举办的 我最喜爱的 Cloud Studio 插件评选大赛正式开始了!在这场比赛里,将会有技术上的碰撞,大牛评委的专业点评,愉快的技术交流,好玩的插件尝试。
- 参赛者可以围绕 Git、实用小工具、腾讯云产品对接、UI 强化、语言支持等 14 个主题提交插件,再加上最具娱乐奖,代码最简单奖,设置功能最复杂奖等;
- 近 30 种奖项,超高中奖率;
- 插件只要提交上架,就有 50 元的话费相赠;
- 只要关注 CODING 公众号并转发活动讯息到朋友圈,即可获得手机充值小礼!
参与方式
注册并登陆腾讯云开发者平台(https://dev.tencent.com) -> 点击进入活动页面 -> 点击进行插件的编写与提交(需要选择参与评选的类别)-> 审核无误后即可上架自动参与评选。
赛程时间
环信特别奖
基于环信开发一款聊天插件,即有机会获得特别奖,根据作品还将获得环信提供的神秘奖品
更多活动信息,请浏览我们的活动页面。
进入活动页面> 收起阅读 »
NIUDAY 11.23 北京站 |环信CEO刘俊彦现场讲述人工智能赋能客户互动之现状与未来
2018 年是见证「奇迹」的一年。AI 从多年的热门话题中开始走下神坛,逐渐深入到了各个行业,加速经济结构优化及行业智慧化升级,AI 已不再是难以企及的神话而是可触摸的美好未来。 政策支持加上资本推动,无论是从新兴行业还是传统行业都出现了人工智能方面的布局者和佼佼者,智慧教育、移动社交、智能语音、智慧客服、传统媒体等行业都在突破技术上和流程上的难点和困惑。
11 月 23 日,一场由七牛云主办主题为「AI 产业技术的渗透与融合」的 NIUDAY 小牛汇共享日将在北京举行。会上,将邀请众多行业内知名企业及技术专家,针对当前 AI 在技术上以及行业中遇到的一些壁垒问题,进行深入探讨和分享。
智慧教育、移动未来,体验指尖上的 AI
在此次活动中,除行业专家的视点剖析之外,对于 AI 与行业的渗透与融合度是理论大于实践亦或是理论与实践已完美结合,已经应用的行业代表案例有哪些?对于这些问题,大家可以在此次活动中得以解惑。
刘俊彦
手机电商、手机软件系统的快速更迭让在线客服、移动客服有了更大的发展空间,成了市场中的一片蓝海,究竟现状如何、客户体验的真实反馈是什么?环信 CEO 刘俊彦先生带来的《人工智能赋能客户互动之现状与未来》让你更贴近生活,更能体会 AI 的无处不在。
谢华亮
科技发展教育先行,技术与教育的结合又将碰撞出怎样的火花,会上,将有好未来 SEG 智慧教育事业部技术总监谢华亮带来主题为《AI 在教育行业中的应用》的精彩演讲。
佘超杰
移动互联网的发展已影响到我们身边的每一个人,游戏、娱乐、社交等手机平台的火爆更是带来了巨大的信息流量。但如何应对这些突然爆发的信息流,如何让产品与技术更好地去结合以吸引更多的受众人群,成为人们关注的话题。以作为专注于移动互联网社交的知名企业 Blued 为例,实践与技术并重的技术专家佘超杰将分享给我们《AI 在 Blued 上的应用》。
连线行业专家,洞悉专业视点
人工智能技术在飞速发展过程中得到了国家以及政府的极大关注与大力支持,中国人工智能产业发展联盟作为国家发展改革委、科学技术部、工业和信息化部、中央网信办四部委共同指导下的人工智能产业权威联盟机构,也将加入到此次 NIUDAY 小牛汇共享日活动中来。
孙明俊
中国信息通信研究院人工智能部副主任,中国人工智能产业发展联盟总体组组长、数据中心联盟秘书长孙明俊女士将出席本次活动,并将从行业专家的角度带来主题为《人工智能产业发展水平分析》的演讲,向大家解析产业发展水平、分享行业利好政策等。
战略签约中国网 传统与创新的再突破
杨新华
值得一提的是,本次 NIUDAY 小牛汇共享日活动上七牛云将与中国互联网新闻中心举行战略合作签约仪式。届时,中国互联网新闻中心·中国网副总编辑杨新华先生将带来《AI 如何讲好中国故事》的演讲,介绍七牛云在未来媒体平台构建中发挥的作用,以及正处于变革中的媒体行业对 AI 的思考。
当然作为本次 NIUDAY 小牛汇活动的主办方,七牛云也准备了满满的干货带给大家。七牛云技术总监陈超《数据智能时代的智慧工厂实践》、七牛云人工智能实验室资深产品经理杨叶青《一站式审核助力无忧运营》带你体会七牛云两大重要产品线的技术与发展。
更多大咖嘉宾,请往下看~
关于环信机器人:
环信机器人已经在中通快递、新东方、天津农商行、宜家、环球捕手等头部企业的“双十一”中发挥着中流砥柱的作用,机器人可以帮助解决80%以上的常见问题,在售后环节能替代超90%的人工,轻松实现7*24无间断客户服务,AI从此让我们的客服人员告别苦逼,云淡风轻,笑对各种节假日促销、狂欢节。
目前,环信机器人已经广泛服务于包括保险、证券、银行、教育、物流、商旅、电商、汽车等行业的数万客户,日机器人会话超千万条。
活动报名地址:http://www.huodongxing.com/event/2464773427600 收起阅读 »
iOS 环信视频语音 细节处理(挂断电话逻辑处理)
对方挂断语言,可能要发送相应的消息(对方拒接 挂断等)在结束实时通话的回调中
- (void)callDidEnd:(EMCallSession *)aSession
reason:(EMCallEndReason)aReason
error:(EMError *)aError
根据EMCallEndReason这个枚举看通话结束的原因,去发送NSNotification通知
然后在聊天页面EaseMessageViewController.m中去监听这个通知,然后在通知方法中去插入消息,可参考以下代码:
NSString *insertStr = @"对方已挂断";
EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:insertStr];
NSString *from = [[EMClient sharedClient] currentUsername];
//生成Message
EMMessage *message = [[EMMessage alloc] initWithConversationID:self.conversation.conversationId from:from to:self.conversation.conversationId body:body ext:nil];
message.chatType = EMChatTypeChat;// 设置为单聊消息
message.status = EMMessageStatusSucceed;
message.direction = callEnder;
[self addMessageToDataSource:message progress:nil];
[self.conversation insertMessage:message error:nil]; 收起阅读 »
- (void)callDidEnd:(EMCallSession *)aSession
reason:(EMCallEndReason)aReason
error:(EMError *)aError
根据EMCallEndReason这个枚举看通话结束的原因,去发送NSNotification通知
然后在聊天页面EaseMessageViewController.m中去监听这个通知,然后在通知方法中去插入消息,可参考以下代码:
NSString *insertStr = @"对方已挂断";
EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:insertStr];
NSString *from = [[EMClient sharedClient] currentUsername];
//生成Message
EMMessage *message = [[EMMessage alloc] initWithConversationID:self.conversation.conversationId from:from to:self.conversation.conversationId body:body ext:nil];
message.chatType = EMChatTypeChat;// 设置为单聊消息
message.status = EMMessageStatusSucceed;
message.direction = callEnder;
[self addMessageToDataSource:message progress:nil];
[self.conversation insertMessage:message error:nil]; 收起阅读 »
2018 freeCodeConf 全国联动报名通道已正式开启
筹划已久的 freeCodeConf 终于和大家见面啦!React、Vue、Java、小程序开发、敏捷开发、区块链技术、开源项目维护......主题丰富,大咖云集,12个城市社区同步呈现。不管你是努力学习、热衷分享的开发者,还是技术粉、编程爱好者,都将收获满满!
freeCodeConf 是 freeCodeCamp 中国举办的每年一度大型联动技术大会,旨在联合 freeCodeCamp 各城市本地开发者社区,促进高度垂直、高度人才集中、高度明星企业集结的互联网与软件行业技术交流。
第一届 freeCodeConf 将于2018年11月10日在上海、天津、深圳、成都、西安、武汉、北京、杭州、广州、重庆、郑州、济南全国12个城市同期举办,诚邀广大开发者共享技术盛宴。
大会亮点
一.12城同期举办
12个城市社区同期举办,促进本地开发者技术分享与交流
二.与大咖面对面交流
与数十位前端、区块链等领域的优秀开发者和技术负责人面对面交流
三.优质企业现场招聘
部分城市设置现场招聘环节,为企业和开发者搭建沟通桥梁
四.丰厚的礼品
华为云体验券、IBM开发者纪念品、技术书籍、个性胸章......各种精美礼品等你来拿!
演讲嘉宾
报名通道
2018 freeCodeConf 成都站
【时间】2018.11.10 9:00-18:00
【地点】天府大道北段 966 号天府国际金融中心 4 号楼 1 层 1 号会议厅
演讲主题(成都站)
《How does freeCodeCamp help people learn to code for free and become developers》|《给 Node.js 插上 C++ 的翅膀》|《开源项目维护》|《敏捷中国史》|《高效 H5 动画开发方式》|《高可用React服务端渲染》|《打造 Vue 组件库》
报名通道(成都站)
扫描二维码报名
2018 freeCodeConf 天津站
【时间】2018 年11月10日(周六) 13:00-18:00
【地点】天津市大学软件学院天软微吧2楼(地铁三号线大学城站)
演讲主题(天津站)
《前端工程师的格局》| 《多租户Saas平台web实践》| 《Vue.js中的DOM和性能优化》| 《工程能力原子化的探索与实践》| 《数据可视化实践》| 《前端代码尺寸精简》
报名通道(天津站)
(扫描二维码报名)
2018 freeCodeConf 深圳站
【时间】2018 年11月10日(周六) 14:00-17:30
【地点】广东深圳深南大道10000号腾讯大厦
演讲主题(深圳站)
《Tencent Server Web 详解》| 《CSS GRIDS 终极探秘》| 《React Native 开发》等
报名通道(深圳站)
(扫描二维码报名)
2018 freeCodeConf 杭州站
【时间】2018 年11月10日(周六) 14:00-17:30
【地点】杭州市文一西路1818-2号中国人工智能小镇
演讲主题(杭州站)
《如何基于graphene开发智能合约》|《基于 VUE SSR 的前后端同构解决方案》等
报名通道(杭州站)
2018 freeCodeConf 上海站
【时间】2018 年11月10日(周六) 14:00-17:30
【地点】上海市静安区愚园路546号3号楼1层
演讲主题(上海站)
《从“零”做起的系统架构演进之路》| 《公有链开发技术分享:从共识算法到性能优化》等
报名通道(上海站)
2018 freeCodeConf 北京站
【时间】2018.11.10 下午 13:30 - 17:00
【地点】北京市朝阳区建国路108号海航实业大厦8、9层(氪空间-国贸海航实业大厦社区)
演讲主题(北京站)
《小程序对战功能实现及浅析排行榜》|《小程序的开发生态变迁》等
报名通道(北京站)
(扫描二维码报名)
2018 freeCodeConf 西安站
【时间】2018 年11月10日(周六) 14:00-18:00
【地点】西安市高新区沣惠南路36号橡树街区B-10101哈希屋HashHouse咖啡厅
演讲主题(西安站)
《Android 热修复背后的那些 Java 进阶知识》| 《Java 与未来发展职业规划》| 《如何阅读源代码》| 《可视化的遗留系统微服务改造》
报名通道(西安站)
(扫描二维码报名)
2018 freeCodeConf 广州站
【时间】2018.11.10 下午 13:30 - 17:00
【地点】广州市番禺区大学城青蓝街22号国家数字家庭应用示范产业基地B区(大学城北C出口)
报名通道(广州站)
(扫描二维码报名)
2018 freeCodeConf 济南站
【时间】2018 年11月10日(周六) 13:00-17:30
【地点】济南市高新区鑫盛大厦2号楼801
报名通道(济南站)
2018 freeCodeConf 郑州站
【时间】2018 年11月10日(周六) 14:00-17:30
【地点】上海市静安区愚园路546号3号楼1层
演讲主题(郑州站)
《互联网下半场产品核心竞争力--UED》| 《运营那点事儿》| 《如何成为一名合格的“接盘侠”》| 《VUE实践框架志iview》| 《大象圈到底是个什么圈?》
报名通道(郑州站)
(扫描二维码报名)
了解活动最新进展,请关注 freeCodeConf 大会官网 https://conf.freecodecamp.one
收起阅读 »
freeCodeConf 是 freeCodeCamp 中国举办的每年一度大型联动技术大会,旨在联合 freeCodeCamp 各城市本地开发者社区,促进高度垂直、高度人才集中、高度明星企业集结的互联网与软件行业技术交流。
第一届 freeCodeConf 将于2018年11月10日在上海、天津、深圳、成都、西安、武汉、北京、杭州、广州、重庆、郑州、济南全国12个城市同期举办,诚邀广大开发者共享技术盛宴。
大会亮点
一.12城同期举办
12个城市社区同期举办,促进本地开发者技术分享与交流
二.与大咖面对面交流
与数十位前端、区块链等领域的优秀开发者和技术负责人面对面交流
三.优质企业现场招聘
部分城市设置现场招聘环节,为企业和开发者搭建沟通桥梁
四.丰厚的礼品
华为云体验券、IBM开发者纪念品、技术书籍、个性胸章......各种精美礼品等你来拿!
演讲嘉宾
报名通道
2018 freeCodeConf 成都站
【时间】2018.11.10 9:00-18:00
【地点】天府大道北段 966 号天府国际金融中心 4 号楼 1 层 1 号会议厅
演讲主题(成都站)
《How does freeCodeCamp help people learn to code for free and become developers》|《给 Node.js 插上 C++ 的翅膀》|《开源项目维护》|《敏捷中国史》|《高效 H5 动画开发方式》|《高可用React服务端渲染》|《打造 Vue 组件库》
报名通道(成都站)
扫描二维码报名
2018 freeCodeConf 天津站
【时间】2018 年11月10日(周六) 13:00-18:00
【地点】天津市大学软件学院天软微吧2楼(地铁三号线大学城站)
演讲主题(天津站)
《前端工程师的格局》| 《多租户Saas平台web实践》| 《Vue.js中的DOM和性能优化》| 《工程能力原子化的探索与实践》| 《数据可视化实践》| 《前端代码尺寸精简》
报名通道(天津站)
(扫描二维码报名)
2018 freeCodeConf 深圳站
【时间】2018 年11月10日(周六) 14:00-17:30
【地点】广东深圳深南大道10000号腾讯大厦
演讲主题(深圳站)
《Tencent Server Web 详解》| 《CSS GRIDS 终极探秘》| 《React Native 开发》等
报名通道(深圳站)
(扫描二维码报名)
2018 freeCodeConf 杭州站
【时间】2018 年11月10日(周六) 14:00-17:30
【地点】杭州市文一西路1818-2号中国人工智能小镇
演讲主题(杭州站)
《如何基于graphene开发智能合约》|《基于 VUE SSR 的前后端同构解决方案》等
报名通道(杭州站)
2018 freeCodeConf 上海站
【时间】2018 年11月10日(周六) 14:00-17:30
【地点】上海市静安区愚园路546号3号楼1层
演讲主题(上海站)
《从“零”做起的系统架构演进之路》| 《公有链开发技术分享:从共识算法到性能优化》等
报名通道(上海站)
2018 freeCodeConf 北京站
【时间】2018.11.10 下午 13:30 - 17:00
【地点】北京市朝阳区建国路108号海航实业大厦8、9层(氪空间-国贸海航实业大厦社区)
演讲主题(北京站)
《小程序对战功能实现及浅析排行榜》|《小程序的开发生态变迁》等
报名通道(北京站)
(扫描二维码报名)
2018 freeCodeConf 西安站
【时间】2018 年11月10日(周六) 14:00-18:00
【地点】西安市高新区沣惠南路36号橡树街区B-10101哈希屋HashHouse咖啡厅
演讲主题(西安站)
《Android 热修复背后的那些 Java 进阶知识》| 《Java 与未来发展职业规划》| 《如何阅读源代码》| 《可视化的遗留系统微服务改造》
报名通道(西安站)
(扫描二维码报名)
2018 freeCodeConf 广州站
【时间】2018.11.10 下午 13:30 - 17:00
【地点】广州市番禺区大学城青蓝街22号国家数字家庭应用示范产业基地B区(大学城北C出口)
报名通道(广州站)
(扫描二维码报名)
2018 freeCodeConf 济南站
【时间】2018 年11月10日(周六) 13:00-17:30
【地点】济南市高新区鑫盛大厦2号楼801
报名通道(济南站)
2018 freeCodeConf 郑州站
【时间】2018 年11月10日(周六) 14:00-17:30
【地点】上海市静安区愚园路546号3号楼1层
演讲主题(郑州站)
《互联网下半场产品核心竞争力--UED》| 《运营那点事儿》| 《如何成为一名合格的“接盘侠”》| 《VUE实践框架志iview》| 《大象圈到底是个什么圈?》
报名通道(郑州站)
(扫描二维码报名)
了解活动最新进展,请关注 freeCodeConf 大会官网 https://conf.freecodecamp.one
收起阅读 »
据说这是18年最值得期待的产品经理大会@产品经理那些事
这是17年深圳产品经理大会。
这也是我们第一次,办这么大的线下活动。
一眨眼间一年过去了……
我们在北京、上海、广州、深圳、成都、杭州、厦门、武汉都建立了自己的分会,让全国各地产品伙伴都能连接在一起,提供产品人一个交流的平台。通过线上社区社群打卡学习、话题交流、发表文章、问答交流......
说出来你可能不相信,PMTalk2018年才一周岁呢!
那“周岁宴”我们要怎么过呢?
现在,我们想诚邀坐标深圳的各位产品经理们,欢聚一堂,一起倾(chī)心(dà)交(dàn)谈(gāo)!!
2018深圳产品经理大会,来了!!
私享级分享嘉宾、参会大咖云集、PMTalk创始故事大曝光、领到手软的礼品、超大型下午茶甜品盛宴、一起切超大的多层蛋糕、与PMTalk创始团队共进烛光晚餐……
这大概是PMTalk在今年举办的最最最最最最盛大的一次线下活动了!
那么这次的一周年盛会到底有什么看点,PMTalk产品经理社区又给各位小伙伴们邀请到了哪些大咖呢?请往下看!
让人看到就再也挪不动腿的下午茶
丰富的周边礼品
(只是冰山一角,神秘大奖我才不会现在就告诉你!)
礼品多到你抱不回家……
活动报名
2018深圳产品经理大会
报
名方式:点击活动报名即可报名。
平台含有限量早鸟票,可享受20元的优惠哦~拼手速赶紧抢啦!(截止时间:11月15日)
收起阅读 »
2018科大讯飞全球1024开发者节,来了!
10月24日-10月25日,安徽·合肥,2018届科大讯飞全球1024开发者节即将重磅开启,邀请MIT科技创新专家等大咖,还有10多场AI+分论坛、1000+AI产品等你体验。点击阅读原文,即刻进入官网抢票!
如果要用一样事物来形容科技,非光莫属。科技革新如光一般照亮我们的生活,让我们看到未来。
讯飞开放平台自2010年成立以来,目前已拥有83万开发者。我们一起在“追光之旅”中不断探索万物互联的缤纷世界,攀登AI之巅!
秉承“开放·合作·生态·共享”理念。2018届科大讯飞全球1024开发者节,我们为开发者们带来更多精彩。
今年1024,你将看到
1.AI领军人物齐聚前沿论坛
如果你想与AI商业化专家吴霁虹、斯坦福大学教授蒋里、科大讯飞董事长刘庆峰、AI大学校长胡郁、讯飞华南公司总裁杜兰、智慧医疗专家陶晓东、MIT35岁以下科技创新青年魏思等AI领袖面对面,请一定不要错过本届1024开发者节。
2.1024计划全新发布
2017届全球1024开发者节,“1024计划”重磅推出。从“AI教引”、“AI生态”、“AI公益”三个方面,辅导、赋能开发者,关注公益事业。
回顾:重磅 | 科大讯飞发布《1024计划》,AI点亮全球
今年开发者节,全新“1024计划”将现场公布,新的计划将:
更大规模:覆盖更宽广的领域,让更多用户感受到AI所带来的便捷!
更大力度:用更先进的AI科技,更全面的支持策略,赋能未来!
3.最强AI开发者现场终极PK
科大讯飞2018年面向全球正式发起首届“顶天立地”iFLYTEK AI开发者大赛。历时4个月,大赛吸引了1万余名世界各地的优秀开发者踊跃参加,共计收到全球3千多支团队提交参赛作品。
10月24日,应用开发AI挑战赛总决赛9支团队,方言种类识别AI挑战赛总决赛8支团队将在开发者节现场展开AI开发技能的巅峰对决和终极PK。
4.公益音乐节,以AI之名传递温暖
2018届科大讯飞全球1024开发者节,将延续“三声有幸”的公益主题,盛邀公益音乐人亲临,为AI献声!以歌声传达鼓舞人心的力量,用音乐唤醒人们对公益的认知。另外,现场还将有特别呈现的“公益天使”带来的的额外惊喜!
注:AI公益音乐节所有门票收入将全部用于讯飞“三声有幸”公益计划。
5.2大展区,50+展位,1000+黑科技
在AI体验区,我们准备了多个神秘环节,带给你突破想象力边界的AI旅程。我们用1000+AI黑科技展示对生活的思考。
在生态展区,科大讯飞集结自己的生态合作伙伴,在50多个展位上展示最前沿的AI落地产品和服务。覆盖企业服务、图像识别、健康医疗、公益、大数据、智能家居、机器人、智能营销、智能招聘……为你带来最in的场景化体验。
AIUI、讯飞医疗、AI大学、智慧城市……科技展区将带来科大讯飞各事业群的最新成果,从大型电信级应用到小型嵌入式应用;从手机到车载;从家电到玩具,你将体验能够满足不同应用环境的各种惊喜。
6.行业大咖做客,10+场AI细分领域论坛
医疗、客服、营销、家居、机器人、城市、AIoT……本届1024开发者节围绕等10多场AI+场景举行深度分论坛。
知名企业的资深大咖将与大家分享AI时代的见解,在圆桌互动交流彼此观点。我们将会展示讯飞和合作伙伴之间共创的各项核心赛道的AI风采,以科技之心为开发者赋能,用AI之光点亮全行业!
7.丰富互动,亲身参与“追光之旅”
你既可以与AI大咖零距离沟通,围绕AI发展进行思维碰撞,也可以现场与他人分享你对AI产品的使用体验。在2018届科大讯飞全球1024开发者节,你不仅是一名AI巅峰领域的见证者,更是追逐AI科技之光的参与者!
如何开启我的“追光之旅”?2018届全球1024开发者节购票通道已正式开启
贵宾票、嘉宾票、学生票、音乐节票任你选择
点击购买门票即刻抢票
想了解更多关于票务及优惠方面的信息,快添加AI小助手微信号:iFLYTEKAI
我们希望全球1024开发者节可以构建人工智能生态链接,打造科技文化独家品牌,影响全球人工智能开发者、爱好者,用人工智能共创美好世界!
2018届科大讯飞全球1024开发者节将用最新最全的人工智能技术和产品促进跨行业链接、多领域碰撞、新技术开发,以科技之光、生态之念,立足当下、放眼未来!
凡是过往,皆为序曲。AI+带来的无限可能,都在此刻埋下伏笔。1024,许你未来之光! 收起阅读 »
如果要用一样事物来形容科技,非光莫属。科技革新如光一般照亮我们的生活,让我们看到未来。
讯飞开放平台自2010年成立以来,目前已拥有83万开发者。我们一起在“追光之旅”中不断探索万物互联的缤纷世界,攀登AI之巅!
秉承“开放·合作·生态·共享”理念。2018届科大讯飞全球1024开发者节,我们为开发者们带来更多精彩。
今年1024,你将看到
1.AI领军人物齐聚前沿论坛
如果你想与AI商业化专家吴霁虹、斯坦福大学教授蒋里、科大讯飞董事长刘庆峰、AI大学校长胡郁、讯飞华南公司总裁杜兰、智慧医疗专家陶晓东、MIT35岁以下科技创新青年魏思等AI领袖面对面,请一定不要错过本届1024开发者节。
2.1024计划全新发布
2017届全球1024开发者节,“1024计划”重磅推出。从“AI教引”、“AI生态”、“AI公益”三个方面,辅导、赋能开发者,关注公益事业。
回顾:重磅 | 科大讯飞发布《1024计划》,AI点亮全球
今年开发者节,全新“1024计划”将现场公布,新的计划将:
更大规模:覆盖更宽广的领域,让更多用户感受到AI所带来的便捷!
更大力度:用更先进的AI科技,更全面的支持策略,赋能未来!
3.最强AI开发者现场终极PK
科大讯飞2018年面向全球正式发起首届“顶天立地”iFLYTEK AI开发者大赛。历时4个月,大赛吸引了1万余名世界各地的优秀开发者踊跃参加,共计收到全球3千多支团队提交参赛作品。
10月24日,应用开发AI挑战赛总决赛9支团队,方言种类识别AI挑战赛总决赛8支团队将在开发者节现场展开AI开发技能的巅峰对决和终极PK。
4.公益音乐节,以AI之名传递温暖
2018届科大讯飞全球1024开发者节,将延续“三声有幸”的公益主题,盛邀公益音乐人亲临,为AI献声!以歌声传达鼓舞人心的力量,用音乐唤醒人们对公益的认知。另外,现场还将有特别呈现的“公益天使”带来的的额外惊喜!
注:AI公益音乐节所有门票收入将全部用于讯飞“三声有幸”公益计划。
5.2大展区,50+展位,1000+黑科技
在AI体验区,我们准备了多个神秘环节,带给你突破想象力边界的AI旅程。我们用1000+AI黑科技展示对生活的思考。
在生态展区,科大讯飞集结自己的生态合作伙伴,在50多个展位上展示最前沿的AI落地产品和服务。覆盖企业服务、图像识别、健康医疗、公益、大数据、智能家居、机器人、智能营销、智能招聘……为你带来最in的场景化体验。
AIUI、讯飞医疗、AI大学、智慧城市……科技展区将带来科大讯飞各事业群的最新成果,从大型电信级应用到小型嵌入式应用;从手机到车载;从家电到玩具,你将体验能够满足不同应用环境的各种惊喜。
6.行业大咖做客,10+场AI细分领域论坛
医疗、客服、营销、家居、机器人、城市、AIoT……本届1024开发者节围绕等10多场AI+场景举行深度分论坛。
知名企业的资深大咖将与大家分享AI时代的见解,在圆桌互动交流彼此观点。我们将会展示讯飞和合作伙伴之间共创的各项核心赛道的AI风采,以科技之心为开发者赋能,用AI之光点亮全行业!
7.丰富互动,亲身参与“追光之旅”
你既可以与AI大咖零距离沟通,围绕AI发展进行思维碰撞,也可以现场与他人分享你对AI产品的使用体验。在2018届科大讯飞全球1024开发者节,你不仅是一名AI巅峰领域的见证者,更是追逐AI科技之光的参与者!
如何开启我的“追光之旅”?2018届全球1024开发者节购票通道已正式开启
贵宾票、嘉宾票、学生票、音乐节票任你选择
点击购买门票即刻抢票
想了解更多关于票务及优惠方面的信息,快添加AI小助手微信号:iFLYTEKAI
我们希望全球1024开发者节可以构建人工智能生态链接,打造科技文化独家品牌,影响全球人工智能开发者、爱好者,用人工智能共创美好世界!
2018届科大讯飞全球1024开发者节将用最新最全的人工智能技术和产品促进跨行业链接、多领域碰撞、新技术开发,以科技之光、生态之念,立足当下、放眼未来!
凡是过往,皆为序曲。AI+带来的无限可能,都在此刻埋下伏笔。1024,许你未来之光! 收起阅读 »
git push -f 是什么?应该怎么处理
我们的内网有使用gitlab作为我们的版本控制工具,最近组里出现了一次误操作,没有更新服务器的代码到本地仓库,直接使用git push -f 强制将本地的修改覆盖了远程仓库的版本,将其他人的commit都给冲掉了,而且无法使用通常的git reset方式回滚,因为使用 git log查看远程仓库的提交历史已经没有其他同事在这之前提交的commit记录了。
一般遇到这种情况,如果同事A将本地覆盖了远程,覆盖了同事B和同事C的commit,而同事B和C本地仓库依旧有他们的提交,这个时候同事B和C只需要同步一下远程,然后再git push -f一下他们的提交,这样就能将被覆盖的commit重新合并到远程仓库里面。
但是我遇到的情况比较特殊,因为当时同事B和同事C是在gitlab的网页版直接编辑的文件,并通过Gitlab提交,也就是说所有人本地都没有同事B和同事C提交的内容,这个时候同事A使用git push -f直接就冲掉了记录,所以就没办法通过上面的办法来回滚了。
废话那么多,下面记录下找回的过程:
1 场景
```bash
Original:
(remote origin:)
branch master -> commit 111111
(local)
branch master -> commit 22222
After git push -f:
(remote origin:)
branch master -> commit 22222
```
2 找回步骤
(1)这个方法的前提是你有权限登陆部署了Gitlab的服务器,我们需要找到Gitlab保存仓库的目录,首先通过ssh登陆上Gitlab的服务器,然后找到gitlab的存放仓库的地方,默认是在```/var/opt/gitlab/git-data/repositories```。
在这个目录下找到自己要回滚的仓库,并cd到该仓库。
(2)在执行回滚操作前一定要先进行仓库备份:
```bash
tar cvzf project-backup.tgz /path/to/project.git
```
备份好之后才可以进行下面的操作。
首先gitlab的仓库的目录是这样的:
```
config description HEAD hooks hooks.old.xxx info objects refs
```
在当前目录使用```git fsck```工具找回上次执行的危险操作,直接执行```git fsck```命令,该命令显示所有未被其他对象引用 (指向) 的所有对象,会有如下输出:
```bash
dangling commit ab1afef80fac8e34258ff41fc1b867c702daa24b
```
ab1afef80fac8e34258ff41fc1b867c702daa24就是可能被丢弃的commit,也就是被冲掉的commit,具体是自己想要的哪个,可以:
```
git log ab1afef80fac8e34258ff41fc1b867c702daa24
```
查看在这之前的commit历史,找到自己想要回滚的commit。
(3)找到了想要回滚的commit 哈希值,是不是可以在本地仓库执行
```
git reset --HEAD ab1afef80fac8e34258ff41fc1b867c702daa24
```
实现回滚了呢?答案是不可以的,因为仓库关于被覆盖的对象已经被清除了,所以clone下来的仓库是没有被覆盖的对象可以回滚的,所以回滚操作还是需要在gitlab的实际仓库里操作。
```
echo ab1afef80fac8e34258ff41fc1b867c702daa24 > refs/heads/master
```
将commit 哈希值直接添加到refs/heads/master文件里,然后克隆远程仓库到本地,你会发现以前的commit又神奇的回来了。
具体的原理需要大家自己去了解git的原理,参考这几篇文章:
https://git-scm.com/book/zh/v1/Git-内部原理-维护及数据恢复
https://superuser.com/questions/297973/how-can-i-recover-from-an-accidental-git-push-f/298015#298015
希望对大家有所帮助。
Regards,
codjust
收起阅读 »
《AI企业级应用产品实力榜单》:环信机器人实力上榜
企服行业头条组织筹办的《AI企业级应用产品实力榜单》对外发榜,从众多报名的产品中,经过层层筛选及评审打分,“环信机器人”凭借深厚的AI技术沉淀以及在保险、教育等领域的大规模落地获得上榜。
作为企业服务领域的资深观察者,企服行业头条一直关注企服领域内的动态,这次组织筹办《AI企业级应用产品实力榜单》,希望寻找有价值的优秀企业级产品,传递企业服务的商业价值和趋势。
榜单综述
企服行业头条根据上榜产品在人工智能方面的特点,把产品划分为几个大类。包括例如商业智能和解决方案类、计算机视觉类、人机交互类等等。
现在,企服君又进一步标注出了这50个产品所处的细分行业,试图进一步挖掘榜单背后蕴藏的行业发展趋势。
在细分行业统计中:纯AI行业产品有25个;信息安全+AI有2个;大数据+AI有11个;云计算+AI有1个;行业SaaS+AI有7个;其他行业+AI有4个。
从行业分布比例图可以很明显看出,虽然此次企服行业头条评选的是企业级AI产品,但细分行业却并非100%全部来自人工智能领域。
不仅如此,榜单甚至一半(50%)的产品都来自非AI行业。而这也恰恰说明,现在越来越多企业开始主动拥抱人工智能技术,人工智能与各个行业的融合也越发紧密。
接下来,企服君就该榜单的典型产品“环信机器人”进行解读。
环信机器人:
AI1.0——4.0,环信机器人全貌展示AI客服发展史
近年,基于规则和传统机器学习的AI 1.0兴起,随着Google等科技大鳄以及资本的推波助澜,基于算法升级的深度学习AI 2.0时代慢慢开始改造客服行业,准确率和召回率(覆盖率)均取得了大幅度提升。到了AI 3.0时代,从单点的文字机器人产品出发实现了全工具链改造,这个阶段环信智能IVR、智能质检、人机协作、环信智能外呼机器人等产品和服务应运而生,到了AI 4.0大规模落地时代,环信又发布了以保险行业为首的四大行业解决方案,环信机器人产品的发展史很好的全貌展示了AI改造中国客服行业的发展历程。
作为自然语言理解技术最先实现商业化落地的领域,智能客服行业吸引了众多市场玩家争相布局。从传统呼叫中心厂商、到SaaS云客服公司、再到客服机器人公司,各类企业都在积极用AI为客服赋能。那么,现阶段什么样的智能客服产品真正实现了大规模落地,帮助企业规模化的优化了生产力结构,助力客服中心从成本中心往利润中心演变,环信机器人4.0似乎已经给出了答案。
对于智能客服赛道上的三类公司,环信CEO刘俊彦用“刀架”和“刀片”做了一个形象的比喻:基础客服系统是刀架,特点是高粘性业务系统,替换成本高,竞争激励毛利相对较低;AI是刀片,特点是技术壁垒高,毛利高,但不能脱离刀架单独存在。传统呼叫中心厂商缺乏一个多租户的、基于云架构的在线客服系统,因此刀架尚不完备;客服机器人厂商从做刀片起家,目前正在竭力补足刀架短板;而像环信这样的云客服厂商先有刀架,后磨砺刀片,能够为客户提供全套智能客服解决方案。
环信机器人4.0,低成本可持续运营的客服机器人解决方案
环信机器人4.0致力于为企业提供一套低成本可持续运营的客服机器人解决方案。4.0版本新推出了三大平台级应用(环信AIROOT运营平台、环信AIROOT Pro训练师平台、环信中文语义计算平台)和保险行业智能机器人解决方案,一举将AI在客服行业的传统单点型产品全面升级为体系化整体解决方案。
环信AIROOT运营平台是一个面向客服团队的简单、智能、低成本可持续运营的客服机器人运营平台。通过打造可快速上手的运营体系,建设快速落地的学习闭环。AIROOT提供了机器人运营环节所有涉及到的常用功能,包括:完整的服务数据产品、运维支撑产品、服务运营产品、机器人知识库、知识运营体系产品,使得平台拥有业界领先的知识构建、知识运营和服务交付能力,并且有效降低使用的难度与成本。
针对诸如100坐席+、日咨询会话1万+、知识点1000以上的大型客户,环信AIROOT可以帮助企业实现1-2周完成知识构建快速上线,并在后续的知识持续运营中实现“1天+1人+2小时”完成的运营工作量。保障广大企业真正实现智能客服机器人的“买得起、用得起”的愿望。
环信AIROOT Pro 训练师平台是一个面向专业知识训练师的专业、高效深度调优的客服机器人运营平台。AIROOT Pro通过完整专业的算法优化调优平台,全面打通从“数据清洗”数据标注””模型训练””效果评测””用户反馈”的完整学习闭环,使得能力调优环节中的大批量数据生产、训练以及模型迭代成为可能。并利用环信中文语义计算平台提供的AI能力,全面提升调优过程中的工具智能化程度,有效降低成本。
经实际落地评测,在无需机器人厂商的算法人员、软件工程师、数据工程师参与的情况下,基于AIROOT Pro的训练相较于普通算法平台的能力调优整体效率提升30%以上,客服机器人知识建设周期整体缩短20%。
环信中文语义计算平台是一个面向开发者的自然语言处理(NLP)的能力开放平台。环信中文语义计算平台在经过了近三年的核心算法能力提升和迭代后,在开放NLP基础算法能力的同时,也完整开放了在NLP领域经常使用的应用级别算法能力诸如:纠错、情感分析、意图识别、语义相似度计算等,帮助开发者们在NLP领域的应用环节具有更强的能力,快速应用落地在更多价值场景。
经实际落地评测,环信中文语义计算平台的基于深度学习的意图分类算法在多个测试集上达到了95%准确率的业界领先水平,语义相似度计算到达了93%准确率的业界领先水平。
环信一直秉承着AI必须通过行业解决方案落地帮助企业提能增效解决实际业务问题来体现生产力。基于环信在保险领域的深入积累,环信率先发布了环信机器人保险行业解决方案。环信机器人保险行业解决方案致力于为保险行业提供开箱可用的AI能力,针对寿险、财险保险智能客服、智能IVR、产品推荐、代理人支持等领域的提供了全套综合智能化保险解决方案。以环信机器人保险行业解决方案中的寿险机器人为例,环信的寿险机器人在业界领先的算法和工程能力基础上,还预装了全套寿险服务知识图谱,涵盖了从保险百科到理赔核保的数百个服务场景下的数千条知识图谱,并与业内主流保险核心业务系统预集成打通,真正做到了开箱可用的行业AI能力。
经过近三年的迭代发展,环信机器人已经在保险、证券、教育、物流、银行、电信运营商、航空等领域树立了一批标杆客户,包括新东方、泰康在线、中意人寿、中信证券、长江证券、天津农商行、南京银行、中通快递、厦门航空等。
客户服务作为现代企业和消费者沟通的核心环节,年产业规模高达5000亿元。对于企业来说,如何利用AI新技术、新产品最大化客服价值,如何把客服这个成本中心转化为利润中心,已成为当务之急,或许人工智能产业化改造整个客服行业的奇点已经来临!
收起阅读 »
环信机器人上榜《AI企业级应用产品实力榜单》
作为企业服务领域的资深观察者,企服行业头条一直关注企服领域内的动态,这次组织筹办《AI企业级应用产品实力榜单》,希望寻找有价值的优秀企业级产品,传递企业服务的商业价值和趋势。
榜单综述
企服行业头条根据上榜产品在人工智能方面的特点,把产品划分为几个大类。包括例如商业智能和解决方案类、计算机视觉类、人机交互类等等。
现在,企服君又进一步标注出了这50个产品所处的细分行业,试图进一步挖掘榜单背后蕴藏的行业发展趋势。
在细分行业统计中:纯AI行业产品有25个;信息安全+AI有2个;大数据+AI有11个;云计算+AI有1个;行业SaaS+AI有7个;其他行业+AI有4个。
从行业分布比例图可以很明显看出,虽然此次企服行业头条评选的是企业级AI产品,但细分行业却并非100%全部来自人工智能领域。
不仅如此,榜单甚至一半(50%)的产品都来自非AI行业。而这也恰恰说明,现在越来越多企业开始主动拥抱人工智能技术,人工智能与各个行业的融合也越发紧密。
接下来,企服君就该榜单的典型产品“环信机器人”进行解读。
环信机器人:
AI1.0——4.0,环信机器人全貌展示AI客服发展史
近年,基于规则和传统机器学习的AI 1.0兴起,随着Google等科技大鳄以及资本的推波助澜,基于算法升级的深度学习AI 2.0时代慢慢开始改造客服行业,准确率和召回率(覆盖率)均取得了大幅度提升。到了AI 3.0时代,从单点的文字机器人产品出发实现了全工具链改造,这个阶段环信智能IVR、智能质检、人机协作、环信智能外呼机器人等产品和服务应运而生,到了AI 4.0大规模落地时代,环信又发布了以保险行业为首的四大行业解决方案,环信机器人产品的发展史很好的全貌展示了AI改造中国客服行业的发展历程。
作为自然语言理解技术最先实现商业化落地的领域,智能客服行业吸引了众多市场玩家争相布局。从传统呼叫中心厂商、到SaaS云客服公司、再到客服机器人公司,各类企业都在积极用AI为客服赋能。那么,现阶段什么样的智能客服产品真正实现了大规模落地,帮助企业规模化的优化了生产力结构,助力客服中心从成本中心往利润中心演变,环信机器人4.0似乎已经给出了答案。
对于智能客服赛道上的三类公司,环信CEO刘俊彦用“刀架”和“刀片”做了一个形象的比喻:基础客服系统是刀架,特点是高粘性业务系统,替换成本高,竞争激励毛利相对较低;AI是刀片,特点是技术壁垒高,毛利高,但不能脱离刀架单独存在。传统呼叫中心厂商缺乏一个多租户的、基于云架构的在线客服系统,因此刀架尚不完备;客服机器人厂商从做刀片起家,目前正在竭力补足刀架短板;而像环信这样的云客服厂商先有刀架,后磨砺刀片,能够为客户提供全套智能客服解决方案。
环信机器人4.0,低成本可持续运营的客服机器人解决方案
环信机器人4.0致力于为企业提供一套低成本可持续运营的客服机器人解决方案。4.0版本新推出了三大平台级应用(环信AIROOT运营平台、环信AIROOT Pro训练师平台、环信中文语义计算平台)和保险行业智能机器人解决方案,一举将AI在客服行业的传统单点型产品全面升级为体系化整体解决方案。
环信AIROOT运营平台是一个面向客服团队的简单、智能、低成本可持续运营的客服机器人运营平台。通过打造可快速上手的运营体系,建设快速落地的学习闭环。AIROOT提供了机器人运营环节所有涉及到的常用功能,包括:完整的服务数据产品、运维支撑产品、服务运营产品、机器人知识库、知识运营体系产品,使得平台拥有业界领先的知识构建、知识运营和服务交付能力,并且有效降低使用的难度与成本。
针对诸如100坐席+、日咨询会话1万+、知识点1000以上的大型客户,环信AIROOT可以帮助企业实现1-2周完成知识构建快速上线,并在后续的知识持续运营中实现“1天+1人+2小时”完成的运营工作量。保障广大企业真正实现智能客服机器人的“买得起、用得起”的愿望。
环信AIROOT Pro 训练师平台是一个面向专业知识训练师的专业、高效深度调优的客服机器人运营平台。AIROOT Pro通过完整专业的算法优化调优平台,全面打通从“数据清洗”数据标注””模型训练””效果评测””用户反馈”的完整学习闭环,使得能力调优环节中的大批量数据生产、训练以及模型迭代成为可能。并利用环信中文语义计算平台提供的AI能力,全面提升调优过程中的工具智能化程度,有效降低成本。
经实际落地评测,在无需机器人厂商的算法人员、软件工程师、数据工程师参与的情况下,基于AIROOT Pro的训练相较于普通算法平台的能力调优整体效率提升30%以上,客服机器人知识建设周期整体缩短20%。
环信中文语义计算平台是一个面向开发者的自然语言处理(NLP)的能力开放平台。环信中文语义计算平台在经过了近三年的核心算法能力提升和迭代后,在开放NLP基础算法能力的同时,也完整开放了在NLP领域经常使用的应用级别算法能力诸如:纠错、情感分析、意图识别、语义相似度计算等,帮助开发者们在NLP领域的应用环节具有更强的能力,快速应用落地在更多价值场景。
经实际落地评测,环信中文语义计算平台的基于深度学习的意图分类算法在多个测试集上达到了95%准确率的业界领先水平,语义相似度计算到达了93%准确率的业界领先水平。
环信一直秉承着AI必须通过行业解决方案落地帮助企业提能增效解决实际业务问题来体现生产力。基于环信在保险领域的深入积累,环信率先发布了环信机器人保险行业解决方案。环信机器人保险行业解决方案致力于为保险行业提供开箱可用的AI能力,针对寿险、财险保险智能客服、智能IVR、产品推荐、代理人支持等领域的提供了全套综合智能化保险解决方案。以环信机器人保险行业解决方案中的寿险机器人为例,环信的寿险机器人在业界领先的算法和工程能力基础上,还预装了全套寿险服务知识图谱,涵盖了从保险百科到理赔核保的数百个服务场景下的数千条知识图谱,并与业内主流保险核心业务系统预集成打通,真正做到了开箱可用的行业AI能力。
经过近三年的迭代发展,环信机器人已经在保险、证券、教育、物流、银行、电信运营商、航空等领域树立了一批标杆客户,包括新东方、泰康在线、中意人寿、中信证券、长江证券、天津农商行、南京银行、中通快递、厦门航空等。
客户服务作为现代企业和消费者沟通的核心环节,年产业规模高达5000亿元。对于企业来说,如何利用AI新技术、新产品最大化客服价值,如何把客服这个成本中心转化为利润中心,已成为当务之急,或许人工智能产业化改造整个客服行业的奇点已经来临!
收起阅读 »
【环信征文】我做面试官的第三个故事
有一天Java主管江总拿着一份简历找我协助他面试一个自称既懂Java又懂Android的人
江总:你Android和Java都会?来面试Android还是Java?
他:(我猜他打量了6年经验的江总和4年经验的我之后选了一个好对付的)我最近今年都在搞Android,还是面试Android吧!
我:(看他的简历和面试登记表上面的信息出入有点大,心里就有底了)请简单介绍下自己吧
他:balah……balah……(就像《海贼王》有漫画版、动画版和国内百度贴吧版三种剧情一样,他的工作经历也有打印简历版、面试登记表手写版和面试口述版三种)
我:(知道他是什么牛鬼蛇神了,一上来就让他出丑)你能说说Fragment的生命周期吗?
他:Fragment的生命周期……emmm……就是和Activity的生命周期差不多嘛
我:这儿有纸笔,你默写Activity的生命周期吧
他:(边嘟囔边写)先是onCreate()……emmm……还有onResume()……onStart()和onDestroy()
我:Activity的生命周期不是写出来了吗?再好好想想Fragment的生命周期是啥样的
他:就是在Activity的onCreate()上边加上onActivityCreate()和onAttach()……emmm……onDestroy()下边再加上onDestroyView()吧~
我:在什么情况下Fragment的onResume()方法执行?
他:Fragment从不可见到可见的情况下
我:同一个Activity里,从A Fragment切换到B Fragment,然后从B切换到A,A执行了onResume()吗?
他:那当然执行咯~
我:我再问问你View事件分发机制吧
他:(感觉死耗子终于来找瞎猫了)这个我知道,就是从最外层的ViewGroup……
我:(打断他的话)ACTION_DOWN和ACTION_UP总是成对出现吗?换句话说,一个View触发了ACTION_DOWN之后,一定会触发ACTION_UP吗?反过来,一个View触发了ACTION_UP之前,它一定先触发ACTION_DOWN吗?
他:那是一定的,ACTION_UP是手指离开屏幕时触发的……
我:(打断他的话)按住A View,拖动到B View,松手,A和B也都触发了ACTION_UP和ACTION_DOWN吗?
他:这个……emmm……我不太清楚哈~
我:(偷偷看一眼江总,江总正在冷笑摇头)我再考考你Java基础吧,ArrayList和LinkedList,哪个线程安全?哪个线程不安全?
他:ArrayList线程不安全,LinkedList线程安全
我:那你知道线程池有哪几种吗?
他:有四种,就是有带缓存的……emmm……还有别的~
我:(看他简历上的银行项目负责人的工作经历)你长期从事银行、金融软件开发,记录存款和消费金额之类和钱账有关的数据用那种数据类型呀?
他:当然用float了,精确度比double高嘛
我:double精度比float低吗?
他:那当然啦,double只精确到小数点后两位嘛,double在英语里就是二的意思
我:你现在拿出手机查查
他:(用手机查了一会)emmm……我刚才口误,我们记钱账的数据类型是double
我:那你听说过BigDecimal吗?
江总:(此时的鄙夷表情已经和相亲时候拜金女听见“租房”关键词时候的表情一样了)你还想继续面Android吗?
他:不,不,我Java经验更丰富些,还是面Java吧
江总:拿你对Spring Cloud了解多少?
他:balah……balah……(说了一大堆驴唇不对马嘴的东西)
江总:好吧,今天的面试就到这里,有消息我们会通知你
我:(送他出去之后)你知道他简历造假的破绽有多明显吗?
江总:他简历上的毕业年份和填表上的不一样
我:我连他以前什么职业都知道,他是送外卖的或者工地上下苦力的,培训几个月出来骗人了
江总:你怎么知道的?
我:常年室内工作的人脸没那么黑
后记:他被我介绍给了一个“工资一开始没那么高,我看你做得好以后会给你股份”的“创业公司CEO”做技术扶贫去了,也不知道他俩到底谁坑了谁
往期推荐:
今天面试一个自称两年经验的Android
我又做了一次面试官 收起阅读 »
江总:你Android和Java都会?来面试Android还是Java?
他:(我猜他打量了6年经验的江总和4年经验的我之后选了一个好对付的)我最近今年都在搞Android,还是面试Android吧!
我:(看他的简历和面试登记表上面的信息出入有点大,心里就有底了)请简单介绍下自己吧
他:balah……balah……(就像《海贼王》有漫画版、动画版和国内百度贴吧版三种剧情一样,他的工作经历也有打印简历版、面试登记表手写版和面试口述版三种)
我:(知道他是什么牛鬼蛇神了,一上来就让他出丑)你能说说Fragment的生命周期吗?
他:Fragment的生命周期……emmm……就是和Activity的生命周期差不多嘛
我:这儿有纸笔,你默写Activity的生命周期吧
他:(边嘟囔边写)先是onCreate()……emmm……还有onResume()……onStart()和onDestroy()
我:Activity的生命周期不是写出来了吗?再好好想想Fragment的生命周期是啥样的
他:就是在Activity的onCreate()上边加上onActivityCreate()和onAttach()……emmm……onDestroy()下边再加上onDestroyView()吧~
我:在什么情况下Fragment的onResume()方法执行?
他:Fragment从不可见到可见的情况下
我:同一个Activity里,从A Fragment切换到B Fragment,然后从B切换到A,A执行了onResume()吗?
他:那当然执行咯~
我:我再问问你View事件分发机制吧
他:(感觉死耗子终于来找瞎猫了)这个我知道,就是从最外层的ViewGroup……
我:(打断他的话)ACTION_DOWN和ACTION_UP总是成对出现吗?换句话说,一个View触发了ACTION_DOWN之后,一定会触发ACTION_UP吗?反过来,一个View触发了ACTION_UP之前,它一定先触发ACTION_DOWN吗?
他:那是一定的,ACTION_UP是手指离开屏幕时触发的……
我:(打断他的话)按住A View,拖动到B View,松手,A和B也都触发了ACTION_UP和ACTION_DOWN吗?
他:这个……emmm……我不太清楚哈~
我:(偷偷看一眼江总,江总正在冷笑摇头)我再考考你Java基础吧,ArrayList和LinkedList,哪个线程安全?哪个线程不安全?
他:ArrayList线程不安全,LinkedList线程安全
我:那你知道线程池有哪几种吗?
他:有四种,就是有带缓存的……emmm……还有别的~
我:(看他简历上的银行项目负责人的工作经历)你长期从事银行、金融软件开发,记录存款和消费金额之类和钱账有关的数据用那种数据类型呀?
他:当然用float了,精确度比double高嘛
我:double精度比float低吗?
他:那当然啦,double只精确到小数点后两位嘛,double在英语里就是二的意思
我:你现在拿出手机查查
他:(用手机查了一会)emmm……我刚才口误,我们记钱账的数据类型是double
我:那你听说过BigDecimal吗?
江总:(此时的鄙夷表情已经和相亲时候拜金女听见“租房”关键词时候的表情一样了)你还想继续面Android吗?
他:不,不,我Java经验更丰富些,还是面Java吧
江总:拿你对Spring Cloud了解多少?
他:balah……balah……(说了一大堆驴唇不对马嘴的东西)
江总:好吧,今天的面试就到这里,有消息我们会通知你
我:(送他出去之后)你知道他简历造假的破绽有多明显吗?
江总:他简历上的毕业年份和填表上的不一样
我:我连他以前什么职业都知道,他是送外卖的或者工地上下苦力的,培训几个月出来骗人了
江总:你怎么知道的?
我:常年室内工作的人脸没那么黑
后记:他被我介绍给了一个“工资一开始没那么高,我看你做得好以后会给你股份”的“创业公司CEO”做技术扶贫去了,也不知道他俩到底谁坑了谁
往期推荐:
今天面试一个自称两年经验的Android
我又做了一次面试官 收起阅读 »
基操!坐下!勿6!5分钟搭建小程序直播
今天你看直播了吗?拥有10亿微信生态用户的小程序已经成为了继移动互联后的又一个现象级风口,随着微信小程序对外开放实时音视频录制及播放等更多连接能力,小程序与直播强强联合,在各行各业找到了非常多的玩法,小程序直播相比微信直播和APP直播更加简洁、流畅、低延时、多入口等众多优势迅速向商业直播领域及泛娱乐直播领域蔓延。从小游戏、内容付费、工具、大数据、社交电商创业者到传统品牌商们,都在努力搭上小程序直播这辆快车,以免错过微信生态里新的流量洼地。
作为一名环信生态圈资深开发者,本着对技术的热衷,对环信的眷恋和对党的忠诚,基于环信即时通讯云写了这个“直播购物小程序”,目前项目源码已全部免费开放,希望对有需求的企业和开发者提供一个思路和参考。
直播购物小程序运行GIF效果图
小程序体验指南(仅需三步):
1.下载小程序直播源码:github源码
2.下载微信小程序开发工具:小程序开发工具下载地址
3.导入源码:将附件的源码解压直接导入
就是这么简单,还有问题?我们准备了视频教程!
手把手教学直播小程序开发【环信公开课35期】:公开课回放地址
公开课视频截图
课程大纲:
环信直播技术流程
1、【环信账号准备】注册、认证。
2、【直播间创建】环信后台创建及api 方式创建
3、【房间主播及推拉流地址设置】创建直播,主播绑定房间,房间设置直播地址。
小程序技术分享
1、【创建小程序项目】创建好已有UI的小程序项目。
2、【环信小程序sdk集成】基于webIM sdk 开发的小程序版本SDK集成配置。
3、【配置小程序进行直播推拉流测试】模拟直播间,使用推流工具进行推流测试。
4、【开发直播间聊天功能及特效】利用小程序SDK进行直播聊天室聊天开发。弹幕、特效延时处理。
附:环信小程序直播开发文档文档地址
关于环信直播聊天室:
1、采用支持高并发的异步架构,轻松应对千万级并发请求; 各项基础服务集群化,确保系统高可用性; 系统冗余度高,容量评估体系完善,弹性扩容应对流量峰值;
2、支持各种消息格式:文字、表情、图片、声音、视频、附件、位置、扩展消息;
3、支持实时配置的消息分级策略,确保重要消息优先必达;
4、支持直播聊天室后台管理及审核功能,提供直播相关数据统计;
5、提供智能反垃圾和自定义敏感词过滤功能;
6、快速集成,demo提供高质量代码示例,可根据运营情况随时扩展;
7、聊天室人数无上限 收起阅读 »
环信公开课35期回放:手把手教学直播小程序开发
微信小程序越来越多的新功能不断上线,流量入口的不断增加,俨然成为互联网公司“必争之地”。 传统的公众号第三方直播由于入口较深,用户次使用路径复杂,很难培养用户的使用习惯;微信小程序入口便捷使用方便,极大提升了用户体验。公开课第35期视频回放
直播商业化最困扰的地方在于哪里?流量!微信有庞大的低成本的流量可以帮助商家获取和转化用户。小程序直播可以说是商业化用户最好的工具。直播和购物的结合就是其中一个较好的表现形式。
8月8日,环信公开课邀请了小程序开发业界大牛席海江老师,分享了全国首个小程序直播源码,在公开课上手把手教学从零开始搭建一套基于环信的小程序直播平台。
公开课回放视频
直播购物小程序运行GIF效果图
席海江老师演示集成环信小程序直播
课程回顾:
环信直播技术流程
1、【环信账号准备】注册、认证。
2、【直播间创建】环信后台创建及api 方式创建
3、【房间主播及推拉流地址设置】创建直播,主播绑定房间,房间设置直播地址。
小程序技术分享
1、【创建小程序项目】创建好已有UI的小程序项目。
2、【环信小程序sdk集成】基于webIM sdk 开发的小程序版本SDK集成配置。
3、【配置小程序进行直播推拉流测试】模拟直播间,使用推流工具进行推流测试。
4、【开发直播间聊天功能及特效】利用小程序SDK进行直播聊天室聊天开发。弹幕、特效延时处理。
课程亮点:全国首个直播购物小程序源码分享,手把手搭建基于环信的直播购物小程序。
课程形式:网络直播 全国各地区、联网电脑、联网手机均可免费参与。
感谢合作伙伴APICLloud,APKBUS,码客,七牛云,客户世界对本期环信公开课的大力支持,环信公开课合作、课程订阅请添加公开课小助手微信:huanixn-hh
强烈推荐:每周三下午3点环信IM在线培训,工程师现场答疑,还有定制T恤送出,观看地址环信公开课 收起阅读 »
环信 ios 发不了录音
发送block 返回 code 401 描述是 录音文件地址+ too small 接手别人的代码 一步一个坑 环信还没技术支持 以前还有在线支持的 求解
2018安卓巴士开发者大会【技术之声 改变世界】
2018安卓巴士开发者大会,是中国最具前沿性、专业性的安卓技术会议。由安卓巴士技术社区首次发起并组织的安卓线下交流大会,集结500位安卓开发,与你一起交流学习,探讨行业动态。
本次上海作为首站,将盛邀数位业内技术大咖为开发者们带来最高质量的技术分享和丰富的现场互动体验项目,让参会者在得到业务成长的同时还能知晓行业动态、结识同僚并享受活动带来的特别体验。
报名链接:http://www.hdb.com/dis/fmjhe7y02o
付费说明:
本次活动经费用于场租、设备、物料、礼品等。
早鸟票¥199,享受活动五折优惠。
标准票¥399,购此票种开发者凭大会当天凭二维码签到可得安卓开发书籍一本。
团购票¥798,此票为套票3张,即三人成行一人免单,经济实惠。
VIP票¥599,此票座位为会场前排、赠高级礼品、有提问机会、可加入讲师群。
活动须知:
请各位小伙伴认真填写所有的报名资料用于审核使用,由于场地位置有限和为了保证活动质量,活动当天拒绝空降,现场签到凭报名成功二维码,谢谢配合!
活动路线:
地铁路线:地铁9号线金桥站2号口骑行1公里。
公交:1045路、浦东27路、上川专线新金桥路唐陆路站下步行418米。
驾车路线:收费停车场位于唐陆公路与新金桥路交叉口西南150米。(前40名可拥有免费停车券)
收起阅读 »
本次上海作为首站,将盛邀数位业内技术大咖为开发者们带来最高质量的技术分享和丰富的现场互动体验项目,让参会者在得到业务成长的同时还能知晓行业动态、结识同僚并享受活动带来的特别体验。
报名链接:http://www.hdb.com/dis/fmjhe7y02o
付费说明:
本次活动经费用于场租、设备、物料、礼品等。
早鸟票¥199,享受活动五折优惠。
标准票¥399,购此票种开发者凭大会当天凭二维码签到可得安卓开发书籍一本。
团购票¥798,此票为套票3张,即三人成行一人免单,经济实惠。
VIP票¥599,此票座位为会场前排、赠高级礼品、有提问机会、可加入讲师群。
活动须知:
请各位小伙伴认真填写所有的报名资料用于审核使用,由于场地位置有限和为了保证活动质量,活动当天拒绝空降,现场签到凭报名成功二维码,谢谢配合!
活动路线:
地铁路线:地铁9号线金桥站2号口骑行1公里。
公交:1045路、浦东27路、上川专线新金桥路唐陆路站下步行418米。
驾车路线:收费停车场位于唐陆公路与新金桥路交叉口西南150米。(前40名可拥有免费停车券)
收起阅读 »
环信即时通讯云V3.5.0更新--多版本实时音视频发布,总有一款适合你
摘要: 为满足不同场景需求,3.5.0版本开始将实时音视频会议划分了不同的类型,不同类型对应了不同场景,使你能够轻松地将实时音视频功能集成到你的应用或者网站中。
iOS SDK 更新日志
版本 V3.5.0 2018-08-13
新功能:
- 为满足不同场景需求,3.5.0版本开始将实时音视频会议划分了不同的类型,不同类型对应了不同场景,使你能够轻松地将实时音视频功能集成到你的应用或者网站中。在创建会议时可以传入以下几种类型:
- 优化实时音视频多人会议功能
- 为满足不同场景需求,3.5.0版本开始将实时音视频会议划分了不同的类型,不同类型对应了不同场景,使你能够轻松地将实时音视频功能集成到你的应用或者网站中。类型如下:
1. Communication:普通通信会议,最多支持参会者6人,会议里的每个参会者都可以自由说话和发布视频,该会议类型在服务器不做语音的再编码,音质最好,适用于远程医疗,在线客服等场景;
2. Large Communication:大型通信会议,最多参会者30人,会议里的每个参会者都可以自由说话,最多支持6个人发布视频,该会议模式在服务器做混音处理,支持更多的人说话,适用于大型会议等场景;
3. Live:互动视频会议,会议里支持最多6个主播和600个观众,观众可以通过连麦与主播互动,该会议类型适用于在线教育,互动直播等场景。
版本历史:Android SDK更新日志 ios SDK更新日志
下载地址:SDK下载 收起阅读 »
图解View测量、布局及绘制原理
来自https://www.jianshu.com/p/3d2c49315d68
Android中自定义View一直是一个高级的技能,入门比较难,看起来很高大上。想要学会自定义View,当然要理解View的测量、布局及绘制原理,本篇文章将以图表的形式讲解View的测量、布局及绘制原理。
一、View绘制的流程框架
View的绘制是从上往下一层层迭代下来的。DecorView-->ViewGroup(--->ViewGroup)-->View ,按照这个流程从上往下,依次measure(测量),layout(布局),draw(绘制)。关于DecorView,可以看这篇文章。
二、Measure流程
顾名思义,就是测量每个控件的大小。
调用measure()方法,进行一些逻辑处理,然后调用onMeasure()方法,在其中调用setMeasuredDimension()设定View的宽高信息,完成View的测量操作。
如果有了widthMeasureSpec, heightMeasureSpec,通过一定的处理(可以重写,自定义处理步骤),从中获取View的宽/高,调用setMeasuredDimension()方法,指定View的宽高,完成测量工作。
MeasureSpec的确定
先介绍下什么是MeasureSpec?
MeasureSpec由两部分组成,一部分是测量模式,另一部分是测量的尺寸大小。
其中,Mode模式共分为三类
UNSPECIFIED :不对View进行任何限制,要多大给多大,一般用于系统内部
EXACTLY:对应LayoutParams中的match_parent和具体数值这两种模式。检测到View所需要的精确大小,这时候View的最终大小就是SpecSize所指定的值,
AT_MOST :对应LayoutParams中的wrap_content。View的大小不能大于父容器的大小。
那么MeasureSpec又是如何确定的?
对于DecorView,其确定是通过屏幕的大小,和自身的布局参数LayoutParams。
这部分很简单,根据LayoutParams的布局格式(match_parent,wrap_content或指定大小),将自身大小,和屏幕大小相比,设置一个不超过屏幕大小的宽高,以及对应模式。
对于其他View(包括ViewGroup),其确定是通过父布局的MeasureSpec和自身的布局参数LayoutParams。
这部分比较复杂。以下列图表表示不同的情况:
当子View的LayoutParams的布局格式是wrap_content,可以看到子View的大小是父View的剩余尺寸,和设置成match_parent时,子View的大小没有区别。为了显示区别,一般在自定义View时,需要重写onMeasure方法,处理wrap_content时的情况,进行特别指定。从这里看出MeasureSpec的指定也是从顶层布局开始一层层往下去,父布局影响子布局。
View的测量流程:
三、Layout流程
测量完View大小后,就需要将View布局在Window中,View的布局主要通过确定上下左右四个点来确定的。
其中布局也是自上而下,不同的是ViewGroup先在layout()中确定自己的布局,然后在onLayout()方法中再调用子View的layout()方法,让子View布局。在Measure过程中,ViewGroup一般是先测量子View的大小,然后再确定自身的大小。
setOpticalFrame()内部也是调用了setFrame(),所以具体看setFrame()怎么确定自身的位置布局。
如果当前View是一个ViewGroup,就需要实现onLayout方法,该方法的实现个自定义ViewGroup时其特性有关,必须自己实现。
由此便完成了一层层的的布局工作。
View的布局流程:
四、Draw过程
View的绘制过程遵循如下几步:
①绘制背景 background.draw(canvas)
②绘制自己(onDraw)
③绘制Children(dispatchDraw)
④绘制装饰(onDrawScrollBars)
从源码中可以清楚地看出绘制的顺序。
五、总结
从View的测量、布局和绘制原理来看,要实现自定义View,根据自定义View的种类不同,可能分别要自定义实现不同的方法。但是这些方法不外乎:onMeasure()方法,onLayout()方法,onDraw()方法。
onMeasure()方法:单一View,一般重写此方法,针对wrap_content情况,规定View默认的大小值,避免于match_parent情况一致。ViewGroup,若不重写,就会执行和单子View中相同逻辑,不会测量子View。一般会重写onMeasure()方法,循环测量子View。
onLayout()方法:单一View,不需要实现该方法。ViewGroup必须实现,该方法是个抽象方法,实现该方法,来对子View进行布局。
onDraw()方法:无论单一View,或者ViewGroup都需要实现该方法,因其是个空方法
收起阅读 »
Android中自定义View一直是一个高级的技能,入门比较难,看起来很高大上。想要学会自定义View,当然要理解View的测量、布局及绘制原理,本篇文章将以图表的形式讲解View的测量、布局及绘制原理。
一、View绘制的流程框架
View的绘制是从上往下一层层迭代下来的。DecorView-->ViewGroup(--->ViewGroup)-->View ,按照这个流程从上往下,依次measure(测量),layout(布局),draw(绘制)。关于DecorView,可以看这篇文章。
二、Measure流程
顾名思义,就是测量每个控件的大小。
调用measure()方法,进行一些逻辑处理,然后调用onMeasure()方法,在其中调用setMeasuredDimension()设定View的宽高信息,完成View的测量操作。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {measure()方法中,传入了两个参数 widthMeasureSpec, heightMeasureSpec 表示View的宽高的一些信息。
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {由上述流程来看Measure流程很简单,关键点是在于widthMeasureSpec, heightMeasureSpec这两个参数信息怎么获得?
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
如果有了widthMeasureSpec, heightMeasureSpec,通过一定的处理(可以重写,自定义处理步骤),从中获取View的宽/高,调用setMeasuredDimension()方法,指定View的宽高,完成测量工作。
MeasureSpec的确定
先介绍下什么是MeasureSpec?
MeasureSpec由两部分组成,一部分是测量模式,另一部分是测量的尺寸大小。
其中,Mode模式共分为三类
UNSPECIFIED :不对View进行任何限制,要多大给多大,一般用于系统内部
EXACTLY:对应LayoutParams中的match_parent和具体数值这两种模式。检测到View所需要的精确大小,这时候View的最终大小就是SpecSize所指定的值,
AT_MOST :对应LayoutParams中的wrap_content。View的大小不能大于父容器的大小。
那么MeasureSpec又是如何确定的?
对于DecorView,其确定是通过屏幕的大小,和自身的布局参数LayoutParams。
这部分很简单,根据LayoutParams的布局格式(match_parent,wrap_content或指定大小),将自身大小,和屏幕大小相比,设置一个不超过屏幕大小的宽高,以及对应模式。
对于其他View(包括ViewGroup),其确定是通过父布局的MeasureSpec和自身的布局参数LayoutParams。
这部分比较复杂。以下列图表表示不同的情况:
当子View的LayoutParams的布局格式是wrap_content,可以看到子View的大小是父View的剩余尺寸,和设置成match_parent时,子View的大小没有区别。为了显示区别,一般在自定义View时,需要重写onMeasure方法,处理wrap_content时的情况,进行特别指定。从这里看出MeasureSpec的指定也是从顶层布局开始一层层往下去,父布局影响子布局。
View的测量流程:
三、Layout流程
测量完View大小后,就需要将View布局在Window中,View的布局主要通过确定上下左右四个点来确定的。
其中布局也是自上而下,不同的是ViewGroup先在layout()中确定自己的布局,然后在onLayout()方法中再调用子View的layout()方法,让子View布局。在Measure过程中,ViewGroup一般是先测量子View的大小,然后再确定自身的大小。
public void layout(int l, int t, int r, int b) {上面看出通过 setFrame() / setOpticalFrame():确定View自身的位置,通过onLayout()确定子View的布局。
// 当前视图的四个顶点
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
// setFrame() / setOpticalFrame():确定View自身的位置
// 即初始化四个顶点的值,然后判断当前View大小和位置是否发生了变化并返回
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
//如果视图的大小和位置发生变化,会调用onLayout()
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
// onLayout():确定该View所有的子View在父容器的位置
onLayout(changed, l, t, r, b);
...
}
setOpticalFrame()内部也是调用了setFrame(),所以具体看setFrame()怎么确定自身的位置布局。
protected boolean setFrame(int left, int top, int right, int bottom) {确定了自身的位置后,就要通过onLayout()确定子View的布局。onLayout()是一个可继承的空方法。
...
// 通过以下赋值语句记录下了视图的位置信息,即确定View的四个顶点
// 即确定了视图的位置
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
}
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {如果当前View就是一个单一的View,那么没有子View,就不需要实现该方法。
}
如果当前View是一个ViewGroup,就需要实现onLayout方法,该方法的实现个自定义ViewGroup时其特性有关,必须自己实现。
由此便完成了一层层的的布局工作。
View的布局流程:
四、Draw过程
View的绘制过程遵循如下几步:
①绘制背景 background.draw(canvas)
②绘制自己(onDraw)
③绘制Children(dispatchDraw)
④绘制装饰(onDrawScrollBars)
从源码中可以清楚地看出绘制的顺序。
public void draw(Canvas canvas) {无论是ViewGroup还是单一的View,都需要实现这套流程,不同的是,在ViewGroup中,实现了 dispatchDraw()方法,而在单一子View中不需要实现该方法。自定义View一般要重写onDraw()方法,在其中绘制不同的样式。
// 所有的视图最终都是调用 View 的 draw ()绘制视图( ViewGroup 没有复写此方法)
// 在自定义View时,不应该复写该方法,而是复写 onDraw(Canvas) 方法进行绘制。
// 如果自定义的视图确实要复写该方法,那么需要先调用 super.draw(canvas)完成系统的绘制,然后再进行自定义的绘制。
...
int saveCount;
if (!dirtyOpaque) {
// 步骤1: 绘制本身View背景
drawBackground(canvas);
}
// 如果有必要,就保存图层(还有一个复原图层)
// 优化技巧:
// 当不需要绘制 Layer 时,“保存图层“和“复原图层“这两步会跳过
// 因此在绘制的时候,节省 layer 可以提高绘制效率
final int viewFlags = mViewFlags;
if (!verticalEdges && !horizontalEdges) {
if (!dirtyOpaque)
// 步骤2:绘制本身View内容 默认为空实现, 自定义View时需要进行复写
onDraw(canvas);
......
// 步骤3:绘制子View 默认为空实现 单一View中不需要实现,ViewGroup中已经实现该方法
dispatchDraw(canvas);
........
// 步骤4:绘制滑动条和前景色等等
onDrawScrollBars(canvas);
..........
return;
}
...
}
五、总结
从View的测量、布局和绘制原理来看,要实现自定义View,根据自定义View的种类不同,可能分别要自定义实现不同的方法。但是这些方法不外乎:onMeasure()方法,onLayout()方法,onDraw()方法。
onMeasure()方法:单一View,一般重写此方法,针对wrap_content情况,规定View默认的大小值,避免于match_parent情况一致。ViewGroup,若不重写,就会执行和单子View中相同逻辑,不会测量子View。一般会重写onMeasure()方法,循环测量子View。
onLayout()方法:单一View,不需要实现该方法。ViewGroup必须实现,该方法是个抽象方法,实现该方法,来对子View进行布局。
onDraw()方法:无论单一View,或者ViewGroup都需要实现该方法,因其是个空方法
收起阅读 »
环信公开课第35期-手把手教学开发直播小程序
微信小程序,依托微信,已经越来越多的新功能不断上线,流量入口的不断增加,俨然成为互联网公司“必争之地”。 传统的公众号第三方直播由于入口较深,用户每次使用都有较复杂的路径,很难培养用户的使用习惯,而小程序呢?使用之后有永久留存,用户下次再使用可以瞬间找到入口,大大提升用户使用直播的习惯。直播商业化最难的地方在于哪里?在于流量,微信有庞大的低成本的流量可以帮助你去获得用户,转化用户。小程序直播可以说是你商业化用户最好的工具,同样也是你客户商业化微信流量的最好的工具。直播和购物的结合比较经典的,也是我们课程主要剖析的一个项目就是“超级购物台”——直播购物小程序。
公开课参与
开课时间:2018年8月8日15:00
课程时长:40分钟
课程亮点:全国首个小程序直播购物Demo分享。手把手搭建基于环信的小程序直播购物平台
课程形式:网络直播 全国各地区、联网电脑、联网手机均可免费参与
课程大纲:
环信直播技术流程
1、【环信账号准备】注册、认证。
2、【直播间创建】包括环信后台创建及api 方式创建。
3、【房间主播及推流拉流地址设置】创建直播。主播绑定房间,房间设置直播地址。
小程序技术分享
1、【创建小程序项目】创建好已有UI的小程序项目。
2、【环信小程序sdk集成】基于webIM sdk 开发的小程序版本SDK集成配置。
3、【配置已有房间到小程序进行直播推流拉流测试】模拟直播间,使用推流工具进行推流测试。
4、【开发直播间聊天功能及聊天功能特殊情况处理】利用小程序SDK进行直播聊天室聊天开发。弹幕、特效等的一些延时。
答疑送奖品:
从提问中抽5名同学送出环信定制T恤,大家多多提问互动
公开参与:报名听课 收起阅读 »
公开课参与
开课时间:2018年8月8日15:00
课程时长:40分钟
课程亮点:全国首个小程序直播购物Demo分享。手把手搭建基于环信的小程序直播购物平台
课程形式:网络直播 全国各地区、联网电脑、联网手机均可免费参与
课程大纲:
环信直播技术流程
1、【环信账号准备】注册、认证。
2、【直播间创建】包括环信后台创建及api 方式创建。
3、【房间主播及推流拉流地址设置】创建直播。主播绑定房间,房间设置直播地址。
小程序技术分享
1、【创建小程序项目】创建好已有UI的小程序项目。
2、【环信小程序sdk集成】基于webIM sdk 开发的小程序版本SDK集成配置。
3、【配置已有房间到小程序进行直播推流拉流测试】模拟直播间,使用推流工具进行推流测试。
4、【开发直播间聊天功能及聊天功能特殊情况处理】利用小程序SDK进行直播聊天室聊天开发。弹幕、特效等的一些延时。
答疑送奖品:
从提问中抽5名同学送出环信定制T恤,大家多多提问互动
公开参与:报名听课 收起阅读 »
滴滴!“全村的希望”环信T恤已发出,收到的同学打个卡
千呼万唤!经过与工厂沟通无数沟通,环信T恤量产到货啦!中奖同学收到了吗?
实物图
希望款:
环信机器人,正面是我们美女设计师设计的三个机器人,扛着“全镇的希望”,“全县的希望”,“全环信的希望”三个小旗,背面印有“全村的希望”
IM款:
正面:IM
背面:IM (即时通讯)
活动回顾:【有奖调查】环信T恤文案征集,你来我们就送!
中奖名单:
- 楼层6:Eternally(额外奖励,提建议被采纳)
- 楼层8:°﹏D.X.F.VIP
- 楼层18:空谷幽兰
- 楼层28:skoxe
- 楼层38.咚咚
欢迎大家以后多参加我们活动,除了T恤还有更多礼品送出,感谢继续支持环信! 收起阅读 »
【开源项目】全国首个开源直播小程序源码
今天你看直播了吗?拥有10亿微信生态用户的小程序已经成为了继移动互联后的又一个现象级风口,随着微信小程序对外开放实时音视频录制及播放等更多连接能力,小程序与直播强强联合,在各行各业找到了非常多的玩法,小程序直播相比微信直播和APP直播更加简洁、流畅、低延时、多入口等众多优势迅速向商业直播领域及泛娱乐直播领域蔓延。从小游戏、内容付费、工具、大数据、社交电商创业者到传统品牌商们,都在努力搭上小程序直播这辆快车,以免错过微信生态里新的流量洼地。
作为一名环信生态圈资深开发者,本着对技术的热衷,对环信的眷恋和对党的忠诚,基于环信即时通讯云写了“直播购物小程序”,目前项目源码已全部免费开放,希望对有需求的企业和开发者提供一个思路和参考。
直播购物小程序源码github地址:https://github.com/YuTongNetworkTechnology/wechat_live/tree/master
git打不开可直接点下面链接下载
直播购物小程序运行预览图
小程序体验指南(仅需两步):
1、下载微信小程序开发工具,下载地址:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
2、导入源码:将附件的源码解压直接导入
环信小程序直播技术文档
一、 使用的技术
1、 环信IM直播室。
2、 微信小程序实时音视频播放组件live-player。
3、 推流软件(obs、易推流)等推流。
4、 视频流服务器(UCLOUD、七牛、腾讯)等视频流服务器。
二、 系统使用流程。
1、 视频推流软件将视频流推到流服务器。
2、 打开视频直播demo小程序注册环信账号。
3、 进入软件直播室进行测试。
三、 技术流程及使用的SDk
1、 注册环信账号
打开https://www.easemob.com/ 环信官网,点击右上角注册按钮,选择[注册即时通讯云]
填写对相关信息进行注册
注册成功后进行登录
注:新注册用户需进行账号的认证。
2、 直播应用创建
登录成功点击应用列表选择创建应用
输入应用名称等信息
创建成功后点击应用进入
需要注意的是应用的OrgName 和AppName这两个是以后都需要用到的两个参数变量
3、 直播创建
1)在创建直播之前需要对应用进行设置首先需要设置应用的直播流地址
第一步获取应用管理员的Token
curl -X POST "https://a1.easemob.com/[应用OrgName]/[应用AppName]/token" -d '{"grant_type":"client_credentials","client_id":"[应用client_id]","client_secret":"[应用] client_secret"}'返回格式
{第二步设置直播流地址
"access_token":"YWMtWY779DgJEeS2h9OR7fw4QgAAAUmO4Qukwd9cfJSpkWHiOa7MCSk0MrkVIco",
"expires_in":5184000,
"application":"c03b3e30-046a-11e4-8ed1-5701cdaaa0e4"
curl -X POST -H "Authorization: Bearer [管理员Token]" " https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms/stream_url -d '{"pc_pull":"[pc拉流地址]","pc_push":"[pc推流地址]","mobile_pull":"[手机拉流地址]","mobile_push":"[手机推流地址]"}'"成功返回格式:
{2)创建主播
"action": "post",
"application": "e1a09de0-0e03-11e7-ad8e-a1d913615409",
"uri": "http://127.0.0.1:8080/easemob-demo/chatdemoui/liverooms/stream_url",
"entities": [ ],
"data": {
"pc_pull": true,
"mobile_push": true,
"mobile_pull": true,
"pc_push": true
},
"timestamp": 1494084474885,
"duration": 1,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}
点击IM用户
点击注册IM用户
填写用户信息
创建用户的过程同样也可以通过REST API形式进行
curl -X POST -i " https://a1.easemob.com/[应用OrgName]/[应用AppName]/users" -d '{"username":"[用户名]","password":"[密码]"}'
注:应用必须为开放注册
将注册的用户添加为主播
curl -X POST -H "Authorization: [管理员Token]" https://a1.easemob.com/[应用OrgName]/[应用AppName]/super_admin -d'{"superadmin":"[IM用户名]"}'返回结果示例:
{3)创建直播
"action": "post",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob-demo/chatdemoui/chatrooms/super_admin",
"entities": [ ],
"data": {
"result": "success"
},
"timestamp": 1496236798886,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}
点击直播
点击新建房间
填写房间信息
创建房间同时也可以使用REST API形式进行详情可以查看http://docs.easemob.com/im/live/server-integration环信官方文档。
4、 小程序demo集成使用
小程序直播购物demo集成官方WebIM SDK详情请查看https://github.com/easemob/webim-weixin-xcx
Demo具体配置如下
打开demo 下sdk配置文件
修改appkey为自己应用的appkey
打开pages/live/index.js修改房间默认拉流地址及直播间房间号
四、 扩展说明
Demo中房间为固定测试房间,实际使用中应获取环信直播的房间信息及房间列表。具体如下:
获取直播间列表:
curl -X GET -H "Authorization: Bearer [用户Token]" https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms?ongoing=true&limit=[获取数量]&cursor=[游标地址(不填写为充开始查询)]
响应:
{获取直播间详情:
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"params": {
"cursor": [
"ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6MzE"
],
"ongoing": [
"true"
],
"limit": [
"2"
]
},
"uri": "http://127.0.0.1:8080/easemob-demo/chatdemoui/liverooms",
"entities": [ ],
"data": [
{
"id": "1924",
"chatroom_id": "17177265635330",
"title": "具体了",
"desc": "就咯",
"startTime": 1495779917352,
"endTime": 1495779917352,
"anchor": "wuls",
"gift_count": 0,
"praise_count": 0,
"current_user_count": 8,
"max_user_count": 9,
"status": "ongoing",
"cover_picture_url": "",
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1"
},
{
"id": "1922",
"chatroom_id": "17175003856897",
"title": "香山",
"desc": "随便",
"startTime": 1495777760957,
"endTime": 1495777760957,
"anchor": "sx001",
"gift_count": 0,
"praise_count": 8,
"current_user_count": 1,
"max_user_count": 3,
"status": "ongoing",
"cover_picture_url": "http://127.0.0.1:8080/easemob-demo/chatdemoui/chatfiles/43a62c20-41d6-11e7-a88e-df409c88cf66",
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1"
}
],
"timestamp": 1496303336669,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"cursor": "ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6NDk",
"count": 2
}
curl -X GET -H "Authorization: Bearer [用户Token]" " https://a1.easemob.com/[应用OrgName]/[应用AppName]/[房间id]/status"响应:
{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob-demo/chatdemoui/liverooms/1946/status",
"entities": [ ],
"data": {
"liveRoomID": "1946",
"status": "ongoing"
},
"timestamp": 1496234759930,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"count": 0
}
使用环信直播购物小程序遇到任何问题欢迎跟帖讨论。 收起阅读 »
环信客户互动云v5.37已发布,新增客户中心日志
新增客户中心日志:在客户中心日志页面下,可查看到客户中心的操作日志,目前记录范围为黑名单操作日志。
进入“管理员模式 > 客户中心 > 客户中心日志”页面,可以查看到黑名单操作日志,点击【筛选排序】按钮可进行自定义筛选,点击操作日志记录可显示客户资料、互动详情及黑名单操作详情。
客服模式
历史会话和质检页面iframe标签页支持拖动移动左右位置
历史会话和质检记录详情,会话聊窗右侧iframe标签页支持拖动移动左右位置。
进入“客服模式 > 历史会话”页面,打开历史会话记录详情,选中聊窗右侧iframe标签页,待鼠标变成小手状时,左右拖动可移动位置。
“客服模式 > 质量检查 > 质检记录”页面,以上优化适用。
注:管理员模式下,历史会话/质量检查页面记录详情页,同样支持iframe标签页拖动移动左右位置。
历史会话和质检页面会话标签可显示所有层级
历史会话和质检页面记录详情,会话标签可显示所有层级。
进入“客服模式 > 历史会话”页面,打开历史会话记录详情,可看到聊窗会话标签显示效果优化并可显示所有层级。
“客服模式 > 质量检查 > 质检记录”页面,“客服模式 > 搜索”页面,以上优化适用。
注:管理员模式下,历史会话/质检/搜索页面,以上优化适用。
历史会话满意度标签页替换为满意度图标显示
历史会话详情,原满意度标签页取消,右上角增加满意度图标,显示满意度评价信息。
进入“客服模式 > 历史会话”页面,打开历史会话记录详情,可看到聊窗右侧满意度标签页取消显示,右上角增加满意度图标,鼠标点击可弹窗显示满意度评价详情。
“客服模式 > 质量检查 > 质检记录”页面,以上优化适用。
注:管理员模式下,历史会话/质检页面,以上优化适用。
管理员模式
新增【客户中心日志】功能
在客户中心日志页面下,可查看到客户中心的操作日志,目前记录范围为黑名单操作日志。
进入“管理员模式 > 客户中心 > 客户中心日志”页面,可以查看到黑名单操作日志,点击【筛选排序】按钮可进行自定义筛选,点击操作日志记录可显示客户资料、互动详情及黑名单操作详情。
新增自定义业务规则模拟测试功能
自定义业务规则新增模拟测试功能,可通过测试,判断规则是否可用。
进入“管理员模式 > 设置 > 自定义业务规则”页面,可点击 图标对指定规则进行模拟测试。点击 ,弹出规则测试详细参数窗口,设定参数后,可点击测试按钮,进行测试。若测试成功,则说明规则可用。
注:自定义业务规则功能为标准版增值服务、旗舰版基础功能。如需开通,请提供租户ID并联系环信商务经理。
权限管理中客服模式-历史会话增加数据权限
自定义角色权限,客服模式-历史会话增加数据权限。
进入“管理员模式 > 设置 > 权限管理”页面,编辑自定义角色权限(或添加自定义角色),找到客服模式下历史会话权限,可查看到右边新增数据权限,下拉列表可选择“客服/技能组/租户”3个选项,可根据需要进行自定义设置。
【优化】客户中心导航菜单优化
管理员模式下,【客户中心】导航菜单优化。
进入“管理员模式 > 客户中心”页面,可看到客户中心下二级菜单项优化。其中包括:
原“客户中心”一级菜单更改为【客户中心】下“客户信息”二级菜单。
原“设置-客户标签”“设置-客户中心设置”二级菜单 更改为【客户中心】下二级菜单。
新增【客户中心日志】功能,详情可参考新增【客户中心日志】功能章节。
【优化】访客端消息关键字匹配后行为设置
关键字匹配功能下,新增访客消息端行为配置选项。
进入“管理员模式 > 设置 > 关键字匹配”页面,点击关键字过滤(或添加关键字匹配规则),在弹出窗口中,消息来源选择“访客消息”,满足以下条件选择“关键字匹配”,可查看到执行下拉列表新增2项“在坐席端提示消息 / 在坐席和访客端提示消息”;
弹出窗口中,消息来源选择“访客消息”,满足以下条件选择“正则表达式”,可查看到执行下拉列表新增1项“发送事件到告警记录面板”。
注:关键字匹配功能为标准版增值服务、旗舰版基础功能。如需开通,请提供租户ID并联系环信商务经理。
【优化】客户标签数量限制增大至100
优化会话窗口客户标签显示问题,数量限制40增大至100。新建及导入客户标签时,支持最大数量为100。
【优化】机器人自定义菜单操作提示统一交互
优化机器人设置页面,自定义菜单标签页操作的交互提示,与其他交互保持一致。
进入“管理员模式 > 智能机器人 > 机器人设置”页面,打开自定义菜单页签,添加/编辑/删除菜单项时,回车或点击输入框以外的其他区域可保存,并在右上角给出保存反馈提示。
【优化】公共常用语新增及导入字符数限制统一为1000
优化了公共常用语新增及导入字符数限制问题,统一为1000字符。1个字母或1个汉字均算作1个字符。
修复历史会话/待接入页面数据权限问题
“管理员模式 > 设置 > 权限管理”,修复如下问题:
- 管理员角色-客服模式-待接入,默认数据权限为“技能组”。
- 坐席角色-客服模式-历史会话,默认数据权限为“客服”。
- 坐席角色-客服模式-待接入,默认数据权限为“技能组”。
并修复了相关前端数据显示问题。
环信客户互动云更新日志:更新日志
环信客户互动云登陆地址:登陆地址 收起阅读 »
环信即时通讯云新版本发布-不在群聊天也能参加多人实时音视频会议!
环信即时通讯云V3.4.2已发布,新增消息邀请参加会议接口,被邀请方不在群聊天也能加入多人音视频会议。
- Android SDK 更新日志
- iOS SDK 更新日志
版本 V3.4.2 2018-06-15
新功能:
通过消息邀请参加其他人参加多人实时音视频会议
修复:
1v1视频通话,iOS端接收视频后,偶尔会不显示视频画面
请注意:为提供高质量且一致的音视频体验,从3.4.1版本开始,1v1 通话不再与3.1.5及以前版本兼容,请及时升级。
SDK更新日志:Android SDK更新日志 ios SDK更新日志
最新SDK下载:下载地址 收起阅读 »
【新手上路--分享一个小确幸 “解决更换Demo下的AppKey不起作用的问题”】
关于更换AppKey无效的问题
下面是我更换AppKey的流程步骤:
很纳闷,明明已经更换了AppKey啊,而且我输入的用户及密码都是对应注册应用下的用户啊,于是我再次利用一开始注册好的账号进行登录,竟然成功!说明我刚才更改为自己的AppKey根本就没起作用!!! 找到问题源之后,我开始留意到[自定义AppKey]的“switch 开关”,我输入完之后,将switch关 了,没错,我竟然傻乎乎的填完自定义AppKey后将switch开关给关了!顿时为自己的智商担忧啊,于是赶紧试了一下填完之后已经保留switch开关已开的状态,然后按照上述的步骤,登录成功了! 收起阅读 »
下面是我更换AppKey的流程步骤:
- 利用随机注册的账号登录后,在Demo的【设置】页面进行更改为已注册好生成应用下的AppKey(已在环信管理后台进行注册 该应用,且利用后台的【IM注册用户】创建了用户user )
- 点击"退出登录",杀死改APP进程,重新登录,输入user 及密码 ,提示登录失败,用户名或密码有误 !
很纳闷,明明已经更换了AppKey啊,而且我输入的用户及密码都是对应注册应用下的用户啊,于是我再次利用一开始注册好的账号进行登录,竟然成功!说明我刚才更改为自己的AppKey根本就没起作用!!! 找到问题源之后,我开始留意到[自定义AppKey]的“switch 开关”,我输入完之后,将switch关 了,没错,我竟然傻乎乎的填完自定义AppKey后将switch开关给关了!顿时为自己的智商担忧啊,于是赶紧试了一下填完之后已经保留switch开关已开的状态,然后按照上述的步骤,登录成功了! 收起阅读 »
环信即时通讯云V3.4.1已发布,更快,更稳定的实时音视频
环信即时通讯云V3.4.1已发布:优化WiFi切4G时与服务器重连速度,优化1V1视频通话,新增音视频弱网检测提示功能。
iOS SDK 更新日志
版本 V3.4.1 2018-05-16
优化:
优化WiFi切4G时与服务器重连速度
优化实时1对1通话
新功能:
新增音视频弱网检测回调
新增加群时填写验证消息
新增聊天室断线时被踢出聊天室回调
请注意:为提供高质量且一致的音视频体验,从3.4.1版本开始,1v1 通话不再与3.1.5及以前版本兼容,请及时升级。
Android SDK 更新日志
版本 V3.4.1 2018-05-11
SDK:
修复安卓中会话消息未读数显示不准确的问题;
修复华为推送自动登录情况下有时不可用的问题;
修复app第一次安装运行,初始化过程有可能报nullpointer的问题;
尝试解决sendDeviceToServer()方法crash的问题;
实现音视频弱网检测提示功能;
实现加群时填写验证消息功能;
提供聊天室断线时被踢出聊天室的提醒;
优化1v1 通话;
EaseUI:
更新消息发送逻辑,用于解决发送消息出现重复,顺序颠倒等问题.
请注意:为提供高质量且一致的音视频体验,从3.4.1版本开始,1v1 通话不再与3.1.5及以前版本兼容,请及时升级。
版本历史:Android SDK更新日志 ios SDK更新日志
下载地址:SDK下载 收起阅读 »
环信客户互动云v5.36已发布:新增历史会话查询方式
环信客户互动云v5.36已发布,支持根据会话创建时间查询历史会话,方便客户相关数据部门统计历史会话信息。
客服模式
历史会话支持【按会话创建时间】查询
支持根据会话创建时间查询历史会话,方便客户相关数据部门统计历史会话信息。
在历史会话列表页,点击右上角【筛选排序】按钮,弹窗内选择时间段为“会话创建时间”,点击右下角【筛选查询】按钮,即可根据会话创建时间进行筛选。筛选完成后,可在列表页查看相关结果。
历史会话详情资料页工单列表支持点击跳转
历史会话工单列表支持点击跳转,便于快速查询相关工单信息。
在历史会话详情资料页,可查看到当前历史会话相关工单列表,点击工单,可快速打开工单页面,查看工单详情。
注:融合工单功能为标准版/旗舰版增值服务。如需开通,请在“管理员模式 > 工单”页面提交申请,环信商务经理会主动联系您。
会话标签为空时支持保存
当前会话/进行中会话:
当“会话结束时强制添加会话标签”开关关闭时,会话标签支持全部删除,会话标签为空时可以保存;
当“会话结束时强制添加会话标签”开关开启时,点击会话标签,会话标签支持全部删除,为空时可以保存;点击结束会话按钮时,弹出添加会话标签窗口,此时必须至少添加一个会话标签,才可保存并结束会话;
历史会话:
会话标签支持全部删除,为空时可以保存;
会话聊窗与右侧iframe布局支持拖动分配大小
会话模式下(包括会话、历史会话),聊天窗口支持最小宽度340px,iframe窗口支持最小宽度195px,聊天窗口与iframe窗口支持拖动分配大小。
注:管理员模式下,“当前会话 / 历史会话 / 质量检查”会话详情页面支持聊窗与iframe窗口拖动分配大小。
【优化】历史会话质检指标单独标签页显示
“客服模式 > 历史会话“ 页面,会话聊窗右侧工具栏中原质检页签下“查看系统质检指标筛选”相关属性单独为“指标”页签进行显示。 “客服模式 > 质量检查 > 质检记录”页面,以上优化适用。
注:管理员模式下,历史会话与质检页面,以上优化适用。
新增客服结束语设置
新增客服结束语设置,可设置客服手动结束会话时的结束语。
进入“客服模式 > 客服信息”页面,可看到客服问候语下新增结束语设置,可设置客服结束语并可选择是否开启。客服结束语开启,客服手动结束会话时,会向访客自动发送客服结束语,客服无需在结束会话前,进行手动发送,以提高工作效率。
客服手动结束会话时,详细发送规则如下:
- 客服结束语开启,系统结束语开启,发送客服结束语。
- 客服结束语关闭,系统结束语开启,发送系统结束语。
- 客服结束语开启,系统结束语关闭,发送客服结束语。
- 客服结束语关闭,系统结束语关闭,不发送结束语。
注:管理员模式下,设置管理员客服结束语,管理员手动结束会话时,以上规则适用;其中客服结束语为管理员客服结束语。
管理员模式
【优化】当前会话列表增加渠道及关联显示
在当前会话列表页面,可以查看到会话渠道及关联信息。
进入“管理员模式 > 当前会话”页面,可以在当前会话列表,查看到会话渠道及关联信息。
技能组溢出增加“客服全部隐身/忙碌/离开”规则设置
技能组溢出增加“客服全部隐身/忙碌/离开”规则设置。
进入“管理员模式 > 设置 > 会话分配规则”页面,打开排队规则TAB页,点击右上角【添加规则】按钮,点击“满足以下条件”下拉选项,选择“客服全部隐身 / 客服全部忙碌 / 客服全部离开”状态,即可设置相应状态下的技能组溢出规则。
注:技能组溢出(排队规则)功能为标准版/旗舰版增值服务。如需开通,请提供租户ID并联系环信商务经理。
黑名单列表默认加载全部数据
黑名单列表,默认情况下加载全部数据。
进入“管理员模式 > 客户中心”页面,打开黑名单标签,默认可查看到所有黑名单信息。
企业欢迎语设置文案更新
进入“管理员模式 > 设置 > 系统开关”页面,可看到“企业欢迎语”设置下文案已更新。
更新内容见以下截图:
【优化】时间计划-节假日设置日期选择范围更新
节假日设置页面,开始日期及结束日期可选择任意日期。
进入“管理员模式 > 设置 > 时间计划”页面,打开节假日设置页签,可以设置节假日,节假日范围可选择任意日期(结束日期晚于开始日期)。
修复技能组查询问题
支持>1000技能组,技能组数量>1000时,可正常查询并显示。
修复当前会话显示二次修改访客资料问题
二次修改访客资料开启:
集成访客资料中描述信息修改后,打开当前会话,可查看到资料页中访客描述信息为最新访客资料。
修复最大接待数显示问题
管理员修改“管理员”或“自定义角色”最大接待数,相应角色当前会话可实时显示最大接待数。
环信客户互动云更新 文档地址
环信客户互动云 登陆地址 收起阅读 »
看了这篇文章你也能自己解决90%的开发问题-环信公开课第26期:客户端日志分析
不管是使用何种编程语言,日志输出几乎无处不在,日志能帮我们追踪问题、 监控状态以及安全审计。作为一名软件开发人员,就必须要学会通过日志分析处理问题。在过去的环信公开课第26期,环信工程师沈冲讲解了环信开发过程中的日志分析处理。
公开课回放视频:https://v.qq.com/x/page/i0679s0ueso.html
1、环信日志介绍
* 如何开启日志打印:
* 初始化SDK时设置:options.enableConsoleLog = YES;
* 日志保存位置:
* iOS:
* 2.x:沙盒/Library/EaseMobLog
* 3.x:沙盒/Documents/HyphenateSDK/easemobLog/
* Android:
* 2.x:/sdcard/Android/data/(您的包名)/(您的appkey)/log/日期/xxx.html
* 3.x:/sdcard/Android/data/(包名)/(appkey)/core_log/easemob.log
* 如何导出日志:
* iOS:
* development:到沙盒中取出日志文件
* distribution:将手机连上Xcode,bundleid不更换,run后导出沙盒文件,取出日志文件
* Android:
* 到本机存储卡中取出日志
* 通过SDK接口导出:
* iOS:[[EMClient sharedClient] getLogFilesPathWithCompletion:^(NSString *aPath, EMError *aError) {}];
* Android:List<EMDeviceInfo> com.hyphenate.chat.EMClient.getLoggedInDevicesFromServer
2、日志分析
* SDK初始化:EMChatClientImpl::init()
* 登录、自动登录、退出登录
* EMSessionManager::login():
AutoLogin
begin logout ..
* 断网与重连
* onNetworkChanged():
EMSessionManager::reconnect()
* 被踢、被禁用
* 被踢:operation : 2
禁用:operation : 1
* 单聊和群聊的发送消息、已读回执
* asyncSendMessage
单聊:chattype : CHAT
群聊:chattype : GROUPCHAT
已读回执:chattype : READ_ACK
消息ID:server_id : 475456018394908684
* 接收消息
* command : NOTICE
消息ID:id : 475456157712910340
* 删除消息:EMDatabase::removeMessage: 475460335331969036
* 删除会话:EMChatManager::removeConversation: 31750912802818 isRemoveMessages: 1
* 群组相关:
* 创建群组: mucCreate:: retCode: 200
* 有用户加入群组: operation : INVITE_ACCEPT, operation : PRESENCE
* 查看群详情、获取群成员列表、获取群公告、群组免打扰
*
收到群邀请、接受群邀请:
收到群邀请:operation : INVITE,
接受群邀请:mucInviteDisposeOperation:: retCode: 200
* 聊天室相关:
* 加入、离开聊天室:
* 加入聊天室:EMChatroomManager::joinChatroom
* 退出聊天室: mucQuit:: retCode: 200
* 有用户加入聊天室: operation : PRESENCE
* 有用户离开聊天室: operation : ABSENCE
看完视频你学会了吗?环信公开课每周三下午三点准时开讲https://ke.qq.com/course/293539?tuin=3441d418
收起阅读 »
不是群主的好友,能否申请加入该群呢?
不是群主的好友,能否申请加入该群呢?
Android收发消息原理,如何集成华为、小米等厂商推送?-环信公开课第25期
Android开发的你是否也有这样疑问,应用在Android手机退到后台太久会收不到消息?第三方推送怎么集成?谷歌推送国内能用吗?
5月30日环信研发工程师张松在环信第25期公开课上分享关于Android收发消息的原理,以及推送通知的实现,详细内容如下。
为什么应用在Android手机上退到后台太久会出现收不到消息的情况?
当app在后台运行时环信SDK通过一个后台服务保持一条连接环信服务器的长连接,但Android为了解决系统待机性能差的问题,随着Android版本的升级逐渐禁止了app级别的后台服务的运行。所以在一些版本比较高的Android系统上会有接收不到消息的情况。
如何解决Android后台收不到消息的问题?
为了提高消息的到达率,环信SDK增加了对第三方推送服务的支持,包括小米推送、华为推送、Google FCM推送。 (服务端也增加了相应功能)
1.Google FCM推送:Firebase Cloud Messaging,用来替代Google的GCM推送服务。
FCM推送在国内是无法正常使用的,需要设备上有Google play service和能连接Google服务器的网络。所以该推送服务主要针对海外用户,如果你的app有海外用户,建议你增加对该推送服务的支持。
推送消息发送流程:
环信Server:
1.环信后台配置推送证书
让环信服务器拥有向你的app发送推送消息的功能
根据配置的证书名称服务器可以判断设备使用了哪种推送
2.目标设备的推送token
Android设备通过集成第三方推送SDK可以得到该推送token.
推送token跟Android设备是一一对应的关系,一个token对应一个Android设备.
不同推送通道获取到的推送token不同.
3.推送token属于哪个推送通道
决定了用哪个通道来发送推送消息.
根据Android设备上传的证书名称确定.
Android设备:
1.判断app所在Android设备上支持哪种推送通道
- 小米推送: 支持小米系统
- 华为推送: 支持华为系统
- FCM:需要Google play service和能连接Google服务器的网络
2.集成第三方推送SDK获取推送token
3.将推送token和推送通道名称上传至环信服务器.
SDK集成第三方推送
小米推送:
小米推送目前集成在了SDK内部,后续也会放在Demo中实现,让开发者可以自己升级小米推送SDK
1.在app AndroidManifest.xml中添加配置:SDK处理流程:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.VIBRATE"/>
<permission
android:name="com.hyphenate.chatuidemo.permission.MIPUSH_RECEIVE" android:protectionLevel="signature" />
<!--这里com.hyphenate.chatuidemo改成app的包名-->
<uses-permission android:name="com.hyphenate.chatuidemo.permission.MIPUSH_RECEIVE" />
<!--这里com.hyphenate.chatuidemo改成app的包名-->
<service
android:name="com.xiaomi.mipush.sdk.PushMessageHandler"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedService" />
<service
android:name="com.xiaomi.mipush.sdk.MessageHandleService"
android:enabled="true" />
<receiver
# EMMipushReceiver extends PushMessageReceiver
android:name="com.hyphenate.chat.EMMipushReceiver"
android:exported="true"
tools:ignore="ExportedReceiver">
<intent-filter>
<action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE" />
</intent-filter>
<intent-filter>
<action android:name="com.xiaomi.mipush.MESSAGE_ARRIVED" />
</intent-filter>
<intent-filter>
<action android:name="com.xiaomi.mipush.ERROR" />
</intent-filter>
</receiver>
EMOptions#setMipushConfig(appKey:"2882303761517426801", appSecret:"5381742660801");
根据小米推送SDK提供的方法MiPushClient#shouldUseMIUIPush(Context context)判断当前设备是否支持小米推送
若当前设备支持推送,则调用MiPushClient#registerPush(Context context, appKey, appSecret)获取小米通道推送
token
将appKey和token上传至环信服务器,该部分实现位于EMMipushReceiver 中,如果开发者需要自定义小米PushMessageReceiver,请继承自EMMipushReceiver .
华为推送
华为推送已放置到demo层实现,方便开发者自己更新华为推送SDK
华为推送SDK做为hmspush 单独module存在
注意修改hmspush module中AndroidManifext.xml文件的appid,两个provider对应authorities
Demo中配置了一个BroadcastReceiver和一个HMSPushHelper
FCM推送
增加了compile 'com.google.android.gms:play-services-base:11.4.0',检查当前设备是否支持fcm推送.
设置fcm numberoptions.setFCMNumber("826964054884");
整体工作流程:
Android设备手动或自动登录时,判断当前设备支持哪种推送通道,将该通道下获取到的token和证明名称上传至环信服务器,当服务端检测到当前设备不在线,需要向当前设备发送推送消息的时候,根据设备上传的证书名称判断出当前设备使用的哪种推送通道接收推送消息,使用相应推送通道向该设备上传的推送token上发送消息,当前设备即可收到该推送消息.
建议:
1.如果你的app有海外用户,建议你在环信SDK上增加对FCM推送的支持
2.建议同时支持小米推送和华为推送,这样在小米和华为设备上都可以接收到推送消息.
课程完整回放视频http://v.qq.com/x/page/m0671pnnzjk.html
收起阅读 »
环信公开课第24期视频回放-IOS本地通知、远程推送的实现
5月23日周三下午3点,环信公开课第24期如期举行。环信IOS工程师沈冲在公开课上讲解了ios本地通知+远程推送。
公开课大纲
环信公开课每周三下午3点,环信公开课不见不散
公开课观看地址:点击观看 收起阅读 »
公开课大纲
1、本地通知;24期公开课视频回放:点击观看
online 不推离线推送 offline 进入离线队列,推离线推送;
什么时候能收到离线推送:
App在后台被系统kill(150s),或手动kill,进程被杀死(用户离线),推离线推送
实现本地通知:
单例注册监听:
遵守协议: EMChatManagerDelegate
注册代理监听:[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];
实现接收消息回调:- (void)messagesDidReceive:(NSArray *)aMessages;
本地通知实现示例:- (void)showNotificationWithMessage:(EMMessage *)message;
2、离线推送
development:开发环境(连xcode run) production:生成环境(ipa)
创建推送证书上传管理后台,(证书appid+工程bundleid+上传管理后台传入packagename三者一致);
初始化SDK设置证书名称: options.apnsCertName = @"hxdemo2Dev”;
注册推送权限: _registerRemoteNotification;
绑定devicetoken: [[EMClient sharedClient] bindDeviceToken:deviceToken];
登录成功后,将app杀死,给之前登录的ID发消息,测试APNs推送。
3、rest+客户端 调试;
Postman;
rest调用示例;
客户端log。
4、私有API被拒
com.apple.springboard.lockcomplete
更新SDK_>提审
环信公开课每周三下午3点,环信公开课不见不散
公开课观看地址:点击观看 收起阅读 »
环信webim,收发消息,收发图片,收发表情,未读已读消息数量
大家好,这是根据我们公司的项目需求集成的webim demo,具体效果如上图。
此demo不依赖后台纯属前端demo,且同步聊天记录安卓和ios(只要用户不清楚浏览器缓存),数据全部储存在本地
那么接下来 咱们开始看看代码吧
第一步:
1、注册环信即时通信云获得appkey,注册账号之后登录环信后台创建应用就可以得到appkey
第二步:
2.引用本地文件
<script type='text/javascript' src='webim.config.js'></script>
<script type='text/javascript' src='strophe-1.2.8.min.js'></script>
<script type='text/javascript' src='websdk-1.4.11.js'></script>
这三个文件的顺序 不要修改 ,就这么引入
然后把appkey替换成自己的就可以。
第三步:
1.注册
var options = {
username: userid,
password: password,
nickname: nickname,
appKey: WebIM.config.appkey,
success: function () {//注册成功之后回调函数
},
error: function () {},
apiUrl: WebIM.config.apiURL
};
conn.registerUser(options);
2.登录
var options = {
apiUrl: WebIM.config.apiURL,
user: user,
pwd: password,
appKey: WebIM.config.appkey
};
conn.open(options);
3.创建连接
var conn = new WebIM.connection({
isMultiLoginSessions: WebIM.config.isMultiLoginSessions,
https: typeof WebIM.config.https === 'boolean' ? WebIM.config.https : location.protocol === 'https:',
url: WebIM.config.xmppURL,
heartBeatWait: WebIM.config.heartBeatWait,
autoReconnectNumMax: WebIM.config.autoReconnectNumMax,
autoReconnectInterval: WebIM.config.autoReconnectInterval,
apiUrl: WebIM.config.apiURL,
isAutoLogin: true
});
conn.listen({
onOpened: function ( message ) {}, //连接成功回调
onClosed: function ( message ) {}, //连接关闭回调
onTextMessage: function ( message ) {//收到文本消息
console.log(message);
console.log('收到'+message.from+'发送的消息:'+message.data);
setTimeout(function(){
//这一步或许有人会问为什么要加setTimeout,在这里解释一下,是为了同步执行下去
var str = message.ext.chatIcon;
if(str.indexOf("http")>=0){
str = str.slice(32);
}
else{
str = message.ext.chatIcon;
}
//这一步是因为ios和安卓发送消息时,人物头像连接一个是http开头地址 一个是不带本域名的地址,所有要做判断
var getUserItem = localStorage.getItem('user_id');//这个user_id 是ios和安卓的conversation_id 这个id是在我们人才详情页面,点击立即沟通时,存储到用户的id
然后进入聊天界面,然后模拟点击显示与此用户的界面
if(message.from==getUserItem){
getNowFormatDate();//时间函数
showChatMessage(timestamp3,str,message.data,message.id);//此函数为展示消息函数
//这里执行这一步,是判断如果收到的消息是当前用户,就直接显示在本聊天界面
detailMessage(timestamp3,message.data,message.from,'text','',message.id,message.from,str,'','','has_read','');//此函数为存储消息函数
//为了显示消息,这里是对所有收到的消息都做了存储,下文会介绍消息函数各个参数。
}
else{//收到的消息不是当前用户
detailMessage(timestamp3,message.data,message.from,'text','',message.id,message.from,str,'','','no_read','');//存储消息
var w = localStorage[message.from];//获取本地所有存储用户的消息列表
var getList = JSON.parse(localStorage[message.from]);//转换成json数组
for(var i = 0; i<getList.length; i++){
var reserve = getList[i].message[0].reserve;//此处是为存储的消息设置的已读、未读状态(上文的has_read,no_read)
if(reserve=='no_read'){
var listNumber = Number(getList.length)-Number(i);//这是获取的未读消息的数量
getList[0].message[0].number=listNumber;//此处是把未读消息的数量存储到 该用户第一条消息的number里面
$('.top-list li.'+message.from+'').find('span.notice-badge').show();
$('.top-list li.'+message.from+'').find('span.notice-badge').text(listNumber);
//此处是显示未读消息的数量,该li的class是收到消息用户的id
localStorage[message.from] = JSON.stringify(getList);
return false;//然后把消息的未读已读状态更改保存回本地
}
}
}
},0)
},
onEmojiMessage: function ( message ) {//收到表情消息
console.log('收到'+message.from+'发送的Emoji'+':'+message.data);
//缓存数据
for(var i=0;i<message.data.length;i++){
var img = message.data[i];
var string;
if (img.type=='txt') {string = string+img.data;}
else{string = string+'<img class="emoji" '+'src="'+img.data +'">';}
}
string = string.replace('undefined','');
console.log(string);
//此处的方法同收到文本消息,不过需要赋予字符串emoji表情标签(转化字符串为img标签)
//下面代码需要拿来出,放到你的$(function(){})里面,放到下面只是为了,解释给读者
WebIM.Emoji = {
path: '../images/faces/',
map: {
'[):]': 'ee_1.png',
'[:D]': 'ee_2.png',
'[;)]': 'ee_3.png',
'[:-o]': 'ee_4.png',
'[:p]': 'ee_5.png',
'[(H)]': 'ee_6.png',
'[:@]': 'ee_7.png',
'[:s]': 'ee_8.png',
'[:$]': 'ee_9.png',
'[:(]': 'ee_10.png',
'[:\'(]': 'ee_11.png',
'[:|]': 'ee_12.png',
'[(a)]': 'ee_13.png',
'[8o|]': 'ee_14.png',
'[|]': 'ee_15.png',
'[+o(]': 'ee_16.png',
'[<o)]': 'ee_17.png',
'[|-)]': 'ee_18.png',
'[*-)]': 'ee_19.png',
'[:-#]': 'ee_20.png',
'[:-*]': 'ee_21.png',
'[^o)]': 'ee_22.png',
'[8-)]': 'ee_23.png',
'[(|)]': 'ee_24.png',
'[(u)]': 'ee_25.png',
'[(S)]': 'ee_26.png',
'[(*)]': 'ee_27.png',
'[(#)]': 'ee_28.png',
'[(R)]': 'ee_29.png',
'[({)]': 'ee_30.png',
'[(})]': 'ee_31.png',
'[(k)]': 'ee_32.png',
'[(F)]': 'ee_33.png',
'[(W)]': 'ee_34.png',
'[(D)]': 'ee_35.png'
}
};
},
onPictureMessage: function ( message ) {//收到图片消息
console.log(message);
console.log('收到'+message.from+'发送的图片'+':'+message.url);
getNowFormatDate();//时间函数
showChatMessage(timestamp3,str,message.data,message.id);//此函数为展示图片消息函数
},
onCmdMessage: function ( message ) {}, //收到命令消息
onAudioMessage: function ( message ) {}, //收到音频消息
onLocationMessage: function ( message ) {},//收到位置消息
onFileMessage: function ( message ) {//收到文件消息
console.log(message);
console.log('收到'+message.from+'发送的文件'+':'+message.url);
},
onVideoMessage: function (message) { }, //收到视频消息
onPresence: function ( message ) {}, //处理“广播”或“发布-订阅”消息,如联系人订阅请求、处理群组、聊天室被踢解散等消息
onRoster: function ( message ) {}, //处理好友申请
onInviteMessage: function ( message ) {}, //处理群组邀请
onOnline: function () {}, //本机网络连接成功
onOffline: function () {}, //本机网络掉线
onError: function ( message ) {}, //失败回调
onBlacklistUpdate: function (list) {}, //黑名单变动
onReceivedMessage: function(message){}, //收到消息送达客户端回执
onDeliveredMessage: function(message){}, //收到消息送达服务器回执
onReadMessage: function(message){
//此处为收到已读消息的回执
setTimeout(function(){//为了保持同步操作
var getLength = $("#recordchat-main li.item-myself").length
//此处获取的长度是,当前聊天窗口的长度,且消息是本人发出的,因为现实已读未读只显示己方消息
for (var i = 0; i < getLength ; i++) {
var getLiName = $('#recordchat-main li.item-myself:eq('+i+')').attr('id');
//获取当前我发出消息的id,此id为user_id也就是上文的conversation_id
// console.log(getLiName)
var getMid = $('#recordchat-main li.item-myself:eq('+i+')').attr('mid');
//此处是获取当前我发出消息的mid,与消息回执的mid进行匹配
//console.log(getMid)
if(message.mid==getMid){
$('#recordchat-main li.item-myself:eq('+i+')').find('.no-read').text('已读');
//如果收到回执的消息mid等于当前窗口消息列表的mid,把当前的未读状态改变成已读状态
}
statusRead('已读',message.mid,getLiName,'');//此函数是把每一条的消息mid存储到本地且存储了状态
}
},0)
}, //收到消息已读回执
onCreateGroup: function(message){}, //创建群组成功回执(需调用createGroupNew)
onMutedMessage: function(message){} //如果用户在A群组被禁言,在A群发消息会走这个回调并且消息不会传递给群其它成员
});
//此处是左侧联系人列表,此方法是应用的layui的流加载,本想着用户多了,会使用layim+环信集成社区聊天模板
flow.load({
elem: '.top-list' //流加载容器
,mb:10
,isAuto: false
,isLazyimg: true
,done: function(page, next){ //执行下一页的回调
//数据插入
$.ajax({
url:'',
type:'get',
data:{page:page,user_type:2},
error:function(data){
layer.msg("世上难得两全法,您看是不是您的网络问题,如果不是刷新一下试试哦~")
},
success:function(data){
// console.log(data)
var obj = eval('('+data+')');
var length = obj.result.length;
var pages = length/10;
var lis = [];
for(var i = 0; i < length; i++)
{
lis.push('<li class='+obj.result[i].conversation_id+' chatId="'+obj.result[i].conversation_id+'" chatIcon="'+obj.result[i].info.user_img+'" add_time="'+obj.result[i].add_time+'" uid="'+obj.result[i].chat_id+'" chatName = "'+obj.result[i].conversation_id+'" career_name = "'+obj.result[i].info.career_name+'" city = "'+obj.result[i].info.city+'" education = "'+obj.result[i].info.education+'" industry_id = "'+obj.result[i].info.industry_id+'" salary_range = "'+obj.result[i].info.salary_range+'" school_name="'+obj.result[i].info.school_name+'" school_status="'+obj.result[i].info.school_status+'" specialty="'+obj.result[i].info.specialty+'" user_age="'+obj.result[i].info.user_age+'" user_sex="'+obj.result[i].info.user_sex+'" user_name="'+obj.result[i].info.user_name+'"><a href="javascript:;" data-url=""><div class="figure"><img src="'+obj.result[i].info.user_img+'"></div><div class="text"><div class="title"><div class="text-clear"><span class="name">'+obj.result[i].info.user_name+'</span><span class="time">'+obj.result[i].add_time+'</span></div><p class="gray"> '+obj.result[i].info.school_name+' | '+obj.result[i].info.career_name+' | '+obj.result[i].info.education+'</p></div><span class="notice-badge" style="display: none;"></span></div></a></li>')
}
next(lis.join(''),page< pages); //总页数
var getUserId = localStorage.getItem("user_id");
//模拟点击 当用户直接从人才列表点击进来
for (var i = 0; i < $(".top-list li").length; i++) {
var getItem = $('.top-list li:eq('+i+')').attr('chatid');
// console.log(getItem)此处是conversation_id
if(getUserId==getItem){
$('.top-list li:eq('+i+')').trigger("click");
var height = $("#recordchat-main").height();
$("#chat-list").scrollTop(height);
//此处代码是项目中有需求,点击左侧联系人的时候,右侧聊天窗口显示最底部的消息
}
if(localStorage[getItem]){
var chatSen = localStorage[getItem];
//console.log(chatSen)然后在加载左侧结束的同时,获取本地存储所有该用户的聊天记录
var chatSenGetItem = JSON.parse(localStorage[getItem]);
//console.log(chatSenGetItem)转换改聊天记录为json数组
var number = chatSenGetItem[0].message[0].number;
//console.log(number)此处为读取存储本地未读的消息
if(number!=''){
$('.top-list li:eq('+i+')').find("span.notice-badge").show();
$('.top-list li:eq('+i+')').find("span.notice-badge").text(number);
}
else{
$('.top-list li:eq('+i+')').find("span.notice-badge").hide();
$('.top-list li:eq('+i+')').find("span.notice-badge").text(number);
}
//未读消息的展示
}
}
}
});
}
})
//此处是左侧联系人列表点击的时候,由于没有后台支撑,所有我把所有的用户信息,全部存储到li里面,此处只是demo测试,正式版本肯定会有接口支持
$(".top-list").on("click","li",function(){
//此处点击切换数据代码已裁剪掉,只写本地存储,im相关
var chatId = $(this).attr("class") //上文提到过conversation_id为了li的class
//获取聊天记录
if(localStorage[chatId]){
var localContent = JSON.parse(localStorage[chatId]);//点击获取该用户的所有聊天记录
if(localContent[0].message[0].number!=''){//此number是未读消息的数量
localContent[0].message[0].number='';//如果未读消息不为空的话,点击该用户,该用户的未读消息数量清空
localStorage[chatId]=JSON.stringify(localContent);
//console.log(localContent)
}
var localContent = JSON.parse(localStorage[chatId]);
//console.log(localContent)
for (var i = 0; i < localContent.length; i++) {
var data = localContent[i].message[0].data;
var chatId = localContent[i].message[0].chatId;
var from = localContent[i].message[0].from;
var time = localContent[i].message[0].time;
var type = localContent[i].message[0].type;
var filename = localContent[i].message[0].filename;
var id = localContent[i].message[0].id;
var mid = localContent[i].message[0].mid;
var chatIcon = localContent[i].message[0].chatIcon;
var reserve = localContent[i].message[0].reserve;
if(chatIcon==undefined){
chatIcon = '/head_img/12064_15087368603690.png';//如果数据库人物头像丢失,显示默认头像
}
if(localContent[i].message[0].reserve=='no_read'){
// localStorage[chatId].message[0].reserve = 'has_read'
localContent[i].message[0].reserve='has_read';
//console.log(localContent)
localContent[0].message[0].number='';
localStorage[chatId]=JSON.stringify(localContent);
//console.log(localStorage[chatId]) 判断该消息的未读已读状态,点击该用户进行转换状态
}
var chat_status;
// console.log(localStorage[chatId])
if(localStorage[id]){
var localStatusContent = JSON.parse(localStorage[id]);
//console.log(localStatusContent)
chat_status = localStatusContent[0].status;
}
else{
//console.log(localStorage[id])
chat_status = '未读';
}
if(from=='me'){
if(type=='text'){
$("#recordchat-main").append('<li class="item-time"><span class="time">'+time+'</span></li><li class="item-myself" id='+id+' mid='+mid+'><div class="no-read">'+chat_status+'</div><div class="text">'+data+'</div></li>');
}
else if(type=='picture'){
$("#recordchat-main").append('<li class="item-time"><span class="time">'+time+'</span></li><li class="selfPrture item-myself" id='+id+' mid='+mid+'><div class="text"><img class="img_url" src='+data+' alt="内容图片" /></div></li>');
}
}else{
if(type=='text'){
$("#recordchat-main").append(
'<li class="item-time"><span class="time">'+time+'</span></li>'
+'<li class="item-friend" id='+mid+'>'
+'<div class="figure"><img src="http://beta.app.first-job-1.com'+chatIcon+'" alt="人物头像" /></div>'//<img src="http://beta.app.first-job-1.com'+message.ext.chatIcon+'">
+'<div class="text">'+data+'</div>'
+'</li>');
}
else if(type=='picture'){
$("#recordchat-main").append(
'<li class="item-time"><span class="time">'+time+'</span></li>'
+'<li class="item-friend selfPrture" id='+mid+'>'
+'<div class="figure"><img src="http://beta.app.first-job-1.com'+chatIcon+'" alt="人物头像" /></div>'
+'<div class="text"><img class="img_url" src='+data+' alt="内容图片" /></div>'
+'</li>');
}
}
}
//刷新时读取本地消息 展示在聊天窗口
//此处为jq写法插入数据,正式版本是改成tpl模板渲染,想想用2句代码就可以展示消息 还是蛮激动的。
}
else{
//console.log("暂无聊天记录")
}
$(this).find('span.notice-badge').hide();//隐藏数量小红点
})
//发送文本消息函数
var sendPrivateText = function(msg_content){
var name = top.$(".figure>a>img").attr("src");//该企业用户头像
var chatIcon = name.slice(31);//该企业用户头像地址裁剪
var chatName = top.$(".figure>span").text();//该企业用户名称
var chatId = $(".chatId").val();//chat-id
var id = conn.getUniqueId();
var msg = new WebIM.message('txt', id);
msg.set({
msg: msg_content,
to: chatId,
roomType: false,
chatType: 'singleChat',
success: function(id, serverMsgId){
$(".chat-input").html("");
getNowFormatDate();
var emojiMessage = WebIM.utils.parseEmoji(msg_content); //表情解析工具
showChatMessage(timestamp3,str,message.data,message.id);
var recordchat = document.getElementById('chat-list');
recordchat.scrollTop = recordchat.scrollHeight;//发送消息时,滚动条出现在底部
//暂时插入数据,tpl绑定模板插入渲染
$("#btn-send").addClass("disabled");//发送按钮置灰
detailMessage(timestamp3,emojiMessage,"me",'text',id,serverMsgId,chatId,'','未读','','','');
},
fail: function(e){
//console.log("fail")
}
});
msg.body.chatType = 'singleChat';
// msg.setGroup('singleChat');
msg.body.ext.chatName= chatName;//传递chatName->ios,java
msg.body.ext.chatIcon= chatIcon;//传递chatIcon->ios,java
conn.send(msg.body);
};
//发送图片消息函数
var sendPrivateFile = function () {
var name = top.$(".figure>a>img").attr("src");//该企业用户头像
var chatIcon = name.slice(31);//该企业用户头像地址裁剪
var chatName = top.$(".figure>span").text();//该企业用户名称
var chatId = $(".chatId").val();//chat-id
var id = conn.getUniqueId(); // 生成本地消息id
var msg = new WebIM.message('img', id); // 创建图片消息
var input = document.getElementById('image'); // 选择图片的input id必填
var file = WebIM.utils.getFileUrl(input); // 将图片转化为二进制文件
var allowType = {'jpg': true,'gif': true,'png': true,'bmp': true};
var img_url;
if (file.filetype.toLowerCase() in allowType) {
var option = {
apiUrl: WebIM.config.apiURL,
file: file,
to: chatId, // 接收消息对象
roomType: false,
chatType: 'singleChat',
onFileUploadError: function () { // 消息上传失败
//console.log('图片发送失败!');
},
onFileUploadComplete: function (aa) { // 消息上传成功
//console.log('onFileUploadComplete');
img_url = aa.uri+"/"+aa.entities[0].uuid;
},
success: function (id, serverMsgId) { // 消息发送成功
//console.log(id)
getNowFormatDate()
showChatMessage(timestamp3,str,message.data,message.id);
var recordchat = document.getElementById('chat-list');
recordchat.scrollTop = recordchat.scrollHeight;
detailMessage(timestamp3,img_url,"me",'picture',file.id,serverMsgId,chatId,'','','','','');
// showMessage();
},
flashUpload: WebIM.flashUpload
};
msg.set(option);
msg.body.chatType = 'singleChat';
msg.body.ext.chatName= chatName;//传递chatName->ios,java
msg.body.ext.chatIcon= chatIcon;//传递chatIcon->ios,java
conn.send(msg.body);
}
};
//时间函数
function getNowFormatDate(){
var timestamp1 = new Date().getTime();//获取时间戳此方法准确
var timestamp2 = new Date(timestamp1);
timestamp3 = timestamp2.toLocaleDateString().replace(/\//g, "-") + " " + timestamp2.toTimeString().substr(0, 8);
}
//展示消息 根据个人项目需求,正式版本会使用tpl模板
function showChatMessage(timestamp3,str,data,id){
$("#recordchat-main").append(
'<li class="item-time"><span class="time">'+timestamp3+'</span></li>'
+'<li class="item-friend" id='+id+'>'
+'<div class="figure"><img src="http://beta.app.first-job-1.com'+str+'"></div>'
+'<div class="text">'+data+'</div>'
+'</li>');
}
//绑定数据模板
function detailMessage(timestamp3,data,from,type,id,mid,chatId,chatIcon,status,filename,reserve,number){
var localContent = new Array();
if (localStorage[chatId]) {
localContent = JSON.parse(localStorage[chatId]);
}
localContent[localContent.length]= { 'message':[{
'time':timestamp3,
'data':data,//数据
'from':from,//谁发的
'type':type,//文本类型 text,file,picture
'id':id,//消息id
'mid':mid,
'chatId':chatId,
'chatIcon':chatIcon,
'status':status,//状态
'filename':filename, //文件名字
'reserve':reserve,//已读未读
'number':number,//未读数量
}]};
localStorage[chatId] = JSON.stringify(localContent);//存储本地;
//console.log(JSON.parse(localStorage[chatId]))
}
//根据每一条消息的id存储本地 存储未读已读状态
function statusRead(status,mid,id,reserve){
var localStatusContent = new Array();
if(localStatusContent[id]){
localStatusContent = JSON.parse(localStorage[id]);
}
localStatusContent[localStatusContent.length]={
'status':status,
'mid':mid,
'id':id,
'reserve':reserve
};
localStorage[id] = JSON.stringify(localStatusContent);
//console.log(localStorage[id])
}
项目需求只有表情和图片,文件视频,音频其实都是属于文件的一种,若有帮助请赞赏一下吧。
收起阅读 »
【有问必答】有温度,有态度,有速度的IMGeek社区!
5分钟,是一个从提问到解答的总时长,有温度有态度有速度!IMGeek循着极客们开放、分享、协作、创新的精神,努力构建一个具有服务质量保障(Service Level Assurance , SLA)的社区。
-IMGeek社区“鲁迅”
在IMGeek社区里征集到一批热心的技术专家,得到他们的承诺自愿回复IMGeek社区问题。只要你在IMGeek社区发布问题,专家们将会收到消息提醒,并及时回复。
当然,如果你在提交一个问题之前,可以先搜索一下,说不定你要提的问题已经有人提过并且得到解答。这样可以省却不少你的时间。
最后提醒一下提问的小伙伴,如果您的问题被解决,占用您一秒钟时间将回复设置为最佳回复,方便后面遇到相同问题的同学快速找到答案!
现在,从一个提问开始你的IMGeek社区之旅。 收起阅读 »
教您5分钟体验客户互动云
欢迎各位小伙伴们注册环信客户互动云!
为了帮助您快速了解环信客户互动云系统,我们为您准备了一个“商城”Demo。您可以先使用“商城”Demo进行客服体验。试用期短暂,机不可失,快来集成体验吧!
请先打开客服后台按照以下步骤添加app渠道关联
添加关联后 您就可以模拟客服场景体验啦
扫码下方二维码,下载并安装访客端DEMO【环信移动客服】APP
打开【环信移动客服】APP,点击右下角的【设置】按钮后,点击右上角【扫一扫】,扫描关联app页面下方二维码,将该客服访客端体验DEMO与您的客服账号关联起来,即可体验与客服聊天或与客户聊天
注意:
初次在苹果手机上使用“商城”Demo时,需要进入“设置 > 通用 > 设备管理 > EaseMob Inc.”页面,点击“信任EaseMob Inc.”。
您也可以 快速集成环信SDK,体验来自您APP的 真实用户咨询
Android SDK:请参考CEC Android SDK集成
iOS SDK:请参考CEC iOS SDK集成
也可以直接查看APP集成指南
您还可以为您的其他渠道接入客户互动云
微博快速集成指南
微信快速集成指南
网页快速集成指南
在您使用中遇到任何问题,可从以下 4 个途径得到解答!
开发文档 - 常见问题的解决方案在这里都能找到!
开发文档收录了所有常见问题,并按照“新手上路、客服模式、管理员模式、多渠道集成、第三方系统对接”对内容进行分类,同时,文档右上角提供模糊搜索。
在线技术咨询 - 超快的问题响应机制,专业技术团队在线解答!
点击【客服后台】-【管理员模式】-【技术支持】-【联系客服】,输入您的问题即可。
环信社区- 使用者交流专区,召唤老司机搞定技术难题!
山不在高,有仙则灵,社区不在大,有大神就行!
电话咨询 - 最直接的方式,专职客服一对一解答!
咨询热线:400-612-1986
感谢读到这里的您,下方附上最新鲜的集成说明文档,据说看完走桃花呦 收起阅读 »
环信公开课19期回放:快速搭建一套直播答题系统实操
本着对技术的热衷,对环信的眷恋和对党的忠诚,环信生态圈开发者基于环信即时通讯云写了直播答题开源项目-小信竞答。作为国内首个直播答题开源项目,“小信竞答”发表在IMGeek社区就受到了技术小伙伴们的好评,鉴于大家对直播答题的热情,3月29日举办了主题为“直播答题开源项目”的环信公开课,直播讲解如何快速搭建一套直播答题系统。公开课第19期回放
课程大纲:
直播答题技术架构
1. 【同步问答】环信IM下发问题到海量用户确保毫秒级必达
2. 【视频美颜】环信人脸特效实时视频动态捕捉,贴纸美颜多种特效
3. 【无上限聊天室】环信直播聊天室高并发异步架构,弹性扩容应对流量峰值
4. 【智能反垃圾】环信智能反垃圾实时消息过滤及关键词屏蔽,及时规避风险
5. 【红包功能】奖金统计发放
6. 【复活卡、开黑组团】新玩法层出不穷
“小信竞答”源码分享
1. 运行效果演示,现场互动答题送礼
2. 源码解读,国内首个开源直播答题项目
3. 经验之鉴,快速搭建一套直播答题系统实操
环信直播聊天室
1. 高并发异步架构,弹性扩容应对流量峰值
2. 多种消息格式,文字、表情、位置、扩展消息
3. 实时配置消息分级策略,重要消息优先必答
4. 后台管理审核功能,直播数据统计
5. 智能反垃圾,自定义敏感词过滤
6. 快速集成demo高质量代码示例
7. 聊天室人数无上限
小信竞答项目源码:github源码地址
感谢合作伙伴APICLloud,APKBUS,码客,七牛云,icustomer对本期环信公开课的大力支持
收起阅读 »
iOS 自定义cell
简书地址:自定义cell(一)https://www.jianshu.com/p/8befabfdc4f0
自定义cell(二)https://www.jianshu.com/p/85921a5da8d0
收起阅读 »
自定义cell(二)https://www.jianshu.com/p/85921a5da8d0
收起阅读 »
【环信征文】|PHP WEBIM整合到项目(一条龙服务包括保存聊天记录到本地数据库)
我是用think PHP做的项目,我们的需求是用IM跟他人聊天并且保存聊天记录到本地。我把整个环信文件下载了下来放在了项目的js文件里。并且没有做单独的页面渲染直接用的环信原来的页面,所以大部分功能都写在了控制器里最后跳转到IM的页面。
第一步,IM集成
这个可以参照文档,但是这个文档好像是过时的,如果集成失败可以上官方登陆页面 http://webim.easemob.com/ 把页面加载的文件抄下来。
第二步,自动登陆和他人聊天
此步要先把网站上的用户注册一个环信账号,然后自动登陆跳过环信的登陆页面,然后把对方加为好友(此步如果对方没有环信账号也需要把好友注册一个环信账号)。上代码
逻辑是这么实现的,因为在控制器里做的操作,所以登陆信息要保存在cookie里。里面的方法都可以在http://api-docs.easemob.com/#/%E8%8E%B7%E5%8F%96token这个环信的服务器端集成Swagger文档里找到,然后用Curl调用,或者一个更简单的方法,有一个完整的类可以用,链接在此https://github.com/easemob/emchat-server-examples/blob/master/emchat-server-php/easemobtest.php,简单的curl是这么用的
好此步就此完成
第三步,头像、昵称 渲染
这步我的处理方法是把你网站用户的头像和昵称查出来然后存在cookie里,然后在demojs文件里进行调用,这步主要麻烦的地方是需要在demo.js的文件里找到页面渲染的地方,大约有8个地方(23288,27321,29764,29862,30101,31211,31360,31465)
这三步完成基本就完成了IM整合进项目了,还有一个保存聊天记录到本地,我没有用官方的接口来下载历史记录,听说下载下来是一个包,而且数据还会有丢失,所以我的做法是每次发送消息调用自己的Ajax来进行保存数据库操作,然后新在打开聊天的时候请求后台获得聊天记录。
好,我在demojs里找到了2个发送消息时候的方法,一个是发送文字和表情,一个是发送图片。sendText(27399)这个方法是发送文字和表情的
msg.boy里面可以获得到你需要保存在数据库里的所有信息,chat_time是因为聊天记录的时间格式是2018/4/9 上午9:53:03这种的所以我自己加上去的。
pictureChange(22977)这个方法是发送图片的,这里的url非常重要,而且在msg.set在外面拿不到所以我放在了方法里面调用的Ajax,这个url 是
https://a1.easemob.com/1110170708115611/thdsmartapp/chatfiles/d50bf690-3b9f-11e8-8a3c-57a542779a8b
这样的二进制一会需要下载到本地或者上传到你需要的图片存储空间,我放在了七牛上。
保存文字信息非常简单,就像下图一样就可以了,下面的做的判断是当消息类型是图片的时候调用一下图片上传方法
这个图片是图片上传方法,我在后台用CURL进行了上传操作,最开始我先把图片保存在了本地然后再上传,后来我找了二进制可以上传,改成了这个样子。(值得注意的是我用CURL调用的url返回的值会有一个红色的符号,所以我用substr给它截掉了,之前没发现怎么都找不到图片。最后发现是database文件的编码有问题改成utf-8之后截取字符串的那步可以去掉了)这里的接口就是一个普通的上传方法然后返回的是图片名称。这样聊天记录也保存完毕了。
表结构
下一步是读取聊天记录releaseChatRecord这个方法是demojs每次你点击聊天窗口都会触发的方法,我把ajax放在这这里。
图片里的targetId就是你当天聊天的好友id,自己的id怎么得到不需要说了吧,你肯定是在登陆状态才能聊天。Demo.chatRecord这个就是需要替换的聊天记录,Ajax请求后台
这里注意的是要获得聊天记录需要进行红圈里的判断,来分别你给我发的消息和你给我发的消息,我之前犯得错误没判断最后只获得了我给他人发的消息记录。。。循环里的是构造demojs需要用的json数据。这个对象结构可以在releaseChatRecord方法里console.log()出来然后自己拼装。
这样已经基本完成了最后上成功后的图片
还有个问题没处理就是未送达那个文字,因为我构造的数据里没有加上read=true.你可以选择把IM里这个功能关闭或者在数据里加上去。
这样保存聊天记录的方式是每次发送都进行保存如果人数太多或太频繁可能会出现问题,以后可能会换成调用官网的获得历史记录的接口。
收起阅读 »
第一步,IM集成
这个可以参照文档,但是这个文档好像是过时的,如果集成失败可以上官方登陆页面 http://webim.easemob.com/ 把页面加载的文件抄下来。
第二步,自动登陆和他人聊天
此步要先把网站上的用户注册一个环信账号,然后自动登陆跳过环信的登陆页面,然后把对方加为好友(此步如果对方没有环信账号也需要把好友注册一个环信账号)。上代码
逻辑是这么实现的,因为在控制器里做的操作,所以登陆信息要保存在cookie里。里面的方法都可以在http://api-docs.easemob.com/#/%E8%8E%B7%E5%8F%96token这个环信的服务器端集成Swagger文档里找到,然后用Curl调用,或者一个更简单的方法,有一个完整的类可以用,链接在此https://github.com/easemob/emchat-server-examples/blob/master/emchat-server-php/easemobtest.php,简单的curl是这么用的
好此步就此完成
第三步,头像、昵称 渲染
这步我的处理方法是把你网站用户的头像和昵称查出来然后存在cookie里,然后在demojs文件里进行调用,这步主要麻烦的地方是需要在demo.js的文件里找到页面渲染的地方,大约有8个地方(23288,27321,29764,29862,30101,31211,31360,31465)
这三步完成基本就完成了IM整合进项目了,还有一个保存聊天记录到本地,我没有用官方的接口来下载历史记录,听说下载下来是一个包,而且数据还会有丢失,所以我的做法是每次发送消息调用自己的Ajax来进行保存数据库操作,然后新在打开聊天的时候请求后台获得聊天记录。
好,我在demojs里找到了2个发送消息时候的方法,一个是发送文字和表情,一个是发送图片。sendText(27399)这个方法是发送文字和表情的
msg.boy里面可以获得到你需要保存在数据库里的所有信息,chat_time是因为聊天记录的时间格式是2018/4/9 上午9:53:03这种的所以我自己加上去的。
pictureChange(22977)这个方法是发送图片的,这里的url非常重要,而且在msg.set在外面拿不到所以我放在了方法里面调用的Ajax,这个url 是
https://a1.easemob.com/1110170708115611/thdsmartapp/chatfiles/d50bf690-3b9f-11e8-8a3c-57a542779a8b
这样的二进制一会需要下载到本地或者上传到你需要的图片存储空间,我放在了七牛上。
保存文字信息非常简单,就像下图一样就可以了,下面的做的判断是当消息类型是图片的时候调用一下图片上传方法
这个图片是图片上传方法,我在后台用CURL进行了上传操作,最开始我先把图片保存在了本地然后再上传,后来我找了二进制可以上传,改成了这个样子。(值得注意的是我用CURL调用的url返回的值会有一个红色的符号,所以我用substr给它截掉了,之前没发现怎么都找不到图片。最后发现是database文件的编码有问题改成utf-8之后截取字符串的那步可以去掉了)这里的接口就是一个普通的上传方法然后返回的是图片名称。这样聊天记录也保存完毕了。
表结构
下一步是读取聊天记录releaseChatRecord这个方法是demojs每次你点击聊天窗口都会触发的方法,我把ajax放在这这里。
图片里的targetId就是你当天聊天的好友id,自己的id怎么得到不需要说了吧,你肯定是在登陆状态才能聊天。Demo.chatRecord这个就是需要替换的聊天记录,Ajax请求后台
这里注意的是要获得聊天记录需要进行红圈里的判断,来分别你给我发的消息和你给我发的消息,我之前犯得错误没判断最后只获得了我给他人发的消息记录。。。循环里的是构造demojs需要用的json数据。这个对象结构可以在releaseChatRecord方法里console.log()出来然后自己拼装。
这样已经基本完成了最后上成功后的图片
还有个问题没处理就是未送达那个文字,因为我构造的数据里没有加上read=true.你可以选择把IM里这个功能关闭或者在数据里加上去。
这样保存聊天记录的方式是每次发送都进行保存如果人数太多或太频繁可能会出现问题,以后可能会换成调用官网的获得历史记录的接口。
收起阅读 »
基于环信的仿QQ即时通讯的简单实现
概述
今天看了环信的API,就利用下午的时间动手试了试,然后做了一个小Demo。详细
我的博客地址
之前一直想实现聊天的功能,但是感觉有点困难,今天看了环信的API,就利用下午的时间动手试了试,然后做了一个小Demo。
因为没有刻意去做聊天软件,花的时间也不多,然后界面就很简单,都是一些基本知识,如果觉得功能简单,可以自行添加,我这就不多介绍了。
照例先来一波动态演示:
功能很简单,注册用户 —> 用户登录 —> 选择聊天对象 —> 开始聊天
使用到的知识点:
依赖的库
首先是看了郭神的《第二行代码》做了聊天界面,用的是RecyclerView
a. 消息类的封装
c. RecyclerView适配器
d. RecyclerView初始化
就是一些基本的初始化,我就不赘述了,讲一下添加数据的细节处理
2. 环信API的简单应用
官网有详细的API介绍 环信及时通讯V3.0,我这里就简单介绍如何简单集成
a. 环信开发账号的注册
环信官网
b. SDK导入
你可以直接下载然后拷贝工程的libs目录下
Android Studio可以直接添加依赖
将以下代码放到项目根目录的build.gradle文件里
c. 清单文件配置
在自定义Application的onCreate中初始化
注册要在子线程中执行
项目文件截图:
到此,一个简单的及时聊天Demo已经完成,功能很简单,如果需要添加额外功能的话,可以自行参考官网,官网给出的教程还是很不错的!
最后希望大家能多多支持我,需要你们的支持喜欢!! 收起阅读 »
今天看了环信的API,就利用下午的时间动手试了试,然后做了一个小Demo。详细
我的博客地址
之前一直想实现聊天的功能,但是感觉有点困难,今天看了环信的API,就利用下午的时间动手试了试,然后做了一个小Demo。
因为没有刻意去做聊天软件,花的时间也不多,然后界面就很简单,都是一些基本知识,如果觉得功能简单,可以自行添加,我这就不多介绍了。
照例先来一波动态演示:
功能很简单,注册用户 —> 用户登录 —> 选择聊天对象 —> 开始聊天
使用到的知识点:
- RecyclerView
- CardView
- 环信的API的简单使用
依赖的库
compile 'com.android.support:appcompat-v7:24.2.1'1、聊天页面
compile 'com.android.support:cardview-v7:24.1.1'
compile 'com.android.support:recyclerview-v7:24.0.0'
首先是看了郭神的《第二行代码》做了聊天界面,用的是RecyclerView
a. 消息类的封装
public class MSG {b. RecyclerView子项的布局
public static final int TYPE_RECEIVED = 0;//消息的类型:接收
public static final int TYPE_SEND = 1; //消息的类型:发送
private String content;//消息的内容
private int type; //消息的类型
public MSG(String content, int type) {
this.content = content;
this.type = type;
}
public String getContent() {
return content;
}
public int getType() {
return type;
}
}
<LinearLayout这是左边的部分,至于右边应该也就简单了。我用CardView把ImageView包裹起来,这样比较好看。效果如下:
android:id="@+id/ll_msg_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
<!-- 设置点击效果为水波纹(5.0以上) -->
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal"
android:padding="2dp">
<android.support.v7.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardCornerRadius="20dp"
app:cardPreventCornerOverlap="false"
app:cardUseCompatPadding="true">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="centerCrop"
android:src="@mipmap/man" />
</android.support.v7.widget.CardView>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/message_left"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_msg_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="10dp"
android:textColor="#fff" />
</LinearLayout>
</LinearLayout>
c. RecyclerView适配器
public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.MyViewHolder> {这部分应该也没什么问题,就是适配器的创建,我之前的文章也讲过 传送门:简单粗暴——RecyclerView
private List<MSG> mMsgList;
public MsgAdapter(List<MSG> mMsgList) {
this.mMsgList = mMsgList;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = View.inflate(parent.getContext(), R.layout.item_msg, null);
MyViewHolder holder = new MyViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
MSG msg = mMsgList.get(position);
if (msg.getType() == MSG.TYPE_RECEIVED){
//如果是收到的消息,显示左边布局,隐藏右边布局
holder.llLeft.setVisibility(View.VISIBLE);
holder.llRight.setVisibility(View.GONE);
holder.tv_Left.setText(msg.getContent());
} else if (msg.getType() == MSG.TYPE_SEND){
//如果是发送的消息,显示右边布局,隐藏左边布局
holder.llLeft.setVisibility(View.GONE);
holder.llRight.setVisibility(View.VISIBLE);
holder.tv_Right.setText(msg.getContent());
}
}
@Override
public int getItemCount() {
return mMsgList.size();
}
static class MyViewHolder extends RecyclerView.ViewHolder{
LinearLayout llLeft;
LinearLayout llRight;
TextView tv_Left;
TextView tv_Right;
public MyViewHolder(View itemView) {
super(itemView);
llLeft = (LinearLayout) itemView.findViewById(R.id.ll_msg_left);
llRight = (LinearLayout) itemView.findViewById(R.id.ll_msg_right);
tv_Left = (TextView) itemView.findViewById(R.id.tv_msg_left);
tv_Right = (TextView) itemView.findViewById(R.id.tv_msg_right);
}
}
}
d. RecyclerView初始化
就是一些基本的初始化,我就不赘述了,讲一下添加数据的细节处理
btSend.setOnClickListener(new View.OnClickListener() {至此界面已经结束了,接下来就是数据的读取
@Override
public void onClick(View v) {
String content = etInput.getText().toString().trim();
if (!TextUtils.isEmpty(content)){
...//环信部分的发送消息
MSG msg = new MSG(content, MSG.TYPE_SEND);
mList.add(msg);
//当有新消息时,刷新RecyclerView中的显示
mAdapter.notifyItemInserted(mList.size() - 1);
//将RecyclerView定位到最后一行
mRecyclerView.scrollToPosition(mList.size() - 1);
etInput.setText("");
}
}
});
2. 环信API的简单应用
官网有详细的API介绍 环信及时通讯V3.0,我这里就简单介绍如何简单集成
a. 环信开发账号的注册
环信官网
创建应用得到Appkey后面要用
b. SDK导入
你可以直接下载然后拷贝工程的libs目录下
Android Studio可以直接添加依赖
将以下代码放到项目根目录的build.gradle文件里
repositories {在你的module的build.gradle里加入以下代码
maven { url "https://raw.githubusercontent.com/HyphenateInc/Hyphenate-SDK-Android/master/repository" }
}
android {如果想使用不包含音视频通话的sdk,用compile 'com.hyphenate [:hyphenate-sdk-lite:] 3.2.3'
//use legacy for android 6.0
useLibrary 'org.apache.http.legacy'
}
dependencies {
compile 'com.android.support:appcompat-v7:23.4.0'
//Optional compile for GCM (Google Cloud Messaging).
compile 'com.google.android.gms:play-services-gcm:9.4.0'
compile 'com.hyphenate:hyphenate-sdk:3.2.3'
}
c. 清单文件配置
<?xml version="1.0" encoding="utf-8"?>APP打包混淆
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="Your Package"
android:versionCode="100"
android:versionName="1.0.0">
<!-- Required -->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:name="Your Application">
<!-- 设置环信应用的AppKey -->
<meta-data android:name="EASEMOB_APPKEY" android:value="Your AppKey" />
<!-- 声明SDK所需的service SDK核心功能-->
<service android:name="com.hyphenate.chat.EMChatService" android:exported="true"/>
<service android:name="com.hyphenate.chat.EMJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"
/>
<!-- 声明SDK所需的receiver -->
<receiver android:name="com.hyphenate.chat.EMMonitorReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<data android:scheme="package"/>
</intent-filter>
<!-- 可选filter -->
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
</application>
</manifest>
-keep class com.hyphenate.** {*;}d. 初始化SDK
-dontwarn com.hyphenate.**
在自定义Application的onCreate中初始化
public class MyApplication extends Application {e. 注册和登陆
private Context appContext;
@Override
public void onCreate() {
super.onCreate();
EMOptions options = new EMOptions();
options.setAcceptInvitationAlways(false);
appContext = this;
int pid = android.os.Process.myPid();
String processAppName = getAppName(pid);
// 如果APP启用了远程的service,此application:onCreate会被调用2次
// 为了防止环信SDK被初始化2次,加此判断会保证SDK被初始化1次
// 默认的APP会在以包名为默认的process name下运行,如果查到的process name不是APP的process name就立即返回
if (processAppName == null || !processAppName.equalsIgnoreCase(appContext.getPackageName())) {
Log.e("--->", "enter the service process!");
// 则此application::onCreate 是被service 调用的,直接返回
return;
}
//初始化
EMClient.getInstance().init(getApplicationContext(), options);
//在做打包混淆时,关闭debug模式,避免消耗不必要的资源
EMClient.getInstance().setDebugMode(true);
}
private String getAppName(int pID) {
String processName = null;
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List l = am.getRunningAppProcesses();
Iterator i = l.iterator();
PackageManager pm = this.getPackageManager();
while (i.hasNext()) {
ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo) (i.next());
try {
if (info.pid == pID) {
processName = info.processName;
return processName;
}
} catch (Exception e) {
// Log.d("Process", "Error>> :"+ e.toString());
}
}
return processName;
}
}
注册要在子线程中执行
//注册失败会抛出HyphenateExceptionf. 发送消息
EMClient.getInstance().createAccount(username, pwd);//同步方法
EMClient.getInstance().login(userName,password,new EMCallBack() {//回调
@Override
public void onSuccess() {
EMClient.getInstance().groupManager().loadAllGroups();
EMClient.getInstance().chatManager().loadAllConversations();
Log.d("main", "登录聊天服务器成功!");
}
@Override
public void onProgress(int progress, String status) {
}
@Override
public void onError(int code, String message) {
Log.d("main", "登录聊天服务器失败!");
}
});
//创建一条文本消息,content为消息文字内容,toChatUsername为对方用户或者群聊的id,后文皆是如此g. 接收消息
EMMessage message = EMMessage.createTxtSendMessage(content, toChatUsername);
//发送消息
EMClient.getInstance().chatManager().sendMessage(message);
msgListener = new EMMessageListener() {接收消息的监听器分别需要在OnResume()和OnDestory()方法中注册和取消注册
@Override
public void onMessageReceived(List<EMMessage> messages) {
//收到消息
String result = messages.get(0).getBody().toString();
String msgReceived = result.substring(5, result.length() - 1);
Log.i(TAG, "onMessageReceived: " + msgReceived);
final MSG msg = new MSG(msgReceived, MSG.TYPE_RECEIVED);
runOnUiThread(new Runnable() {
@Override
public void run() {
mList.add(msg);
mAdapter.notifyDataSetChanged();
mRecyclerView.scrollToPosition(mList.size() - 1);
}
});
}
@Override
public void onCmdMessageReceived(List<EMMessage> messages) {
//收到透传消息
}
@Override
public void onMessageRead(List<EMMessage> list) {
}
@Override
public void onMessageDelivered(List<EMMessage> list) {
}
@Override
public void onMessageChanged(EMMessage message, Object change) {
//消息状态变动
}
};
EMClient.getInstance().chatManager().addMessageListener(msgListener);//注册需要注意的是,当接收到消息,需要在主线程中更新适配器,否则会不能及时刷新出来
EMClient.getInstance().chatManager().removeMessageListener(msgListener);//取消注册
项目文件截图:
到此,一个简单的及时聊天Demo已经完成,功能很简单,如果需要添加额外功能的话,可以自行参考官网,官网给出的教程还是很不错的!
最后希望大家能多多支持我,需要你们的支持喜欢!! 收起阅读 »
源码下载:全国首个直播答题开源项目了解一下!!!(基于环信即时通讯云)
2018年伊始,全民直播答题浪潮来袭,一度被认为是一个新的互联网风口,王思聪凭借在现象级产品《冲顶大会》上疯狂"撒币"一时风光无二,凭借超高奖金和超低门槛吸引了大量网民参与和市场的目光。正因为直播答题是一种通过极低的成本来推动APP获客、保留存、拉活跃的新模式,各类直播答题APP如雨后春笋般进入大家的视野,越来越多企业希望赶上这波风口,快速搭建一套直播答题系统。作为一名环信生态圈资深开发者,本着对技术的热衷,对环信的眷恋和对党的忠诚,基于环信即时通讯云写了“小信竞答”这个直播答题开源项目,目前项目源码已全部免费开放,希望对有需求的企业和开发者提供一个思路和参考。
小信竞答技术架构图:
整个项目分为管理员端,观众端和服务端,首先在服务端预设好题目,由管理员发起直播开始答题,服务端收到指令将12道题目利用环信IM推送到观众端,观众端收到题目开始答题,将答案返回给服务端由服务端进行判断,如果答题正确进入下一题,答题错误判断是否使用复活卡,这里要注意的是需要加一个复活卡的使用次数判断。
在整个答题过程中,管理员端会定时去服务端查询答题结果,等到全部答题结束,点击结束本次答题,服务端将计算好的结果返回并发放奖金,使用环信IM推送将答题结果推给观众端。
小信竞答效果图
关于直播间:
直播间由直播画面和聊天室两个部分组成,“小信竞答”的聊天室使用环信聊天室,集成比较简单,基础版就能支持5000人在线聊天,增值服务版聊天室人数无上限,可以去环信官网注册一个开发者账号,创建应用将APPKEY替换成自己的;环信直播聊天室可以集成所有市场主流CDN厂商的推拉流功能(腾讯,七牛,UCloud,网宿等)。
环信直播聊天室特点
This is Title
1、采用支持高并发的异步架构,轻松应对千万级并发请求; 各项基础服务集群化,确保系统高可用性; 系统冗余度高,容量评估体系完善,弹性扩容应对流量峰值;
2、支持各种消息格式:文字、表情、图片、声音、视频、附件、位置、扩展消息;
3、支持实时配置的消息分级策略,确保重要消息优先必达;
4、支持直播聊天室后台管理及审核功能,提供直播相关数据统计;
5、提供智能反垃圾和自定义敏感词过滤功能;
6、快速集成,demo提供高质量代码示例,可根据运营情况随时扩展;
7、聊天室人数无上限
小信竞答项目源码: github源码地址
写到最后:
小信竞答源码全部开放,仅供学习和参考,如果作为商业用途,按照广电总局对网络直播答题节目管理的,需要 “网络视听许可证、主持人持证、还有通过审批发放的节目备案号”,三证缺一不可,未持有《信息网络传播视听节目许可证》的任何机构和个人,一律不得开办网络直播答题节目。
本月底《环信公开课第19期-直播答题开源项目》将线上讲解“小信竞答”实现思路,手把手教您从零开始搭建一个直播答题项目,扫码加入公开课微信群与大牛面对面交流。
收起阅读 »
【环信征文】| iOS集成环信推送,最详细流程(证书创建、环信集成、测试)
这几天项目里又用到了环信的推送,虽然之前做过,但是很久不做还是有很多细节没有注意到,所以还是决定从头开始做一遍,把每一个环节都详细记录下来,同样的把每一个坑也记录下来.方便自己以后做的时候忘记哪个流程了可以在看一遍.我很能理解那种遇到问题网上百度一堆类似答案但是并不好使的情况,所以我会将我在项目中遇到的问题都贴出来,希望能给大家带来些许参考和帮助,
一.推送的原理和流程(着急做推送的可以跳过这一步)
首先给大家推荐一个介绍推送机制很优秀的帖子:http://www.jianshu.com/p/e347f999ed95 ,里面关于本地推送和远程推送的介绍都很详细,至少我看了感觉还是收获很多的.尤其是里面有几张图片不知道是博主在哪里找的,但是真的是一看就透,太赞了,所以我果断盗过来了0.0. 这里我对推送的流程做了一个简单的叙述,力求用最简单的语言能说明整个推送的机制.
先搬过来一张图再说
再搬一张:
当我们的苹果手机联网的时候,会自动与苹果的服务器建立长连接,长连接的好处有很多,比如系统升级、时间校准、数据传输和响应比较快以及数据可以保持最新状态等功能.上面这两张图片简单的讲述了推送的流程:
- 1.首先我们需要将自己设备的UDID和应用的Bundle Identifier发送到苹果的服务器,然后苹果的服务器会返回给我们一个DeviceToken,这个在我看来就是创建推送证书和描述文件的过程.
- 2.我们将包含手机和应用标示的打包文件上传到做推送的服务器上去,当我们从推送服务器的后台发起推送消息的时候,推送服务器会将我们的DeviceToken和需要发送的消息Message发送到苹果的APNS(Apple push Notification Service)服务器.
- 3.当苹果的服务器收到DeviceToken和需要发送的消息Message时,会根据DeviceToken中的UDID查找设备,根据DeviceToken中的Bundle Identfier查找该应用,并将Message发送到该设备上.
下面是以QQ服务器为栗子说明的即时通讯的机制:
图片已经说得够详细明了了,我就不插嘴了,下面开始我们的工程.
二.具体流程
我们创建一个名为TestDemo的工程,我是使用Xcode8.1来开发的,工程名为PushDemo,创建好的工程界面如下(Xcode8)
从Xcode8之后,Xcode提供了自动管理证书的功能,这个用起来很方便,我目前在工程中用到的最多的地方就是创建好一个Demo之后,如果想真机运行的话,那么只需要在Team选择框里选择我的开发账户,接下来下面会出现一个加载提示圈,等它加载完了就可以在真机上运行了,这个过程实际上是Xcode使用你当前的BundleId去该账户的开发这中心创建了对应的AppId和描述文件,但是我们既然是作为一个开发流程的记录,就自己来创建这些东西,所以,我们取消选择Automatically manage signing选项.此时界面如下:
好了,我们要正式开始我们的工作了GO GO GO!
1. 首先我们先去官网创建AppID和描述文件.
我们是要集成推送的,所以我们需要用到cer文件,这个东西实际上就是苹果给开发者颁发的一个证书,我们需要将它导入到我们的AppId配置里,否则的话是无法集成推送的,还记得安装应该的时候会提示"无法安装为认证发布者的应用"之类的信息么,我猜测这个cer文件就是我们身份的标示,使我们开发的应用可以供人们正常安装使用,关于证书有一篇很详细的帖子,希望了解证书之类信息的看官可以去瞅瞅:http://m.blog.csdn.net/article/details?id=8617788
创建cer文件的流程很简单,打开"钥匙串访问"(虽然很好找,但是还是把图贴出来吧,怕小朋友迷路)
打开钥匙串之后点击"从证书颁发机构请求证书"
邮箱和常用名随便填写,记住下面的选择框选择"存储到磁盘"
点击存储
已经在桌面保存了
到此,我们已经创建好了cer文件,接着我们去开发者中心创建AppId和描述文件
2. 创建AppId和描述文件
首先进入开发者中心,百度搜索Apple Developer,(哎 真的是详细到家了啊,我都人不下去了)
上图
输入开发者账户,登录进去
你将看到这个页面
点击看到:
输入AppId文件名和BundleId
选中下面的PushNotifications
点击Continue:
点击register:
点击Done回到AppId列表页面
在AppId列表页面可以看到我们的AppID了
但是,还没有完成,因为我们是要做推送的,所以需要上传我们的cer文件
,点击我们的AppId,在展开的详情里可以看到:
Push Notification的两个指示灯还是黄色的状态,我们要将它启用,点击Edit,在点开的页面里滑动至底部,记得要选中Push Notification按钮,接着点击上方的开发证书下的创建证书按钮:
点击Continue
点击 choose file:
将我们从开发机构请求的证书传上去,之后点击Register:
点击Register之后的页面,点击download,将其下载到桌面上,download之后记得点击done完成文件创建:
桌面上的文件:
现在我们就完成了给AppID创建开发者证书,然后我们要给它创建发布者证书,点击Done之后回到AppIds列表,如果找不到的话,点击右边的App IDs
点开项目的AppId,此时界面如下,点击最下面的CreateCertificate,开始给AppID创建发布者证书,给AppId创建发布者证书流程跟创建开发者证书是一样的!给AppId创建发布者证书流程跟创建开发者证书是一样的!给AppId创建发布者证书流程跟创建开发者证书是一样的!重要的事情说三遍!!因为我不贴出来创建发布证书的图了,所以各位根据创建开发证书的流程再走一遍就好,同样也要将发布者证书下载到本地.:
当创建好之后在回到这个页面时,应该显示如下所示:
此时本地我们下载的文件如下:
然后将这两个证书拖到钥匙串里,步骤如下:
首先打开钥匙串:
然后先点击:系统-证书,然后将两个文件拖进去,会提示你输入开机密码,输入就好了(建议添加之前先对这个界面截屏,添加完之后可以对比刚刚添加了那些文件)
添加完之后是这个样子,画框的是我们的证书
然后选择左边的"登录"选项,可以看到我们刚才创建的证书
选中第一个证书,然后右键(你懂得右键的意思),选择导出...
选择导出为P12文件,存储在桌面上,获取到P12文件.对这两个证书进行同样的操作.(记得标题有(Develop)的起名为Product文件,第二个证书导出的时候起名为Develop,名字可以自己定,只是为了区别)
然后会提示你输入密码,这里我设置的密码是zx123456,自己设定好一定要记住,一会儿要用.
然后可以在桌面上看到我们导出的P12文件啦
现在我们就完成了所有的证书的创建,可以去环信上创建我们的应用啦.
3.创建真机调试文件以及导入到项目中
因为必须要进行真机测试,而且我们关闭了自动管理证书,就导致Xcode8不会自动帮我们生成证书,所以我们要自己创建真机调试证书并导入到项目中去,流程如下:
创建描述文件:
选择开发模式,下一步:
选择对应的AppID,选择我们刚才创建的AppId:
选择开发团队,我一般都是全选的,下一步:
选择真机调试的机器,全选,下一步:
下一步:
将创建好的描述文件下载下来,放到桌面上:
创建好的描述文件:
首先选择debug模式下载的真机调试描述文件:
选择桌面上刚刚下载的描述文件:
使用同样的步骤,选择Release模式下的真机调试文件,一模一样的操作,不贴图了.两个文件都导入进去之后,插上真机,就可以进行真机调试了.
4.在环信创建我们的应用
首先百度搜索环信,打开他们的官网,先注册账户,注册过的可以跳过了,上图:
注册的时候选择"注册即时通讯云"
注册的时候需要填写各种信息,按照格式填写就好了,填写完之后登陆,点击创建应用
填写应用信息
填写完如下图咯
然后需要上传我们的P12文件,图片很清晰- -,不多说,第一次我选择上传的是生产证书:
第二次上传开发证书:
至此,我们的证书开发也都上传完了,路漫漫其修远兮,开始集成环信到代码里吧
5.集成环信到项目中
首先在这里下载最新的SDK(截至到写本文时最新的SDK为)
http://www.easemob.com/download/im 环信推送SDK下载链接
点击iOS的最新SDK下载,这里下载的是V3.x的SDK
下载到桌面是这个鬼样子
我们只需要将画圈的两个文件夹导进去工程里就好了,其他的用不上
导进去之后文件列表是这样,编译会出错别急,慢慢改.
向项目里添加需要的库
上面的图片是截取的环信官方文档,我添加完是这个样子的:
方便复制库名的文字:
CoreMedia.framework
AudioToolbox.framework
AVFoundation.framework
MobileCoreServices.framework
ImageIO.framework
libc++.dylib
libz.dylib
libstdc++.6.0.9.dylib
libsqlite3.dylib
(如果使用的是 xcode7,后缀为 tbd。)
这一步很重要,因为SDK 不支持 bitcode,所以要将 Build Settings → Linking → Enable Bitcode 中设置 NO。
command+B编译工程,大量爆红.别着急,修改我们的PCH文件就好了
在PCH文件添加
```
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#endif
```
将我们所有定义和添加的头文件和宏定义,都放在#ifdef __OBJC__和#endif中间
就可以解决这个问题.
然后在项目里打开推送:
6.测试是否集成成功
首先,我们去环信的后台给我们的应用添加一个用户
用户名我设置成了:13051698888 密码设置成了:222222
接着我们要去appledate.m文件里添加东西了,很重要一步,废话不多说,直接贴出来需要配置的代码,直接拿去用0.0,需要添加的东西我在注释里注释的很明白...
记得要导进去头文件
#import "EMSDK.h"
```
@interface AppDelegate ()<EMChatManagerDelegate>
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//AppKey:注册的AppKey,点击"应用概述"可以看到AppKey,粘贴过来就可以。
//apnsCertName:推送证书名,填写你的开发证书或者发布证书名,就是上传到环信后台的两个中的一个,什么环境下测试使用什么环境的证书。
EMOptions *options = [EMOptions optionsWithAppkey:@"1192161108178165#testpushdemo"];
options.apnsCertName = @"Develop";
[[EMClient sharedClient] initializeSDKWithOptions:options];
//登录环信 这里使用的是我刚才在环信后台创建的账户名和密码,使用这个账户登录,到时候如果在后台给客户端发消息的话,就可以找到该用户
[[EMClient sharedClient] loginWithUsername:@"13051698888"
password:@"222222"
completion:^(NSString *aUsername, EMError *aError) {
if (!aError) {
NSLog(@"环信登陆成功");
EMPushOptions *emoptions = [[EMClient sharedClient] pushOptions];
//设置有消息过来时的显示方式:1.显示收到一条消息 2.显示具体消息内容.
//自己可以测试下
emoptions.displayStyle = EMPushDisplayStyleSimpleBanner;
[[EMClient sharedClient] updatePushOptionsToServer];
} else {
NSLog(@"环信登陆失败");
}
}];
/**
注册APNS离线推送 iOS8 注册APNS
*/
if ([application respondsToSelector:@selector(registerForRemoteNotifications)]) {
[application registerForRemoteNotifications];
UIUserNotificationType notificationTypes = UIUserNotificationTypeBadge |
UIUserNotificationTypeSound |
UIUserNotificationTypeAlert;
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:notificationTypes categories:nil];
[application registerUserNotificationSettings:settings];
}
else{
UIRemoteNotificationType notificationTypes = UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeSound |
UIRemoteNotificationTypeAlert;
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:notificationTypes];
}
//添加监听在线推送消息
[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];
return YES;
}
//监听环信在线推送消息
- (void)messagesDidReceive:(NSArray *)aMessages{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"收到环信通知" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
[alertView show];
//aMessages是一个对象,包含了发过来的所有信息,怎么提取想要的信息我会在后面贴出来.
}
// 将得到的deviceToken传给SDK
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
[[EMClient sharedClient] bindDeviceToken:deviceToken];
}
// 注册deviceToken失败
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
NSLog(@"error -- %@",error);
}
// APP进入后台
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[EMClient sharedClient] applicationDidEnterBackground:application];
}
// APP将要从后台返回
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[[EMClient sharedClient] applicationWillEnterForeground:application];
}
```
上面的几个方法在appdelegate里是必须重写的,不然会直接导致推送不成功.其中.需要重点说明的是:
- 只有在应用完全退出被杀掉的状态下,才可以收到环信推送的通知;
- 如果要发送在线的通知,需要在messagesDidReceive方法里获取到环信推送的消息之后给用户发起一个本地通知,这个大家可以自己研究下.
- 通过设置emoptions.displayStyle = EMPushDisplayStyleSimpleBanner;(上面代码有)可以设置有通知过来的时候的显示方式,显示一个提示或者显示完整的消息.
- 上传证书下面填写的应用包名,指的是你的BundleID !!!!我在这里踩了坑,切记!!.
测试推送:
1. 在应用完全退出的情况下(使用在环信注册的账户登录一次,确认登录成功之后再完全退出),选中我们的用户,点击发送消息:
点击发送:
测试结果:
2.程序在线的时候测试推送,还是发送"你好啊",然后我们在messagesDidReceive拦截环信的EMMessage对象,针对EMMessage对象的解析方式如下,完整的抽取环信推送消息的方法:
```
- (void)messagesDidReceive:(NSArray *)aMessages{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"收到环信通知" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
[alertView show];
for (EMMessage *message in aMessages) {
EMMessageBody *msgBody = message.body;
switch (msgBody.type) {
case EMMessageBodyTypeText:
{
// 收到的文字消息
EMTextMessageBody *textBody = (EMTextMessageBody *)msgBody;
NSString *txt = textBody.text;
NSLog(@"收到的文字是 txt -- %@",txt);
}
break;
case EMMessageBodyTypeImage:
{
// 得到一个图片消息body
EMImageMessageBody *body = ((EMImageMessageBody *)msgBody);
NSLog(@"大图remote路径 -- %@" ,body.remotePath);
NSLog(@"大图local路径 -- %@" ,body.localPath); // // 需要使用sdk提供的下载方法后才会存在
NSLog(@"大图的secret -- %@" ,body.secretKey);
NSLog(@"大图的W -- %f ,大图的H -- %f",body.size.width,body.size.height);
NSLog(@"大图的下载状态 -- %u",body.downloadStatus);
// 缩略图sdk会自动下载
NSLog(@"小图remote路径 -- %@" ,body.thumbnailRemotePath);
NSLog(@"小图local路径 -- %@" ,body.thumbnailLocalPath);
NSLog(@"小图的secret -- %@" ,body.thumbnailSecretKey);
NSLog(@"小图的W -- %f ,大图的H -- %f",body.thumbnailSize.width,body.thumbnailSize.height);
NSLog(@"小图的下载状态 -- %u",body.thumbnailDownloadStatus);
}
break;
case EMMessageBodyTypeLocation:
{
EMLocationMessageBody *body = (EMLocationMessageBody *)msgBody;
NSLog(@"纬度-- %f",body.latitude);
NSLog(@"经度-- %f",body.longitude);
NSLog(@"地址-- %@",body.address);
}
break;
case EMMessageBodyTypeVoice:
{
// 音频sdk会自动下载
EMVoiceMessageBody *body = (EMVoiceMessageBody *)msgBody;
NSLog(@"音频remote路径 -- %@" ,body.remotePath);
NSLog(@"音频local路径 -- %@" ,body.localPath); // 需要使用sdk提供的下载方法后才会存在(音频会自动调用)
NSLog(@"音频的secret -- %@" ,body.secretKey);
NSLog(@"音频文件大小 -- %lld" ,body.fileLength);
NSLog(@"音频文件的下载状态 -- %u" ,body.downloadStatus);
NSLog(@"音频的时间长度 -- %u" ,body.duration);
}
break;
case EMMessageBodyTypeVideo:
{
EMVideoMessageBody *body = (EMVideoMessageBody *)msgBody;
NSLog(@"视频remote路径 -- %@" ,body.remotePath);
NSLog(@"视频local路径 -- %@" ,body.localPath); // 需要使用sdk提供的下载方法后才会存在
NSLog(@"视频的secret -- %@" ,body.secretKey);
NSLog(@"视频文件大小 -- %lld" ,body.fileLength);
NSLog(@"视频文件的下载状态 -- %u" ,body.downloadStatus);
NSLog(@"视频的时间长度 -- %u" ,body.duration);
NSLog(@"视频的W -- %f ,视频的H -- %f", body.thumbnailSize.width, body.thumbnailSize.height);
// 缩略图sdk会自动下载
NSLog(@"缩略图的remote路径 -- %@" ,body.thumbnailRemotePath);
NSLog(@"缩略图的local路径 -- %@" ,body.thumbnailLocalPath);
NSLog(@"缩略图的secret -- %@" ,body.thumbnailSecretKey);
NSLog(@"缩略图的下载状态 -- %u" ,body.thumbnailDownloadStatus);
}
break;
case EMMessageBodyTypeFile:
{
EMFileMessageBody *body = (EMFileMessageBody *)msgBody;
NSLog(@"文件remote路径 -- %@" ,body.remotePath);
NSLog(@"文件local路径 -- %@" ,body.localPath); // 需要使用sdk提供的下载方法后才会存在
NSLog(@"文件的secret -- %@" ,body.secretKey);
NSLog(@"文件文件大小 -- %lld" ,body.fileLength);
NSLog(@"文件文件的下载状态 -- %u" ,body.downloadStatus);
}
break;
default:
break;
}
}
}
```
发送成功之后打印结果如下:
```
2016-12-01 16:03:26.060088 PushDemo[1392:450230] 收到的文字是 txt -- 你好啊
```
三.结语
至此,我们就成功集成了环信推送到我们的项目中.另外提供一些在做推送的时候经常会用到的小方法
- 设置应用图标右上角数字角标.
```
UIApplication *application = [UIApplication sharedApplication]; [application setApplicationIconBadgeNumber:3];
```
补充一个环信坑:
编译通过后,运行的时候爆炸,提示:dyld: Library not loaded: @rpath/Hyphenate.framework/Hyphenate
需要设置Hyphenate.frameword的Embedded Binariew属性
- 如果推送证书那里没看特别明白的话,提供一个创建推送证书的链接:http://www.jianshu.com/p/78282e16db66
- 设置推送过来时候的_apns_昵称:
```
[[EMClient sharedClient] setApnsNickname:@"推送昵称"];
```
一直在抽时间写这篇博客,平常比较忙没有大块的时间来写和记录这些东西,不过东拼西凑,没事写一点最终还是写完了.希望看到这里的小伙伴都可以成功集成环信推送,有问题可以在下面留言,我看到了肯定会回复的.希望围观的小伙伴可以不吝指点这篇博客中出现的错误,不管是文字错误还是逻辑错误等等,我一定会尽快修正,不给后人留坑.....
给出项目gitHub地址:
https://github.com/TheRuningAnt/PushDemo.git 喜欢的话,记得给小星星哈
排版可能有点问题,原博客地址:http://blog.csdn.net/mumubumaopao/article/details/53423393
收起阅读 »
一.推送的原理和流程(着急做推送的可以跳过这一步)
首先给大家推荐一个介绍推送机制很优秀的帖子:http://www.jianshu.com/p/e347f999ed95 ,里面关于本地推送和远程推送的介绍都很详细,至少我看了感觉还是收获很多的.尤其是里面有几张图片不知道是博主在哪里找的,但是真的是一看就透,太赞了,所以我果断盗过来了0.0. 这里我对推送的流程做了一个简单的叙述,力求用最简单的语言能说明整个推送的机制.
先搬过来一张图再说
再搬一张:
当我们的苹果手机联网的时候,会自动与苹果的服务器建立长连接,长连接的好处有很多,比如系统升级、时间校准、数据传输和响应比较快以及数据可以保持最新状态等功能.上面这两张图片简单的讲述了推送的流程:
- 1.首先我们需要将自己设备的UDID和应用的Bundle Identifier发送到苹果的服务器,然后苹果的服务器会返回给我们一个DeviceToken,这个在我看来就是创建推送证书和描述文件的过程.
- 2.我们将包含手机和应用标示的打包文件上传到做推送的服务器上去,当我们从推送服务器的后台发起推送消息的时候,推送服务器会将我们的DeviceToken和需要发送的消息Message发送到苹果的APNS(Apple push Notification Service)服务器.
- 3.当苹果的服务器收到DeviceToken和需要发送的消息Message时,会根据DeviceToken中的UDID查找设备,根据DeviceToken中的Bundle Identfier查找该应用,并将Message发送到该设备上.
下面是以QQ服务器为栗子说明的即时通讯的机制:
图片已经说得够详细明了了,我就不插嘴了,下面开始我们的工程.
二.具体流程
我们创建一个名为TestDemo的工程,我是使用Xcode8.1来开发的,工程名为PushDemo,创建好的工程界面如下(Xcode8)
从Xcode8之后,Xcode提供了自动管理证书的功能,这个用起来很方便,我目前在工程中用到的最多的地方就是创建好一个Demo之后,如果想真机运行的话,那么只需要在Team选择框里选择我的开发账户,接下来下面会出现一个加载提示圈,等它加载完了就可以在真机上运行了,这个过程实际上是Xcode使用你当前的BundleId去该账户的开发这中心创建了对应的AppId和描述文件,但是我们既然是作为一个开发流程的记录,就自己来创建这些东西,所以,我们取消选择Automatically manage signing选项.此时界面如下:
好了,我们要正式开始我们的工作了GO GO GO!
1. 首先我们先去官网创建AppID和描述文件.
我们是要集成推送的,所以我们需要用到cer文件,这个东西实际上就是苹果给开发者颁发的一个证书,我们需要将它导入到我们的AppId配置里,否则的话是无法集成推送的,还记得安装应该的时候会提示"无法安装为认证发布者的应用"之类的信息么,我猜测这个cer文件就是我们身份的标示,使我们开发的应用可以供人们正常安装使用,关于证书有一篇很详细的帖子,希望了解证书之类信息的看官可以去瞅瞅:http://m.blog.csdn.net/article/details?id=8617788
创建cer文件的流程很简单,打开"钥匙串访问"(虽然很好找,但是还是把图贴出来吧,怕小朋友迷路)
打开钥匙串之后点击"从证书颁发机构请求证书"
邮箱和常用名随便填写,记住下面的选择框选择"存储到磁盘"
点击存储
已经在桌面保存了
到此,我们已经创建好了cer文件,接着我们去开发者中心创建AppId和描述文件
2. 创建AppId和描述文件
首先进入开发者中心,百度搜索Apple Developer,(哎 真的是详细到家了啊,我都人不下去了)
上图
输入开发者账户,登录进去
你将看到这个页面
点击看到:
输入AppId文件名和BundleId
选中下面的PushNotifications
点击Continue:
点击register:
点击Done回到AppId列表页面
在AppId列表页面可以看到我们的AppID了
但是,还没有完成,因为我们是要做推送的,所以需要上传我们的cer文件
,点击我们的AppId,在展开的详情里可以看到:
Push Notification的两个指示灯还是黄色的状态,我们要将它启用,点击Edit,在点开的页面里滑动至底部,记得要选中Push Notification按钮,接着点击上方的开发证书下的创建证书按钮:
点击Continue
点击 choose file:
将我们从开发机构请求的证书传上去,之后点击Register:
点击Register之后的页面,点击download,将其下载到桌面上,download之后记得点击done完成文件创建:
桌面上的文件:
现在我们就完成了给AppID创建开发者证书,然后我们要给它创建发布者证书,点击Done之后回到AppIds列表,如果找不到的话,点击右边的App IDs
点开项目的AppId,此时界面如下,点击最下面的CreateCertificate,开始给AppID创建发布者证书,给AppId创建发布者证书流程跟创建开发者证书是一样的!给AppId创建发布者证书流程跟创建开发者证书是一样的!给AppId创建发布者证书流程跟创建开发者证书是一样的!重要的事情说三遍!!因为我不贴出来创建发布证书的图了,所以各位根据创建开发证书的流程再走一遍就好,同样也要将发布者证书下载到本地.:
当创建好之后在回到这个页面时,应该显示如下所示:
此时本地我们下载的文件如下:
然后将这两个证书拖到钥匙串里,步骤如下:
首先打开钥匙串:
然后先点击:系统-证书,然后将两个文件拖进去,会提示你输入开机密码,输入就好了(建议添加之前先对这个界面截屏,添加完之后可以对比刚刚添加了那些文件)
添加完之后是这个样子,画框的是我们的证书
然后选择左边的"登录"选项,可以看到我们刚才创建的证书
选中第一个证书,然后右键(你懂得右键的意思),选择导出...
选择导出为P12文件,存储在桌面上,获取到P12文件.对这两个证书进行同样的操作.(记得标题有(Develop)的起名为Product文件,第二个证书导出的时候起名为Develop,名字可以自己定,只是为了区别)
然后会提示你输入密码,这里我设置的密码是zx123456,自己设定好一定要记住,一会儿要用.
然后可以在桌面上看到我们导出的P12文件啦
现在我们就完成了所有的证书的创建,可以去环信上创建我们的应用啦.
3.创建真机调试文件以及导入到项目中
因为必须要进行真机测试,而且我们关闭了自动管理证书,就导致Xcode8不会自动帮我们生成证书,所以我们要自己创建真机调试证书并导入到项目中去,流程如下:
创建描述文件:
选择开发模式,下一步:
选择对应的AppID,选择我们刚才创建的AppId:
选择开发团队,我一般都是全选的,下一步:
选择真机调试的机器,全选,下一步:
下一步:
将创建好的描述文件下载下来,放到桌面上:
创建好的描述文件:
首先选择debug模式下载的真机调试描述文件:
选择桌面上刚刚下载的描述文件:
使用同样的步骤,选择Release模式下的真机调试文件,一模一样的操作,不贴图了.两个文件都导入进去之后,插上真机,就可以进行真机调试了.
4.在环信创建我们的应用
首先百度搜索环信,打开他们的官网,先注册账户,注册过的可以跳过了,上图:
注册的时候选择"注册即时通讯云"
注册的时候需要填写各种信息,按照格式填写就好了,填写完之后登陆,点击创建应用
填写应用信息
填写完如下图咯
然后需要上传我们的P12文件,图片很清晰- -,不多说,第一次我选择上传的是生产证书:
第二次上传开发证书:
至此,我们的证书开发也都上传完了,路漫漫其修远兮,开始集成环信到代码里吧
5.集成环信到项目中
首先在这里下载最新的SDK(截至到写本文时最新的SDK为)
http://www.easemob.com/download/im 环信推送SDK下载链接
点击iOS的最新SDK下载,这里下载的是V3.x的SDK
下载到桌面是这个鬼样子
我们只需要将画圈的两个文件夹导进去工程里就好了,其他的用不上
导进去之后文件列表是这样,编译会出错别急,慢慢改.
向项目里添加需要的库
上面的图片是截取的环信官方文档,我添加完是这个样子的:
方便复制库名的文字:
CoreMedia.framework
AudioToolbox.framework
AVFoundation.framework
MobileCoreServices.framework
ImageIO.framework
libc++.dylib
libz.dylib
libstdc++.6.0.9.dylib
libsqlite3.dylib
(如果使用的是 xcode7,后缀为 tbd。)
这一步很重要,因为SDK 不支持 bitcode,所以要将 Build Settings → Linking → Enable Bitcode 中设置 NO。
command+B编译工程,大量爆红.别着急,修改我们的PCH文件就好了
在PCH文件添加
```
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#endif
```
将我们所有定义和添加的头文件和宏定义,都放在#ifdef __OBJC__和#endif中间
就可以解决这个问题.
然后在项目里打开推送:
6.测试是否集成成功
首先,我们去环信的后台给我们的应用添加一个用户
用户名我设置成了:13051698888 密码设置成了:222222
接着我们要去appledate.m文件里添加东西了,很重要一步,废话不多说,直接贴出来需要配置的代码,直接拿去用0.0,需要添加的东西我在注释里注释的很明白...
记得要导进去头文件
#import "EMSDK.h"
```
@interface AppDelegate ()<EMChatManagerDelegate>
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//AppKey:注册的AppKey,点击"应用概述"可以看到AppKey,粘贴过来就可以。
//apnsCertName:推送证书名,填写你的开发证书或者发布证书名,就是上传到环信后台的两个中的一个,什么环境下测试使用什么环境的证书。
EMOptions *options = [EMOptions optionsWithAppkey:@"1192161108178165#testpushdemo"];
options.apnsCertName = @"Develop";
[[EMClient sharedClient] initializeSDKWithOptions:options];
//登录环信 这里使用的是我刚才在环信后台创建的账户名和密码,使用这个账户登录,到时候如果在后台给客户端发消息的话,就可以找到该用户
[[EMClient sharedClient] loginWithUsername:@"13051698888"
password:@"222222"
completion:^(NSString *aUsername, EMError *aError) {
if (!aError) {
NSLog(@"环信登陆成功");
EMPushOptions *emoptions = [[EMClient sharedClient] pushOptions];
//设置有消息过来时的显示方式:1.显示收到一条消息 2.显示具体消息内容.
//自己可以测试下
emoptions.displayStyle = EMPushDisplayStyleSimpleBanner;
[[EMClient sharedClient] updatePushOptionsToServer];
} else {
NSLog(@"环信登陆失败");
}
}];
/**
注册APNS离线推送 iOS8 注册APNS
*/
if ([application respondsToSelector:@selector(registerForRemoteNotifications)]) {
[application registerForRemoteNotifications];
UIUserNotificationType notificationTypes = UIUserNotificationTypeBadge |
UIUserNotificationTypeSound |
UIUserNotificationTypeAlert;
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:notificationTypes categories:nil];
[application registerUserNotificationSettings:settings];
}
else{
UIRemoteNotificationType notificationTypes = UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeSound |
UIRemoteNotificationTypeAlert;
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:notificationTypes];
}
//添加监听在线推送消息
[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];
return YES;
}
//监听环信在线推送消息
- (void)messagesDidReceive:(NSArray *)aMessages{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"收到环信通知" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
[alertView show];
//aMessages是一个对象,包含了发过来的所有信息,怎么提取想要的信息我会在后面贴出来.
}
// 将得到的deviceToken传给SDK
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
[[EMClient sharedClient] bindDeviceToken:deviceToken];
}
// 注册deviceToken失败
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
NSLog(@"error -- %@",error);
}
// APP进入后台
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[EMClient sharedClient] applicationDidEnterBackground:application];
}
// APP将要从后台返回
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[[EMClient sharedClient] applicationWillEnterForeground:application];
}
```
上面的几个方法在appdelegate里是必须重写的,不然会直接导致推送不成功.其中.需要重点说明的是:
- 只有在应用完全退出被杀掉的状态下,才可以收到环信推送的通知;
- 如果要发送在线的通知,需要在messagesDidReceive方法里获取到环信推送的消息之后给用户发起一个本地通知,这个大家可以自己研究下.
- 通过设置emoptions.displayStyle = EMPushDisplayStyleSimpleBanner;(上面代码有)可以设置有通知过来的时候的显示方式,显示一个提示或者显示完整的消息.
- 上传证书下面填写的应用包名,指的是你的BundleID !!!!我在这里踩了坑,切记!!.
测试推送:
1. 在应用完全退出的情况下(使用在环信注册的账户登录一次,确认登录成功之后再完全退出),选中我们的用户,点击发送消息:
点击发送:
测试结果:
2.程序在线的时候测试推送,还是发送"你好啊",然后我们在messagesDidReceive拦截环信的EMMessage对象,针对EMMessage对象的解析方式如下,完整的抽取环信推送消息的方法:
```
- (void)messagesDidReceive:(NSArray *)aMessages{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"收到环信通知" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
[alertView show];
for (EMMessage *message in aMessages) {
EMMessageBody *msgBody = message.body;
switch (msgBody.type) {
case EMMessageBodyTypeText:
{
// 收到的文字消息
EMTextMessageBody *textBody = (EMTextMessageBody *)msgBody;
NSString *txt = textBody.text;
NSLog(@"收到的文字是 txt -- %@",txt);
}
break;
case EMMessageBodyTypeImage:
{
// 得到一个图片消息body
EMImageMessageBody *body = ((EMImageMessageBody *)msgBody);
NSLog(@"大图remote路径 -- %@" ,body.remotePath);
NSLog(@"大图local路径 -- %@" ,body.localPath); // // 需要使用sdk提供的下载方法后才会存在
NSLog(@"大图的secret -- %@" ,body.secretKey);
NSLog(@"大图的W -- %f ,大图的H -- %f",body.size.width,body.size.height);
NSLog(@"大图的下载状态 -- %u",body.downloadStatus);
// 缩略图sdk会自动下载
NSLog(@"小图remote路径 -- %@" ,body.thumbnailRemotePath);
NSLog(@"小图local路径 -- %@" ,body.thumbnailLocalPath);
NSLog(@"小图的secret -- %@" ,body.thumbnailSecretKey);
NSLog(@"小图的W -- %f ,大图的H -- %f",body.thumbnailSize.width,body.thumbnailSize.height);
NSLog(@"小图的下载状态 -- %u",body.thumbnailDownloadStatus);
}
break;
case EMMessageBodyTypeLocation:
{
EMLocationMessageBody *body = (EMLocationMessageBody *)msgBody;
NSLog(@"纬度-- %f",body.latitude);
NSLog(@"经度-- %f",body.longitude);
NSLog(@"地址-- %@",body.address);
}
break;
case EMMessageBodyTypeVoice:
{
// 音频sdk会自动下载
EMVoiceMessageBody *body = (EMVoiceMessageBody *)msgBody;
NSLog(@"音频remote路径 -- %@" ,body.remotePath);
NSLog(@"音频local路径 -- %@" ,body.localPath); // 需要使用sdk提供的下载方法后才会存在(音频会自动调用)
NSLog(@"音频的secret -- %@" ,body.secretKey);
NSLog(@"音频文件大小 -- %lld" ,body.fileLength);
NSLog(@"音频文件的下载状态 -- %u" ,body.downloadStatus);
NSLog(@"音频的时间长度 -- %u" ,body.duration);
}
break;
case EMMessageBodyTypeVideo:
{
EMVideoMessageBody *body = (EMVideoMessageBody *)msgBody;
NSLog(@"视频remote路径 -- %@" ,body.remotePath);
NSLog(@"视频local路径 -- %@" ,body.localPath); // 需要使用sdk提供的下载方法后才会存在
NSLog(@"视频的secret -- %@" ,body.secretKey);
NSLog(@"视频文件大小 -- %lld" ,body.fileLength);
NSLog(@"视频文件的下载状态 -- %u" ,body.downloadStatus);
NSLog(@"视频的时间长度 -- %u" ,body.duration);
NSLog(@"视频的W -- %f ,视频的H -- %f", body.thumbnailSize.width, body.thumbnailSize.height);
// 缩略图sdk会自动下载
NSLog(@"缩略图的remote路径 -- %@" ,body.thumbnailRemotePath);
NSLog(@"缩略图的local路径 -- %@" ,body.thumbnailLocalPath);
NSLog(@"缩略图的secret -- %@" ,body.thumbnailSecretKey);
NSLog(@"缩略图的下载状态 -- %u" ,body.thumbnailDownloadStatus);
}
break;
case EMMessageBodyTypeFile:
{
EMFileMessageBody *body = (EMFileMessageBody *)msgBody;
NSLog(@"文件remote路径 -- %@" ,body.remotePath);
NSLog(@"文件local路径 -- %@" ,body.localPath); // 需要使用sdk提供的下载方法后才会存在
NSLog(@"文件的secret -- %@" ,body.secretKey);
NSLog(@"文件文件大小 -- %lld" ,body.fileLength);
NSLog(@"文件文件的下载状态 -- %u" ,body.downloadStatus);
}
break;
default:
break;
}
}
}
```
发送成功之后打印结果如下:
```
2016-12-01 16:03:26.060088 PushDemo[1392:450230] 收到的文字是 txt -- 你好啊
```
三.结语
至此,我们就成功集成了环信推送到我们的项目中.另外提供一些在做推送的时候经常会用到的小方法
- 设置应用图标右上角数字角标.
```
UIApplication *application = [UIApplication sharedApplication]; [application setApplicationIconBadgeNumber:3];
```
补充一个环信坑:
编译通过后,运行的时候爆炸,提示:dyld: Library not loaded: @rpath/Hyphenate.framework/Hyphenate
需要设置Hyphenate.frameword的Embedded Binariew属性
- 如果推送证书那里没看特别明白的话,提供一个创建推送证书的链接:http://www.jianshu.com/p/78282e16db66
- 设置推送过来时候的_apns_昵称:
```
[[EMClient sharedClient] setApnsNickname:@"推送昵称"];
```
一直在抽时间写这篇博客,平常比较忙没有大块的时间来写和记录这些东西,不过东拼西凑,没事写一点最终还是写完了.希望看到这里的小伙伴都可以成功集成环信推送,有问题可以在下面留言,我看到了肯定会回复的.希望围观的小伙伴可以不吝指点这篇博客中出现的错误,不管是文字错误还是逻辑错误等等,我一定会尽快修正,不给后人留坑.....
给出项目gitHub地址:
https://github.com/TheRuningAnt/PushDemo.git 喜欢的话,记得给小星星哈
排版可能有点问题,原博客地址:http://blog.csdn.net/mumubumaopao/article/details/53423393
收起阅读 »
环信即时通讯云V3.3.8已发布,新增服务诊断接口,方便自定位接口问题
Android SDK V3.3.8
更新日志:Android更新 ios更新
SDK下载:下载地址 收起阅读 »
1.添加服务诊断接口ios SDK V3.3.8
2. 添加设置音频码率接口
3. 优化重连逻辑,减少重连次数
4. 添加社区版SDK注册用户,创建群组\聊天室达到数量限制的提示
新功能:服务端诊断接口介绍:
• 服务诊断接口, demo UI体现在“setting-debug-服务器诊断”
• 设置音频码率, 接口[EMCallOptions maxAudioKbps]
• 添加新的错误码(达到服务器上限),体现在创建用户,创建群组,创建聊天室
功能更新:
• [EMClient isLoggedIn]语义有所改变,原意是是否已经完成登录操作,现在的意思是是否成功登录过
服务诊断功能是为了在用户使用SDK出现登录失败等问题时用于辅助定位哪个接口服务出现问题.
服务诊断流程:
1.未登录账号诊断:
1.输入用户名和密码
2.验证用户名密码的合法性
3.诊断获取DNS列表接口
4.诊断获取Token接口
5.用户登录
7.用户登出
2.已登录账号诊断:
1.获取当前登录账户的账号和密码(无需用户输入)
2.诊断获取DNS列表接口
3.诊断获取Token接口
4.用户登录
说明: 已登录账号诊断无登出操作,以免影响已登录账户的正常使用.
使用说明:
1.Android:
1.登录界面: 右下方 "SDK接口诊断" link.(该link会被软键盘遮挡)
1.用户已登录: "设置-SDK接口诊断"
2.iOS:
“setting-debug-服务器诊断”
更新日志:Android更新 ios更新
SDK下载:下载地址 收起阅读 »
easeUI聊天界面中好友头像是如何传值的
easeUI聊天界面中好友头像是如何传值的
php服务器端怎么获取到用户聊天记录。
public function chatRecord($ql = '', $cursor = '', $limit = 20) { $ql = ! empty ( $ql ) ? "ql=" . $ql : "order+by+timestamp+desc"; $cursor = ! empty ( $cursor ) ? "&cursor=" . $cursor : ''; $url = $this->url . "chatmessages?" . $ql . "&limit=" . $limit . $cursor; $access_token = $this->token (); $header [] = 'Authorization: Bearer ' . $access_token; $result = $this->curl ( $url, '', $header, $type = "GET " ); echo $result; }
在网上看别人是这样写的,但不知道$ql的值是什么格式 收起阅读 »
环信客户互动云v5.32已发布,新增质检申诉功能
本次更新的主要内容为:客服模式
新增质检申诉功能,用于质检员完成会话质检后,客服可以向打分的质检员发起申诉,提交证据并要求复核质检结果。客服对质检结果发起申诉后,管理员和质检员可以查看客服的质检申诉详情,并对申诉进行处理。
新增质检申诉功能
质检申诉功能用于质检员完成会话质检后,若客服认为质检结果不合理,可以向打分的质检员发起申诉,提交证据并要求复核质检结果。
客服模式新增质量检查模块,包含“质检记录”和“申诉记录”两个页面。在质检记录页面,客服可以查询会话质检结果并发起申诉;在申诉记录页面,客服可以查询申诉的进展状态,与质检员沟通会话相关情况。
查询质检记录
进入“质量检查 > 质检记录”页面,可以查看自己的会话质检结果。支持根据时间段、渠道、质检员、关联、会话标签等指标进行筛选,并支持导出筛选后的质检记录。
在质量检查页面的列表中,点击任意会话,可以查看该会话的详细质检评分和备注。
发起质检申诉
在会话详情的“质检”页签,点击“质检申诉”按钮,并填写申诉主题和申诉理由,可以对会话的质检结果发起申诉。
质检申诉将由打分的质检员处理。
查询申诉记录
进入“质量检查 > 申诉记录”页面,可以查看自己的申诉记录,包括未开启、处理中和已结束的申诉。
申诉状态:
未开启:客服发起会话的质检申诉后,默认为“未开启”状态。
处理中:质检员开始进行申诉处理后,申诉为“处理中”状态。
已结束:质检员与客服沟通完毕并结束申诉处理后,申诉为“已结束”状态。
在申诉记录列表中,点击任意申诉,可以查看质检员对该申诉的评论,申诉流程的记录,以及提交评论(包含文字和附件)。
管理员模式
新增申诉管理功能
客服对质检结果发起申诉后,管理员和质检员可以查看客服的质检申诉详情,并对申诉进行处理。只有原打分的质检员能够修改申诉的会话的质检评分。
进入“管理员模式 > 质量检查 > 申诉管理”页面,查看所有未开启、处理中、已结束的质检申诉单。支持根据申诉单号搜索申诉单,以及支持根据时间段、申诉人筛选申诉单。
申诉状态:
- 未开启:客服发起会话的质检申诉后,默认为“未开启”状态。
- 处理中:质检员开始进行申诉处理后,申诉为“处理中”状态。
- 已结束:质检员与客服沟通完毕并结束申诉处理后,申诉为“已结束”状态。
在质检申诉列表中,点击任意申诉,可以查看该申诉的详情,包括创建人、会话ID、申诉主题、申诉理由、评论、申诉流程的记录等。
点击申诉详情右下角的“开始进行”按钮,开始处理该申诉。然后:
点击“会话详情”按钮,进入会话详情页修改质检评分(注:只有原打分的质检员能够修改申诉的会话的质检评分)。
在评论区,输入评论内容,对申诉进行评论。评论对发起申诉的客服可见。
完成申诉处理后,点击申诉详情右下角的“结束申诉”按钮,将申诉状态修改为“已结束”。
新增“访客完成满意度评价”事件
自定义事件推送功能,新增“访客完成满意度评价”事件。支持在访客完成对会话的满意度评价时,将满意度评价详情(包含满意度评价、会话、访客、客服信息)推送到接收事件的服务器,方便企业对客户满意度进行统计、留档。
推送“访客完成满意度评价”事件:
[list=1]
关于“访客完成满意度评价”事件的消息内容和注释,请查看环信官网文档“自定义事件推送”章节。
注:自定义事件推送为标准版/旗舰版增值服务。如需开通,请提供租户ID并联系环信商务经理。
Android客服工作台
当前版本:V3.7
新功能/优化:
- 新增自定义表情功能(增值服务)
- 优化告警功能部分提醒
环信客户互动云更新文档地址
环信客户互动云登陆地址 收起阅读 »
2017 本土开源大盘点!年度编程语言和开源项目 TOP 榜
摘要:年度最受国内开发者喜爱的编程语言?最受欢迎的开源项目?为你呈现最详细的国内开发者画像。
在即将过去的2017年,作为程序员的你过得怎么样呢?在这一年中你是否参与过开源项目的创建或运营?今天为大家带来最详细的本土开源现状报告,为你展现国内开发者喜爱的编程语言、最受欢迎的国内开源项目等丰富信息。
1. 国内开发者的地域分布
2. 国内开发者的性别分布
3. 2017 年新增756 K 开源项目——Star 数增长排行
4. 2017 年新增756 K 开源项目——Fork 数增长排行
5. 2017 年,更多人开始参与开源项目
6. 国内开源项目的语言占比是什么情况?
7. 国内开源项目的功能分类是什么情况?
8.国内开发者最喜爱的编程语言
9. 2017 年增速最快的语言
本文数据由 码云 2017年度数据报告 提供,码云是开源中国旗下的云端协作开发平台,上线四年来用户突破200万,托管项目近300万,已成为经过大规模部署的成熟产品。码云的社区版的私有库完全免费,企业版(5人以下免费)则提供代码托管、项目管理、文档协作的一体化开发服务,竭诚为广大本土开发者服务。 收起阅读 »
webIM发送的文件,能接收到,但是收到的消息中没有文件大小
在webIM中发送附件消息,接收到的消息中文件大小为0