注册
环信即时通讯云

环信即时通讯云

单聊、群聊、聊天室...
环信开发文档

环信开发文档

环信FAQ

环信FAQ

集成常见问题及答案
RTE开发者社区

RTE开发者社区

汇聚音视频领域技术干货,分享行业资讯
技术讨论区

技术讨论区

技术交流、答疑
资源下载

资源下载

收集了海量宝藏开发资源
iOS Library

iOS Library

不需要辛辛苦苦的去找轮子, 这里都有
Android Library

Android Library

不需要辛辛苦苦的去找轮子, 这里都有

swift集成环信(1)

    1. 为了swift工程能使用OC编写的环信文件,我们需要一个桥接文件。在这里有个简单的方法,我们创建一个OC的Test类。如下,点击 Create Bridging Header 。系统就自动为我们生成了一个 桥接文件。 以后要在swift文件中...
继续阅读 »
 



 
1. 为了swift工程能使用OC编写的环信文件,我们需要一个桥接文件。在这里有个简单的方法,我们创建一个OC的Test类。如下,点击 Create Bridging Header 。系统就自动为我们生成了一个 桥接文件。 以后要在swift文件中引用的OC类 我们就需要把OC的头文件(*.h文件)导入到桥接文件里面。


100.png



ps. 如果操作失误没有自动提示生成,请自己创建并加入以下设置


200.png



2. 首先根据官网SDK导入流程 导入SDK 以及 easeUI.


2.png





3. 导入所需要的静态库, 此版本不包含实时语音。向Build Phases → Link Binary With Libraries 中添加依赖库
CoreMedia.framework
AudioToolbox.framework
AVFoundation.framework
MobileCoreServices.framework
ImageIO.framework
libc++.dylib
libz.dylib
libstdc++.6.0.9.dylib
libsqlite3.dylib

4. 将以下头文件加入到 Bridging Header 文件(Xcode自动生成的那个头文件)
#import "EMSDK.h" 
#import "EaseUI.h"
















此时运行工程大部分的时候会报错。 我们需要将EaseUI-Prefix.pch 加入到以下位置


222.png

// pch中的代码 
#ifdef __OBJC__ // 这个在自己写pch的时候也必须加
#import <UIKit/UIKit.h>
#import "EMSDK.h"
#import "EaseUI.h"
#endif

5. 此时运行工程一般来说就没什么问题了,我们来初始化SDK试试
   
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
EaseSDKHelper.shareHelper().easemobApplication(application,
didFinishLaunchingWithOptions: launchOptions,
appkey: "appkey",
apnsCertName: "apnsCertName",
otherConfig: ["SDKConfigEnableConsoleLogger":"1"])
return true
}

6. 集成SDK以及easeUI到此结束。
 
swiftDemo git 地址:https://github.com/myafer/SwiftEaseUIDemo 收起阅读 »

java.lang.UnsatisfiedLinkError: 的问题

java.lang.UnsatisfiedLinkError: 的问题 集中回复 导致产生unsatisfiedlink的几个原因 1. 相关信息    hyphenatechatsdk提供的指令集类型仅提供armeabi, arm64-v8a, x86三种,...
继续阅读 »
java.lang.UnsatisfiedLinkError: 的问题
集中回复
导致产生unsatisfiedlink的几个原因
1. 相关信息
   hyphenatechatsdk提供的指令集类型仅提供armeabi, arm64-v8a, x86三种,这里需要解释一下相关信息。
   armeabi和armeabi-v7a是相近似的指令集,v7a是增强型指令集,运行速度,效率均有所提高,他们都是32位指令,并且兼容。
   arm64-v8a对应arm 64位指令集,arm的64位策略和intel IA32不一样,intel的64位指令是兼容32位指令,32位指令编译的程序可以直接在64位机器上运行。
但是arm不是,arm64位和arm32位是彼此独立的指令系统,不兼容。arm这样设计的原因是因为运行在嵌入式上,设计指标更趋向于效率,和耗电考量。
   实际上arm64位芯片上同时包含着64指令处理器,和32位指令处理器,只不过两个处理器彼此独立。
2. 影响链接的限制条件
   armeabi的so实际上可以运行在64位机器上,只不过google增加了限制条件。
   a. Android4.x, 只要能找到so,就可以运行,so可以在armeabi,armeabi-v7a, arm64-v8a,so位置可以很随意。
   b. Android5.x开始,检查更加严格,会只有和芯片型号对应目录的so会安装到手机中。
      举个例子,开发环境下目录结构如下
          libs/armeabi/: libhyphenate.so libhyphenate_av.so    
          libs/armeabi-v7a: libmediadata.so 
      手机对应的指令集是armeabi-v7a,之中安装到手机的只有libmediadata.so
   c. Android6.x下,检查更加严格。有一条规则,之前测试又遇到,现在不太确认。
      libs/armeabi/: libhyphenate.so libhyphenate_av.so。
      libs/arm64-v8a (没有此目录)。
      在64位机器上也可以运行。
      但是作为开发者通常会依赖其他开发包,比如baiduMap,也会用其他so,不能让所有开发者都删掉libs/arm64-v8a的目录。
      不过开发者可以尝试下删除arm64-v8a,只留armeabi,这样安装包会很小,在各个平台上也能运行。google考量点是执行速率,更流畅的用户体验,作为开发者。服务提供者,我们希望apk尽可能小,对执行速度要求不高。
   d. armeabi和armeabi-v7a可以互换,现在市面上的手机很少有armeabi的,基本上是armeabi-v7a,或者64位的高端机器。
   e. 查看手机芯片型号: cat /proc/cpuinfo, 仔细看一下打印信息,能够看明白手机指令集,是32位还是64位。
   f. x86目录,通常对应虚拟机,很多开发者喜欢在genymotion上开发调试,这个就对应x86. x86和前面说的intel IA32是一回事,所以只提供32位的,也能在x86-64位机器上运行。
   g. 我们的so还依赖于libsqlite.so,不过由于这个包从来没有变化,使用的是系统默认提供的(/system/lib/)。在Android 6.x及以下的平台可以运行。
      Android7.x执行更严格的安全检查,禁止使用系统目录的内容。所以如果希望在7.x以上版本,需要把系统目录的libsqlite.so拷贝出来,也放在自己app对应指令目录下。   
      由于目前Android7.x市面上没有机型,所以目前不在考虑范围。目前微信...等大app都不能在7.x上运行。
   i. mips指令集的手机很少见,听说联想有出过,没见过。
   h. libs/armeabi/libhyphenate.so 和libs.without.audio/armeabi/libhyphenate.so是不同,libs/armeabi/libhyphenate.sos会依赖于libs/armeabi/libhyphenate_av.so,如果找不到会报java.lang.UnsatisfiedLinkError. 收起阅读 »

准备万全的应用还是无法通过App Store审核?别着急,可能是IPv6的锅

最近很多开发者反映,自己的iOS应用明明已经考虑的事无巨细了,却还是无法通过App Store的审核,是不是苹果的审核规则变的更严格了? 这是因为有些开发者可能没有注意到,5月4日的时候苹果在其开发者官网上有发过这样一条通知 这条通知中提到,201...
继续阅读 »
最近很多开发者反映,自己的iOS应用明明已经考虑的事无巨细了,却还是无法通过App Store的审核,是不是苹果的审核规则变的更严格了?

这是因为有些开发者可能没有注意到,5月4日的时候苹果在其开发者官网上有发过这样一条通知

449763-4d71f95315a415db.png


这条通知中提到,2016年6月1日开始,所有提交到App Store上的应用必须支持IPv6-only的网络。

苹果还在最新的 App Store Review Guidelines中增加了这样的表述

449763-61b6798c970a753d.png


同时苹果也声明,大部分应用不需要进行任何更改,因为NSURLSession和CFNetwork API已经支持IPv6了,所以使用NSURLSession和CFNetwork API进行开发的开发者们大可不必担心这个问题。
但是,如果你的应用使用了IPv4专属API或硬编码IP地址,那么就需要开发者对自己的应用做出一些更改了。

这样看来,那些应用没有过审的开发者们可能就恍然大悟了,不是自己不给力,而是苹果太淘气。

那么问题来了,到底怎样才能让自己的应用支持IPv6-only网络呢?

在苹果官方的文档中,给出了以下几个解决方案确保IPv6 DNS64/NAT64的兼容性:
使用高级网络框架


449763-d68469cfb5edf48b.jpg


不要直接使用IP地址
使用容量更大,足以存储IPv6地址的地址结构,比如sockaddr_storage
检查并删除不兼容IPv6 DNS64/NAT64的代码,比如:
inet_addr()
inet_aton()
inet_lnaof()
inet_makeaddr()
inet_netof()
inet_network()
inet_ntoa()
inet_ntoa_r()
bindresvport()
getipv4sourcefilter()
setipv4sourcefilter()
将IPv4类型的代码转换为IPv6的形式​

449763-f9476c5e1d3c1ed1.png


无需预检(Preflight),直接连接网络
使用系统API生成IPv6地址
 
那么如何用Mac在本地搭建一个IPv6的测试环境呢?这里是操作步骤:

1.确保你的Mac以非Wi-Fi的方式连接到互联网
2.通过Dock、LaunchPad或Apple菜单打开系统偏好设置
3.按住键盘上的Option键并点击共享,且不要松开Option键

449763-29f9fd2ae160a242.png


4.在共享服务列表中选择互联网共享


449763-c2b48844d101e7dc.png


5.松开Option键
6.选择创建NAT64网络

449763-24bf283e252f8acb.png


7.选择一个网络接口,提供您的互联网连接

449763-dca2482ca037077b.png


Tip:如果你的Mac是用有线拨号上网的话,请选择PPOE选项作为共享源。如果你的Mac是用有线上网(不用拨号的)的话,请选择Thunderbolt以太网有线网选项作为共享源。

8.将Wi-Fi选项打勾

449763-88f02a1d1f272050.png


9.点击Wi-Fi选项,配置网络名称和安全选项

449763-2d9850c42ab9ea6b.png


10.选择互联网共享,启动本地网络

449763-a879ba4663a40982.png




449763-8440105df6b08aa8.png


当共享处于活动状态后,在Wi-Fi显示处,会有一个朝上的箭头,这表示互联网共享已经启用,现在就有一个IPv6 NAT64网络可以让开发者并可以从其他设备访问并测试应用了。

449763-60e46eb56800c8fb.png


Tip:为了确保测试只发生在本地IPv6网络上,请确保你的测试设备没有其他活动的网络接口。例如,如果你正在使用iOS设备进行测试,请确保蜂窝服务被禁用,所以你只能通过Wi-Fi测试。
 
环信IM已经支持IPv6,如果遇到提交审核因为网络问题没通过的检查下sdk版本号,V3.1.3/V2.2.5 以后支持IPv6,最新sdk下载地址http://www.easemob.com/download/im
  收起阅读 »

【社区福利】环信直播解决方案小范围开源内测,私信发源码

环信直播什么鬼? 大概长这样 环信直播主界面 大家期待已久的环信直播解决方案新鲜出炉,本套解决 方案包括 集成文档、demo源码还有技术支持。让你的APP轻松拥有直播功能 环信直播效果图预览   话不多说,开车啦,本次直播解决方案小...
继续阅读 »
环信直播什么鬼?
大概长这样



QQ图片20160624160546.jpg


环信直播主界面


大家期待已久的环信直播解决方案新鲜出炉,本套解决 方案包括 集成文档、demo源码还有技术支持。让你的APP轻松拥有直播功能



QQ图片20160624155856.jpg


环信直播效果图预览


 
话不多说,开车啦,本次直播解决方案小范围开源内测,你所看到的直播应用现在我手里,私信直接发源码,关于直播有什么建议需要帮助的也请私信我。 收起阅读 »

GMTC小伙伴们约起来,环信礼品送不停

环信颜值担当@一乐 主题演讲敬请期待,环信展台各种礼品等你来领 [[来]] 】 本次大会设置了9大专题、4大Workshop,为移动开发者献上一场技术盛宴。 环信首席架构师梁宇鹏 将于25日下午1:30带来主题演讲“跨平台的云服务SDK需要什么?”和...
继续阅读 »
环信颜值担当@一乐 主题演讲敬请期待,环信展台各种礼品等你来领 [[来]] 】
本次大会设置了9大专题、4大Workshop,为移动开发者献上一场技术盛宴。
环信首席架构师梁宇鹏

ea790d9djw1f564qa2azhj20ci0btt9a.jpg


将于25日下午1:30带来主题演讲“跨平台的云服务SDK需要什么?”和现场开发者小伙伴们一起论道SDK的那些事儿。
 
GMTC为期两天,主要面向中高级移动开发技术人员,大会聚焦移动开发的前沿技术及实践经验,旨在帮助参会者了解移动开发领域最新的技术趋势与最佳实践 收起阅读 »

环信参展世界O2O博览会,“一言不合”就拿奖!

2016年6月22日,由世界O2O组织(WOO)、全球移动游戏联盟(GMGC)以及光合资本共同主办的世界O2O博览会暨IN+2016创新大会在北京国家会议中心盛大举行,环信作为企业级服务领导厂商受邀参展,环信联合创始人王明成在大会论坛做主题演讲,与参会的意见领...
继续阅读 »
2016年6月22日,由世界O2O组织(WOO)、全球移动游戏联盟(GMGC)以及光合资本共同主办的世界O2O博览会暨IN+2016创新大会在北京国家会议中心盛大举行,环信作为企业级服务领导厂商受邀参展,环信联合创始人王明成在大会论坛做主题演讲,与参会的意见领袖们一起探讨移动互联时代的O2O行业的服务营销新体验。



@HEOTI]HL6]8A@JNQR


环信联合创始人王明成做主题演讲



 本届大会的特别板块OSCA2016最佳应用评选颁奖典礼也于当日下午隆重开幕。OSCA2016最佳应用评选是世界O2O组织(WOO)围绕移动互联网最佳应用及商业模式为核心内容展开的全球性评选活动,由第三方权威数据研究机构易观、Talking Data提供的TOP榜单,并参考百度指数,通过报名参评、联合60+媒体展开公众投票、专家评审团最终评审的方式,含金量极高。其中环信凭借其在即时通讯云和全媒体智能云客服领域的深耕和市场优势一举夺得“OSCA最佳服务商奖”。
 



mmexport1466585742646.jpg


环信荣获“OSCA最佳服务商奖”


环信获奖专家组评语:环信作为国内最早和最大的即时通讯云PaaS厂商,通过一段代码即可让APP实现单聊、群聊、发语音图片以及实时音视频等社交功能。让开发者摆脱繁重的移动IM通讯底层开发,让所有的App从此都能“聊”起来。环信首推的全媒体智能云客服深度契合O2O行业的客服特点,包括来自网页端、APP端、社交媒体端(微博、微信)的服务请求均可以一键接入一键处理,其自主研发的智能机器人帮助“58到家”等标杆O2O企业降低了客服成本提高了效率,实现了订单、客单价双丰收。


据gartner预测未来60%的客户服务都将来自移动端,70%的O2O和电商业务将发生在移动端,而O2O业务流最关键的就是移动端服务营销。如果做不好包括移动端的全渠道客户服务,辛辛苦苦通过各种渠道引流到自己App、网站、平台、账号的客户,90%都会流失掉。因此,移动端的客户服务体验显得尤为重要。环信联合创始人王明成认为要解决多渠道、成本高、效率低、弱网络、转化低这五大客服挑战,环信首推的全媒体智能云客服提供了最佳解决方案。

对于O2O企业而言,无需任何技术投入,集成环信移动客服马上就可以享受全媒体客服带来的优势。首先是服务渠道的一体化,不论客户是来自APP端、微信公众号、微博还是网站,都可以实现客户请求的统一接入,统一分配和统一管理。其次是服务的专业化,可以进行客户画像、订单轨迹、服务质检、报表统计等,使商户拥有了媲美大型呼叫中心的的服务品质。环信提供的智能机器人更是帮助客户大幅降低了人工服务成本,80%的重复问题机器人都可以帮助解答。在下班时间,可以由智能机器人代替人工客服,提供7*24的不间断服务。

O_8C996[WAR6GK_WZDYFGB.png


环信基于自有的长连接技术优势,在移动客服产品上提供了一个主动外呼功能,帮助商户轻松实现客服营销。长连接技术的关键之处在于为每一个客户维持一个 TCP连接,可以随时向客户端发送消息(当然,这种设计对后台系统资源和调度能力有很高要求)。客服人员可以把最新的优惠促销以后台消息的形式发送给指定人群。

RWY9NM4HFT@{PMF7N769]5.png


随着交易平台的扩大,对客服系统的即时消息传递能力也将提出更高的要求。在所有SaaS客服厂商中,环信是唯一一家同时拥有即时通讯云PaaS产品和 SaaS客服产品的厂商,即时通讯技术是环信的起家之本。截至2015年12月,环信即时通讯SDK已覆盖手机终端3.19亿,日发送消息2.1亿,这些数据展示了环信即时通讯技术的强劲性能和扩展性,也是环信登顶SaaS客服行业的致胜法宝。对于58到家这种平台级的O2O服务商而言,选择环信,意味着性能将不会成为瓶颈,能够为未来快速扩张扫除障碍。 收起阅读 »

使用PullToRefreshListView代替ChatFragment里的SwipeRefreshLayout时出现的界面适配问题

在聊天界面ChatFragment里下拉刷新的控件使用的是google官方的SwipeRefreshLayout,当我换成PullToRefreshListView这个控件时,界面出现了不能随着键盘的弹出/收回自动适配界面的bug。此问题困扰了我两三天,goo...
继续阅读 »
在聊天界面ChatFragment里下拉刷新的控件使用的是google官方的SwipeRefreshLayout,当我换成PullToRefreshListView这个控件时,界面出现了不能随着键盘的弹出/收回自动适配界面的bug。此问题困扰了我两三天,google多次无果。

于是我写了个简单的demo,里面只有一个Activity,fragment。fragment布局上有个TextView充当TitleBar,下方有个EditText充当输入框,中间有个PullToRefreshListView充当聊天内容。当最外层布局为RelativeLayout时,随着键盘的弹出/收回,界面可以自适应。但是当最外层的布局为LinearLayout时,输入框被挤上去之后,就下不来了。到此,不敢说是PullToRefreshListView在fragment里有问题,因为毕竟这个控件的使用量非常大,如果有问题,不可能google不到同样的问题的。

最终在StackOverflow上发现一篇文章:点击http://stackoverflow.com/questions/25303285/activity-with-fragments-does-not-resize-when-the-keyboard-opens?answertab=votes#tab-top
这篇文章中提到了键盘弹出界面适配的原理:
1.如果界面上包含ListView,ScrollView,应用会缩放这两个view的尺寸以显示全部的内容。2.如果这方法不可用,则滚动整个view,以确保当前获取焦点的view不被遮挡。但是LinearLyout是不支持滚动的,所以1和2都无效。


When the soft keyboard appears on the screen, the amount of space available for the application's UI reduces. The system makes decision on how to organize the available space between the application's UI and soft keyboard.

If the window content contains ListView, ScrollView, the application's window is resized provided all the content is visible.
If re-sizing is not feasible, pan and scan approach is used, which simply involves scrolling the application's window so that the currently focused view is visible.

Since your layout does not contain any ListView, ScrollView, re-sizing is ruled out.

The window's root view is a FrameLayout to which you were originally adding LinearLayout. Since LinearLayout does not support scrolling, pan and scan approach is also ruled out. Hence wrapping the layout inside ScrollView solves the scrolling issue.


于是我想到了如果让RelativeLayout加上可以滚动的属性,是不是就能解决问题了。我的chatFragment布局如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:background="#f2f0eb" >

<com.hyphenate.easeui.widget.EaseTitleBar
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:titleBarBackground="#F15645"
app:titleBarLeftImage="@drawable/back_icon"
app:titleBarRightImage="@drawable/ic_share" />

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/input_menu"
android:layout_below="@id/title_bar" >

<****.ease.widget.ChatMessageListLife
android:id="@+id/message_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />

<****.ease.widget.VoiceRecorderViewLife
android:id="@+id/voice_recorder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="invisible" />
</RelativeLayout>

<com.hyphenate.easeui.widget.EaseChatInputMenu
android:id="@+id/input_menu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />

</RelativeLayout>

ChatMessageListLife是继承自EaseChatMessageList
package *****.ease.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ListView;

import com.handmark.pulltorefresh.library.PullToRefreshBase;
import com.hyphenate.easeui.widget.EaseChatMessageList;
import ******.R;

public class ChatMessageListLife extends EaseChatMessageList {

protected static final String TAG = "EaseChatMessageList";
private PullToRefreshBase<ListView> ptrListView;

public ChatMessageListLife(Context context, AttributeSet attrs, int defStyle) {
this(context, attrs);
}

public ChatMessageListLife(Context context, AttributeSet attrs) {
super(context, attrs);
}

public ChatMessageListLife(Context context) {
super(context);
}

protected void init(Context context) {
this.context = context;
LayoutInflater.from(context).inflate(
R.layout.ease_chat_message_list_life, this);
ptrListView = (PullToRefreshBase<ListView>) findViewById(R.id.listView1);
listView = ptrListView.getRefreshableView();
}

public PullToRefreshBase<ListView> getRefreshListView() {
return ptrListView;
}
}










R.layout.ease_chat_message_list_life就是我替换PullToRefreshListView的布局:
<?xml version="1.0" encoding="utf-8"?>
<com.handmark.pulltorefresh.library.PullToRefreshListView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ptr="http://schemas.android.com/apk/res-auto"
android:id="@+id/listView1"
style="@style/pulltorefresh_style"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:transcriptMode="alwaysScroll"
ptr:ptrMode="pullFromStart" >

</com.handmark.pulltorefresh.library.PullToRefreshListView>

 
现在重新回到chatFragment布局。最外层是RelativeLayout,于是我尝试着在chatFragment布局聊天内容的RelativeLayout中增加了
android:scrollbars="vertical"
    <RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/input_menu"
android:layout_below="@id/title_bar"
[b]android:scrollbars="vertical"[/b] >

<****.ease.widget.ChatMessageListLife
android:id="@+id/message_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />

<****.ease.widget.VoiceRecorderViewLife
android:id="@+id/voice_recorder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="invisible" />
</RelativeLayout>

这个属性。谢天谢地,it works. 收起阅读 »

【环信荣获世界O2O博览会OSCA最佳服务商奖】

世界O2O博览会暨IN+2016创新大会如期在北京国家会议中心举办。四大板块:O2OEXPO大会、OSCA颁奖典礼、互动展区,创新峰会,百余位演讲嘉宾,总结+反思,引领O2O行业理性回归。环信作为全球最大的即时通讯云以及在SaaS客服市场的辛勤耕耘荣获OSCA...
继续阅读 »
世界O2O博览会暨IN+2016创新大会如期在北京国家会议中心举办。四大板块:O2OEXPO大会、OSCA颁奖典礼、互动展区,创新峰会,百余位演讲嘉宾,总结+反思,引领O2O行业理性回归。环信作为全球最大的即时通讯云以及在SaaS客服市场的辛勤耕耘荣获OSCA最佳服务商奖!


mmexport1466585748023.jpg




mmexport1466585742646.jpg


  收起阅读 »

环信移动客服v4.8产品更新--客户留言全面处理

【新增】留言 在长时间排队或者客服忙碌无法及时回复时,访客可以选择主动进入留言页面给客服留言;在下班时间并且机器人客服处于关闭状态时,网页端的访客会直接进入留言页面。访客的留言不会再创建待接入的会话。 客服和管理员可以查询等待处理的留言,选择某个留言进行处...
继续阅读 »

42I58PICpnH_1024.jpg


【新增】留言

在长时间排队或者客服忙碌无法及时回复时,访客可以选择主动进入留言页面给客服留言;在下班时间并且机器人客服处于关闭状态时,网页端的访客会直接进入留言页面。访客的留言不会再创建待接入的会话。

客服和管理员可以查询等待处理的留言,选择某个留言进行处理,或对留言进行评论。

Ÿ客服可以在客服面板查询所有未分配的留言和分配给自己的留言;

Ÿ管理员可以在管理员面板查询所有留言,包括未分配和已分配的留言。

目前,只有APP端和网页端(包括桌面网页和H5网页)支持留言功能。其中,APP端需要集成留言功能,网页端可以直接使用。
 
【优化】工作质量导出报表增加评分明细

工作质量导出报表增加客服获得的满意度评分明细,即获得的评分1、评分2、评分3、评分4、评分5的数量。

使用管理员账号登录环信移动客服,选择“管理员模式 -> 统计查询 -> 工作质量”,筛选需要的工作质量数据,并点击“导出报表”,可以将工作质量数据以Excel表格形式导出到本地。
 
体验环信移动客服http://kefu.easemob.com/
 
环信移动客服v4.8.1_产品更新说明_v1.1 收起阅读 »

IOS快速集成环信IM - 基于官方的Demo优化,5分钟集成环信IM功能

 领导说一天内要集成IM功能,我顿时被吓尿了~~ 说明 本项目是基于官方 ChatDemo-UI3.0 (官方Demo源码下载)项目的简化封装,目的是为了让大家更加方便快速地集成环信IM功能。   环信IOS简版...
继续阅读 »

00.png



 领导说一天内要集成IM功能,我顿时被吓尿了~~


1400864-1.jpg




说明

本项目是基于官方 ChatDemo-UI3.0 (官方Demo源码下载)项目的简化封装,目的是为了让大家更加方便快速地集成环信IM功能。
 
环信IOS简版DemoGit地址:https://github.com/mengmakies/ChatDemo-UI3.00-Simple   (本项目会保持与环信的SDK同步更新)
 
 环信Android简版Demo地址:
https://github.com/mengmakies/ChatDemo-UI3.00-Simple-Android

奋斗目标

分离第三方依赖库,避免与开发者现有项目的其它类库发生冲突;
抽象开发者可定制化的方法或配置参数;
其它未确定的封装工作,最终目的:高内聚,低耦合;
整理开发者开始集成时反馈的常见性问题,从实际项目考虑优化SDK集成的简易度。

最终成果

1.便于开发者在新项目或现有项目快速集成环信SDK,实现聊天界面和会话列表功能,而且可以灵活地定制化一些基础模块; 
2.低耦合,用尽可能少的代码集成环信功能,尽量少污染开发者的项目代码;

如有任何问题,请联系QQ: 364223587


注意:由于Git不支持上传大于100MB的文件,所以项目源码中不包含 HyphenateFullSDK 和HyphenateSDK动态库,下载地址https://pan.baidu.com/s/1c1RsUrA ,解压后后将HyphenateFullSDK和HyphenateSDK文件夹拷贝到目录【/ChatDemo-UI3.0-Sample/ChatDemo-UI3.0/ChatSDK/】下才能正常运行。




经过对ChatUIDemo-UI3.0中的源码进行分析可知,用户初次集成EaseUI时,会遇到如下几个常见问题:

问题1


引用Parse.framework、Bolts.framework时项目容易出错或出现Not found问题,其实这两个库并不是必须的,而且Facebook已经确定在2017年1月份停止提供Parse服务。


解决方案:删除Parse相关类,用 UserCacheManager替代管理用户缓存。

问题2


ChatDemoHelper辅助类集成了很多聊天相关界面的操作方法,开发者一般会直接复用,但是ChatDemoHelper对MainViewController的函数依赖度比较高,比如


[weakself.mainVC setupUnreadMessageCount]; 
[self.mainVC networkChanged:connectionState];

解决方案
(1)将ChatDemoHelper中的mainVC类型更换成UIViewController;
(2).将MainController中的几个方法用通知(NSNotificationCenter)实现;

问题3


聊天相关页面与业务逻辑页面放在同一目录中,对于开发者来说,需要分拣; 


解决方案:将环信相关的文件、资源统一放在【ChatUI】和【ChatSDK】目录中,方便开发者直接拖拽这两个文件夹即可快速集成聊天功能。

关于昵称和头像的问题
IOS中如何显示开发者服务器上的昵称和头像 http://community.easemob.com/article/825307855
草草们的忧伤:环信IM昵称和头像  http://www.imgeek.org/article/825308536 
 
>>>>>>>>接下来,我们就一步步教大家如何快速集成环信IM功能:
1.新建一个IOS项目。打开XCode -> 【File】 -> 【New】 -> 【Project...】-> 【Single View Application】 ,如下图所示:

s1.png


项目命名为【HxChatDemo】,然后选择路径保存即可:

s2.png


 
2.加入环信IM相关的SDK以及界面代码。下载源码https://github.com/mengmakies/ChatDemo-UI3.00-Simple,将解压后文件夹中的【3rdparty】、【ChatSDK】和【ChatUI】目录复制到新建项目【HxChatDemo】目录下,然后下载SDK(地址https://pan.baidu.com/s/1c1RsUrA) ,解压后后将HyphenateFullSDK和HyphenateSDK文件夹拷贝到目录【/ChatDemo-UI3.0-Sample/ChatDemo-UI3.0/ChatSDK/】目录下, 然后将【ChatSDK】和【ChatUI】这两个目录拖入Xcode中:

00001.png



3.引入SDK的依赖库。在Xcode中为项目新建一个名为【Frameworks】的group,然后依次按照下面的目录添加依赖库:
SDK 包含实时语音依赖库有:

CoreMedia.framework
AudioToolbox.framework
AVFoundation.framework
MobileCoreServices.framework
ImageIO.framework
libc++.dylib
libz.dylib
libstdc++.6.0.9.dylib
libsqlite3.dylib
libiconv.dylib
 
// **********官方漏了以下这些系统库********************
CoreMotion.framework   
UserNotifications.framework 
AssetsLibrary.framework
MapKit.framework
Photos.framework
libbz2.1.0.dylib
// **********官方漏了以下这些系统库*****end***************
 
后端云LeanCloud需要引入的依赖库:
libicucore.dylib
SystemConfiguration.framework
CoreTelephony.framework
CoreLocation.framework

(如果使用的是 xcode7+,后缀为 tbd。)
4.向General → Embedded Binaries 中添加依赖库.【千万别漏了这个步骤!!!】

ios_import_dynamic_3.0sdk_.jpg


注意:在【Linked Frameworks and Libraries】中,找到【HyphenateLite.framework】和【Hyphenate.framework】,然后将【Required】改为【Optional】。

5.增加预编译头文件pch,保证头文件的全局引用。在【Supporting Files】目录上单机右键 -> 【New File...】 -> 【Other】 -> 【PCH File】 -> 命名为【HxChatDemo-Prefix.pch】,加入如下代码:
#import 

#ifndef __IPHONE_3_0
#warning "This project uses features only available in iOS SDK 3.0 and later."
#endif

// 如果不需要红包功能,直接把这个REDPACKET_AVALABLE宏注释掉,然后:

// 1.删掉红包相关代码:注释掉后,建议在xcode中搜索“REDPACKET_AVALABLE”,然后删掉相关代码;

#define REDPACKET_AVALABLE

// 如果不需要语音通话功能,直接把这个宏注释掉或者改成0,然后:
// 1.删掉音视频相关代码:注释掉后,建议在xcode中搜索“DEMO_CALL”,然后删掉相关代码;
// 2.隐藏音视频通话按钮:EaseChatBarMoreView.m  第115-131行代码注释掉。
#define DEMO_CALL 1 // 1:启用音视频通话;0:禁用音视频通话

#ifdef __OBJC__

    #import
    #import
    #import "ChatUIDefine.h"
    #import "EMAlertView.h"
    #import "TTGlobalUICommon.h"
    #import "EaseUI.h"

#if DEMO_CALL == 1
    #import // 包含音视频通话功能
#else
    #import // 不包含音视频通话功能
#endif

    #import "AVOSCloud/AVOSCloud.h"// 后端云LeanCloud
    #import "UserCacheManager.h"
    #import "UserWebManager.h"

#endif







效果如下图:

00002.png



关联PCH文件,打开【Build Settings】,在【Apple LLVM7.1- language】组中进行如下设置:
1.将【Precompile Prefix Header】设置为YES,预编译后的pch文件会被缓存起来,可以提高编译速度;
2.修改pch文件路径按照如下格式:将【Building setting】中的【Prefix Header】选项的路径设置为“$(SRCROOT)/项目名称/pch文件名”,例如:
$(SRCROOT)/HxChatDemo/HxChatDemo-Prefix.pch
 
如下图所示:

s7.png



记得将ENABLE_BITCODE设置为NO


OK~编译运行成功!!!大功告成!经过如上几个步骤,环信IM功能所需要的SDK以及EaseUI界面代码都被成功导入项目了。
 
--------->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
如果需要引入单聊、群聊、联系人列表等界面功能,你就要参考简版Demo项目中的代码一步步操作了,比如:
1.在项目中注册环信APPKEY,参考Appdelegate.m中的代码
2.注册【接收消息】、【好友通知消息】、【网络变化】等回调事件,参考MainViewController.m中的代码
NOTIFY_ADD(setupUntreatedApplyCount, kSetupUntreatedApplyCount);
NOTIFY_ADD(setupUnreadMessageCount, kSetupUnreadMessageCount);
NOTIFY_ADD(networkChanged, kConnectionStateChanged);

3.注意,某些回调事件已经在ChatUIHelper.m里实现,所以需要在适当的位置初始化相关的ViewController:
[ChatUIHelper shareHelper].contactViewVC = _contactsVC;
[ChatUIHelper shareHelper].conversationListVC = _chatListVC;
[ChatUIHelper shareHelper].mainVC = self.mainController;

------》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
盆友,先别急关掉页面~~~~彩蛋往往都是最后才隆重登场的~~~


QQ图片20160927164924.jpg



不信?那我们就举个例子,一分钟内快速集成单聊功能:
1.在AppDelegate.m加入如下代码:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// 注册环信SDK以及推送证书

    [[EaseSDKHelper shareHelper] hyphenateApplication:application
                        didFinishLaunchingWithOptions:launchOptions
                                             appkey:@"easemobdemo#chatdemoui"
                                         apnsCertName:@""// 推送证书名称
                otherConfig:@{kSDKConfigEnableConsoleLogger:[NSNumber numberWithBool:YES],@"easeSandBox":@NO}];

  
    [ChatUIHelper shareHelper];// 初始化环信相关回调

    // 初始化web缓存配置, appkey需要自己去LeanCloud官网注册存储服务
    // http://www.imgeek.org/article/825308536
    [UserWebManager config:launchOptions
                     appId:@"utUG5ot9Y64dqJIFG9Ir2rqu-gzGzoHsz"
                    appKey:@"IbHhNkPo4gfrFFc3epCw3eG2"];
return YES;
}
2.然后,在ViewController.m中添加一个按钮,并加入按钮响应代码,实现环信登录成功后自动打开单聊页面的功能:
// 登录环信并打开单聊界面
- (IBAction)doChat:(id)sender {

NSString *userName = @"martin1234";

    NSString *pwd = @"martin1234";

    // 因为设置了自动登录模式:[[EMClient sharedClient].options setIsAutoLogin:YES];
    // 所以,登录之前要先注销之前的用户,否则重复登录会抛异常
    EMError *error = [[EMClient sharedClient] logout:YES];

    if (!error) {
        NSLog(@"退出成功");
    }
    
    [self showHudInView:self.view hint:@"Loading..."];
    [[EMClient sharedClient] loginWithUsername:userName password:pwd completion:^(NSString *aUsername, EMError *aError) {

        [self hideHud];

        if (error) {
            [self showHint:aError.errorDescription];
            NSLog(@"登录报错了:%@",aError.errorDescription);
            return ;
        }
        // 测试:登录成功后,自动添加martin1234为好友
        EMError *error = [[EMClient sharedClient].contactManager addContact:@"martin12345" message:@"我想加您为好友"];

        if (!error) {
            NSLog(@"添加成功");
        }
        
        NSString *userOpenId = userName;// 用户环信ID
        NSString *nickName = [NSString stringWithFormat:@"小草%d", arc4random_uniform(100)];// 用户昵称
        NSString *avatarUrl = @"http://avatar.csdn.net/E/8/5/2_duruiqi_fx.jpg";// 用户头像(绝对路径)

        
        // 登录成功后,如果后端云没有缓存用户信息,则新增一个用户
        [UserWebManager createUser:userOpenId nickName:nickName avatarUrl:avatarUrl];

        //设置是否自动登录
        [[EMClient sharedClient].options setIsAutoLogin:YES];
        [[ChatUIHelper shareHelper] asyncGroupFromServer];
        [[ChatUIHelper shareHelper] asyncConversationFromDB];
        [[ChatUIHelper shareHelper] asyncPushOptions];

        //发送自动登陆状态通知
        [[NSNotificationCenter defaultCenter] postNotificationName:KNOTIFICATION_LOGINCHANGE object:@([[EMClient sharedClient] isLoggedIn])];

        ChatViewController *vc = [[ChatViewController alloc]initWithConversationChatter:@"admin" conversationType:EMConversationTypeChat];

        vc.title = @"江南孤鹜";
        [self.navigationController pushViewController:vc animated:YES];
    }];
}
效果图如下:

IMG_6441_meitu_1.jpg



注意:IOS9以上系统如果头像不显示,是因为IOS9禁止了非https的请求,只要在Info.plist中增加App Transport Security Settings子项Allow Arbitrary Loads设置为YES。

22334.png



要不你也动手试试,看看能否5分钟内集成环信IM功能?记得在文章底下留言告诉我们花了多长时间哦 ~.~
 
相关问题:
1.环信MJRefresh与自己项目已有的MJRefresh冲突,怎么办?
解决:保留自己项目的MJRefresh,删除环信里的MJRefresh文件夹即可。MBProgressHUD也是同样的情况,但是不能直接删除环信里的MBProgressHUD目录,只需删除MBProgressHUD.h和MBProgressHUD.m即可。
 
2.环信EaseUI里将SDWebImage改名为EMSDWebImage,与自己项目中的SDWebImage冲突,怎么办?
解决:删除环信里的EMSDWebImage文件夹,去掉EaseUI中引用SDWebImage中的【EM】前缀,例如:
(1).#import "UIImageView+EMWebCache.h"  换成  #import "UIImageView+WebCache.h";
(2).#import "UIImage+EMGIF.h"  换成 #import "UIImage+GIF.h";
(3).#import "EMSDWebImageDecoder.h"  换成 #import "SDWebImageDecoder.h"
(4).EMSDImageCache改为SDImageCache;
 

3.编译运行时报错:Reason: image not found
解决:【Build Phases】-> 【Link Binary With Libraries】->找到【HyphenateLite.framework】和【Hyphenate.framework】->将【Required】改为【Optional】
 
4.登录的时候界面一直显示【loading】,或者音视频通话时,安卓端无法收到通话请求。
解决:一般都是漏了这个步骤【4.向General → Embedded Binaries 中添加依赖库】引起的,此外,在【Linked Frameworks and Libraries】中,找到【HyphenateLite.framework】和【Hyphenate.framework】,然后将【Required】改为【Optional】。



本文HxChatDemo项目下载地址:http://git.oschina.net/markies/HxChatDemo
简版Demo下载地址:https://github.com/mengmakies/ChatDemo-UI3.00-Simple 
如有任何问题,请咨询【环信IM互帮互助群】,群号:340452063
或者加本人QQ:[b]364223587 (好友数已达1000人,如无特殊问题,请到群里询问)


收起阅读 »

iOS 环信Demo集成即时聊天和UI 经验分享

刚做完一个拥有即时聊天模块的项目,项目中用的是环信3.0,虽然感觉中间有点小坑,但是总体感觉还是棒棒哒,比我两年前纯用XMPP+openfire便捷多了,而且环信的开发文档感觉好对我的胃口,写的很用心,所以喽,今天分享一下环信的集成和UI页面的摘取。一、准备工...
继续阅读 »
刚做完一个拥有即时聊天模块的项目,项目中用的是环信3.0,虽然感觉中间有点小坑,但是总体感觉还是棒棒哒,比我两年前纯用XMPP+openfire便捷多了,而且环信的开发文档感觉好对我的胃口,写的很用心,所以喽,今天分享一下环信的集成和UI页面的摘取。
一、准备工作

1、sdk和静态库的导入

首先是导入环信的sdk,编译,这个过程的困难不是很多,简单提一下,首先下载好EaseMobSDK,这里注意一点的是,lib静态库中包括EaseMobClientSDKLite和EaseMobClientSDK这两种,如果你需要实时语音的话选择EaseMobClientSDK,否则选择EaseMobClientSDKLite,两者只能导入一个。
添加静态库
CoreMedia.framework
AudioToolbox.framework
AVFoundation.framework
MobileCoreServices.framework
ImageIO.framework
libc++.dylib
libz.dylib
libstdc++.6.0.9.dylib
libsqlite3.dylib

SDK包含实时语音依赖库有
CoreMedia.framework
AudioToolbox.framework
AVFoundation.framework
MobileCoreServices.framework
ImageIO.framework
libc++.dylib
libz.dylib
libstdc++.6.0.9.dylib
libsqlite3.dylib
libiconv.dylib

(注意:SDK 不支持 bitcode,向 Build Settings → Linking → Enable Bitcode 中设置 NO。)
然后编译工程,不出意外就成功啦。啊哈哈,开瓶啤酒庆祝一下自己的智商。如果,有些小猴子不成功,例如我


1749914-70edd6763efa88d5.png


上面的原因是,导入的静态库重复,自己检查吧

二、移植环信的demo中需要的到自己的项目

我这里用到了聊天列表,消息的页面和聊天的主页面,大家再倒入这个之前,一定要先备份!项目,否则一失足成千古恨,或者把svn中的项目弄坏了,就自己站墙角去吧。做这个一定要细心,不能着急,最好要记录移过来的每一个类都是干啥的,防止出错,不知道怎么排查。直接看我的图,按照我这张图细心点,相信可以的。

1.以下这张是消息列表需要导过来的类,有些View里面的类是我自己添加的,和demo比对吧,有的就添加进来



1749914-5458dd68d14de923.png


消息列表


2.以下是聊天页面需要的类,CL开头的类,是我自己写的,不用管,剩下的都是需要的



1749914-4f9c4d2b55b1e72e.png


聊天页面


3.剩下需要的东西,这个很重要,要细心哦



1749914-1721afb021ce978d.png


相关


其中有些文件夹里的东西,也需要注意,上图:



1749914-172401b50f5ecdda.png


环信相关


还有,这里面APPDelegate的拓展也比较重要,AppDelegate+EaseMob这个不用白不用了,很方便。

4.做完这些,细活出来啦
做完这些,就是表演真正的技术的时候了。不要想着运行程序跑起来,那肯定是和中彩票一样的概率。具体的报错地方很多,但是都是一些用不上的类不错,我也记不清楚了,只能你们一点一点把不用的地方删除就ok。这个东西不少,但是不复杂,要细心(说了好几遍了)

常见问题:
1.当你导入FixFopen.c的时候会出现


1749914-528d1ec0fee416d4.png


这个是因为添加chatView聊天页面时候缺少依赖库



1749914-d3c130f9a9efc202.png


依赖库


2.如果你需要pch文件,可能会遇见下面问题,需要加上#ifdef __OBJC__ #endif 就好了



1749914-ff74359772cc057d.png


pch问题


3.一些警告的问题,都可以忽略了。因为聊天列表和聊天页面,demo3.0中有环信机器人相关的代码,通通删除,不会有影响的。
 
三、经验之谈

1.环信好用的首要一点就是,各种方法都包含EMMessage这个模型,里面可以用到自己需要的东西,还有拓展,如果开发什么新的模块和写新的方法,建议都传过去EMMessage。

2.chatViewController是继承于EaseMessageViewController这个基类的,里面包含了基本用到的代理方法,方便灵活使用。

1749914-0391af1c45ebf7d0.png


3.肯定会操作聊天气泡和自定义消息,就是从MessageCellBubbleView里面修改的,自定义消息,也是要重新写view的拓展,例如下图中我订制的EaseBubbleView+Card类等



1749914-ae4a25aa3fd43542.png


消息cell和自定义消息


4.自定义消息的发送直接调用就好了,这个很方便,消息拓展Ext的字段和类型需要提前商定
[EaseSDKHelper sendTextMessage:@"自定义消息"
to:self.easeGroupId
messageType:eMessageTypeGroupChat
requireEncryption:NO
messageExt:tmpDict];
暂时完结

暂时分享这么多,希望大家都能成功集成,顺利拿下项目。用第三方的demo集成,一定要知其所以然,一点一滴做起。有什么问题可以随时留言交流,希望我写的能让你们少走点弯路。谢谢大家支持。
 
作者:环信热心用户睡不着的叶 收起阅读 »

环信亮相第十四届软交会,摘取“2016中国最佳SaaS产品奖”

被誉为国内软件业第一展,为期四天的2016中国国际软件和信息服务交易会于6月19日在大连落下帷幕。随着“互联网+”与各行业的深度融合,软件和信息服务行业将发挥更加重要的作用。作为企业级服务的明星垂直领域,SaaS客服也成为此次大会关注的焦点。本届软交会共吸引6...
继续阅读 »
被誉为国内软件业第一展,为期四天的2016中国国际软件和信息服务交易会于6月19日在大连落下帷幕。随着“互联网+”与各行业的深度融合,软件和信息服务行业将发挥更加重要的作用。作为企业级服务的明星垂直领域,SaaS客服也成为此次大会关注的焦点。本届软交会共吸引60多个国家和地区的743家企业、1.5万名业内人士和3.6万名观众参会。



]_`)DQ7{H{[5QWMIZX2@RS.png




“企业服务创新论坛”是一年一度的软交会的重点组成部分,前面已成功举办三届。在第十四届大连软交会“2016企业服务创新论坛”上,企业家们齐聚一堂共同探讨中国企业级服务的未来。同时,2016企业服务创新论坛金i奖颁奖盛典揭晓,环信作为中国企业级服务明星企业一举摘取“2016中国最佳SaaS产品奖”。



QQ截图20160621170708.jpg


环信荣膺“2016中国最佳SaaS产品奖”


 评委们给了环信企业级服务的最高评价:“当企业级SaaS服务如雨后春笋般涌现,我们在百花齐放的景象中能找到如此坚实沉静一朵,绽放自身的光彩。作为SaaS客服软件行业唯一一家同时拥有PaaS和SaaS产品的公司,环信以此建立其强大的壁垒;不急不缓,水到渠成,它们为中国企业提供着最高效可靠的SaaS服务。”



RVY14C11U`B4J)0398.png


环信副总裁发表主题演讲《客户服务的智能时代》


环信副总裁程旭文在论坛做主题演讲诠释了在“互联网+”时代智能SaaS客服的重要性。程旭文认为,互联网+时代的用户获客成本非常高,例如电商行业获取一个新用户成本是维护一个老用户的三倍到十倍。如何能让一个用户长期留在我们的产品和服务上?而且客户的渠道来源多种多样,解决这些问题非常重要。环信移动客服全媒体接入可以实现多渠道的用户来源一键管理一键回复。客户服务是有温度的,温度的高低同时决定了服务的满意度,环信移动客服可以实现对客户的交互进行情感分析,基于大数据技术的环信客户声音可以极大提升留存和转化从而实现精细化运营。



QQ截图20160621170339.jpg



随着人口红利消失,人工成本上升,企业客服面临的“用工荒”将持续扩大,运营成本将越来越高。环信利用智能机器人技术实现精准高效自动推送,自动问答,可以帮助客户解答80%的常见问题,极大提升效率降低成本,而环信首推的“人工+智能机器人”的协作服务被证明是现阶段最适合的客户服务方式。



QQ截图20160621170415.jpg



 本届软交会以“数据·共享,智慧·创新”为主题,企业级服务应以构建生态圈的创新能力为本,以客户为中心迭代,最终演变为“连接、创新改变生意”,环信即时通讯云就是这样一根连接“人与人”的管道。资源共享整合,以开放的态度,方能合作共赢。大数据是另一种连接,是海量的、相关或非相关的数据的连接,连接创造价值,并且数据的价值因连接的广度和共享而产生质变。环信移动客服基于大数据研发的创新产品“客户声音”,通过热点话题分析发现新畅销商品,通过情感度分析发现服务问题,帮助企业实现广度客户的连接从而创造更大价值。
环信移动客服致力于连接“人与商业”,作为全媒体智能云客服倡领者,支持全媒体接入,包括网页在线客服、社交媒体客服(微博、微信)、移动端客服和呼叫中心等多种渠道。基于业界领先的IM长连接技术保证消息必达,不丢消息不丢单,并通过强大的智能机器人技术极大降低人工客服工作量。
截至2015年底,环信移动客服共服务了12000家企业用户,现已覆盖包括电商、O2O、互联网金融、在线教育、在线旅游、移动医疗、智能硬件、游戏等20大领域的Top10客户,典型用户包括国美在线、58到家、楚楚街、随手记、海尔、51talk,链家自如客、神州专车等众多互联网和传统企业。根据易观国际发布的《中国SaaS客服市场专题研究报告2015》显示:截至2015年第三季度,环信移动客服在SaaS移动端客服用户覆盖占比为77.4%,以绝对优势稳居行业第一。
收起阅读 »

【环信招聘】精英人才邀请

  即时通讯云技术团队简介环信即时通讯云是一个提供即时通讯服务的PaaS平台,迄今为止,平台已经支持: 超过100000家App开发者 每天10000000+同时在线长连接 服务1000000000+不同用户  为了实现这个目...
继续阅读 »
 

QQ截图20160621122441.jpg


即时通讯云技术团队简介
环信即时通讯云是一个提供即时通讯服务的PaaS平台,迄今为止,平台已经支持:

超过100000家App开发者

每天10000000+同时在线长连接

服务1000000000+不同用户

 为了实现这个目标,通过分布在中国和美国的 5+ 主要IDC 以及日本、香港、欧洲多个中转节点,为全球用户提供即时可靠的通讯通道。

前端SDK已经可以横跨iOS、Android、Windows以及众多Linux系统,React Native、Swift等等跨平台技术也在不断扩展。

后台的大规模分布式的系统也正在进行微服务化改造,全球多机房同步技术、容器化技术也在深入研究。

这里的每一项技术都会直接帮助平台上的App开发者,并在真正的应用里得到实践检验。
我们不仅是技术的研发者,也是技术的输出方。

这里的每一项技术都在改变世界!

欢迎加入!我们支持开源运动!

 
查看热门职位,敬请 查看详情



系统研发高级工程师
 
您的职责:
 
负责开发和维护大规模分布式系统,为环信上亿用户 提供稳定可靠的服务;

或负责完善基础组件,完成后端系统的微服务改造;

或负责环信开放平台的开发,构建安全可靠的服务平 台;


 




通讯工程师
 
您的职责:

负责开发和维护即时通讯系统,优化提高后端服务的 承载能力;

或负责优化、改进和实现IM协议,为移动互联网以及 物联网用户提供可靠实时的即时通讯服务;


 




高级产品经理

您的职责:

负责环信即时通讯云产品;

负责编写产品需求书、产品原型,协助开发团队理解和 掌握需求,对产品求方向和易用性负责;

对所负责的产品进行用户行为数据监测、性能跟踪、数 据分析、提出产品优化方案;

研究市场上各种产品和应用,挖掘与把握在目标用户对 于信息不同层面的需求,并形成具体的产品功能;

研究市场和用户需求,竞品分析,不断优化产品,提升 用户体验及活跃度,同时策划有竞争力的产品;


 
还想看看别的高薪职位,点击查看
 

有意者请发简历至邮箱:
talent@easemob.com / job@easemob.com

  收起阅读 »

环信CEO:SaaS客服的难点和坑点全解析

移动和社交风头正劲,企业协作与通信市场风云变幻。互联网巨头和创业公司纷纷入场,共同分食企业协作与通信这块大蛋糕,也深度促进了中国企业级服务的市场繁荣。 6月14日,国内企业协作与通信市场最大的盛会——第十八届CENCE 企业协作及通信大会在北京国际会议中心盛大...
继续阅读 »



FY456RX(7W(XFR1Y



移动和社交风头正劲,企业协作与通信市场风云变幻。互联网巨头和创业公司纷纷入场,共同分食企业协作与通信这块大蛋糕,也深度促进了中国企业级服务的市场繁荣。 6月14日,国内企业协作与通信市场最大的盛会——第十八届CENCE 企业协作及通信大会在北京国际会议中心盛大开幕。包括国内三大运营商、IBM、Cisco、华为、环信等几十家行业领头羊均受邀参展本次大会,共同探讨移动社交时代的企业协作、OA、音视频通信、企业社交、CRM、客服联络中心等领域的重要议题。



4)QG7V})WHYO0HHW0VG[T4F.png




4_7CRYE0V947@B@ZX1DD3U5.png


大会深受企业IT信息主管、CIO、渠道、ISV人群高度关注


 环信展台人头攒动,同时,环信CEO刘俊彦作为企业级服务KOL受邀发表主题演讲《智能全媒体客服时代的最佳实践》,用环信的真实大客户案例与会众共同探讨客户服务的痛点以及SaaS客服的难点和坑点!



{R3~6B}]ADUQV@_{VUJS}8L.png



环信CEO刘俊彦认为,虽然目前SaaS客服产品同质化严重,但只是宣传同质化严重,主流厂商产品成熟度和差异化还是明显。比如现在领先的 SaaS客服厂商都可以提供全媒体接入。国内主流的全媒体接入渠道包括电话呼叫中心,网页在线客服,微信公共账号客服,APP内置客服。前3个渠道因为所需要的技术已经很成熟,技术难度低,所以在这3个接入渠道上,各个厂家确实是同质化的。但在APP内置客服这个接入渠道上,各个厂家的解 决方案差异会非常大。怎么解决移动弱网络环境下客服咨询绝不掉线,绝不丢消息,怎么解决数千款碎片化Android手机的适配,怎么支撑千万级乃至亿级用户同时在线等。环信客服SDK历经6万家APP验证,覆盖3.19亿独立手机终端,完美适配所有中高低端安卓和IOS机型。所以环信在全媒体接入的移动APP端是有巨大优势的。

虽然智能机器人处在风口,但是客服智能机器人有些被厂商过度消费。刘俊彦认为目前客服智能机器人的成熟应用还主要在售后阶段,解决标准化重复性问题为主,比如在电信和银行业,解答海量用户的海量重复标准化咨询问题。而在售前和售中需要深度挖掘销售机会且客单价还高的行业,比如电商、O2O、教育、医疗行业等,环信主推的“人机混合模式”将发挥更大价值,而且环信移动客服在这些行业大客户众多,优势明显。

同时刘俊彦透露环信基于大数据分析的创新产品“环信客户声音”,通过热点话题分析发现新畅销商品,通过情感度分析发现服务问题,将给SaaS客服市场带来更多惊喜。以下是本次主题演讲干货内容:

全媒体客服接入核心在于移动端,移动端核心在于用户体验为王!

环信CEO总结到:“来自不同媒体的服务请求均可以统一接入,一键回复,打造跨网、跨界、跨平台的极致客户服务体验。”链家自如客使用环信实现了全媒体客服接入,环信帮助链家自如客打通了来自App端的客服入口+网页端客服入口+微信端客服入口,不仅可以统一接入回复且后台数据打通共享,此举帮助链家自如客优化了客服团队,提升了效率,节省了成本。



DO{HF_3YGUR_SN`8@_HJOHJ.png


链家自如客使用环信实现全媒体客服




Z]1ALABY`7@I2]JL7Y{T76.png



同时,环信还提供“一体化”客服工作台,支持从APP、微信公众号、微博、网页、呼叫中心等渠道接入,且每个不同渠道均有不同标识进行识别。

从传统呼叫中心到全媒体客服,一场“效率”革命!

随着人口红利消失,呼叫中心的升级转型将越来越普遍,2015年中国劳动力规模由2012年的9.37亿降至9.11亿人。中国劳动力人口连续4年绝对值下降企业客服面临的“用工荒”将持续扩大,运营成本将越来越高,越来越多的企业将复制环信客户“学而思”的客服转型之路,从语音呼叫中心为主转而采用全媒体客服,拥抱移动互联网。

学而思每年10—11月是交费季,以前主要靠呼叫中心外呼,工作量巨大,效果不满意。学而思在2015年集成环信移动客服以后,服务模式改为 APP内缴费,并在APP内提供客服支持。整个2015年缴费季,APP客服部门数十人完美解决了往年数百个语音客服的工作。环信CEO刘俊彦认为:“APP在线客服相比电话客服,大幅度提高了服务效率,从一对一的独占式的同步沟通,变成了一对多的异步式沟通,是一场效率革命,全渠道客服也已经成为企业刚需。”

ITR将取代IVR进入触摸时代,“人工+智能机器人“混合模式将是现阶段最适合客服方式!

传统IVR,用户需要听完所有菜单再做选择。而现在主流的ITR导航,用户只需在手机上直接选择关注的问题,简单方便。其中神州租车就采购环信移动客服,其中的智能ITR大大缓解了人工客服压力。Gartner预测到2020年,ITR将完全取代IVR全面进入触摸时代。



_M~1VE{XMB~U}5CA4A`TQYD.png


神州租车使用环信智能ITR缓解人工客服压力


环信是客服行业少数自主开发智能应答机器人产品的公司,环信首推的“人机混合模式”将在现阶段的客服服务中发挥更大价值。环信知识库+智能聊天机器人可以帮助人工坐席挡住80%的常见问题。同时具有以下特性:1,灵活可定制的智能会话、自定义菜单导航功能。2,预置的行业知识库,行业相关的常见问答可以一键拥有。3,与现有知识库系统对接,机器学习,智能优化知识库。4,人机无缝配合,更少的成本,更好的客户体验 。



QQ截图20160620110628.jpg


环信首推的人机混合服务极大提高客服应答效率


环信CEO表示“人机混合服务”将是现阶段最适合也是最具效率的客户服务方式。​
服务式营销,从成本中心转向利润中心

随着客服中心不断的被新时代赋予新的含义,传统的客服中心也正逐渐从成本中心向营销中心和利润中心转化。其中众多电商标杆企业均使用环信移动客服实现了服务式反向营销,先通过“客户标签”功能+“大数据分析”找到目标群体,然后通过环信移动客服的精准营销推送接口,将富媒体商品信息定向推送给目标客户。这种反向营销不仅用户体验好,转化率也极高,帮助电商企业实现了订单数和客单价双丰收。



[7FE4{Y9Q1H`~348~MTT66X.png


移动互联网的电话外呼——金融界为电话销售配置环信APP主动营销平台




HPT53UQF{I{ZPT6979LLX1E.png



近期,环信还上线了业界首个“客户声音”产品,可以通过热点话题分析发现新畅销商品,通过情感度分析发现服务问题,来帮助企业更好的来倾听客户的声音。
 
最后,环信CEO预测:“随着国内SaaS客服产品的逐渐成熟完善,中小企业将全面拥抱SaaS客服,建设全媒体客户关系中心。而传统大型企业也将增量部署全媒体客服,保护已有投资,拥抱移动互联网。”SaaS客服也将逐渐成长为一个千亿级市场。同时,环信CEO认为未来远程办公,移动办公和众包客服将解决客服行业人力资源不足的问题,而环信移动客服的手机端工作后台将提供很大的助力。



V`~0EK7~W)`DCDKEV~_Q3EO.png



收起阅读 »

ios 关于GCD 浅谈

笔者也是在边学习边整理,有不对的地方欢迎指出​           类型:(1)并发队列(Concurrent Dispatch Queue)  可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)并发功能只有在异步(dispatch_asyn...
继续阅读 »
  • 笔者也是在边学习边整理,有不对的地方欢迎指出
​         

图片1.png

  •  类型:
  • (1)并发队列(Concurrent Dispatch Queue)
  •   可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)并发功能只有在异步(dispatch_async)函数下才有效
  •  
  • (2)串行队列(Serial Dispatch Queue)
  •   让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
 
  • 将任务添加到队列中,GCD会自动将队列中的任务取出,放到对应的线程中执行,任务的取出遵循队列的FIFO原则:先进先出,后进后出
  • 同步和异步决定了要不要开启新的线程
  • 同步:在当前线程中执行任务,不具备开启新线程的能力
  • 异步:在新的线程中执行任务,具备开启新线程的能力
  •  
  • 并发和串行决定了任务的执行方式
  • 并发:多个任务并发(同时)执行
  •  
  • 串行:一个任务执行完毕后,再执行下一个任务
  • (1)用异步函数往并发队列中添加任务
  •  
  •  总结:同时开启多个子线程
  •  
  • (2)用异步函数往串行队列中添加任务
  •  
  •  总结:会开启线程,但是只开启一个线程
  •  
  • (3)用同步函数往并发队列中添加任务
  •  
  •  总结:不会开启新的线程,并发队列失去了并发的功能
  •  
  • (4)用同步函数往串行队列中添加任务
  •  
  •  总结:不会开启新的线程
  •  
  • 说明:同步函数不具备开启线程的能力,无论是什么队列都不会开启线程;异步函数具备开启线程的能力,开启几条线程由队列决定(串行队列只会开启一条新的线程,并发队列会开启多条线程)。
  •  
  • 同步函数
  •  
  • (1)并发队列:不会开线程
  •  
  • (2)串行队列:不会开线程
  •  
  • 异步函数
  •  
  • (1)并发队列:能开启N条线程
  •  
  • (2)串行队列:开启1条线程
  •  
  • dispatch_async(queue,block)  async 异步队列,dispatch_async 函数会立即返回, block会在后台异步执行。
  •  
  • dispatch_sync(queue,block)  sync 同步队列,dispatch_sync 函数不会立即返回,及阻塞当前线程,等待 block同步执行完成。
 
  • 下面上代码,可以理解的更透彻一些:
  •  
  • (1)同步串行队列
  • 这里加sleep方便从运行结果更好的看出区别
  • 如图-同步串行队列

  • 同步串行队列.png

  • 可以看出,这里的时间间隔就是sleep,也就是说会按顺序添加到队列中,并且只能等上一个任务结束才能进行下一个任务,所有的操作都是在主线程中进行,并不会开辟新的子线程。
  • 如图-同步串行队列-2
         

同步串行队列-2.png

  • (2)同步并发队列
  • 从运行结果显而易见,同步串行和同步并发结果是相同的,也是上一个任务结束才能进行下一个任务,也是在主线程中进行,并且也没有开辟新的子线程,也就是说同步函数不具备开启线程的能力。
  • 如图-同步并发队列、同步并发队列-2
       

同步并发队列.png

       

同步并发队列-2.png

  • (3)异步串行队列
  • 从结果可以看出,同步和异步串行队列的区别主要在于开辟子线程的能力,在同步串行队列中,所有的任务都是在主线程执行,在异步串行队列中,会开辟一个新的子线程,不论有多少个任务,都只会开辟一个子线程。
  • 如图-异步串行队列、异步串行队列-2
       

异步串行队列.png

       

异步串行队列-2.png

  • (4)异步并发队列
  • 从结果可以看出,异步并发队列是多个任务同时执行,并且会根据任务的多少开辟多个子线程,可以把sleep去掉会发现,输出的顺序是无序的。
  • 如图-异步并发队列、异步并发队列-2
       

异步并发队列.png

       

同步并发队列-2.png

  • (5)GCD延时操作
  • 如图-GCD延时操作
       

GCD延时操作.png

  • (6)队列组
  • 首先创建队列组,把需要执行的任务都放到队列组里,notify是监听队列组中所有任务完成之后才会调用的。
  • 如图-队列组、队列组-2
       

队列组.png

       

队列组-2.png

  • 死锁问题(笔者也是根据自己的理解,有问题之处还望指出)
  • 其实死锁的原因就是线程之间相互阻塞(相互等待)
  •  
  • 第一种情况:
  • 因为同步函数会等block块执行完成之后才接着往下执行,执行到图中所示位置时,会在主线程新加入一个队列,所以sync会等待block执行完成之后在继续往下执行,但是sync在主线程中,而且是串行队列,sync是后加入进来的,sync想执行必须等主线程执行完成,主线程等待sync返回,所以执行到此处就会造成死锁,不会继续往下执行了。简单来说,就是sync等待主线程执行完成, 主线程等待sync函数返回。
  • 如图-死锁1、死锁2
       

死锁1.png

       

死锁2.png

  • 第二种情况:
  • 对比下面图的运行结果,可以看出,异步函数中不会等待block执行完成,会立刻返回,两个同步函数之间相互等待,造成死锁。
  • 从输出时间可以看出,1和2几乎同时执行,因为是在异步函数中,当执行到2的sync时1存在两种情况:(1)执行中 (2)已经执行完成 ,而且主线程中并没有执行耗时操作,所以1和2同时完成;如果把2处改成sleep(3),那么就会出现前面说异步函数时出现的情况,输出时间至少会差3秒;如果阻塞了主线程,2处的sync就会一直等待永远都不会执行。
  • 如图-死锁3、死锁4
         

死锁3.png

         

死锁4.png


收起阅读 »

Android Lollipop (5.0) 屏幕录制实现

引言 从 Android 4.4 开始支持手机端本地录屏,但首先需要获取 root 权限才行,Android 5.0 引入 MediaProject,可以不用 root 就可以录屏,但需要弹权限获取窗口,需要用户允许才行,这里主要介绍 Android 5.0...
继续阅读 »
引言

从 Android 4.4 开始支持手机端本地录屏,但首先需要获取 root 权限才行,Android 5.0 引入 MediaProject,可以不用 root 就可以录屏,但需要弹权限获取窗口,需要用户允许才行,这里主要介绍 Android 5.0+ 利用 MediaProject 在非 root 情况下实现屏幕录制。

基本原理

在 Android 5.0,Google 终于开放了视频录制的接口,其实严格来说,是屏幕采集的接口,也就是 MediaProjection 和 MediaProjectionManager。

具体实现步骤
1 申请权限


在 AndroidManifest 中添加权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
Android 6.0 加入的动态权限申请,如果应用的 targetSdkVersion 是 23,申请敏感权限还需要动态申请
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, STORAGE_REQUEST_CODE);
}
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.RECORD_AUDIO}, AUDIO_REQUEST_CODE);
}
2 获取 MediaProjectionManager 实例

MediaProjectionManager 也是系统服务的一种,通过 getSystemService 来获取实例
MediaProjectionManager projectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
3 发起屏幕捕捉请求
Intent captureIntent= projectionManager.createScreenCaptureIntent(); 
startActivityForResult(captureIntent, REQUEST_CODE);
4 获取 MediaProjection

通过 onActivityResult 返回结果获取 MediaProjection
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RECORD_REQUEST_CODE && resultCode == RESULT_OK) {
mediaProjection = projectionManager.getMediaProjection(resultCode, data);
}
}
5 创建虚拟屏幕

这一步就是通过 MediaProject 录制屏幕的关键所在,VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR 参数是指创建屏幕镜像,所以我们实际录制内容的是屏幕镜像,但内容和实际屏幕是一样的,并且这里我们把 VirtualDisplay 的渲染目标 Surface 设置为 MediaRecorder 的 getSurface,后面我就可以通过 MediaRecorder 将屏幕内容录制下来,并且存成 video 文件
private void createVirtualDisplay() {
virtualDisplay = mediaProjection.createVirtualDisplay(
"MainScreen",
width,
height,
dpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mediaRecorder.getSurface(),
null, null);
}
6 录制屏幕数据

这里利用 MediaRecord 将屏幕内容保存下来,当然也可以利用其它方式保存屏幕内容,例如:ImageReader
private void initRecorder() {
File file = new File(Environment.getExternalStorageDirectory(), System.currentTimeMillis() + ".mp4");
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mediaRecorder.setOutputFile(file.getAbsolutePath());
mediaRecorder.setVideoSize(width, height);
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mediaRecorder.setVideoEncodingBitRate(5 * 1024 * 1024);
mediaRecorder.setVideoFrameRate(30);
try {
mediaRecorder.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}

public boolean startRecord() {
if (mediaProjection == null || running) {
return false;
}
initRecorder();
createVirtualDisplay();
mediaRecorder.start();
running = true;
return true;
}
源码地址​:
完整代码
  收起阅读 »

聊天机器人七大商业模式:客服行业将是重要变现场景!

毫无疑问,聊天机器人如今已经成为继移动应用之后最热门的话题之一。在过去的一段时间里,Google、Facebook、微软、Amazon 等众多科技巨头也都开始纷纷加强在聊天机器人领域的布局。然而很多人可能要开始问了,这些聊天机器人将如何获得盈利呢?客服行业将是...
继续阅读 »

1D74.tmp_.jpg


毫无疑问,聊天机器人如今已经成为继移动应用之后最热门的话题之一。在过去的一段时间里,Google、Facebook、微软、Amazon 等众多科技巨头也都开始纷纷加强在聊天机器人领域的布局。然而很多人可能要开始问了,这些聊天机器人将如何获得盈利呢?客服行业将是重要变现场景!

在过去的几个月里,我和我的同事体验了一些聊天机器人产品,也和很多聊天机器人的开发商就如何盈利这个问题进行了深入探讨。经过认真了解和分析,我认为聊天机器人在未来可能会有以下七种可行的商业模式:
 
商业模式一:用于零售销售的聊天机器人,未来将扩展到整个客服行业。
 



42A2.tmp_.jpg


(Kit 上的 H&M 零售聊天机器人)


聊天机器人最直接的一种使用场景是商家直接向消费者(B2C)销售产品。想象一下,沃尔玛、Harry’ s、Target、Amazon 和京东等开发了这样一种聊天机器人,你可以问聊天机器人是否销售 “牙膏” 或 “刮胡刀” 等,然后聊天机器人会直接回复你这些商品的购买链接,所以用户在于聊天机器人的交流中就能直接完成商品的购买,国内SaaS客服厂商诸如环信智能聊天机器人能够帮助客户解答80%的常见问题,极大的提高效率节省成本。客户服务行业显然是聊天机器人最容易变现的途径之一,参与交易必然离钱更近。中国SaaS客服市场已经有诸如环信等厂商开始自主研发基于深度学习和自然语义分析的智能聊天机器人,虽然目前各家SaaS客服厂商都具备智能聊天功能,未来一年将逐渐转化到拼产品成熟度和易用性阶段。
 
商业模式二:BaaS(Bots as a Services,聊天机器人即服务)

B2B 领域的聊天机器人主要是帮助用户和团队更有效率地开展工作、管理任务或解决团队沟通方面出现的问题。所以 B2B 领域的聊天机器人可能会复制目前已经存在的 B2B 软件领域的商业模式。

对于 B2B 聊天机器人而言,我个人坚信,SaaS 式的免费增值模式可能会成为它最可行的商业模式。对于一些聊天机器人来说,根据你购买的增值服务的不同,那么你能使用到的聊天机器人的功能也是不同的。根据市场调研公司 Forrester 发布的数据,在 2016年,SaaS 和基于云的商业应用服务的营收有望达到 328 亿美元。因此可以想象,B2B 聊天机器人市场的营收应该也不会低。Slack 平台上的大部分应用基本都是基础功能免费,要想使用更高级的功能则需要付费,如下图中的 Growbot.io 那样:



7B48.tmp_.jpg


(Growbot.io 的价格截图)


SaaS产品的商业模式是 B2B 领域的客户都非常熟悉的。在此基础上,聊天机器人未来可能会采取更为复杂的定价模式。
 
 商业模式三:聊天机器人 + 赞助内容和原生内容

因为 BuzzFeed、VICE 等的出现,原生内容和原生广告在过去几年里慢慢变成了一个大趋势。原生内容或赞助内容是这样一种模式:媒体公司(如 BuzzFeed)将那些付费品牌商家的赞助内容直接发布到自己的内容频道上,让读者阅读的时候感觉这篇内容好像是媒体自己创作发布的内容而非品牌商发布的广告。下面这个例子就是杜蕾斯在 BuzzFeed 上发布的原生广告内容:


97BB.tmp_.jpg


现在设想一下你正在咨询一个烹饪方面的聊天机器人,聊天机器人基于自己的原生功能可能会回答你说,在某些菜谱中,使用香菜代替茴香是可以的,然后会发给你一篇文章《这五道用 ‘是拉差辣椒酱’(一种泰式料理常用的香甜辣椒酱)烹饪的菜,吃后绝对让你惊叹不已》。当然了,这里的是拉差辣椒酱就是赞助内容。

这种原生广告的效果要比传统的横幅广告的效果要好很多。这种类型的广告对品牌商和出版商都有益。未来,你可能会看到这种广告形式将被出版商应用到聊天机器人里。
 
 商业模式四:利用聊天机器人做联盟网络营销

联盟广告营销最近很多年已经成为一种非常流行的商业化策略。联盟广告营销指的是一种网站 A 为网站 B 设置推广链接,然后从为网站 B 带来的销售额中获取一定提成的一种广告系统。

Forrester 发布的数据报告显示,2016年,美国在联盟广告营销上的花费将达到45亿美元。联盟广告营销也可以作为聊天机器人的一种商业模式。举个例子,对于聊天机器人的开发商,你可以开发一款健身方面的聊天机器人,在如何保持健康的身体方面为用户提供专业的建议,然后给用户发送一些附有商业推广链接的健身方面的产品。
 
购物聊天机器人 Kip 现在已经开始采用这种商业化策略了。用户可以问 Kip “巧克力” 或 “咖啡” 等很多产品方面的问题,然后它会回复一些产品的购买链接,如下图所示:


B037.tmp_.jpg


用户如果通过 Kip 发的链接购买产品,那么 Kip 团队就能从销售收入中收取一定的提成。下面就有一位用户说他自己是在和 Kip 聊天机器人聊天后通过 Kip 发的链接购买 Amazon Echo 这款产品的。

CDA4.tmp_.jpg


 
 商业模式五:用聊天机器人做用户调研 



E15D.tmp_.jpg


(DisOrDatBot 截图)


最近美国总统大选正在如火如荼地进行中,想了解千禧一代都是怎么看待美国总统大选的吗?你可以付费使用一些聊天机器人来进行这方面的调研。虽然我现在还没有看到有人利用聊天机器人做这方面的事,但我觉得如果有专门的 Q&A 聊天机器人来专门帮助人们做调研的话还是非常靠谱的。

目前像 DisOrDatBot 这样的聊天机器人已经开始向用户问一下调研类问题了。想象一下你作为一次活动的策划者,现在正在发愁究竟邀请哪支乐队来你所在的城市进行表演,是邀请电台司令乐队(Radiahead)还是五分钱乐队(Nickelback,加拿大的著名乐队),这时,与其花很多钱请调研公司帮你做调研,还不如使用 DisOrDatBot 进行调研,看你所在城市的用户到底喜欢哪支乐队。

如果你已经开发了一个定期给一群小众用户提供有价值内容的聊天机器人,那些想触及这些用户或是想向这群用户销售产品的公司可能会比较有兴趣通过你的聊天机器人做调研。
 
商业模式六:将聊天机器人用于潜在客户开发中

我预测,聊天机器人未来将会被应用到潜在客户开发中,一开始主要利用内容去开发潜在客户。通过在房产所有权、保险、婚礼和理财等方面为用户提供专业的信息、想法和见解,聊天机器人然后将自己获得的这些用户信息给到那些销售相关产品和服务的公司。

举个例子,加入你正在和一个 “生活聊天机器人” 聊天,向聊天机器人咨询一些购房方面的问题,随着聊天的深入,聊天机器人搜集了更多有关你的信息,包括你手头有多少首付资金、你想在哪里购房定居、你是否在职、你购买的是否是你的第一套房产等等。在和聊天机器人建立起一定的关系后,聊天机器人于是问你下面这个问题:

“你是否介意我介绍一家比较适合你的房产公司和你联系?”

在经过你的同意之后,聊天机器人就会将你的信息给到你所在地区的一家房地产公司。这家房产公司第二天就会和你联系沟通你的购房需求。然后这家房产公司会给聊天机器人开发商一定的佣金作为为其开发潜在客户的报酬。

商业模式七:按完成的咨询次数或任务收费

人们都希望得到专业的好建议,也愿意为好建议付费。随着聊天机器人变得越来越专业和智能,我认为未来人们会在生活中的很多方面都希望得到聊天机器人的建议和帮助,并愿意为这些建议付费。例如,如果你需要生活方面的建议,你可以和 “Oprah 聊天机器人” 交流,如果你需要获得汽车方面的信息,那么你可以和 “机械聊天机器人” 交流,如果你希望获得匿名的婚姻咨询,你可以和 “婚姻聊天机器人” 交流咨询。当然了,为了得到聊天机器人的建议,你是需要支付一定的费用的。
 
 
(本文由36氪编译自:venturebeat.com 作者:达达 环信稍有改编) 收起阅读 »

环信(Android)设置头像和昵称的方法(最简单暴力的基于环信demo的集成)。

        最近,经常有朋友问到,如何集成环信头像,怎么才能快速显示头像,因时间紧急,很多朋友都没有时间慢慢的研究代码,这里大家稍微花10分钟看一下文章,看完后再花5分钟改一下代码,即可达到你们所要的效果。         当然这个是在你直接复制了demo...
继续阅读 »
        最近,经常有朋友问到,如何集成环信头像,怎么才能快速显示头像,因时间紧急,很多朋友都没有时间慢慢的研究代码,这里大家稍微花10分钟看一下文章,看完后再花5分钟改一下代码,即可达到你们所要的效果。
        当然这个是在你直接复制了demo中的工具类和必要的UI的前提下实现的。简短说明简单暴力的方法:除UI外的其他所有类先复制到自己工程中,UI部分MainActivity中的代码需要一个一个复制过去,如与自己项目冲突的需要调整,然后ChatActivity/ChatFragment也复制过去,这就可以进行聊天了。不过头像就是大家所遇到的头像显示不了,昵称显示为环信号码。
        环信官方是有给出头像的设置的,不过大部分朋友看了之后都是晕呼呼的。官方给出的提示如下:方法一 从APP服务器获取昵称和头像


昵称和头像的获取:当收到一条消息(群消息)时,得到发送者的用户ID,然后查找手机本地数据库是否有此用户ID的昵称和头像,如没有则调用APP服务器接口通过用户ID查询出昵称和头像,然后保存到本地数据库和缓存,下次此用户发来信息即可直接查询缓存或者本地数据库,不需要再次向APP服务器发起请求

昵称和头像的更新:当点击发送者头像时加载用户详情时从APP服务器查询此用户的具体信息然后更新本地数据库和缓存。当用户自己更新昵称或头像时,也可以发送一条透传消息到其他用户和用户所在的群,来更新该用户的昵称和头像。

方法二 从消息扩展中获取昵称和头像

昵称和头像的获取:把用户基本的昵称和头像的URL放到消息的扩展中,通过消息传递给接收方,当收到一条消息时,则能通过消息的扩展得到发送者的昵称和头像URL,然后保存到本地数据库和缓存。当显示昵称和头像时,请从本地或者缓存中读取,不要直接从消息中把赋值拿给界面(否则当用户昵称改变后,同一个人会显示不同的昵称)。

昵称和头像的更新:当扩展消息中的昵称和头像URI与当前本地数据库和缓存中的相应数据不同的时候,需要把新的昵称保存到本地数据库和缓存,并下载新的头像并保存到本地数据库和缓存。


---------------------------------------------------------------------------------------------
        个人推荐使用方法2,优势比较明显,不仅可以设置头像、昵称,甚至以后出现的是否管理员,或者自己APP中的身份标志,如:店小二、医生、客服等等都可以再定义注明,暴力而简单,不需要考虑对方更新头像或昵称,而软件没重启的情况下怎么去更新头像和昵称等等复杂的问题。本指导以最简单的集成为指导,工具类可使用自己软件中的,或通过别的方式实现。
        方法2的集成步骤:
1、在登录的时候,把自己登录成功时后台返回的信息保存到sharedpreferences中,需要包含需要的头像和昵称。
new Thread(new Runnable() {
@Override
public void run() {
EMClient.getInstance().login(username, password, new EMCallBack() {
@Override
public void onSuccess() {
// 登陆成功,保存用户昵称与头像URL
AppSPUtils.setValueToPrefrences("name", loginBean.getName());
AppSPUtils.setValueToPrefrences("logoUrl", loginBean.getLogoUrl());

// 将自己服务器返回的环信账号、昵称和头像URL设置到帮助类中。
DemoHelper.getInstance().getUserProfileManager().updateCurrentUserNickName(loginBean.getName());
DemoHelper.getInstance().getUserProfileManager().setCurrentUserAvatar(loginBean.getLogoUrl());
DemoHelper.getInstance().setCurrentUserName(loginUser.getHxId()); // 环信Id

// ------以下参考demo中的,加载群组和加载消息。然后跳转到首页-------
2、AppSPUtils是个人写的一个工具类,大家可以自己写一个,给出参考代码。
public class AppSPUtils {

private final static int MODE_SPEC = android.os.Build.VERSION.SDK_INT <= 10 ? 0 : Context.MODE_MULTI_PROCESS;

public static SharedPreferences getSharedPreferences(String name) {
return MainApplication.getContext().getSharedPreferences(name,
Context.MODE_PRIVATE | MODE_SPEC);
}

public static SharedPreferences getAppSharedPreferences() {
return getSharedPreferences(Constants.SP_APP);
}

public static String getValueFromPrefrences(String key, String defaultValue) {
return getValueFromPrefrences(getAppSharedPreferences(), key, defaultValue);
}

public static void setValueToPrefrences(String key, String value) {
try {
SharedPreferences preferences = getAppSharedPreferences();
if (null != preferences) {
preferences.edit().putString(key, value).commit();
}
} catch (Exception e) {
e.printStackTrace();
}
}

// 退出登录时要调用
public static void clean() {
try {
SharedPreferences preferences = getAppSharedPreferences();
if (null != getAppSharedPreferences()) {
getAppSharedPreferences().edit().clear().commit();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、保存好自己的信息后,已经成功了四分之一,现在是如何将自己的头像昵称等信息发送出去,最简单的就是使用扩展消息了,在ChatActivity中可以看到,基本上只做了一件事,保证只有一个ChatActivity,那我们的代码在哪里?就在ChatFragment里面。找到代码,可以看到其实它是继承自EaseChatFragment,很多内容在easeui中已经做好了。
这时你会发现ChatFragment类中有一个扩展属性的说明,没错,就是这个方法,把你要发送的内容尽情的发送吧,现附上发送头像和昵称的代码:
@Override
public void onSetMessageAttributes(EMMessage message) {
if (isRobot) {
// 设置消息扩展属性
message.setAttribute("em_robot_message", isRobot);
}

// 通过扩展属性,将userPic和userName发送出去。
String userPic = AppSPUtils.getValueFromPrefrences("logoUrl", "");
if (!TextUtils.isEmpty(userPic)) {
message.setAttribute("userPic", userPic);
}
String userName = AppSPUtils.getValueFromPrefrences("name", "");
if (!TextUtils.isEmpty(userName)) {
message.setAttribute("userName", userName);
}
}
4、发送完成,你已经完成了四分之二的任务了。发送完成后,肯定需要一个接收,其实demo中的广播已经接收好了,那么我们跟随着广播的脚步,来到DemoHelper这个类,初次看这个类,都是云一样的感觉,经过一番查找,发现有个onMessageReceived的方法,并且还有注释“全局监听”,赶紧开工,试试接收吧,可以自己打一下log出来看是不是拿到了我们要的。并且照着demoHelper中的保存方法,将接收到的内容保存起来。
@Override
public void onMessageReceived(List<EMMessage> messages) {
for (EMMessage message : messages) {
message.setMsgTime(System.currentTimeMillis());
//************接收并处理扩展消息***********************
String userName = message.getStringAttribute("userName", "");
String userPic = message.getStringAttribute("userPic", "");
String hxIdFrom = message.getFrom();
EaseUser easeUser = new EaseUser(hxIdFrom);
easeUser.setAvatar(userPic);
easeUser.setNick(userName);

// 存入内存
getContactList();
contactList.put(hxIdFrom, easeUser);
// 存入db
UserDao dao = new UserDao(MainApplication.getContext());
List<EaseUser> users = new ArrayList<EaseUser>();
users.add(easeUser);
dao.saveContactList(users);

getModel().setContactSynced(true);

// 通知listeners联系人同步完毕
notifyContactsSyncListener(true);
if (isGroupsSyncedWithServer()) {
notifyForRecevingEvents();
}

// ******************扩展信息处理完成**********************
EMLog.d(TAG, "onMessageReceived id : " + message.getMsgId());
// 应用在后台,不需要刷新UI,通知栏提示新消息
if (!easeUI.hasForegroundActivies()) {
getNotifier().onNewMsg(message);
}
}
}
5、信息都收到了,就差最后一步就可以显示了,不知道你们是不是激动,反正我是激动了。那在哪里进行显示呢?还是在DemoHelper中,找到getUserInfo方法,代码如下:
private EaseUser getUserInfo(String hxId) {
// 获取user信息,demo是从内存的好友列表里获取,
// 实际开发中,可能还需要从服务器获取用户信息,
// 从服务器获取的数据,最好缓存起来,避免频繁的网络请求

if (hxId.equals(EMClient.getInstance().getCurrentUser())) {
EaseUser currentUserInfo = getUserProfileManager().getCurrentUserInfo();
return currentUserInfo;
}
EaseUser easeUser;
if (contactList != null && contactList.containsKey(hxId)) {

} else { // 如果内存中没有,则将本地数据库中的取出到内存中。
getContactList();
}
// // TODO 获取不在好友列表里的群成员具体信息,即陌生人信息,demo未实现
// if (user == null && getRobotList() != null) {
// user = getRobotList().get(hxId);
// }
easeUser = contactList.get(hxId);
if(easeUser == null){
easeUser = new EaseUser(hxId);
} else {
if(TextUtils.isEmpty(easeUser.getNick())){ // 如果名字为空,则显示环信号码
easeUser.setNick(easeUser.getUsername());
}
}
return easeUser;
}
OK,大功告成,到这里你的头像已经可以显示了。。恭喜你!
有朋友还有疑问,为什么头像是方形的,要变成圆形怎么办?指个路,在easeui中的utils包下,找到EaseUserUtils,这里就是显示用户头像和用户昵称的地方,通过Glide可以轻松显示圆形头像,百度有很多方法,这里就不多讲了,同样,你也可以通过其他图形加载框架来完成。
很多朋友不明白扩展消息的是什么东西,也不明白为什么照着代码敲就能够完成头像的显示,那下面就给大家粗浅的讲讲所涉及到的逻辑关系。
扩展消息:就是你每一次发送消息,都会附带在你发送内容上面的额外消息,他会随着你的内容发送出去,每次会多一点点流量,但微乎其微,个人认为并无多大影响。
显示头像逻辑:区分为本地化缓存和运行内存缓存,在demoHelper中可以发现,有一个成员变量private Map<String, EaseUser> contactList; 这个就是用来保存在运行内存缓存的,只有通过运行缓存,显示头像是最顺畅的,如果每次都从数据库中读取的话,聊天界面会比较卡,有朋友保存在SP里面,以ID做为KEY,头像和昵称等拼接做为value进行缓存,取出后再拆分开分别取值,据说是不卡,大家可以试一下。
         本地化存储是为了在每次打开软件的时候,从本地存储中拿到运行内存中使用做准备,大家可以认真看看demoHelper和MainActivity中的代码。

----------------------------------------------------------------------------------
讲了方法2设置头像,那么方法1,个人不推荐,但这里给出处理的过程,如有个别有需求的,可以按方法1来处理:
1、登录成功后,在手机子线程,访问你们的后台服务器,拿到所有好友的环信ID,头像,昵称。然后按上面的保存方法,保存到本地中,完成后发送广播或EventBus之类到,刷新消息界面和ChatFragment。
2、在getUserInfo中,写和上面扩展消息一样的内容,将如果运行缓存和本地都拿不到,则发起网络请求到后台获取头像,等请求到内容后,再发出广播或EventBus通知刷新消息界面和ChatFragment。

-------------------------------------------------------------------------------------
以上仅为本人在开发过程中的一点小小心得,demo中的保存运存和本地储存的方法,大家也可以单独写,并不会影响程序的运行,当然也有兄弟写过发表,大概根据实际情况择优选取即可。小弟学识浅薄,如果有错漏的,欢迎大家纠正。
如有需要,可以到494167135群中与大家交流学习。本人:乐奇奇,QQ:730326762。大家优先加群,谢谢。
  收起阅读 »

【节日海报】环信祝小伙伴们端午节快乐!

农历五月初五,是中国民间的传统节日——端午节,它是中华民族古老的传统节日之一。端午也称端五,端阳。此外,端午节还有许多别称,如:午日节、重五节,五月节、浴兰节、女儿节,天中节、地腊、诗人节、龙日等等。赛龙舟是端午节的习俗,也是汉族在端午节最重要的民俗活动之一,...
继续阅读 »
农历五月初五,是中国民间的传统节日——端午节,它是中华民族古老的传统节日之一。端午也称端五,端阳。此外,端午节还有许多别称,如:午日节、重五节,五月节、浴兰节、女儿节,天中节、地腊、诗人节、龙日等等。赛龙舟是端午节的习俗,也是汉族在端午节最重要的民俗活动之一,在中国的南方普遍存在,在北方靠近河湖的城市也有赛龙舟习俗,而大部分是划旱龙舟舞龙船的形式。俗话说“水能载舟,亦可赛艇”。环信祝福小伙伴们“端午节快乐”,在工作、学习和生活中都能保持“亦可赛艇”!


33期行业活动.jpg


  收起阅读 »

Android V3.1.3 release ,支持红包功能 、适配Android 6.0

新功能/优化: 消息支持按照本地时间或服务器时间排序 实时音视频支持动态码率 Demo支持红包功能(单聊及群聊红包) Demo适配了Android 6.0运行时权限,现在把targetSdkVersion设到23程序也能正常运行 Bug fix: 修...
继续阅读 »

24958PICwjQ_1024.jpg


新功能/优化:


消息支持按照本地时间或服务器时间排序
实时音视频支持动态码率
Demo支持红包功能(单聊及群聊红包)
Demo适配了Android 6.0运行时权限,现在把targetSdkVersion设到23程序也能正常运行



Bug fix:



修复自动同意好友请求有延迟的问题
修复在targetSdkVersion设为23时,视频通话可能crash的问题


 
版本历史Android更新日志 
 
下载地址sdk下载
 
关于新版sdk使用有任何问题或建议欢迎在下方评论留言   收起阅读 »

环信移动客服v4.7产品更新--新增全新的工作量报表

环信移动客服v4.7产品更新说明 新增功能 1 全新的工作量报表     1.1 工作量总和数据     1.2 新增会话和消息趋势图     1.3 新增24小时进线量分布图     1.4 新增会话标签分布图     1.5 新增会话数分...
继续阅读 »
环信移动客服v4.7产品更新说明


QQ截图20160523180624.jpg




新增功能


1 全新的工作量报表    
1.1 工作量总和数据    
1.2 新增会话和消息趋势图    
1.3 新增24小时进线量分布图    
1.4 新增会话标签分布图    
1.5 新增会话数分布图(按单条会话消息数)    
1.6 新增会话数分布图(按会话时长)    
1.7 新增在线时长数据    


 
优化功能    


 【优化】上下班时间设置    
 【优化】绑定微博渠道界面增加仅支持认证用户的提示    


进入体验最新功能吧:http://kefu.easemob.com/
 
  收起阅读 »

android开发查找聊天记录功能

由于环信本生没有开放出查找聊天记录的接口,但是需求要实现该功能,只能用其他方式实习。   实现聊天功能需要用到EventBus   EventBus的作用的是回来传值,因为本生的Intent传值,接受不了太多的数据。   1.当在聊天页面的时候,去2级页面,获...
继续阅读 »
由于环信本生没有开放出查找聊天记录的接口,但是需求要实现该功能,只能用其他方式实习。
 
实现聊天功能需要用到EventBus
 
EventBus的作用的是回来传值,因为本生的Intent传值,接受不了太多的数据。
 
1.当在聊天页面的时候,去2级页面,获取当前聊天的总数。

2.开启一个异步线程,new EMConversation 类,然后清空

3.在拿聊天页面的当前会话类mConversation .loadMoreMsgFromDB("", 5000) 一次拉取5000条数据出来
通过EventBug 发送出去(发送EventBug的时候不能用post,而且是用postSticky),通过bug应该知道,不知道的百度下。

4.clear  new 出的新会话类

5.在用当前聊天页面的会话类mConversation.loadMoreMsgFromDB("", mMsgCount); 获取本生显示的消息数据
然后在
mConversation.getAllMessages()
这样做的目的,主要是避免内存中出现重复的数据。
 
数据已经筛选出来了,现在开始模糊查询,跳到2级页面的时候,此时因为有EventBus收到的聊天消息,通过关键字段,来模糊出当前消息,附件有配图,匹配出来后,显示聊天的适配器中,附件有配图,后面下啦的,也会拉出最新的数据附件有配图 ,模糊查询和下拉更多,就是相关逻辑了,不多说直接贴代码。
 
1.通过关键字获取当前聊天记录的数据
EMMessage m = mSearchData.get(position); 
String msgId = m.getMsgId(); mIndex = 0;
/** * 获取当前所有数据的索引 */
mTempDatas.clear(); mTempDatas.addAll(mDatas);

for (int i = 0; i < mTempDatas.size(); i++) {
if (msgId.equals(mTempDatas.get(i).getMsgId())) {
mIndex = i;
break;
}
}

/** * 获取匹配到的数据并且获得最新的10条数据 */

for (int j = mIndex; j < mTempDatas.size(); j++) {
if (j == 10) {
break;
}
messages.add(mTempDatas.get(j));
}

mTempDatas.removeAll(messages);
Collections.reverse(mTempDatas);
mChatAdapter.notifyDataSetChanged();
vList_emm.setVisibility(View.GONE);
vHint.setVisibility(View.GONE);
findViewById(R.id.rela_edit).setVisibility(View.GONE); findViewById(R.id.btn_view).setVisibility(View.GONE);
listView.setSelection(0);

 
2.下拉更多
if (mTempDatas.size() == 0) {
IShowToast("已经加载完成");
}else
{

List<EMMessage> temps = new ArrayList<EMMessage>();
temps.addAll(messages);
messages.clear()
for (int i = 0; i < mTempDatas.size(); i++) {
messages.add(mTempDatas.get(i));
if (i == 10) {
break;
}
}

int index = 0;
index = messages.size();
Collections.reverse(messages);
mTempDatas.removeAll(messages);
messages.addAll(temps);
mChatAdapter.notifyDataSetChanged();
listView.setSelection(index);

}

 
 
大致这些了,这个代码排版好麻烦。。。为了方便看,只能一排排的粘贴
 
后期有空,会在教大家如何优化聊天适配器,如何快速扩展,使代码更加清晰。
 
 
  收起阅读 »

环信北京、上海、深圳三地千张《魔兽》电影票免费送,老司机们还等神马!!!

本人强力兽人战士,操作犀利,走位风骚,意识YD,输出恐怖。魂斗罗一命通,拳皇单手无限连,DOTA美杜莎守三路无人破,扫雷12秒通关最高难度;支持TS、UT、YY、环信音视频等多种语音工具,怕延迟可接受全程电话指挥,20秒内上线,会6国语言接受各国队伍,身强体壮...
继续阅读 »

9{75J~1G@J45FK6}3{JH38.png

	本人强力兽人战士,操作犀利,走位风骚,意识YD,输出恐怖。魂斗罗一命通,拳皇单手无限连,DOTA美杜莎守三路无人破,扫雷12秒通关最高难度;支持TS、UT、YY、环信音视频等多种语音工具,怕延迟可接受全程电话指挥,20秒内上线,会6国语言接受各国队伍,身强体壮能连续战斗100场不休息不上厕所, 语音指挥5小时不喝水,战士中的战斗机,全职业认识深刻,熟练魔兽各职业战斗理论,曾发表过《魔兽世界基本理论与数据分析》《论PVP先手与反手》《全职业技能压制与反压制大全》《环信移动客服集成全攻略》等PvP专业文章,会编程能按需制作专用lua插件,熟悉各种环信文档,熟练使用各种环信API接口调用,分分钟帮魔兽主题社交APP添加IM功能。门口已埋雷无人拜访,泡面矿泉水已备,自带发电机,水表在门外,有自用电源专用网络防断电断网......
 
	6月8日环信包场北京、上海、深圳三地影院免费送千张《魔兽》电影票,我答题抢到1张免费票,但是那天刚好去不了,因为要结婚。这张票抢的很辛苦,当时不知道刚好是自己的婚礼当天。现在我想问一下:“谁那天有空能替我结婚么?"




QQ截图20160602204728.jpg


那些年为了部落的程序猿们,你听到《魔兽》的召唤了么?For The Horde!!!


 
抢票规则


每人只有一次答题机会,共计10题。答题后将生成您的专属活动页面,可将页面分享给朋友,每个通过您的专属页面参与活动的用户答题数将累计至您的总答题数,我们将在活动截止前选取前1000名用户送出电影票。例如:A答对了8题,然后A将自己的专属页面分享到朋友圈被B看到,B通过此页面答对了7题,那么此时A累计答题数是8+7=15题,B此时答题数是7题。如果B再将自己的专属页面发给C,C答对6题,那么此时B的总答题数是7+6=13题,A的答题数依旧是15题。


 观影时间、影院地址



时间:6月8日
地点:北京(美嘉欢乐影城中关村店)/上海/深圳(太平洋国际影城天利店)


 
进入抢票地址: http://www.easemob.com/event/film_ms/ 收起阅读 »

Android项目从Eclipse导入到Android Studio中遇到的一些坑

之前开发环境刚从Eclipse切换到Android Studio时做过Android项目由Elipse导入到Studio的总结,也踩过几个坑,随手解决了。但是并没有记录下来。今天遇到一个需求需要导入到Studio中跑一下项目时,费了好长时间才弄成功。干脆,就把...
继续阅读 »
之前开发环境刚从Eclipse切换到Android Studio时做过Android项目由Elipse导入到Studio的总结,也踩过几个坑,随手解决了。但是并没有记录下来。今天遇到一个需求需要导入到Studio中跑一下项目时,费了好长时间才弄成功。干脆,就把踩过的几个坑记录一下,既可以回头来看,也兴许能帮助到别人。
1.项目中使用了其他library的项目.

我导的项目刚好就是在Eclipse中依赖了两个额外的独立的library;在Android Studio 选择条目界面选择从Eclipse项目中导入时,向下continue两步就会提示无法完成设置sdk,导入不成功。
解决方案:该问题的解决办法是,到原来的Eclipse项目中,把原来的依赖解除掉。具体的步骤为:在Eclipse项目根目录下的project.properties文件中将设置的target,以及android.library.reference全部注视掉,之后再次open选择项目,就能够导入打开。

2.依赖模块指定sdk版本不存在的问题

由于很多项目都有依赖,所以导入主module之后还要依次导入依赖的module,然后做依赖关联。在进行完这些操作之后,编译项目时,有可能还会编译失败,这个时候首先要检查一下依赖是否已经进行关联,其次要检查一下配置文件是否有误,也就是各个module的gradle文件以及整个项目的build.gradle文件;很多时候都是出在配置文件上。我遇到的问题是依赖的module导入后build.gradle中指定的编译sdk为10,我本地环境不存在该版本的sdk,所以编译失败不通过。
解决方案:根据我自己的经验,说下我的解决思路,不一定对,仅供参考。遇到编译失败的问题,首先就是看主module和各依赖module的依赖关系是否已经关联,然后查看各个module的配置文件以及整个项目的build.gradle,一般的编译失败在terminal中都会有相应的错误日志,以及提示如何修改,根据日志进行修改能更明确。另外,像我遇到的各个module中指定sdk版本不一致的问题,为了方便,可以对所有的module都指定统一的编译sdk,具体的做法就是在项目根目录下的配置文件中声明指定,然后在各个module的build.gradle文件中进行引用

3.某些png图片编译时提示:libpng warning : iCCP: Not recognizeizing known sRGB profile that has been edited问题

解决了上面的问题之后就是提示该问题了,还是编译失败。其实之前在别的项目中遇到过类似的警告,没有解决也能编译通过,但是今天编译走到这就提示这些,然后编译失败,没办法,只能解决了把该问题排除。具体的原因真没弄明白,看晚上的资料说新版本编译条件比旧版本要苛刻,所以会提示这个问题。下面说下该问题的解决办法。
解决方案:解决该问题需要借助一个图片编辑工具,我选择的是Image Maglick,下载安装没得说。然后打开终端,执行如下命令:
find <path to res folder> -name *.png -exec mogrify +profile sRGB {}  \;
等待命令执行完成即可。将terminal中提示的所有的目录都执行一遍该命令,然后再进行编译就可以。具体的上面这句命令的意思大概就是:删除所有png文件内的profile sRGB。在写这篇博客的时候,我重新搜了下别人提供的答案,有人说是5.0以后编译会提示该警告,所以还有一种方案是修改编译工具的版本由
buildToolsVersion "22.0.1" 改为 buildToolsVersion "20.0.0"
这种方案我并没有试过,有遇到该问题的可以尝试解决试试。
 
4.编译提示多个资源文件被重复定义的问题

有的时候编译失败之后,terminal内的编译日志会提示多个文件重复定义的问题,导致编译不能通过。
解决方案:该问题的解决方案很简单,也很暴力,直接根据提示重复的文件名找到主module中的该资源文件,删除即可。需要注意的是,删除时要删除主module中的资源文件,保留依赖module中的声明。因为在Android Studio中主module中能够引用依赖module中的资源,而依赖module中不能引用主module中的资源。

5.Java finished with non-zero exit value 1的问题

出现这个value 1的问题就是项目中有明确的报错的问题了。比如AndroidManifest文件中存在项目中已不存在的Activity声明,比如布局文件中引用资源错误,比如drawable提示找不到或者程序中有问题等,都是value 1的失败提示。总之,value 1的问题一定是程序中有明显的错误,Android Studio编译时检查比Eclipse要严格,所以就会提示错误,这个需要自己根据自己的项目去找,思路同样也是结合terminal日志提示,外加从配置文件到程序,依次进行。

6.Java finished with non-zero exit value 2的问题

出现value 2的问题原因比较好找,就是jar包冲突,出现此问题意味着项目中引用了重复的jar包。通常最最常见的jar包冲突就是v4包的冲突。我们的项目中配置别人的依赖时也遇到过v4的冲突,这个需要在配置里面将v4去除。具体的配置格式大概如下:
compile('cn.trinea.android.view.autoscrollviewpager:android-auto-scroll-view-pager:1.1.2') {    
exclude module: 'support-v4'
}
我遇到的大概的就是这几类问题,可能有重复,也肯定是不全。有误的地方欢迎追加指出。

本文作者喜欢而非坚持
  收起阅读 »

环信编程大赛优秀开源项目之季军:咚咚,一款专注团队高效沟通的移动客户端

根据IDC数据显示,中国有近200万开发者,身为一个程序员,我们生活在一个 IT 系统越发复杂且多变化的时代。有时候执行一个简单的开源项目,开发一个基础功能都需要精准定义并耗费大量时间专注任务。随着云计算的兴起,API 和SDK开始作为软件之间重要媒介而作为一...
继续阅读 »
根据IDC数据显示,中国有近200万开发者,身为一个程序员,我们生活在一个 IT 系统越发复杂且多变化的时代。有时候执行一个简单的开源项目,开发一个基础功能都需要精准定义并耗费大量时间专注任务。随着云计算的兴起,API 和SDK开始作为软件之间重要媒介而作为一种独立应用而存在,“一切皆软件,一切皆API,一切皆SDK”。通过API和SDK可以让开发者摆脱繁重的基础功能底层开发,短时间即可让App拥有各种诸如内置IM、统计等基础功能组件能力。 

5月14日,由环信联合猿圈共同推出的“首届环信编程大赛”颁奖典礼在中关村义创空间隆重举行。本次环信编程大赛历时两个月,由线上初赛、决赛和颁奖典礼三个环节组成,总计报名人数2000+,收到决赛项目100+。最终由评委会认定的13个优秀开源项目及开发者集体亮相颁奖典礼。其中“方圆十里”、“高仿微信“和“咚咚”三个开源项目名列前三,共同分享了15000元奖金和价值12000元的专属表情包。



501377d7f597821d8d48d87e2f0c85d2[1].jpg


优秀项目开发者合影




tmpdir--16_6_1_15_39_16.jpg


小鲜肉可畏,“咚咚”项目负责人95后蔡斯仪分享技术开发细节


其余入围的十余个优秀开源项目同样引起了到场开发者的热烈追捧,环信将分期将入围的优秀项目代码免费开源给小伙伴们。今天我们带来的是本次环信编程大赛的季军选手——咚咚,一款专注团队高效沟通的移动客户端。咚咚基于环信平台进行开发,旨在打造一款团队高效沟通的移动客户端,供企业内部协作使用,适应移动办公需要,提升企业沟通协同效率,增强企业办公管理效率。


 


6963.tmp_.jpg



“咚咚”APP界面截图


1.软件介绍


咚咚基于环信平台进行开发,旨在打造一款团队高效沟通的移动客户端,供企业内部协作使用,适应移动办公需要,提升企业沟通协同效率,增强企业办公管理效率。(咚咚一期实现了用户登入登出功能、通讯功能、投票功能。)


2.功能介绍


一、用户登录注册功能

1.系统登录界面

2系统注册界面

注册功能实现:限制账号长度必须为11位,出生日期选择,头像选择(从系统自带头像中选择)

3.系统首页

登陆成功,即进入系统主页面

4.个人信息查看及修改

进入主页面后点击个人信息查看,即可查看相关信息,并对其进行修改

主界面

(1)头像修改功能实现

(2)名字修改功能实现

(3)部门修改功能实现

(4)性别选择功能实现

(5)个性签名修改功能实现

二、通讯功能

主界面

功能实现:群组聊天,单对单私人聊天,查看好友列表,查看好友详情,查看群组详情

三、投票功能

主界面

功能实现:展示用户发起的投票列表,新增投票,投票提交


3.使用技术


环信即时通讯云


4.作者心得


咚咚基于环信即时通讯云平台,避开了即时通讯等繁杂底层技术开发,使得项目的难点得以轻松解决。例如在平台上可以快速使用即时通讯功能、用户好友管理以及群组管理等功能,让项目得以快速开发成型,产品团队只需要专注于APP核心业务层开发即可,也给移动互联时代的APP开发指出了一条明路。




f5b3ff1766d133aa66bec8ad18f9ddb0[1].jpg



 特别感谢以下企业的大力支持:


义创空间提供颁奖场地
 
萌岛从自有形象库中授权一套价值12000元的表情包
 
Emokit赞助Apple Watch一台
 
猿圈全程提供技术评测支持


 git源码下载https://github.com/caisiyi/SYTeamApp
 
更多开源项目请点击http://community.easemob.com/article/825307813
 
咚咚项目作者演讲PPT下载↓↓↓
  收起阅读 »

环信DEMO群里要发红包啦~~~

本周环信将携手云账户,在环信demo里进行福利红包大派送。   这里不光可以聊工作,还能每天抢红包。环信承诺0成本+3小时,还能让你的APP轻轻松松接入红包功能。 活动参与指南: 打开环信demo, 记住是打开环信即时通讯云...
继续阅读 »
本周环信将携手云账户,在环信demo里进行福利红包大派送。
 

43期_产品快递.jpg



这里不光可以聊工作,还能每天抢红包。环信承诺0成本+3小时,还能让你的APP轻轻松松接入红包功能。

活动参与指南:



打开环信demo, 记住是打开环信即时通讯云DEMO哦,环信即时通讯DEMO哦,环信即时通讯云DEMO哦,搜索公开群申请加入(注,为防止出现领取不到红包,请更新到最新版本demo,最新版本demo下载地址:点击进入下载页面
 
还没有安装的,可以扫码下载安装哦:  


QQ截图20160531200332.jpg



加群: 

环信红包群①
1464 6843 22478 (搜索时数字间没有空格)


环信红包群②
1464 6873 54665 (搜索时数字间没有空格)

 


未命名.jpg




红包已准备好,就等你来抢!


 关于环信红包:
环信与云账户联手打造聊天红包,红包SDK产品已迭代至2.0版,目前已为拉拉公园,YOU+公寓等近100家APP接入红包功能,用户留存和活跃度大幅提升。 
收起阅读 »

楚楚街10亿元C轮融资,90后移动电商独角兽的背后!

互联网+时代,传统电商格局已定,移动电商领域仍然风起云涌。近日,定位90后个性化需求的移动电商楚楚街已完成10亿元C轮融资。本轮融资由软银中国资本、新天域资本领投,基石资本、钟鼎创投跟投。在“资本寒冬”阴影还未完全散去的当下,楚楚街的大手笔运作可谓一剂市场强心...
继续阅读 »
互联网+时代,传统电商格局已定,移动电商领域仍然风起云涌。近日,定位90后个性化需求的移动电商楚楚街已完成10亿元C轮融资。本轮融资由软银中国资本、新天域资本领投,基石资本、钟鼎创投跟投。在“资本寒冬”阴影还未完全散去的当下,楚楚街的大手笔运作可谓一剂市场强心针。同时其估值将接近70亿元,已经成长为90后移动电商领域的首个“独角兽”。

楚楚街专注于商品特卖领域,定位为时尚化、年轻化、全球化的移动电商平台,提供的服务主要包括 “全球购”、“逛啦” 及 “限时抢购” 等板块。目前,楚楚街在移动端的安装量已经超过1亿,年销售额超过50亿,平均每天超过1300万成交额。近期楚楚街将推出“609”大促来回馈客户,谁都可以“买买买”,但不是谁都可以“买的漂亮”!

ZJT77AY[U3S{VFQXO@C@5MH.png


对于楚楚街来说,每天千万级的成交额意味着每天有百万量级的下单量。这么庞大的交易规模下,需要非常稳定且能支持千万级高并发的客服技术来支撑楚楚街数万家商户完成销售闭环,这其中环信移动客服功不可没。更重要的是,快速成长的楚楚街,在客服业务上的需求以及环信提供的全媒体智能云客服解决方案,对于其他电商平台具有很强的借鉴意义,所谓窥一斑而知全豹。

在即将到来的609年中购物嘉年华中,楚楚街将带来5亿红包的史上最大力度的优惠,而环信提供的全媒体智能云客服解决方案也与楚楚街联手,为本次的“嘉年华”助力。6月7日到6月9日期间,楚楚街13大分会场+3个特别会场将全面启动,满足用户“买买买”的夏日疯狂购物需求,而“千款单品秒杀,爆款商品凑单”的重磅优惠,更是最大程度为消费者谋取福利。

初衷:帮助商户构建APP内销售闭环,用环信移动客服升级服务

楚楚街决心在自己平台中,植入在线客服功能,为商户打造一个在APP内就能完成销售闭环的客服系统。顾客无需跳出APP,在商品展示页或是订单页即可一键呼唤客服,这样就使顾客和商家之间无缝连接,让交易更加简单。

将客服功能内置入APP中,最基础的一点是要求APP具备即时通讯功能,能够实时进行商家和顾客之家的图文消息传递。但楚楚街希望为商户打造的是一个专业的客服系统,这就要求有更多的专业功能。

梳理业务需求,确定客服功能需求

在确定植入在线客服系统后,下一步要做的就是根据楚楚街的业务模式,确定客服系统功能需求。对于楚楚街这样的电商平台而言,客服系统除了会话排队、客服质检等基础功能外,又有独特的功能需求:

· 多租户支持

由于楚楚街上有数万家商户,如果让每个商户自己搭建客服系统,不仅技术上难以实现,经济上也不现实。因此,楚楚街从一开始就选择了“统一平台接入,独立运作管理”的客服平台建设思路。所有商户共享同一客服平台,但每个商户又是以“租户”的形式独立运作管理的,这种集约化的建设方式为商户解决了技术和成本难题。

· 营销型客服

对电商而言,在线客服人员提供售后服务外,更多时候承担的是销售的角色,为客户提供售前咨询服务,这是和传统商业的一个很大不同。因此,在线客服系统应当从销售的角度,具备部分营销功能。

众多SaaS客服厂商,楚楚街为什么选择环信?

当前SaaS客服厂商众多,楚楚街在经过细致评比、测试后,最终选择环信。看中的是环信解决方案的先进性、稳定性以及与楚楚街电商经营理念的契合。

楚楚街选择的是环信多租户版的全媒体移动客服,可以在一个平台上对所有商户的客服进行管理,包括商户账号设置、权限分配等。同时,每个商户也可以自行进行配置调整,如客服人员数量,接待量设置等。这样既实现了统一部署的集约化优势,又满足商户的个性化需求。

对商户而言,无需任何技术投入,马上就可以享受全媒体客服带来的优势。首先是服务渠道的一体化,不论客户是来自楚楚街APP端,还是来自商户微信公众号、微博,甚至是商户自己门户网站,都可以实现客户请求的统一接入,统一分配和统一管理。其次是服务的专业化,可以进行客户画像、订单轨迹、服务质检、报表统计等,使商户拥有了媲美大型呼叫中心的的服务品质。环信提供的智能机器人更是帮助客服大幅降低了人工服务成本,80%的重复问题机器人都可以帮助解答。在下班时间,可以由智能机器人代替人工客服,提供7*24的不间断服务。

KHJT)A13E(VG6(ARW()QE.png


除了与楚楚街的客服需求匹配度最高外,环信还具备一系列与其它同类厂商相比的竞争优势:

· 独有的主动回呼技术,帮助商户实现轻松营销

环信基于自有的长连接技术优势,在移动客服产品上提供了一个主动外呼功能,帮助商户轻松实现客服营销。长连接技术的关键之处在于为每一个客户维持一个TCP连接,可以随时向客户端发送消息(当然,这种设计对后台系统资源和调度能力有很高要求)。客服人员可以把最新的促销以后台消息的形式发送给指定人群。例如,把一款儿童座椅的促销信息发给妈妈用户。如果客户端没有打开APP,那么会在系统通知一栏呈现出该消息,客户可以在闲暇时候再打开看。这是一种无干扰式的营销手段,配合客户画像,就能实现具有良好用户体验的精准式营销。

此外,主动回呼还可以帮助商家提高订单成交率。根据统计,楚楚街每日近百万订单中,问题订单大概占千分之五,也就是说每天就会产生数千个有问题的订单。有主动回呼功能,就可以在出席问题订单时,主动联系客户,从而提高成交率,同时提供了更好的客户体验。

· 强大技术支撑,满足楚楚街未来发展预期

随着交易平台的扩大,对客服系统的即时消息传递能力也将提出更高的要求。在所有SaaS客服厂商中,环信是唯一一家同时拥有即时通讯云PaaS产品和SaaS客服产品的厂商,即时通讯技术是环信的起家之本。截至2015年12月,环信即时通讯SDK已覆盖手机终端3.19亿,日发送消息2.1亿,这些数据展示了环信即时通讯技术的强劲性能和扩展性,这是和竞争对手在技术层面的最大差异,因为大多数友商并不具备即时通讯能力,需要借用第三方平台。对于楚楚街而言,选择环信,意味着性能将不会成为担心的问题,能够为楚楚街未来快速扩张扫除障碍。

· 历经市场检验,产品成熟度更高

楚楚街在产品选型时,除了考虑功能、性能外,还尤其关注产品的稳定性。在APP中,每一次和客服的对话对商户而言都是一个潜在的商机,一旦出现服务中断或是消息丢失,损失的不单是客户体验,更是销售机会。而环信在产品稳定性和成熟度上,则明显胜于竞品。根据易观智库2016年1月的数据,在移动端SaaS客服市场,环信的市场占有率高达77.4%,产品稳定性、成熟度已经经过了海量用户的检验。

帮助商户省钱、省时、省力,楚楚街成长为90后移动电商首个独角兽!

楚楚街能够持续增长,赢得资本青睐,背后靠的是数万家商户支撑起的商业生态。通过部署环信移动客服,楚楚街使自己平台的商业价值得以充分体现: 

· 通过在一个平台内管理所有商户客服,楚楚街共创立商户客服账号20000多个,节省60%客服体系搭建成本。

· 强大的移动端客服SDK,保证了楚楚街高并发性、稳定性和可靠性,降低了20%的投诉率,提高了40%的访客转化率。

· 商户客服利用环信客服系统回呼500+次/日,减少退换货率79%。

对楚楚街而言,用移动客服帮助他的客户——也就是平台上数万家商户实现商业成功,是实现自己成功的基石,合作共赢,方能实现最大价值。 收起阅读 »

有一种六一节叫别人家的公司

员工六一假+迪斯尼礼物,环信你值得加入!环信祝福所有小朋友们六一快乐 [[礼物]] 想要工作家庭两不误还等神马?猛戳大图或者登陆拉勾网了解环信最新招聘信息tanlent@Easemob.com [[来]]  

ea790d9djw1f4djxhn24vj20c80fpgmr.jpg


员工六一假+迪斯尼礼物,环信你值得加入!环信祝福所有小朋友们六一快乐 [[礼物]] 想要工作家庭两不误还等神马?猛戳大图或者登陆拉勾网了解环信最新招聘信息tanlent@Easemob.com [[来]]

ea790d9djw1f4djxidvx4j20qo1bfgt8.jpg




ea790d9djw1f4djxj1kcyj20qo0yudn5.jpg




ea790d9djw1f4dk9rvtiyj20hs4vowr5.jpg


 

ios V3.1.3 release 支持ipv6

  IOS V3.1.3 2016-5-27 更新日志    新功能: SDK增加实时视频通话切换摄像头功能。 SDK支持ipv6。 消息支持按照本地时间或者服务器时间排序。 Demo支持单聊发送红包和群聊发送红包。 bug fix: 修复自动同意好友...
继续阅读 »

24958PICwjQ_1024.jpg


 
IOS V3.1.3 2016-5-27 更新日志 
 
新功能:


SDK增加实时视频通话切换摄像头功能。
SDK支持ipv6。
消息支持按照本地时间或者服务器时间排序。
Demo支持单聊发送红包和群聊发送红包。



bug fix:


修复自动同意好友请求有延迟的问题。



SDK细节调整:


SDK将第三方依赖从SDK静态库分离出来(libssl.a,libcrypto.a,libcurl.a)


版本历史:ios更新日志ios更新日志
下载地址:sdk下载
 
关于新版sdk使用有任何问题或建议请在下方评论留言 收起阅读 »

EMContactManager.getInstance().addContact 怎么判断是否发送成功啊

EMContactManager.getInstance().addContact  怎么判断是否发送成功啊
EMContactManager.getInstance().addContact  怎么判断是否发送成功啊

找一家靠谱SaaS服务商的五个关键因素

如今,企业都在争先恐后采用云来改造其混合架构以获取竞争优势,而SaaS(软件及服务)的出现使得企业能够轻松驾驭“云”这匹“快马”。SaaS目前被公认为最有效的帮助企业创新的方案,其通过分发IT和商业应用方案为企业解决问题,同时还能提供极佳的用户体验。 ...
继续阅读 »
如今,企业都在争先恐后采用云来改造其混合架构以获取竞争优势,而SaaS(软件及服务)的出现使得企业能够轻松驾驭“云”这匹“快马”。SaaS目前被公认为最有效的帮助企业创新的方案,其通过分发IT和商业应用方案为企业解决问题,同时还能提供极佳的用户体验。



QQ截图20160603173526.jpg



无论是通过公有云还是私有云,SaaS都能提供最及时的信息反馈和对资源的优化。而IT技术与业务范围实现精准对接使得使用者在云端拥有提供快速、高质量的应用开发的能力,这也让企业能够实现更高的运营效率。SaaS能够通过永远在线(always-on)、和即时启动(instant-on)应用分发管理工具帮助开发人员更快更高效地实现代码编写。

然而,企业管理者也不能过于随意地购买SaaS服务,您应当选择最适合您企业并能帮您的企业持续进步的服务供应商。

规划您自己的云服务实现路线

首先您要明白一点,实现混合云方案需要克服种种困难,这一点非常重要,而且您的云服务的实现路线与其他企业都会有差异。这条路线的规划受制于您的目标、成熟度、风险预测等等其他因素。所以从一开始您就要做好规划,以避免昂贵的试错成本。

挑选SaaS服务商 您需要关注哪些?

在挑选SaaS服务商的时候,您需要关注五个关键点,这五点都是SaaS应该能够给予您的看得见摸得着的对业务的帮助。在讨论这五点之前,我想要告诉您一个合格的SaaS服务提供商应该为您做什么?

一家合格的SaaS服务提供商应该能够接管您所有设施的维护和升级工作。他们应该能够高质量地保证服务的持续可用。
 
在选择SaaS服务商应关注以下这些方面
 
他们有稳健的全球性的服务拓展规划,以满足您企业在地区的和全球的商业需求;(环信全球拥有6个数据中心,包括北美、法兰克福、新加坡、日本、以及北京和杭州数据中心。海外AWS数据中心plus海外代理,拓展海外用户自此无忧。)

·拥有标准的、类似于Web服务的集成API,可以最大化地利用市场上可用的一切技术和能力;(环信免费提供上百种API功能,包括单聊、发送文字/表情/语音/地理位置/照片/视频/名片/自定义扩展消息、消息回执、集成第三方用户体系、群聊、离线消息、离线消息推送、实时音频/视频......

·这个合作伙伴与您的业务共同成长进步,这样您就可以在需要获得服务时发挥非核心职能,同时能够获得最为精确的相应等级和数量的服务。(环信CSM团队和技术支持团队7*12小时帮助客服成功

避免碎片化的SaaS解决方案极其关键,因为它们会导致您管理复杂度和成本的上升。您应该及时获取您所需要的服务,从而在操作成本和经济成本上的双重获益。
 
好的SaaS服务商应给予您的五大帮助
 
1、在您需要帮助的时候,它可以立刻给您相应的帮助。我们相信一个好的SaaS提供商应该保证您把所有注意力放在业务上,而从不需要为支持业务的软件和系统的维护发愁。需要什么,它就立刻能给您什么,不多也不少。

2、可以简便快速地访问SaaS服务。我们相信好的SaaS服务可以快速便捷的得以访问,就像使用内部应用一样。这些服务应该与已有的工作流程和谐共存。这一点的实现靠的是SaaS的提供商能够简化业务的集成流程,在增加SaaS服务的同时保护您对于已有业务的投资,并扩展您的混合网络架构。

3、保证您一直能用上最新的软件,没有延迟,也没有升级带来的麻烦。企业应用的升级通常既费时又费钱,因此许多时候因为困难和经费导致升级不及时,企业就没有办法享受到最新的功能。

4、选择已获得安全认证的企业。我们承认找一家世界级的SaaS服务提供商利用其数十年的专业运营经验来提供企业级的服务的要求有点离谱了,很少有供应商能有10年以上的SaaS服务经验并拥有服务多个客户的全球数据中心,且能够立即扩展满足您的业务需求。

5、在数据中心的管理之中追求最高服务质量和创新。我们相信数据中心的建设和管理实践对于维持最佳的服务和创新同等关键,您应该充分信任SaaS提供商的专业能力,而不是对于所有事情都亲力亲为。(环信编译自 http://community.hpe.com/ 如有转载请注明出处。) 
环信成立于2013年4月,是一家全通讯能力云服务提供商。产品包括全球最大的即时通讯云PaaS平台——环信即时通讯云,以及全球首创的全媒体智能云客服平台——环信移动客服。现已覆盖包括电商、O2O、互联网金融、在线教育、在线旅游、移动医疗、智能硬件、游戏等20大领域的Top10客户,典型用户包括国美在线、58到家、快牙、随手记、猎聘、海尔、神州专车等。截至2015年底,环信共服务了50833家 App 客户,SDK覆盖手机终端3.19亿台,平台日均发送消息2.1亿条。


JBOA)0)5DRO3H{JPDKLPM73.png


  收起阅读 »

Fmpeg惊爆漏洞:环信现有用户完全不受此漏洞影响!

 据国内媒体报道,近日全球领先的多媒体框架FFmpeg被曝出漏洞,通过该漏洞可在播放漏洞视频或在转码过程中触发本地文件,读取获得指定文件。FFmpeg已于4月发布更新,但仍有大量Android及iOS APP使用该开源程序用于播放功能。因为开放源码的便利性和强...
继续阅读 »

1.jpg


 据国内媒体报道,近日全球领先的多媒体框架FFmpeg被曝出漏洞,通过该漏洞可在播放漏洞视频或在转码过程中触发本地文件,读取获得指定文件。FFmpeg已于4月发布更新,但仍有大量Android及iOS APP使用该开源程序用于播放功能。因为开放源码的便利性和强大的多媒体功能,FFmpeg被广泛用于Android及iOS APP的播放功能,百度云、爱奇艺视频、网易云音乐、斗鱼TV、疯狂猜词等多款手机用户常用的APP均使用了FFmpeg库文件,大量用户可能受此漏洞威胁。


2.jpg



使用率最高的FFmpeg文件库top10



3.jpg



市场占有率高真不是宝宝的错

360互联网安全中心对国内主流应用市场的124371款app进行扫描,发现有超过6000款应用受此漏洞影响,占到总数的5%,受影响APP类型涵盖各个类型,其中仅通讯社交、便捷生活、影音视听三类就占到一半以上,进一步对受漏洞影响的6314款app所使用的ffmepg库文件分析发现,使用率最高的libeasemod_jni.so属于环信SDK的库文件,排名第二的libcyberplay-core.so均为目前最流行的视频SDK,拥有千万级的用户量。



4.jpg




本地打开m3u8文件并使用FFmpeg解析时就会触发漏洞 

该漏洞与HLS协议的.m3u8文件相关。攻击者可以制作一个特殊的.m3u8或其他视频文件,当ffmpeg播放此特殊文件时会把本地文件内容传送到远程服务器上。

环信SDK只使用FFmpeg作解码和录制之用,没有包含HLS相关功能,也没有对外提供任何播放视频文件的api,所以该漏洞对环信用户是没有任何影响的!

环信音视频专家通过跟360互联网安全中心工程师沟通发现,360互联网安全中心是通过检测FFmpeg版本号来判断app是否受此漏洞影响,由于环信SDK所用的FFmpeg版本较老,被360互联网安全中心误认为受此漏洞影响。

综上,环信现有用户完全不受此漏洞影响!!!环信现有用户完全不受此漏洞影响!!!环信现有用户完全不受此漏洞影响!!!重要事情说三遍!!!

后续环信的SDK也会接受360互联网安全中心建议升级到FFmpeg新版本。


漏洞新闻: http://www.ccstock.cn/finance/minshengxiaofei/2016-05-24/A1464060408687.html

俄罗斯工程师最先发现此漏洞新闻:https://habrahabr.ru/company/mailru/blog/274855/ 收起阅读 »

服务器端:关于Request body is invalid.解决

Request body is invalid是由于发起访问前会验证请求体是否为空及类型是否正确。 1、需要检查自己是否传递的参数 2、需要检查类型是否正确   环信给的java代码中有个小bug 在很多API实现中并没有传递body:例如Easem...
继续阅读 »
Request body is invalid是由于发起访问前会验证请求体是否为空及类型是否正确。
1、需要检查自己是否传递的参数
2、需要检查类型是否正确
 
环信给的java代码中有个小bug
在很多API实现中并没有传递body:例如EasemobIMUsers中的getIMUsersByUserName方法中


1.png


但是请求环信api之前增加了校验:HttpClientRestAPIInvoker
看图:

2.png




3.png


 
 
解决方案:
方案1:注释body校验的代码
方案2:添加body信息
  收起阅读 »

android中如何显示开发者服务器上的昵称和头像

本文方法已经废弃!请看作者另外一篇文章: 在android中5分钟实现昵称头像的显示  http://www.imgeek.org/article/825308757 无论是IOS还是安卓,集成环信SDK遇到的第一个问题,就是如何显示自有用户体系中的昵称...
继续阅读 »


本文方法已经废弃!请看作者另外一篇文章:



在android中5分钟实现昵称头像的显示  http://www.imgeek.org/article/825308757


无论是IOS还是安卓,集成环信SDK遇到的第一个问题,就是如何显示自有用户体系中的昵称和头像。运行环信的demo app,注册用户是直接使用环信ID(username)作为用户名,但是在我们实际应用中,需要将自有用户体系的UserId生成GUID作为环信ID(username)【参考:http://docs.easemob.com/im/100serverintegration/20users】,这时候如果不经过处理,则会显示如下界面:

444.png



那么如何处理,才能显示正确的用户昵称和头像呢?
其实官方已经提供有解决方案了,只不过没有给出示例代码而已。
http://docs.easemob.com/im/490integrationcases/10nickname
 
引用一下关键文字:
方法二:从消息扩展中获取昵称和头像

昵称和头像的获取:把用户基本的昵称和头像的URL放到消息的扩展中,通过消息传递给接收方,当收到一条消息时,则能通过消息的扩展得到发送者的昵称和头像URL,然后保存到本地数据库和缓存。当显示昵称和头像时,请从本地或者缓存中读取,不要直接从消息中把赋值拿给界面(否则当用户昵称改变后,同一个人会显示不同的昵称)。
昵称和头像的更新:当扩展消息中的昵称和头像 URI 与当前本地数据库和缓存中的相应数据不同的时候,需要把新的昵称保存到本地数据库和缓存,并下载新的头像并保存到本地数据库和缓存。
 
没错,官方提供了两种思路,鉴于项目的实际情况,我选择了【方法二】。
于是,无论安卓,还是IOS,我们都是这样处理的:
【1】.APP间传递用户属性信息:发送(文本、图片...)消息时,要在消息扩展(message.ext)中附带当前用户的属性信息;
【2】.本地缓存用户信息:在接收消息的回调函数里,读取消息扩展(message.ext)里用户属性(键值对)信息;如果本地缓存(sqlite)不存在该用户,则新增缓存记录,如果存在,则更新记录;(用户登录或注册成功后,也要更新用户缓存信息)
【3】.获取用户属性信息:在需要显示昵称的地方,根据环信ID,读取sqlite缓存数据,获取用户昵称和头像;
 
用户属性信息:ChatUserId(环信ID),ChatUserNick(用户昵称), ChatUserPic(用户头像,完整的url地址);
 
关键代码:
/***************** 环信用户缓存信息 *******************************/
public static final String ChatUserId = "ChatUserId";// 用户的环信ID
public static final String ChatUserPic = "ChatUserPic";
public static final String ChatUserNick = "ChatUserNick";
/***************** 环信用户缓存信息***********end ********************/



UserInfoCacheSvc.java是环信用户信息缓存管理类
/**
* 缓存用户信息(主要用于聊天显示昵称和头像)
*/
public class UserInfoCacheSvc {
public static List<UserApiModel> getAllList(){
Dao<UserApiModel, Integer> daoScene = SqliteHelper.getInstance().getUserDao();
try {
List<UserApiModel> list = daoScene.queryBuilder().query();
return list;
} catch (SQLException e) {
e.printStackTrace();
}

return null;
}

public static UserApiModel getByChatUserName(String chatUserName){
Dao<UserApiModel, Integer> dao = SqliteHelper.getInstance().getUserDao();
try {
UserApiModel model = dao.queryBuilder().where().eq("EaseMobUserName", chatUserName).queryForFirst();
return model;
} catch (SQLException e) {
e.printStackTrace();
}

return null;
}

public static UserApiModel getById(long id){
Dao<UserApiModel, Integer> dao = SqliteHelper.getInstance().getUserDao();
try {
UserApiModel model = dao.queryBuilder().where().eq("Id", id).queryForFirst();
return model;
} catch (SQLException e) {
e.printStackTrace();
}

return null;
}

public static boolean createOrUpdate(String chatUserName, String userNickName, String avatarUrl){
try {
Dao<UserApiModel, Integer> dao = SqliteHelper.getInstance().getUserDao();

UserApiModel user = getByChatUserName(chatUserName);

int changedLines = 0;
if (user == null){
user = new UserApiModel();
user.setUsername(userNickName);
user.setHeadImg(avatarUrl);
user.setEaseMobUserName(chatUserName);

changedLines = dao.create(user);
}else {
user.setUsername(userNickName);
user.setHeadImg(avatarUrl);
user.setEaseMobUserName(chatUserName);

changedLines = dao.update(user);
}

if(changedLines > 0){
Log.i("UserInfoCacheSvc", "操作成功~");
return true;
}
} catch (SQLException e) {
e.printStackTrace();
Log.e("UserInfoCacheSvc", "操作异常~");
}

return false;
}

public static boolean createOrUpdate(UserApiModel model){

if(model == null) return false;

try {
Dao<UserApiModel, Integer> dao = SqliteHelper.getInstance().getUserDao();

UserApiModel user = getById(model.Id);

if (!StringUtils.isNullOrEmpty(model.getHeadImg())){
String fullPath = "http://image.baidu.com" + model.getHeadImg();
//特别注意:这里用是图片的完整链接地址,如果要取缩略图,需要服务端配合;

model.setHeadImg(fullPath);
}
int changedLines = 0;
if (user == null){
changedLines = dao.create(model);
}else {
model.setRecordId(user.getRecordId());
changedLines = dao.update(model);
}

if(changedLines > 0){
Log.i("UserInfoCacheSvc", "操作成功~");
return true;
}
} catch (SQLException e) {
e.printStackTrace();
Log.e("UserInfoCacheSvc", "操作异常~");
}

return false;
}

}
首先要在用户登录或注册成功后,返回用户登录信息Model时,缓存一下用户信息,@DatabaseField注解是ormlite库的特性,文章后面的附件包含了此类库:
public class UserApiModel implements Serializable {

@DatabaseField(generatedId=true)
private int RecordId;

@DatabaseField

public long Id;

@DatabaseField
public String Username;

@DatabaseField
public String Email;

@DatabaseField
public String HeadImg;

@DatabaseField
public String EaseMobUserName;

@DatabaseField
public String EaseMobPassword;
}
 
private static void SaveUserInfo(UserApiModel userInfo){

if(userInfo == null) return;

// 缓存用户信息
PrefUtils.setUserId(userInfo.Id);
PrefUtils.setUserEmail(userInfo.Email);
PrefUtils.setUserName(userInfo.Username);
PrefUtils.setUserPic(userInfo.HeadImg);
PrefUtils.setUserChatId(userInfo.EaseMobUserName);
PrefUtils.setUserChatPwd(userInfo.EaseMobPassword);

UserInfoCacheSvc.createOrUpdate(userInfo);
}
然后在接收环信消息的回调函数里保存用户信息,ActyMain.java是我们项目的主框架,我们在这里写了回调函数,无论群聊还是单聊消息,都会调用这里:
private EMMessageListener mMessageListener = new EMMessageListener() {

@Override
public void onMessageReceived(List<EMMessage> messages) {
// 提示新消息
for (EMMessage message : messages) {

// 先将头像和昵称保存在本地缓存
try {
String chatUserId = message.getStringAttribute(SharePrefConstant.ChatUserId);
String avatarUrl = message.getStringAttribute(SharePrefConstant.ChatUserPic);
String nickName = message.getStringAttribute(SharePrefConstant.ChatUserNick);

UserInfoCacheSvc.createOrUpdate(chatUserId, nickName, avatarUrl);

} catch (HyphenateException e) {
e.printStackTrace();
}

ChatHelper.getInstance().getNotifier().onNewMsg(message);
}
refreshUIWithMessage();
}

// 此处省略N行代码....
};
在安卓项目里,环信页面显示昵称和头像时,都会统一从DemoHelper的getUserInfo函数里获取信息,所以我们要在这里从缓存取用户头像和昵称:
private EaseUser getUserInfo(String username){
//获取user信息,demo是从内存的好友列表里获取,
//实际开发中,可能还需要从服务器获取用户信息,
//从服务器获取的数据,最好缓存起来,避免频繁的网络请求
EaseUser user = null;
// 从缓存里取昵称和头像
UserApiModel userInfo = UserInfoCacheSvc.getByChatUserName(username);
if (userInfo != null){
user = new EaseUser(username);
user.setAvatar(userInfo.getHeadImg());
user.setNick(userInfo.getUsername());
}

return user;
}
最后,为了让另外一个客户端也能正确显示头像和昵称,app发送消息时,要在消息扩展里附带用户信息,代码写在
ChatFragment.java里:
    @Override
public void onSetMessageAttributes(EMMessage message) {
setUserInfoAttribute(message);
}

/**
* 设置用户的属性,
* 通过消息的扩展,传递客服系统用户的属性信息
* @param message
*/
private void setUserInfoAttribute(EMMessage message) {
try {
message.setAttribute(SharePrefConstant.ChatUserId, PrefUtils.getUserChatId());
message.setAttribute(SharePrefConstant.ChatUserNick, PrefUtils.getUserName());
message.setAttribute(SharePrefConstant.ChatUserPic, "http://image.baidu.com" + PrefUtils.getUserPic()) ;//这里用是图片的完整链接地址,如果要取缩略图,需要服务端配合;

} catch (Exception e) {
e.printStackTrace();
}
}
有不当之处,欢迎指正~~~谢谢
部分源码因为是公司项目代码,所以没有在文章里提供(而且代码量太多,也不便于阅读)。需要源码的童鞋,可以联系我。等我有空,我整理一下公司项目,然后再share开源给大家。
相关类文件下载:


 
本文方法已经废弃!请看作者另外一篇文章:
在android中5分钟实现昵称头像的显示  http://www.imgeek.org/article/825308757


如有任何问题,请咨询【环信IM互帮互助群】,群号:340452063,
或者加本人QQ:364223587,加Q请认准以下正宗小马头像:


收起阅读 »

IOS中如何显示开发者服务器上的昵称和头像

无论是IOS还是安卓,集成环信SDK遇到的第一个问题,就是如何显示自有用户体系中的昵称和头像。运行环信的demo app,注册用户是直接使用环信ID(username)作为用户名,但是在我们实际运用中,需要将自有用户体系的UserId生成GUID作为环信ID(...
继续阅读 »
无论是IOS还是安卓,集成环信SDK遇到的第一个问题,就是如何显示自有用户体系中的昵称和头像。运行环信的demo app,注册用户是直接使用环信ID(username)作为用户名,但是在我们实际运用中,需要将自有用户体系的UserId生成GUID作为环信ID(username)【参考:http://docs.easemob.com/im/100serverintegration/20users】,这时候如果不经过处理,则会显示如下界面:

1.png


 
那么如何处理,才能显示正确的用户昵称和头像呢?
其实官方已经提供有解决方案了,只不过没有给出示例代码而已。
http://docs.easemob.com/im/490integrationcases/10nickname
 
引用一下关键文字:


方法二:从消息扩展中获取昵称和头像

昵称和头像的获取:把用户基本的昵称和头像的URL放到消息的扩展中,通过消息传递给接收方,当收到一条消息时,则能通过消息的扩展得到发送者的昵称和头像URL,然后保存到本地数据库和缓存。当显示昵称和头像时,请从本地或者缓存中读取,不要直接从消息中把赋值拿给界面(否则当用户昵称改变后,同一个人会显示不同的昵称)。

昵称和头像的更新:当扩展消息中的昵称和头像 URI 与当前本地数据库和缓存中的相应数据不同的时候,需要把新的昵称保存到本地数据库和缓存,并下载新的头像并保存到本地数据库和缓存。


 
没错,官方提供了两种思路,鉴于项目的实际情况,我选择了【方法二】。
于是,无论安卓,还是IOS,我们都是这样处理的:
【1】.APP间传递用户属性信息:发送(文本、图片...)消息时,要在消息扩展(message.ext)中附带当前用户的属性信息;
【2】.本地缓存用户信息:在接收消息的回调函数里,读取消息扩展(message.ext)里用户属性(键值对)信息;如果本地缓存(sqlite)不存在该用户,则新增缓存记录,如果存在,则更新记录;(用户登录或注册成功后,也要更新用户缓存信息)
【3】.获取用户属性信息:在需要显示昵称的地方,根据环信ID,读取sqlite缓存数据,获取用户昵称和头像;
用户属性信息:ChatUserId(环信ID),ChatUserNick(用户昵称), ChatUserPic(用户头像,完整的url地址);

 
IOS关键代码:
// 环信聊天用的昵称和头像(发送聊天消息时,要附带这3个属性)
#define kChatUserId @"ChatUserId"// 环信账号
#define kChatUserNick @"ChatUserNick"
#define kChatUserPic @"ChatUserPic"

 ChatUserCacheInfo是环信用户信息缓存管理类
ChatUserCacheInfo.h
#import <Foundation/Foundation.h>

@interface ChatUserCacheInfo : NSObject
@property(nonatomic,copy)NSString* Id;
@property(nonatomic,copy)NSString* NickName;
@property(nonatomic,copy)NSString* AvatarUrl;
@end


@interface ChatUserCacheUtil : NSObject

+(void)saveInfo:(NSString *)openId
imgId:(NSString*)imgId
nickName:(NSString*)nickName;

+(void)saveDict:(NSDictionary *)userinfo;

+(void)saveModel:(UserApiModel*)user;

+(ChatUserCacheInfo*)queryById:(NSString *)userid;

@end









ChatUserCacheUtil.m
#import "ChatUserCacheUtil.h"
#import "FMDB.h"

#define DBNAME @"cache_data.db"

@implementation ChatUserCacheInfo

@end

@implementation ChatUserCacheUtil

+(void)createTable:(FMDatabase *)db
{
if ([db open]) {
if (![db tableExists :@"userinfo"]) {
if ([db executeUpdate:@"create table userinfo (userid text, username text, userimage text)"]) {
NSLog(@"create table success");
}else{
NSLog(@"fail to create table");
}
}else {
NSLog(@"table is already exist");
}
}else{
NSLog(@"fail to open");
}
}

+ (void)clearTableData:(FMDatabase *)db
{
if ([db executeUpdate:@"DELETE FROM userinfo"]) {
NSLog(@"clear successed");
}else{
NSLog(@"fail to clear");
}
}

+(FMDatabase*)getDB{
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *dbPath = [docsPath stringByAppendingPathComponent:DBNAME];
FMDatabase *db = [FMDatabase databaseWithPath:dbPath];
[self createTable:db];
return db;
}

+(void)saveModel:(UserApiModel*)user{
[ChatUserCacheUtil saveInfo:user.EaseMobUserName imgId:user.HeadImg nickName:user.Username];
}

+(void)saveInfo:(NSString *)openId
imgId:(NSString*)imgId
nickName:(NSString*)nickName{
NSMutableDictionary *extDic = [NSMutableDictionary dictionary];
[extDic setValue:openId forKey:kChatUserId];
[extDic setValue:@"[url=http://img.baidu.com"]http://img.baidu.com/"[/url]+imgId forKey:kChatUserPic];//完整图片路径"http://img.baidu.com/1234"。如果imgId是相对路径,那完整路径就是类似"http://img.baidu.com/abc.jpg"
[extDic setValue:nickName forKey:kChatUserNick];
[ChatUserCacheUtil saveDict:extDic];
}

+(void)saveDict:(NSDictionary *)userinfo{
FMDatabase *db = [self getDB];

NSString *userid = [userinfo objectForKey:kChatUserId];
if ([db executeUpdate:@"DELETE FROM userinfo where userid = ?", userid]) {
DLog(@"删除成功");
}else{
DLog(@"删除失败");
}
NSString *username = [userinfo objectForKey:kChatUserNick];
NSString *userimage = [userinfo objectForKey:kChatUserPic];
if ([db executeUpdate:@"INSERT INTO userinfo (userid, username, userimage) VALUES (?, ?, ?)", userid,username,userimage]) {
DLog(@"插入成功");
}else{
DLog(@"插入失败");
}

// NSLog(@"%d: %@", [db lastErrorCode], [db lastErrorMessage]);
FMResultSet *rs = [db executeQuery:@"SELECT userid, username, userimage FROM userinfo where userid = ?",userid];
if ([rs next]) {
NSString *userid = [rs stringForColumn:@"userid"];
NSString *username = [rs stringForColumn:@"username"];
NSString *userimage = [rs stringForColumn:@"userimage"];
DLog(@"查询一个 %@ %@ %@",userid,username,userimage);
}

rs = [db executeQuery:@"SELECT userid, username, userimage FROM userinfo"];
while ([rs next]) {
NSString *userid = [rs stringForColumn:@"userid"];
NSString *username = [rs stringForColumn:@"username"];
NSString *userimage = [rs stringForColumn:@"userimage"];
DLog(@"查询所有 %@ %@ %@",userid,username,userimage);
}
[rs close];
// NSLog(@"%d: %@", [db lastErrorCode], [db lastErrorMessage]);
[db close];

}

+(ChatUserCacheInfo*)queryById:(NSString *)userid{
FMDatabase *db = [self getDB];
if ([db open]) {
FMResultSet *rs = [db executeQuery:@"SELECT userid, username, userimage FROM userinfo where userid = ?",userid];
if ([rs next]) {

ChatUserCacheInfo *userInfo = [[ChatUserCacheInfo alloc] init];

userInfo.Id = [rs stringForColumn:@"userid"];
userInfo.NickName = [rs stringForColumn:@"username"];
userInfo.AvatarUrl = [rs stringForColumn:@"userimage"];
DLog(@"查询一个 %@",userInfo);
return userInfo;
}else{
return nil;
}
}else{
return nil;
}
}


@end
首先要在用户登录或注册成功后,返回用户登录信息时,缓存一下用户信息:
@protocol UserApiModel <NSObject>

@end

@interface UserApiModel : BaseJSONModel
@property(nonatomic, assign)int Id;
@property(nonatomic, copy)NSString *Username;
@property(nonatomic, copy)NSString *Email;
@property(nonatomic, copy)NSString *HeadImg;
@property(nonatomic, copy)NSString *EaseMobUserName;
@property(nonatomic, copy)NSString *EaseMobPassword;
@end

// 登录成功后返回用户model,需要为环信聊天窗口缓存用户信息
[ChatUserCacheUtil saveModel:user];

然后在接收环信消息的回调函数里保存用户信息,HsMainViewController.m是我们项目的主框架,我们在这里写了回调函数,无论群聊还是单聊消息,都会调用这里:
// 收到消息回调
-(void) didReceiveMessage:(EMMessage *)message
{
[ChatUserCacheUtil saveDict:message.ext];

BOOL needShowNotification = (message.messageType != eMessageTypeChat) ? [self needShowNotification:message.conversationChatter] : YES;
if (needShowNotification) {
#if !TARGET_IPHONE_SIMULATOR

BOOL isAppActivity = [[UIApplication sharedApplication] applicationState] == UIApplicationStateActive;
if (!isAppActivity) {
[self showNotificationWithMessage:message];
}else {
[self playSoundAndVibration];
}
#endif
}
}
环信页面主要是在ChatViewController和ConversationListViewController里显示用户对话,所以我们要在这两个页面里从缓存取用户头像和昵称,先从ChatViewController开始:
- (id<IMessageModel>)messageViewController:(EaseMessageViewController *)viewController
modelForMessage:(EMMessage *)message
{
id<IMessageModel> model = [[EaseMessageModel alloc] initWithMessage:message];

ChatUserCacheInfo *userinfo = [ChatUserCacheUtil queryById:model.nickname];
if (userinfo != nil) {
model.nickname = userinfo.NickName;
model.avatarURLPath = userinfo.AvatarUrl;
}

model.avatarImage = [UIImage imageNamed:@"EaseUIResource.bundle/user"];
model.failImageName = @"imageDownloadFail";

return model;
}
然后是ConversationListViewController:
#pragma mark - EaseConversationListViewControllerDataSource

- (id<IConversationModel>)conversationListViewController:(EaseConversationListViewController *)conversationListViewController
modelForConversation:(EMConversation *)conversation
{
EaseConversationModel *model = [[EaseConversationModel alloc] initWithConversation:conversation];
if (model.conversation.conversationType == eConversationTypeChat) {
ChatUserCacheInfo *userinfo = [ChatUserCacheUtil queryById:model.conversation.chatter];
if (userinfo != nil) {
model.title = userinfo.NickName;
model.avatarURLPath = userinfo.AvatarUrl;
}
model.avatarImage = PlaceholderImgChatUser;
} else if (model.conversation.conversationType == eConversationTypeGroupChat) {
// 此处省略100行代码........
}
}
最后,为了让另外一个客户端也能正确显示头像和昵称,app发送消息时,要在消息扩展里附带用户信息,代码写在EaseSDKHelper.m里:
// 重新消息扩展组织
+(NSMutableDictionary*)reGetMessageExt:(NSDictionary *)messageExt{
NSMutableDictionary *extDic = [NSMutableDictionary dictionaryWithDictionary:messageExt];
[extDic setValue:[SettingData share].UserChatId forKey:kChatUserId];
[extDic setValue:[SettingData share].UserHeadImg.ServerThumbUrlStr forKey:kChatUserPic];
[extDic setValue:[SettingData share].UserName forKey:kChatUserNick];
return extDic;
}

+ (EMMessage *)sendTextMessage:(NSString *)text
to:(NSString *)toUser
messageType:(EMMessageType)messageType
requireEncryption:(BOOL)requireEncryption
messageExt:(NSDictionary *)messageExt

{
// 表情映射。
NSString *willSendText = [EaseConvertToCommonEmoticonsHelper convertToCommonEmoticons:text];
EMChatText *textChat = [[EMChatText alloc] initWithText:willSendText];
EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithChatObject:textChat];
EMMessage *message = [[EMMessage alloc] initWithReceiver:toUser bodies:[NSArray arrayWithObject:body]];
message.requireEncryption = requireEncryption;
message.messageType = messageType;

message.ext = [self reGetMessageExt:messageExt];
EMMessage *retMessage = [[EaseMob sharedInstance].chatManager asyncSendMessage:message
progress:nil];

return retMessage;
}


+ (EMMessage *)sendImageMessageWithImage:(UIImage *)image
to:(NSString *)to
messageType:(EMMessageType)messageType
requireEncryption:(BOOL)requireEncryption
messageExt:(NSDictionary *)messageExt
progress:(id<IEMChatProgressDelegate>)progress
{
return [self sendImageMessageWithImage:image to:to messageType:messageType requireEncryption:requireEncryption messageExt:messageExt quality:0.6 progress:progress];
}

+ (EMMessage *)sendImageMessageWithImage:(UIImage *)image
to:(NSString *)to
messageType:(EMMessageType)messageType
requireEncryption:(BOOL)requireEncryption
messageExt:(NSDictionary *)messageExt
quality:(float)quality
progress:(id<IEMChatProgressDelegate>)progress
{
// 此处省略9行代码....

message.ext = [self reGetMessageExt:messageExt];
EMMessage *retMessage = [[EaseMob sharedInstance].chatManager asyncSendMessage:message
progress:progress];

return retMessage;
}

+ (EMMessage *)sendVoiceMessageWithLocalPath:(NSString *)localPath
duration:(NSInteger)duration
to:(NSString *)to
messageType:(EMMessageType)messageType
requireEncryption:(BOOL)requireEncryption
messageExt:(NSDictionary *)messageExt
progress:(id<IEMChatProgressDelegate>)progress
{
// 此处省略4行代码....

message.ext = [self reGetMessageExt:messageExt];
EMMessage *retMessage = [[EaseMob sharedInstance].chatManager asyncSendMessage:message
progress:progress];

return retMessage;
}

+ (EMMessage *)sendVideoMessageWithURL:(NSURL *)url
to:(NSString *)to
messageType:(EMMessageType)messageType
requireEncryption:(BOOL)requireEncryption
messageExt:(NSDictionary *)messageExt
progress:(id<IEMChatProgressDelegate>)progress
{
// 此处省略4行代码....

message.ext = [self reGetMessageExt:messageExt];
EMMessage *retMessage = [[EaseMob sharedInstance].chatManager asyncSendMessage:message
progress:progress];

return retMessage;
}

+ (EMMessage *)sendFileMessage:(EMChatFile *)chatFile
to:(NSString *)to
messageType:(EMMessageType)messageType
requireEncryption:(BOOL)requireEncryption
messageExt:(NSDictionary *)messageExt
progress:(id<IEMChatProgressDelegate>)progress
{
// 此处省略4行代码....

message.ext = [self reGetMessageExt:messageExt];
EMMessage *retMessage = [[EaseMob sharedInstance].chatManager asyncSendMessage:message
progress:progress];

return retMessage;
}

有不当之处,欢迎指正~~谢谢。QQ:364223587
 
以上代码为SDK V2版本,如果集成的是V3版本,请移步源码:
http://git.oschina.net/markies/ChatDemo-UI3.00-Simple
思路其实跟V2差不多,最大区别V3的回调方法didReceiveMessages比V2多了个【s】。

ChatUserCacheUtil我已经重命名为:UserCacheManager
 

如有任何问题,请咨询【环信IM互帮互助群】,群号:340452063
  收起阅读 »

环信编程大赛优秀开源项目系列之二:“图忆”一款基于地理位置信息的社交APP

根据IDC数据显示,中国有近200万开发者,身为一个程序员,我们生活在一个 IT 系统越发复杂且多变化的时代。有时候执行一个简单的开源项目,开发一个基础功能都需要精准定义并耗费大量时间专注任务。随着云计算的兴起,API 和SDK开始作为软件之间重要媒介而作为一...
继续阅读 »
根据IDC数据显示,中国有近200万开发者,身为一个程序员,我们生活在一个 IT 系统越发复杂且多变化的时代。有时候执行一个简单的开源项目,开发一个基础功能都需要精准定义并耗费大量时间专注任务。随着云计算的兴起,API 和SDK开始作为软件之间重要媒介而作为一种独立应用而存在,“一切皆软件,一切皆API,一切皆SDK”。通过API和SDK可以让开发者摆脱繁重的基础功能底层开发,短时间即可让App拥有各种诸如内置IM、统计等基础功能组件能力。 

5月14日,由环信联合猿圈共同推出的“首届环信编程大赛”颁奖典礼在中关村义创空间隆重举行。本次环信编程大赛历时两个月,由线上初赛、决赛和颁奖典礼三个环节组成,总计报名人数2000+,收到决赛项目100+。最终由评委会认定的13个优秀开源项目及开发者集体亮相颁奖典礼。其中“方圆十里”、“高仿微信“和“咚咚”三个开源项目名列前三,共同分享了15000元奖金和价值12000元的专属表情包。



9d743f79a696245c9c93ac614b13fe79[1].jpg


优秀项目开发者合影




73ERYP`OJ`QEY4PXGPEJ(AG.png


“图忆”项目负责人梁桂栋分享技术开发细节


 
其余入围的十余个优秀开源项目同样引起了到场开发者的热烈追捧,环信将分期将入围的优秀项目代码免费开源给小伙伴们。今天我们带来的是一款基于地理位置信息的社交分享应用——“图忆”。图忆是一款基于地理位置信息的社交分享应用。实现了将用户记录的不同类型的事件标刻于地图之上,查看自己的记录足迹,同时用户可以轻松查看附近分享的记事,添加好友聊天,建立兴趣圈子,发现志趣相投的好友,并且用户记事可以分享到公共社区平台,分享乐趣的同时也发现了更多的乐趣,社区推荐策略让用户发现更多有价值的乐趣。



QQ截图20160523170643.jpg


 “图忆”APP界面截图


 
1.软件介绍


图忆是一款基于地理位置信息的社交分享应用。实现了将用户记录的不同类型的事件标刻于地图之上,查看自己的记录足迹,同时用户可以轻松查看附近分享的记事,添加好友聊天,建立兴趣圈子,发现志趣相投的好友,并且用户记事可以分享到公共社区平台,分享乐趣的同时也发现了更多的乐趣,社区推荐策略让用户发现更多有价值的乐趣。




2.功能介绍


【记录记忆】你可以记录自己的生活点滴在地图之上,可以公开给别人看,也可以保存为自己的私有记忆。
【离线记录】没有网络也可以轻松保存离线记录,WIFI连接后直接批量上传,省心
【地图附近】你将通过地图查看到附近用户公开的说有分享记录,当然是直接在地图上展示的哟,很直观的说,还有五个标签分类查询哟,就等你来发现了。
【雷达】发现同时在附近开启雷达的小伙伴,自定义雷达显示的内容,让小伙伴更容易发现你
【聊天圈子】与TA尽情畅聊,兴趣小伙伴建圈子一起聊。
【图忆社区】点赞,评论,分享,收藏Ta的分享



3.使用技术


环信IM
百度地图API
有盟API



4.作者心得


IM正越来越得到开发者重视,也逐渐成为APP标配,绝大部分App中都集成了即时通讯功能。将APP的核心功能紧密与即时通讯良好结合,将更有利于APP的用户体验和留存。
APP的多元发展中需要使用多功能的有机结合。而作为一个完整的SDK需要越少的干涉APP原本的逻辑,而不降低功能与体验,这些方面环信的IM SDK都做的挺好。 


 



7ec7f4aad12e067bb7bd46f03a22c657[1].jpg



  特别感谢以下企业的大力支持:


义创空间提供颁奖场地
 
萌岛从自有形象库中授权一套价值12000元的表情包
 
Emokit赞助Apple Watch一台
 
猿圈全程提供技术评测支持


 
git源码下载https://github.com/donlan/Tuyi
 
更多开源项目请点击http://community.easemob.com/article/825307813

图忆项目作者演讲PPT下载↓↓↓
  收起阅读 »

开源了一个简单实用的http服务压力测试工具Alex,自带web ui,使用golang实现

Alex ================= Alex是基于vegeta library和boom封装的压力测试web UI。Vegeta提供稳定的qps压力源,boom提供稳定的并发数压力源。 github地址 https://github.com/ir...
继续阅读 »
Alex

=================

Alex是基于vegeta library和boom封装的压力测试web UI。Vegeta提供稳定的qps压力源,boom提供稳定的并发数压力源。
github地址 https://github.com/ireaderlab/alex

English

Alex架构图

Alex ArchitectureAlex 主要功能

1. 保存压力测试参数以便反复压测

2. 保存压力测试报告以便后续查看和分享

3. 提供了简单直接的图形和文字报告

4. 可以同时对多个http接口进行压力测试

5. 可以同时对集群内多个host:port对进行压测

6. 使用多组调用参数避免压测时出现的数据热点问题

7. 使用步骤设置,生成渐进式的压力源

8. 提供简单的压测机器系统状态实时显示功能

Alex Limitations

1. Alex运行在单一进程里,如果你需要分布式的压测环境,就得部署多个节点,压测时需要多人同时操作。

2. Vegeta在压力过载时没有提供立即停止的方法。这就需要你细心设计压测步骤,仔细观察系统状态避免系统过载。

3. Qps和并发数不宜过大。我曾经使用Alex工具单进程测试了HelloWorld的web程序每个请求吐出1500字节,qps最多可以达到60000,基本让千兆网卡打满。

4. 在大型压力测试下,尽量避免Gzip解压缩。解压缩会消耗大量的cpu资源,会导致压测报告不准确。你可以通过部署多个节点来进行大型压力测试。

5. 只支持Http协议。Https协议不打算支持,因为加密解密也同样会消耗大量cpu资源,导致报告不准确。

6. 报告只是提供一种性能参考,要勇于对报告进行质疑。

7. Alex虽然有如此诸多限制,这不影响它的日常使用。

安装


install mongodb 
install golang # 1.4+ is required 
go get github.com/go-martini/martini 
go get github.com/tsenart/vegeta 
go get gopkg.in/mgo.v2 
go get github.com/shirou/gopsutil # godep restore 
git clone https://github.com/shellquery/alex.git 
cd alex 
go build 
./alex 
./alex -c config.json
open browser http://localhost:8000/ 
 


 
配置


config.json{ 
"BindAddr": "localhost:8000",
"MongoUrl": "mongodb://localhost:27017/alex",
"Teams": [ "python", "java", "php", "go" ]
 }


 
引用
棒棒的vegeta https://github.com/tsenart/vegeta

简单直接的boom https://github.com/rakyll/boom

截屏

Randomize Host:ports

Randomize Parameters

Step Settings

Benchmark Reports 收起阅读 »

没想到你们是这种环信--happy520

将这张图转发到你的朋友圈、微博并 @环信 将获得由环信送出的神秘大奖一份!  


30期_社区活动.jpg



将这张图转发到你的朋友圈、微博并 @环信 将获得由环信送出的神秘大奖一份!
 

【公告】IOS SDK 2.2.5版本已支持IPV6-only

致亲爱的环信小伙伴们   因为6.1日起苹果要求IPV6-only,不支持ipv6的app将无法通过appstore审核。我们于昨天(5.18)发布了支持ipv6版本的IOS SDK 2.2.5版本   建议进行升级,以免因为这个问题导致app新版本通不过苹果...
继续阅读 »
致亲爱的环信小伙伴们
 
因为6.1日起苹果要求IPV6-only,不支持ipv6的app将无法通过appstore审核。我们于昨天(5.18)发布了支持ipv6版本的IOS SDK 2.2.5版本
 
建议进行升级,以免因为这个问题导致app新版本通不过苹果审核。
  
新版本介绍http://community.easemob.com/article/825307836
 
新版SDK下载http://www.easemob.com/download/im
 
13个基于环信集成的开源项目集体登场http://community.easemob.com/article/825307813 收起阅读 »

优秀程序员的十个习惯

在这个世界上,有数百万的人热衷于软件开发,他们有很多名字,如:软件工程师(Software Engineer),程序员(Programmer),编码人(Coder),开发人员(Developer)。经过一段时间后,这些人也许能够成为一个优秀的编码人员,他们会非...
继续阅读 »
在这个世界上,有数百万的人热衷于软件开发,他们有很多名字,如:软件工程师(Software Engineer),程序员(Programmer),编码人(Coder),开发人员(Developer)。经过一段时间后,这些人也许能够成为一个优秀的编码人员,他们会非常熟悉如何用计算机语言来完成自己的工作。但是,如果你要成为一个优秀的程序员,你还可以需要有几件事你需要注意,如果你能让下面十个条目成为你的习惯,那么你才能真正算得上是优秀程序员。



2005483-1835f6de0ef0b7ba.png


学无止境


  1. 学无止境。就算是你有了10年以上的程序员经历,你也得要使劲地学习,因为你在计算机这个充满一创造力的领域,每天都会有很多很多的新事物出现。你需要跟上时代的步伐。你需要去了解新的程序语言,以及了解正在发展中的程序语言,以及一些编程框架。还需要去阅读一些业内的新闻,并到一些热门的社区去参与在线的讨论,这样你才能明白和了解整个软件开发的趋势。在国内,一些著名的社区例如:CSDN,ITPUB,CHINAUINX等等,在国外,建议你经常上一上digg.com去看看各种BLOG的聚合。



2005483-d942b123971dfa5e.png


掌握多种语言


  1. 掌握多种语言。程序语言总是有其最适合的领域。当你面对需要解决的问题时,你需要找到一个最适合的语言来解决这些问题。比如,如果你需要性能,可能C/C++是首选,如果你需要跨平台,可能Java是首选,如果你要写一个Web上的开发程序,那么PHP,ASP,Ajax,JSP可能会是你的选择,如果你要处理一些文本并和别的应用交互,可能Perl, Python会是最好的。所以,花一些时间去探索一下其它你并熟悉的程序语言,能让你的眼界变宽,因为你被武装得更好,你思考问题也就更为全面,这对于自己和项目都会有好的帮助。



2005483-3c0cf4d04b268055.png


理性面对不同的操作系统或技术


  1. 理性面对不同的操作系统或技术。程序员们总是有自己心目中无可比拟的技术和操作系统,有的人喜欢Ubuntu,有的人喜欢Debian,还有的人喜欢Windows,以及FreeBSD,MacOSX或Solaris等等。只有一部分优秀的程序员明白不同操作系统的优势和长处和短处,这样,在系统选型的时候,才能做到真正的客观和公正,而不会让情绪影响到自己。同样,语言也是一样,有太多的程序员总是喜欢纠缠于语言的对比,如:Java和Perl。哪个刚刚出道的程序员没有争论去类似的话题呢?比如VC++和Delphi等等。争论这些东西只能表明自己的肤浅和浮燥。优秀的程序并不会执着于这些,而是能够理性的分析和理心地面对,从而才能客观地做出正确的选择。
  2. 别把自己框在单一的开发环境中。 再一次,正如上面所述,每个程序员都有自己忠爱的工具和技术,有的喜欢老的(比如我就喜欢Vi编辑程序),而有的喜欢新的比如gedit或是Emacs等。有的喜欢使用像VC++一样的图形界面的调试器,而我更喜欢GDB命令行方面的调式器。等等等等。程序员在使用什么样的工具上的争论还少吗?到处都是啊。使用什么样的工具本来无所谓,只要你能更好更快地达到你的目的。但是有一点是优秀程序员都应该了解的——那就是应该去尝试一下别的工作环境。没有比较,你永远不知道谁好谁不好,你也永远不知道你所不知道的。

 



2005483-1ad1a0fb435fb600.png


使用版本管理工具管理你的代码。


  1. 使用版本管理工具管理你的代码。千万不要告诉我你不知道源码的版本管理,如果你的团队开发的源代码并没有版本管理系统,那么我要告诉你,你的软件开发还处于石器时代。赶快使用一个版式本管理工具吧。CVS 是一个看上去平淡无奇的版本工具,但它是被使用最广的版本管理系统,Subversion 是CVS的一个升级版,其正在开始接管CVS的领地。Git 又是一个不同的版本管理工具。还有Visual SourceSafe等。使用什么样的版本管理工具依赖于你的团队的大小和地理分布,你也许正在使用最有效率或最没有效率的工具来管理你的源代码。但一个优秀的程序员总是会使用一款源码版本管理工具来管理自己的代码。如果你要我推荐一个,我推荐你使用开源的Subversion。

 



2005483-43672577a1a07a0d.png


是一个优秀的团队成员


  1. 是一个优秀的团队成员。 除非你喜欢独奏,除非你是孤胆英雄。但我想告诉你,今天,可能没有一个成熟的软件是你一个人能做的到的,你可能是你团队中最牛的大拿,但这并不意味着你就是好的团队成员。你的能力只有放到一个团队中才能施展开来。你在和你的团队成员交流中有礼貌吗?你是否经常和他们沟通,并且大家都喜欢和你在一起讨论问题?想一想一个足球队吧,你是这个队中好的成员吗?当别人看到你在场上的跑动时,当别人看到你的传球和接球和抢断时,你的团员成员能因为你的动作受到鼓舞吗?
  2. 把你的工作变成文档。 这一条目当然包括了在代码中写注释,但那还仅仅不够,你还需要做得更多。有良好的注释风格的代码是一个文档的基础,他能够让你和你的团队容易的明白你的意图和想法。写下文档,并不仅仅是怕我们忘了当时的想法,而且还是一种团队的离线交流的方法,更是一种知识传递的方法。记录下你所知道的一切会是一个好的习惯。因为,我相信你不希望别人总是在你最忙的时候来打断你问问题,或是你在休假的时候接到公司的电话来询问你问题。而你自己如果老是守着自己的东西,其结果只可能是让你自己长时间地深陷在这块东西内,而你就更本不可以去做更多的事情。包括向上的晋升。你可能以为“教会徒弟能饿死师父”,但我告诉你,你的保守会让你失去更多更好的东西,请你相信我,我绝不是在这里耸人听闻。
  3. 注意备份和安全。 可能你觉得这是一个“废话”,你已明白了备份的重要性。但是,我还是要在这里提出,丢失东西是我们人生中的一部份,你总是会丢东西,这点你永远无法避免。比如:你的笔记本电脑被人偷了,你的硬盘损坏了,你的电脑中病毒了,你的系统被人入侵了,甚至整个大楼被烧了,等等,等等。所以,做好备份工作是非常非常重要的事情,硬盘是不可信的,所以定期的刻录光盘或是磁带可能会是一个好的方法,网络也是不可信的,所以小心病毒和黑客,不但使用软件方面的安全策略,你更需要一个健全的管理制度。此外,尽量的让你的数据放在不同的地方,并做好定期(每日,每周,每月)的备份策略。
  4. 设计要足够灵活。 可能你的需求只会要求你实现一个死的东西,但是,你作为一个优秀的程序,你应该随时在思考这个死的东西是否可以有灵活的一面,比如把一些参数变成可以配置的,把一些公用的东西形成你的函数库以便以后重用,是否提供插件方面的功能?你的模块是否要以像积木一样随意组合?如果要有修改的话,你的设计是否能够马上应付?当然,灵活的设计可能并不是要你去重新发明轮子,你应该尽可能是使用标准化的东西。所谓灵话的设计就是要让让考虑更多需求之外的东西,把需求中这一类的问题都考虑到,而不是只处理需求中所说的那一特定的东西。比如说,需要需要的屏幕分辨率是800×600,那么你的设计能否灵活于其他的分辨率?程序设计总是需要我们去处理不同的环境,以及未来的趋势。我们需要用动态的眼光去思考问题,而不是刻舟求剑。也许有一天,你今天写的程序就要移植到别的环境中去,那个时候你就能真正明白什么是灵活的设计了。
  5. 不要搬起石头砸自己的脚。程序员总是有一种不好的习惯,那就是总是想赶快地完成自己手上的工作。但情况却往往事已愿违。越是想做得快,就越是容易出问题,越是想做得快,就越是容易遗漏问题,最终,程序改过来改过去,按下葫芦起了瓢,最后花费的时间和精力反而更多。欲速而不达。优秀程序员的习惯是前面多花一些时间多作一些调查,试验一下不同的解决方案,如果时间允许,一个好的习惯是,每4个小时的编程,需要一个小时的休息,然后又是4个小时的编码。当然,这因人而异,但其目的就是让你时常回头看看,让你想一想这样三个问题:1)是否这么做是对的?2)是否这么做考虑到了所有的情况?3)是否有更好的方法?想好了再说,时常回头看看走过的路,时常总结一下过去事,会对你有很大的帮助。

 以上是十条优秀程序员的习惯或行为规范,希望其可以对你有所帮助。

本文来源于网上phil的BLOG,但我在写作过程中使用了自己的语言和方法重新描述了一下这十条,所以,我希望你在转载的时候能够注明作者和出处以表示对我的尊重。谢谢! 收起阅读 »

检测一下大家的OC基础,最新ios面试题,你能回答上哪些?

哈哈,没有地址,此时我的内心的meng 比的!  1.说说内存管理 2、ASIRequest是什么; 3、怎么输出json字符串; 4、说说http头部有哪些内容; 5、说说OC生命周期; 6、运用第三方框架,到时候出了问题,谁来负责 7、自己写...
继续阅读 »



2100853-64f1f043f15325bc.jpg


哈哈,没有地址,此时我的内心的meng 比的!


 1.说说内存管理

2、ASIRequest是什么;

3、怎么输出json字符串;

4、说说http头部有哪些内容;

5、说说OC生命周期;

6、运用第三方框架,到时候出了问题,谁来负责

7、自己写一个strcpy函数

8、字母统计(如,输入字符串“aabbbccddddaaaaa”,输出“2a3b2c4d5a”)

9、你用过哪些框架

10、进程与线程的区别

11、开辟线程的方式有哪些

12、实现进程同步的方式有哪些,或者说你怎么实现进程同步

13、请你谈谈同步和异步,用操作系统知识解释一下。

14、请你谈谈多态

15、怎么将数据写入文件(归档,解当)

16、写一个set方法(retain和copy权限)

17


Int* fun()

{

Int a=5;

Int * p=&a;

Return p;

}

请问:在主函数里面调用fun函数,这样可以吗?如果不可以,请说明为什么,并给出一种解决方案。

18、在颜色中,有GB8888和 GB565标准,前者32位,其中R占8位,G占8位,B占8位,透明度占8位,后者16位,其中,R占5位,G占6位,B占5位。现在要将一个GB8888类型颜色转换成GB565类型,怎么转

19、判断一个数是否为素数

20、优化代码

1、int a=b*4;

2、int a=b/8;

3、int a=b%1;

4、int a=b;

5、int a=(b*3)/8;

21、什么是内联函数?

22、assign,retain,copy的区别

23、面向对象的特性

24、实现一个view从顶部移到底部的动画

25、#ff3344转换成uicolor

26、判断一个链表是否有循环

27、写一个代理类

28、进程之间是怎么通信的

29、oc有哪些优点和缺点

30、什么时候用delegate,什么时候用Notification?

31、写一个"标准"宏MIN ,这个宏输入两个参数并返回较小的一个。当你写下面的代码时会发生什么事?

least = MIN(*p++, b);

32、MVC模式的理解

33、堆和栈的区别

34、自动释放池是什么,如何工作

35、写一个委托的interface

36、objective-c的内存管理

37、什么是Notification?

38、下面的声明都是什么意思?

constint a;

intconst a;

constint *a;

int* const a;

intconst * a const;

  收起阅读 »

IT垂直领域的今日头条

“今天要开会,看今日头条条条~~~~!”,伴随着魔性的鬼畜广告,个性化聚合类新闻这块巨大的蛋糕, 谁都想抢一步先咬一口。    纵观目前互联网垂直个性化聚合类新闻平台鱼龙混杂,真正能做到满足用户需求,戳中用户痛点的平台有几个!    如今互联网垂直领域...
继续阅读 »
“今天要开会,看今日头条条条~~~~!”,伴随着魔性的鬼畜广告,个性化聚合类新闻这块巨大的蛋糕, 谁都想抢一步先咬一口。
 
 纵观目前互联网垂直个性化聚合类新闻平台鱼龙混杂,真正能做到满足用户需求,戳中用户痛点的平台有几个! 
 
如今互联网垂直领域的新闻聚合类平台和今日头条的差别在与范围的大小以及定位的不同,但核心意义仍不改变,就是根据推荐算法将更优质的更符合用户阅读习惯的内容呈现。 
 
据最近了解来看 , 在此处能称为IT领域的今日头条, 当属浙江网新恒天软件公司的摘客。 
 
虽然网新恒天是做外包服务为主的公司, 但是看到摘客这个产品的时候, 我便对他刮目相看。 
 
1.首先我们来说下他的页面交互 


软文用图.png


 
 身为一个外包公司来说,互联网产品的思想应该属于较弱的情况,但是他却采用较为先进的设计风格,将icon,色彩,排版都做了仔细斟酌。 
 
2.其次我们来说下他爬来的内容 
 
将内容分类,同时也可根据自己的喜好订阅相应类别。基于个人阅读的情况个性化推荐。 
 
再也不用因为找不到想看的内容而急躁! 
 
叔本华说,“每个人都将自身所感知的范围当做世界的范围”,我们能够在互联网上看到什么样的世界,就意味着自己会生活在哪类世界里,信息无价,用更好的工具来看世界,机器的来临,已然改变了信息的基本构成,你需要什么,平台就得往你需要的地方去。
 
垂直个性化聚合类平台热已来临,希望摘客能够继续坚挺。 收起阅读 »

IOS V2.2.5 AndroidV2.2.9 release ,支持ipv6,增加群红包、支持拼手气红包和普通群红包

Android V2.2.9 2016-5-18更新日志 这个版本主要对红包功能做了更新,用Eclipse导入项目的时候需要把demoui3.0根目录下的redpacketlibrary也导入到Eclipse中。1、增加群红包,可以发拼手气红包、普通群红包;...
继续阅读 »

24958PICwjQ_1024.jpg



Android V2.2.9 2016-5-18更新日志


这个版本主要对红包功能做了更新,用Eclipse导入项目的时候需要把demoui3.0根目录下的redpacketlibrary也导入到Eclipse中。1、增加群红包,可以发拼手气红包、普通群红包;

2、优化支付流程,支付更便捷;

3、优化绑卡流程,绑卡更安全;

4、HTML5的页面基于React重构,主要流程通过原生SDK实现,速度更快、交互体验更流畅;

5、增加了红包记录,可以查看收发的红包记录;

6、提供了红包产品的数据统计,App可登陆红包的管理后台查看;

7、增加了太平洋保险的账户安全险,因账户被盗导致的资金损失可以获得赔偿。


 
IOS V2.2.5 2016-05-18 更新日志 


新功能:SDK支持ipv6

红包新版本:

增加群红包,支持拼手气红包和普通群红包;

优化支付流程,支付更便捷;

优化绑卡流程,绑卡更安全;

HTML5的页面基于React重构,主要流程通过原生SDK实现,提升速度和交互体验;

增加了红包历史记录;

提供了红包产品的数据统计,App可登陆红包的管理后台查看;

增加了太平洋保险的账户安全险。

bug fix:

SDK bug:正常网络下登录、退出偶尔超时问题


 
版本历史:Android SDK更新历史  IOS SDK更新历史

下载地址:SDK下载

 
关于新版sdk使用有任何问题或建议请在下方评论留言,我们将直接现金打赏。 收起阅读 »

【创业星生代】36 氪联合环信助力最优秀的创业公司

我们知道,创业从来不是件容易或轻松的事。也许你已经拥有足够好的行业布局,却缺乏更发散的商业想象力;也许你的项目已经有绝对牛逼的团队,却没有量身定做的推广渠道及品牌曝光;也许你已打磨好一个足够打动人的故事以及一份无可挑剔的 BP,万事俱备只欠融资;当然你更拥有不...
继续阅读 »
我们知道,创业从来不是件容易或轻松的事。也许你已经拥有足够好的行业布局,却缺乏更发散的商业想象力;也许你的项目已经有绝对牛逼的团队,却没有量身定做的推广渠道及品牌曝光;也许你已打磨好一个足够打动人的故事以及一份无可挑剔的 BP,万事俱备只欠融资;当然你更拥有不少投资人人脉资源,却只差最合适、最懂你的那一人。      
如果你也有这样的“也许”,那么 36 氪倾心打造的 “创业星生代” 舞台正是你的期许。在这里,创投助手 App、媒体、社群氪空间、融资平台、企业服务被集中整合,五大机构联体,九位大咖携手,联合打造一站式创业生态闭环,解决创业者面临的最大痛点。

“创业星生代” 导师将为你的项目打分并择优约谈,通过每周、每月项目打榜的方式,产生周十强以及月十强,充分挖掘项目商业潜力,助力最优秀的团队。我们事无巨细,竭尽全力,全方位为最优秀的你提供优质、匹配的服务。


活动时间:5月16日 全面开启


 参与流程:     


 
 step1、报名,提交项目信息     

 step2、项目审核,通过后登陆「创投助手APP」    

 step3、投资人发起约谈     

 step4、依据数据产生周 10 强     

 step5、顶级投资机构进行评选,产生月10 强  



活动奖励:​


1、融资礼包:

 项目入驻 36 氪「创投助手APP」

 专享 36 氪轻 FA 服务

“创业星生代” 全国创业大赛线下直接参赛

 氪空间诊疗专家 1 对 1 项目深度辅导
 
2、品牌曝光:

36 氪媒体独家优先报道

北京地铁 LCD 大屏广告

企业服务豪华礼包(价值 10 万元)

36 氪专栏报道套餐

3、创业者社群:

直入氪空间孵化器最终面试

直入氪空间俱乐部

直接参与 36Kr Demo Day 全国线下路演


 合作机构及大咖投资人 :    


IDG 创始合伙人:熊晓鸽    

IDG 合伙人:李骁军    

北极光创投董事总经理:姜皓天    

峰瑞资本创始合伙人:李丰    

峰瑞资本创始合伙人:林中华    

高榕创始合伙人:高翔    

高榕创始合伙人:张震    

华创资本管理合伙人:吴海燕    

华创资本合伙人:熊伟铭




}KQH@TQMH(}X}6CPYET`DF.png



合作伙伴:



X85{_E{)A90K75E7VMR@R{C.png



合作伙伴环信简介:
环信成立于2013年4月,是一家全通讯能力云服务提供商。产品包括国内上线最早的即时通讯云平台——环信即时通讯云,以及移动端最佳实践的全媒体智能云客服平台——环信移动客服。公司产品现已覆盖电商、O2O、互联网金融、在线教育、在线旅游、移动医疗、智能硬件、游戏等20大领域的Top10客户,典型用户包括国美在线、58到家、快牙、楚楚街、随手记、猎聘、海尔等。截至2015年底,环信共服务了50833家App客户,SDK覆盖手机终端3.19亿,平台日均发送消息2.1亿条。根据易观智库《2015中国SaaS客服市场专题研究报告》,在中国移动端SaaS客服市场中,环信市场占有率高达77.4%。
也许你所有的坚持都在等待这一刻的爆发。现在开始,你要做的只是点击“阅读原文”,加入星生代!

阅读原文 收起阅读 »

android播放网络音频

android播放网络音频,很简单的技术,但是可以学习下   很简单的一个获取网络音频播放器,有进度条,播放,暂停,停止,重新播放,支持缓存,以下是源码,希望可以帮到大家 布局文件很简单,就几个按钮,TextView,和SeekBar。  ...
继续阅读 »
android播放网络音频,很简单的技术,但是可以学习下
 
很简单的一个获取网络音频播放器,有进度条,播放,暂停,停止,重新播放,支持缓存,以下是源码,希望可以帮到大家

M~WQX18UUTK7E4NRF3}E]W.png


布局文件很简单,就几个按钮,TextView,和SeekBar。 
activity_audio_palyer.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:orientation="vertical" >

<TextView
android:id="@+id/tips"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:text="文件地址" />

<EditText
android:id="@+id/file_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="http://sc1.111ttt.com/2016/1/02/23/195231349486.mp3" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="4.0dip"
android:orientation="horizontal" >

<Button
android:id="@+id/btnPlayUrl"
android:layout_width="80dip"
android:layout_height="wrap_content"
android:text="播放" >
</Button>

<Button
android:id="@+id/btnPause"
android:layout_width="80dip"
android:layout_height="wrap_content"
android:text="暂停" >
</Button>

<Button
android:id="@+id/btnStop"
android:layout_width="80dip"
android:layout_height="wrap_content"
android:text="停止" >
</Button>

<Button
android:id="@+id/btnReplay"
android:layout_width="80dip"
android:layout_height="wrap_content"
android:text="重播" >
</Button>
</LinearLayout>

<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dip"
android:orientation="horizontal" >

<SeekBar
android:id="@+id/skbProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1.0"
android:max="100"
android:paddingLeft="10dip"
android:paddingRight="10dip" >
</SeekBar>
</LinearLayout>
</LinearLayout>

</FrameLayout>
Player.Java文件
public class Player implements OnBufferingUpdateListener, OnCompletionListener,
MediaPlayer.OnPreparedListener {
public MediaPlayer mediaPlayer;
private SeekBar skbProgress;
private Timer mTimer = new Timer();
private String videoUrl;
private boolean pause;
private int playPosition;

public Player(String videoUrl, SeekBar skbProgress) {
this.skbProgress = skbProgress;
this.videoUrl = videoUrl;
try {
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnPreparedListener(this);
} catch (Exception e) {
Log.e("mediaPlayer", "error", e);
}

mTimer.schedule(mTimerTask, 0, 1000);
}

/*******************************************************
* 通过定时器和Handler来更新进度条
******************************************************/
TimerTask mTimerTask = new TimerTask() {
@Override
public void run() {
if (mediaPlayer == null)
return;
if (mediaPlayer.isPlaying() && skbProgress.isPressed() == false) {
handleProgress.sendEmptyMessage(0);
}
}
};

Handler handleProgress = new Handler() {
public void handleMessage(Message msg) {
int position = mediaPlayer.getCurrentPosition();
int duration = mediaPlayer.getDuration();
if (duration > 0) {
long pos = skbProgress.getMax() * position / duration;
skbProgress.setProgress((int) pos);
}
};
};

/**
* 来电话了
*/
public void callIsComing() {
if (mediaPlayer.isPlaying()) {
playPosition = mediaPlayer.getCurrentPosition();// 获得当前播放位置
mediaPlayer.stop();
}
}

/**
* 通话结束
*/
public void callIsDown() {
if (playPosition > 0) {
playNet(playPosition);
playPosition = 0;
}
}

/**
* 播放
*/
public void play() {
playNet(0);
}

/**
* 重播
*/
public void replay() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.seekTo(0);// 从开始位置开始播放音乐
} else {
playNet(0);
}
}

/**
* 暂停
*/
public boolean pause() {
if (mediaPlayer.isPlaying()) {// 如果正在播放
mediaPlayer.pause();// 暂停
pause = true;
} else {
if (pause) {// 如果处于暂停状态
mediaPlayer.start();// 继续播放
pause = false;
}
}
return pause;
}

/**
* 停止
*/
public void stop() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
}

@Override
/**
* 通过onPrepared播放
*/
public void onPrepared(MediaPlayer arg0) {
arg0.start();
Log.e("mediaPlayer", "onPrepared");
}

@Override
public void onCompletion(MediaPlayer arg0) {
Log.e("mediaPlayer", "onCompletion");
}

@Override
public void onBufferingUpdate(MediaPlayer arg0, int bufferingProgress) {
skbProgress.setSecondaryProgress(bufferingProgress);
int currentProgress = skbProgress.getMax()
* mediaPlayer.getCurrentPosition() / mediaPlayer.getDuration();
Log.e(currentProgress + "% play", bufferingProgress + "% buffer");
}

/**
* 播放音乐
*
* @param playPosition
*/
private void playNet(int playPosition) {
try {
mediaPlayer.reset();// 把各项参数恢复到初始状态
mediaPlayer.setDataSource(videoUrl);
mediaPlayer.prepare();// 进行缓冲
mediaPlayer.setOnPreparedListener(new MyPreparedListener(
playPosition));
} catch (Exception e) {
e.printStackTrace();
}
}

private final class MyPreparedListener implements
android.media.MediaPlayer.OnPreparedListener {
private int playPosition;

public MyPreparedListener(int playPosition) {
this.playPosition = playPosition;
}

@Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();// 开始播放
if (playPosition > 0) {
mediaPlayer.seekTo(playPosition);
}
}
}
}
MainActivity.java文件
public class MainActivity extends Activity {

private Button btnPause, btnPlayUrl, btnStop,btnReplay;
private SeekBar skbProgress;
private Player player;
private EditText file_name_text;
private TextView tipsView;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_audio_palyer);

this.setTitle("在线音乐播放---ouyangpeng编写");

btnPlayUrl = (Button) this.findViewById(R.id.btnPlayUrl);
btnPlayUrl.setOnClickListener(new ClickEvent());

btnPause = (Button) this.findViewById(R.id.btnPause);
btnPause.setOnClickListener(new ClickEvent());

btnStop = (Button) this.findViewById(R.id.btnStop);
btnStop.setOnClickListener(new ClickEvent());

btnReplay = (Button) this.findViewById(R.id.btnReplay);
btnReplay.setOnClickListener(new ClickEvent());

file_name_text=(EditText) this.findViewById(R.id.file_name);
tipsView=(TextView) this.findViewById(R.id.tips);

skbProgress = (SeekBar) this.findViewById(R.id.skbProgress);
skbProgress.setOnSeekBarChangeListener(new SeekBarChangeEvent());

String url=file_name_text.getText().toString();
player = new Player(url,skbProgress);

TelephonyManager telephonyManager=(TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(new MyPhoneListener(), PhoneStateListener.LISTEN_CALL_STATE);
}

/**
* 只有电话来了之后才暂停音乐的播放
*/
private final class MyPhoneListener extends android.telephony.PhoneStateListener{
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING://电话来了
player.callIsComing();
break;
case TelephonyManager.CALL_STATE_IDLE: //通话结束
player.callIsDown();
break;
}
}
}

class ClickEvent implements OnClickListener {
@Override
public void onClick(View arg0) {
if (arg0 == btnPause) {
boolean pause=player.pause();
if (pause) {
btnPause.setText("继续");
tipsView.setText("暂停播放...");
}else{
btnPause.setText("暂停");
tipsView.setText("继续播放...");
}
} else if (arg0 == btnPlayUrl) {
player.play();
tipsView.setText("开始播放...");
} else if (arg0 == btnStop) {
player.stop();
tipsView.setText("停止播放...");
} else if (arg0==btnReplay) {
player.replay();
tipsView.setText("重新播放...");
}
}
}

class SeekBarChangeEvent implements SeekBar.OnSeekBarChangeListener {
int progress;
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// 原本是(progress/seekBar.getMax())*player.mediaPlayer.getDuration()
this.progress = progress * player.mediaPlayer.getDuration()
/ seekBar.getMax();
}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {

}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// seekTo()的参数是相对与影片时间的数字,而不是与seekBar.getMax()相对的数字
player.mediaPlayer.seekTo(progress);
}
}
}
OK,在项目文件AndroidManifest.xml里面添加权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.netmusic"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- 注意:这里要加入一个监听电话的权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.netmusic.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
这样做出来的就很原始了,1毛钱特效都没有的那种。 

)7IQP84I3_JP`O(R)SML(0S.png


按钮这些可以自己定义背景,有专门的媒体按钮,去http://www.iconfont.cn/上找,很多的。 
最主要的是咱们的进度条SeekBar,,原始是不是太丑了?来,我们加个样式吧。 
在style文件里面:
 </style>
<style name="Widget.SeekBar.Normal" parent="@android:style/Widget.SeekBar">
<item name="android:maxHeight">8.0dip</item>
<item name="android:indeterminateOnly">false</item>
<item name="android:indeterminateDrawable">@android:drawable/progress_indeterminate_horizontal</item>
<item name="android:progressDrawable">@drawable/seekbar_horizontal</item>
<item name="android:minHeight">8.0dip</item>
<item name="android:thumb">@drawable/seek_thumb</item>
<item name="android:thumbOffset">10.0dip</item>
</style>
新建seekbar_horizontal.xml,drawable里面的
<?xml version="1.0" encoding="UTF-8"?>  
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background" android:drawable="@drawable/seek_bkg" />
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<corners android:radius="2.0dip" />
<gradient android:startColor="#80ffd300" android:endColor="#a0ffcb00" android:angle="270.0" android:centerY="0.75" android:centerColor="#80ffb600" />
</shape>
</clip>
</item>
<item android:id="@android:id/progress">
<clip android:drawable="@drawable/seek" />
</item>
</layer-list>
ok,还有几个图片素材 
 



1L0{YQ_XOX5S1Y70HKSI.png


seek.9.png




F{(26PX(W`EP}U8G0QETL]G.png


 seek_bkg.9.png




716K_JO8`CS]O98FA@XPTR.png


 seek_thumb.png


代码里面引用:
 <SeekBar
android:id="@+id/skbProgress"
style="@style/Widget.SeekBar.Normal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1.0"
android:max="100"
android:paddingLeft="10dip"
android:paddingRight="10dip" >
</SeekBar>
这样就可以了,看下效果: 

 
本文由环信热心用户SeanMis小七发表,个人博客地址SeanMis小七
作者QQ:1453022932 收起阅读 »

Android Studio使用技巧

作为开题,我们先看看 log 输出的快捷键,在需要大量输出 log 调试程序时,快捷键能为我们节省大量的工作量。 //logd + Enter Log.d(TAG, "onCreate "); //logm + Enter 可以快速输...
继续阅读 »
作为开题,我们先看看 log 输出的快捷键,在需要大量输出 log 调试程序时,快捷键能为我们节省大量的工作量。
//logd + Enter
Log.d(TAG, "onCreate ");
//logm + Enter 可以快速输出方法中的参数log信息
Log.d(TAG, "onCreate() called with " + "savedInstanceState = [" + savedInstanceState + "]");
//loge + Enter
Log.e(TAG, "onCreate ");

 快捷键及相关的使用:

【1】新建了Android library module -> settings.gradle多了':mylibrary'
【2】自动导入包:File->Settings->Editor->General->Auto Import->把Java的勾都打上
【3】设置快捷键类型:File->Settings->搜索keymap
【4】Ctrl+Alt+空格 代码提示
【5】Ctrl+Shift+↑或↓ 移动代码位置
【6】复制上一行代码,并显示在当行 Ctrl+D
【7】删除一行代码 Ctrl+Y
【8】在方法间快速移动 Alt+↑或↓
【9】移动滚动条 Ctrl+↑或↓
【10】Ctrl+W 选中代码,多次按会不同效果
【11】Ctrl+N 查找类
【12】查找文件,如xml Ctrl+Shift+N
【13】在本类中按Ctrl+U 查找本类的父类
【14】选中方法按Ctrl+Alt+h 查找这个方法被调用的地方
【15】查看一个方法的实现 选中方法按Ctrl+Shift+i
【16】在本类中按Ctrl+H 查看本类的层级结构
【17】Ctrl+Alt+← 返回代码跳转前的位置
【18】Alt+→或← 切换打开的文件
【19】光标在方法里,按Ctrl + -或+ 展开或折叠方法
【20】Alt+1 隐藏或显示左侧的工程面板
【21】Ctrl+Shift+Alt+N 查找本类中的方法
【22】Ctrl+F12 查看本类的结构,显示本类的方法和数据域等, 在此基础上按Ctrl+I或打勾右边,可查看匿名内部类
【23】Ctrl+O 覆盖父类的方法
【24】光标处于方法的一个大括号,按Ctrl+ [ 或 ] 跳转到方法大括号的另一端
【25】选中模块,按Ctrl+Alt+T,可快速生成try catch等语句
【26】Ctrl+鼠标左键点击Activity左边的布局图标,可快速打开与本Activity有关联的布局
【27】Ctrl+J 快捷生成判空、循环、findViewById、Toast等代码,同时能查看其他快捷键使用方式
【28】Alt + Enter 错误提示
【29】Ctrl + F 在本类中查找相同元素
【30】Shift+F6 在本类中整体修改元素( 牵一发而动全身 )
【31】Ctrl+R 在本类中整体查找,整体替换
【32】Ctrl+E 查看最近打开的文件
【33】格式化代码 Ctrl+Alt+L

Debug介绍:

F8单步调试,F7进入方法,Shift+F8跳到另一个断点位置
若想在"不修改"代码的前提下,在控制台Console而不是Logcat中输出log,则可采用以下方法:

右键断点



1362950-9f397e6b478df701.png


点击Suspend


1362950-3016c4b437cfe513_(1).png


在Log evaluated expression中输入要打印的log

1362950-feb959ce81a1255b.png


输出效果如下,这种方法能 "避免修改代码",读者可常用

1362950-7f596df23246c09b.png


在debug过程中可快速修改变量值,在下面 "i=1" 处右键,点 Set Value 即可

1362950-7df50e09921952c0.png


也可以点击 Add to Watches 把要观察的变量添加到 Watches 中

点击上图的第二个红色按钮,View Breakpoints

1362950-614b2d8d0710b7c7.png


左侧显示了已标注的断点位置,可通过取消勾选,来实现 ”不去除断点,但不运行已取消勾选的断点“ 。 收起阅读 »

环信编程大赛优秀开源项目系列之一:“文播”一款文字直播APP

根据IDC数据显示,中国有近200万开发者,身为一个程序员,我们生活在一个 IT 系统越发复杂且多变化的时代。有时候执行一个简单的开源项目,开发一个基础功能都需要精准定义并耗费大量时间专注任务。随着云计算的兴起,API 和SDK开始作为软件之间重要媒介而作为一...
继续阅读 »
根据IDC数据显示,中国有近200万开发者,身为一个程序员,我们生活在一个 IT 系统越发复杂且多变化的时代。有时候执行一个简单的开源项目,开发一个基础功能都需要精准定义并耗费大量时间专注任务。随着云计算的兴起,API 和SDK开始作为软件之间重要媒介而作为一种独立应用而存在,“一切皆软件,一切皆API,一切皆SDK”。通过API和SDK可以让开发者摆脱繁重的基础功能底层开发,短时间即可让App拥有各种诸如内置IM、统计等基础功能组件能力。 

5月14日,由环信联合猿圈共同推出的“首届环信编程大赛”颁奖典礼在中关村义创空间隆重举行。本次环信编程大赛历时两个月,由线上初赛、决赛和颁奖典礼三个环节组成,总计报名人数2000+,收到决赛项目100+。最终由评委会认定的13个优秀开源项目及开发者集体亮相颁奖典礼。其中“方圆十里”、“高仿微信“和“咚咚”三个开源项目名列前三,共同分享了15000元奖金和价值12000元的专属表情包。



QQ截图20160516205232.jpg


优秀项目开发者合影




_OU}M`{}CR`T@5D52W6RW1.png



这枚可爱的小鲜肉竟然是本次环信编程大赛发起人,目前单身,私信可获得联系方式!


其余入围的十余个优秀开源项目同样引起了到场开发者的热烈追捧,环信将分期将入围的优秀项目代码免费开源给小伙伴们。今天我们带来的是一款基于环信sdk进行个性化改造的文字直播平台App——“文播”。典型的使用场景包括经典的文字直播项目——直播球赛,以及现在流行的直播游戏,再加上直播生活技能、直播课程等,都能在“文播”里找到对应的频道。




QQ截图20160516204700.jpg


“文播”项目负责人董艺菲分享技术开发细节


 



QQ截图20160516202827.jpg


“文播”APP界面截图


功能:


本项目是一款基于环信sdk进行个性化改造的文字直播平台性的安卓app。
在参赛报名的时候,曾想过这样一个问题:一款完全为IM而生的sdk,到底能有如何的潜力?因此,另辟蹊径将环信提供的IM群聊功能,通过重新设计,改造成了现在的文字直播的平台类型app。
每个直播间,其实就是一个“只有群创建者才能发言”的IM群组或讨论组,再进行一些界面上的改造,就可以实现一款类似于从早期非智能机时代流行至今的纯文字直播的app。
典型的使用场景包括经典的文字直播项目——直播球赛,以及现在流行的直播游戏,再加上直播生活技能、直播课程等,都能在《文播》里找到对应的频道。
提交的该版本目前为纯游客端,主播端另行实现。


技术:


·客户端使用DrCoSu工作室开源的dileber框架,MVP设计模式,整个项目冗余较低。
·融合环信SDK,并进行了个性化的改造。
·采用.9格式存储图片,ttf方式呈现界面与图标,各个机型兼容性较好。
·服务端采用Java(Spring),配合ngix和redis极大提升了访问响应速度。
·采用http通信和json、xml等数据格式,移植性和通用性好。


心得


重复造轮子虽然好,但是在实际开发中,往往可以使用更好的方式来加快你的节奏,从中获得更大的成就感。
环信SDK在即时通讯云领域是一款足够优秀的SDK。配合JPush和好的创意,能实现无限多的可能性。
创意是一款新型软件的核心竞争力。


介绍


文字的直播,一样精彩。




QQ截图20160516203637.jpg



特别感谢以下企业的大力支持:


义创空间提供颁奖场地
 
萌岛从自有形象库中授权一套价值12000元的表情包
 
Emokit赞助Apple Watch一台
 
猿圈全程提供技术评测支持


项目托管地址:https://sourceforge.net/p/wenbo-im/git/ci/master/tree/
 “文播”源码下载及演讲PPT下载↓↓↓ 收起阅读 »

环信“客户声音”后台谍照泄露

PS:小编觉得是产品故意发粗来的。 [[嘘]] 环信近期上线了业界首个“客户声音”产品,可以通过热点话题分析发现新畅销商品,通过情感度分析发现服务问题,来帮助企业更好的来倾听客户的声音。小编将持续为您报道,更多细节请密切关注
PS:小编觉得是产品故意发粗来的。 [[嘘]] 环信近期上线了业界首个“客户声音”产品,可以通过热点话题分析发现新畅销商品,通过情感度分析发现服务问题,来帮助企业更好的来倾听客户的声音。小编将持续为您报道,更多细节请密切关注

基于环信开发的部分项目展示

基于环信开发的部分项目                                 ...
继续阅读 »
基于环信开发的部分项目

1.jpg


 


3.jpg


 
 


4.png


 
 


5.png


 
 


6.jpg


 
 


9.jpg


 
 


11.png


 
 


12.png


 
 


14.jpg


 
 


34.png


 
 
  收起阅读 »