接入说明
版本说明
本节说明 SDK 版本适用范围、升级路径和版本选择方式,帮助开发者在接入前确认应使用的版本。
导入 SDK
本节说明如何将 SDK 集成到项目中,包括依赖引入、手动集成和自动集成等接入方式。
配置 SDK 到项目中
使用Android Studio,在工程 build.gradle 文件中添加依赖即可。最新版本地址
// 最新版本可查看上面的链接地址
implementation 'com.qiyukf.unicorn:unicorn:版本'初始化与打开客服
本节说明 SDK 初始化、基础配置以及打开客服会话界面的流程。
基础配置
配置初始化代码
初始化分两步:先调用 Unicorn.config() 完成 YSFOptions 和 UnicornImageLoader 配置,再调用 Unicorn.initSdk() 启动 SDK。
config方法描述
在 Application 的 onCreate 中调用 Unicorn.config 方法,注意不要限制进程,也就是说不要只在主进程中初始化。七鱼的 Unicorn.config方法没有隐私问题和耗时操作,只做了基础配置,所以不用将该方法放在“同意隐私协议”后,否则APP第一次启动因为没有调用 Unicorn.config 方法而连不上客服。
public static boolean config(Context context, String appKey, YSFOptions options, final UnicornImageLoader imageLoader)参数说明
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
| context | Context | 是 | 应用上下文 |
| appKey | String | 是 | 管理后台申请的 AppKey |
| options | YSFOptions | 是 | 选项配置 |
| imageLoader | UnicornImageLoader | 是 | 图片和表情加载器,开发者可根据自身项目实际实现,不可为空,且同步和异步方法都要实现 |
initSdk方法描述
在APP主界面调用Unicorn.initSdk方法,当下次打开APP的时候,若客服主动发送消息则可以立刻收到,或未读消息等功能也能及时收到通知。
public static boolean initSdk();
public static boolean initSdk(RequestCallbackWrapper<Boolean> callback);参数说明
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
| callback | RequestCallbackWrapper | 否 | 如果不想在主界面调用初始化方法,重载方法的参数初始化应在打开聊天界面前完成。具体实现可放在相关操作的成功回调函数中。 |
代码示例
public class YourApplication extends Application {
public void onCreate() {
//初始化方法不要限制进程,也就是说不要只在主进程中初始化
Unicorn.config(this, "appKey", options(), new GlideImageLoader());
}
}
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Unicorn.initSdk();
}
}打开客服界面
在 App 的合适页面调用 Unicorn.openServiceActivity() 打开客服界面,并通过 ConsultSource 携带咨询来源和分配信息。
public static void openServiceActivity(Context context, String title, ConsultSource source)参数说明
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
| context | Context | 是 | 应用上下文 |
| title | String | 是 | 打开的聊天窗口标题,以管理后台配置的导航栏信息优先 |
| source | ConsultSource | 是 | 发起咨询的来源信息 |
代码示例
String title = "聊天窗口的标题";
/**
* 设置访客来源,标识访客是从哪个页面发起咨询的,用于客服了解用户是从什么页面进入。
* 三个参数分别为:来源页面的url,来源页面标题,来源页面额外信息(保留字段,暂时无用)。
* 设置来源后,在客服会话界面的"用户资料"栏的页面项,可以看到这里设置的值。
*/
ConsultSource source = new ConsultSource(sourceUrl, sourceTitle, "custom information string");
/**
* 请注意: 调用该接口前,应先检查Unicorn.isServiceAvailable(),
* 如果返回为false,该接口不会有任何动作
*/
Unicorn.openServiceActivity(context, title, source);fragment接入
SDK 默认使用 Activity 打开客服界面;需要嵌入自有页面时,可以调用 Unicorn.newServiceFragment() 以 Fragment 方式接入。
public static ServiceMessageFragment newServiceFragment(String title, ConsultSource source)
// 重载方法
public static ServiceMessageFragment newServiceFragment(String title, ConsultSource source, ViewGroup actionMenuContainer)参数说明
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
| title | String | 是 | 打开的聊天窗口标题,以管理后台配置的导航栏信息优先 |
| source | ConsultSource | 是 | 发起咨询的来源信息 |
| actionMenuContainer | ViewGroup | 否 | 放置SDK的一些icon入口 |
代码示例
LinearLayout sdkIconContainer = new LinearLayout(this);
sdkIconContainer.setOrientation(LinearLayout.HORIZONTAL);
sdkIconContainer.setGravity(Gravity.CENTER);
addViewMenu(sdkIconContainer);
ConsultSource source = new ConsultSource(sourceUrl, sourceTitle, "custom information string");
ServiceMessageFragment fragment = Unicorn.newServiceFragment(title, source, sdkIconContainer);
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
transaction.replace(R.id.ll_fragment_container, fragment); // 将 fragment 放到对应的 containerId 中,containerId 为 ViewGroup 的 resId
transaction.commitAllowingStateLoss();图片加载配置
本节说明 SDK 中图片、表情或资源加载相关配置,避免与宿主应用的图片库或资源体系冲突。
配置图片加载库
图片加载基本上每个 App 都会用到,各个 App 也都会有自己的实现或者依赖的第三方库。为了避免 SDK 引入第三方图片库后,与 App 自身依赖的图片库冲突,或者与 App 用了不同的库造成浪费,七鱼 SDK 需要由 App 实现图片加载接口,并传给 config方法的第四个参数UnicornImageLoader。
Glide实现
该实现依赖 Glide 库,需要在工程的 build.gradle 文件中添加依赖:
implementation 'com.github.bumptech.glide:glide:版本号'代码示例
public class GlideImageLoader implements UnicornImageLoader {
private Context context;
public GlideImageLoader(Context context) {
this.context = context.getApplicationContext();
}
@Nullable
@Override
public Bitmap loadImageSync(String uri, int width, int height) {
if (width <= 0) {
width = Target.SIZE_ORIGINAL;
}
if (height <= 0) {
height = Target.SIZE_ORIGINAL;
}
Bitmap bitmap = null;
try {
bitmap = Glide.with(context.getApplicationContext())
.asBitmap()
.load(uri)
.submit(width, height).get(3, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
@Override
public void loadImage(String uri, int width, int height, final ImageLoaderListener listener) {
if (width <= 0) {
width = Target.SIZE_ORIGINAL;
}
if (height <= 0) {
height = Target.SIZE_ORIGINAL;
}
Glide.with(context.getApplicationContext())
.asBitmap()
.load(uri)
.into(new CustomTarget<Bitmap>(width, height) {
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
}
@Override
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
if (listener != null) {
listener.onLoadComplete(resource);
}
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
}
});
}
}fresco实现
fresco 库本身提供了一整套图片缓存和加载的功能,由于 SDK 中部分 ImageView 需要做自定义的绘制,因此只会用到其下载,解码和缓存的逻辑。
该实现依赖 fresco 库,需要在工程的 build.gradle 文件中添加依赖:
implementation 'com.facebook.fresco:fresco:版本号'代码示例
public class FrescoImageLoader implements UnicornImageLoader {
private Context context;
public FrescoImageLoader(Context context) {
this.context = context.getApplicationContext();
}
@Override
public Bitmap loadImageSync(String uri, int width, int height) {
Bitmap resultBitmap = null;
ImagePipeline imagePipeline = Fresco.getImagePipeline();
boolean inMemoryCache = imagePipeline.isInBitmapMemoryCache(Uri.parse(uri));
if (inMemoryCache) {
ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(uri));
if (width > 0 && height > 0) {
builder.setResizeOptions(new ResizeOptions(width, height));
}
ImageRequest imageRequest = builder.build();
DataSource<CloseableReference<CloseableImage>> dataSource =
imagePipeline.fetchImageFromBitmapCache(imageRequest, context);
CloseableReference<CloseableImage> imageReference = dataSource.getResult();
try {
if (imageReference != null) {
CloseableImage closeableImage = imageReference.get();
if (closeableImage != null && closeableImage instanceof CloseableBitmap) {
Bitmap underlyingBitmap = ((CloseableBitmap) closeableImage).getUnderlyingBitmap();
if (underlyingBitmap != null && !underlyingBitmap.isRecycled()) {
resultBitmap = underlyingBitmap.copy(Bitmap.Config.RGB_565, false);
}
}
}
} finally {
dataSource.close();
CloseableReference.closeSafely(imageReference);
}
}
return resultBitmap;
}
@Override
public void loadImage(String uri, int width, int height, final ImageLoaderListener listener) {
ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(uri));
if (width > 0 && height > 0) {
builder.setResizeOptions(new ResizeOptions(width, height));
}
ImageRequest imageRequest = builder.build();
ImagePipeline imagePipeline = Fresco.getImagePipeline();
DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage(imageRequest, context);
BaseBitmapDataSubscriber subscriber = new BaseBitmapDataSubscriber() {
@Override
public void onNewResultImpl(@Nullable Bitmap bitmap) {
if (listener != null) {
new AsyncTask<Bitmap, Void, Bitmap>() {
@Override
protected Bitmap doInBackground(Bitmap... params) {
Bitmap bitmap = params[0];
Bitmap result = null;
if (bitmap != null && !bitmap.isRecycled()) {
result = bitmap.copy(Bitmap.Config.RGB_565, false);
}
return result;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null) {
listener.onLoadComplete(bitmap);
} else {
listener.onLoadFailed(null);
}
}
}.execute(bitmap);
}
}
@Override
public void onFailureImpl(DataSource dataSource) {
if (listener != null) {
listener.onLoadFailed(dataSource.getFailureCause());
}
}
};
dataSource.subscribe(subscriber, UiThreadImmediateExecutorService.getInstance());
}
}Picasso实现
该实现依赖 Picasso 库,需要在工程的 build.gradle 文件中添加依赖:
implementation 'com.squareup.picasso:picasso:版本号'代码示例
public class PicassoImageLoader implements UnicornImageLoader {
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private Context context;
private ExecutorService threadPool;
private Handler uiHandler;
public PicassoImageLoader(Context context) {
this.context = context.getApplicationContext();
uiHandler = new Handler(Looper.getMainLooper());
threadPool = Executors.newFixedThreadPool(CPU_COUNT + 1);
}
@Nullable
@Override
public Bitmap loadImageSync(String uri, int width, int height) {
// 创建请求构造器
RequestCreator requestCreator = Picasso
.with(context)
.load(uri)
.config(Bitmap.Config.RGB_565);
// 如果指定了宽高,则进行缩放和裁剪
if (width > 0 && height > 0) {
requestCreator = requestCreator
.resize(width, height)
.centerCrop();
}
try {
// 同步获取 Bitmap
return requestCreator.get();
} catch (IOException e) {
e.printStackTrace();
return null;
} catch (IllegalStateException e) {
// 处理 Picasso 未初始化等情况
e.printStackTrace();
return null;
}
}
@Override
public void loadImage(final String uri, final int width, final int height, final ImageLoaderListener listener) {
threadPool.execute(new Runnable() {
@Override
public void run() {
RequestCreator requestCreator = Picasso
.with(context)
.load(uri)
.config(Bitmap.Config.RGB_565);
if (width > 0 && height > 0) {
requestCreator = requestCreator
.resize(width, height)
.centerCrop();
}
Bitmap bitmap = null;
try {
bitmap = requestCreator.get();
} catch (IOException e) {
e.printStackTrace();
}
if (listener == null) {
return;
}
if (bitmap != null && !bitmap.isRecycled()) {
final Bitmap finalBitmap = bitmap;
uiHandler.post(new Runnable() {
@Override
public void run() {
listener.onLoadComplete(finalBitmap);
}
});
} else {
uiHandler.post(new Runnable() {
@Override
public void run() {
listener.onLoadFailed(null);
}
});
}
}
});
}
}gif 图片加载
SDK开放了加载 gif 图片的功能,用户可以通过实现 UnicornGifImageLoader 接口来完成 gif 图片的加载。
示例代码只展示了 Glide 的加载,因为我们的 loadGifImage 方法已经把 url 和 ImageView 都给到 App 侧了,所以具体实现开发者自由发挥
代码示例
//第一步
public class GlideGifImagerLoader implements UnicornGifImageLoader {
Context context;
public GlideGifImagerLoader(Context context) {
this.context = context.getApplicationContext();
}
//当需要加载 gif 图片的时候会回调该方法
@Override
public void loadGifImage(String url, ImageView imageView,String imgName) {
if (url == null || imgName == null) {
return;
}
Glide.with(context).load(url).placeholder(R.drawable.ic_launcher).error(R.drawable.nim_default_img_failed).into(imageView);
}
}视频客服集成
本节说明视频客服能力的接入方式、依赖配置和进入视频会话时需要关注的参数。
视频客服
版本要求 >=7.9.1
- 在七鱼SDK的基础上,添加视频客服 SDK 到项目中。 在工程 build.gradle 文件中添加依赖。最新视频客服版本地址
// 最新视频客服版本可查看上面的链接地址
implementation 'com.qiyukf.unicorn:video:版本'- 进行视频客服会话。 在 ConsultSource类增加了isEnableVideo参数,用于是否进行视频客服会话,默认为false。如果此参数设置为true,表示进行视频客服会话。
是否开启视频客服
APP同时集成了视频客服SDK,在进入会话时需要开启视频客服,则将该字段设置为 true
| 参数名 | 类型 | 参数说明 | 取值说明 |
|---|---|---|---|
| isEnableVideo | boolean | 是否开启视频客服 | 默认为false |
App 侧配置示例
视频客服不是全局开关,而是进入某次会话时通过 ConsultSource.isEnableVideo 指定。App 可以在视频咨询入口单独设置为 true,普通在线客服入口保持默认 false。
ConsultSource source = new ConsultSource("video_service", "视频客服", null);
source.isEnableVideo = true;
Unicorn.openServiceActivity(context, "视频客服", source);视频客服悬浮窗点击响应
接口描述
视频客服以悬浮窗展示或进行屏幕共享时,需要配置点击悬浮窗后的返回回调。建议返回客服聊天界面;如果未配置,SDK 无法确定需要返回的界面。
参数说明
| 参数名 | 类型 | 参数说明 | 取值说明 |
|---|---|---|---|
| onVideoFloatBackIntent | OnVideoFloatBackIntent | 视频客服悬浮窗点击回调 | 建议配置为返回客服聊天界面 |
OnVideoFloatBackIntent
| 接口回调函数 | 说明 |
|---|---|
| onVideoFloatBackIntent(Context context) | 点击悬浮窗时触发,在回调中打开需要返回的界面 |
Activity 接入示例
OnVideoFloatBackIntent onVideoFloatBackIntent = new OnVideoFloatBackIntent() {
@Override
public void onVideoFloatBackIntent(Context context) {
ConsultSource source = new ConsultSource("video_service", "视频客服", null);
Unicorn.openServiceActivity(context, "视频客服", source);
}
};
options.onVideoFloatBackIntent = onVideoFloatBackIntent;Fragment 接入示例
Fragment 接入时,应返回承载客服 Fragment 的 App Activity。将示例中的 CustomerServiceActivity 替换为实际页面类。
OnVideoFloatBackIntent onVideoFloatBackIntent = new OnVideoFloatBackIntent() {
@Override
public void onVideoFloatBackIntent(Context context) {
Intent intent = new Intent(context, CustomerServiceActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
context.startActivity(intent);
}
};
options.onVideoFloatBackIntent = onVideoFloatBackIntent;隐私合规
本节说明 SDK 权限申请说明、运行时弹框建议和隐私合规相关要求。
权限申请说明
根据合规要求,申请相机,麦克风,存储,通知栏等权限的时候,需要弹框说明申请权限的原因,SDK暴露了申请权限的接口,具体请参考申请权限回调。
SDK在国内10.11.0版本提供了默认运行时弹框说明,但是建议客户还是根据申请权限回调自行实现运行时弹框说明,更符合客户APP的客服使用场景。
更多合规要求,请参考合规指南
构建配置与常见问题
本节说明编译、构建、混淆、架构兼容等工程配置问题及常见处理方式。
混淆配置
如果你的 apk 最终会经过代码混淆,请在 proguard 配置文件中加入以下代码:
-dontwarn com.qiyukf.**
-keep class com.qiyukf.** {*;}
-dontwarn com.netease.**
-keep class com.netease.** {*;}海外版本与环境切换
本节说明国内版、海外版以及环境切换相关配置,帮助开发者选择正确的服务环境。
海外版本
海外版本与国内版本接入方式一样,在工程 build.gradle 文件中添加依赖即可。最新海外版本地址
版本要求 >=7.8.0
// 最新海外版本可查看上面的链接地址
implementation 'com.qiyukf.unicorn:unicorn_abroad:版本'
- 注意:使用海外版SDK只能连接访问海外域名企业,与国内版不互通。海外版下载中心地址
- 新接入海外版客户,除获取未读的客服消息数Unicorn.getUnReadCount()外,其余的监听API和主动发送消息API只有等进入客服界面才生效,如有疑问,请联系技术支持。
log开关
log 开关,用于控制 SDK 的 log 是否要输出到 logcat。
| 参数名 | 类型 | 参数说明 | 取值说明 |
|---|---|---|---|
| logSwitch | boolean | SDK 的 log 是否要输出到 logcat | 默认为true,表示SDKlog输出到logcat |
是否开启日志记录
是否开启日志记录,开启会生成日志文件,如果关闭,SDK运行将不会有任何日志记录,将影响问题排查。
| 参数名 | 类型 | 参数说明 | 取值说明 |
|---|---|---|---|
| openLog | boolean | 是否开启日志记录 | 默认为true,表示会开启日志记录 |
是否开启更多日志
是否开启更多日志,主要是数据库日志,开启后与数据库操作相关的会记录在本地日志,日志文件最大为16M,超过16M会进行截取,所以不用担心日志文件过大
| 参数名 | 类型 | 参数说明 | 取值说明 |
|---|---|---|---|
| isOpenMoreLog | boolean | 是否开启更多日志 | 默认为false,表示不开启 |
注意事项
不要调用 Unicorn.initSdk后立即进入客服界面,中间间隔至少2.5S,等七鱼SDK初始化完成,否则会出现界面样式与后台配置不一样的问题。
由于开发环境的不同,很多情况下会出现集成报错或者配置无效的问题。官网提供了demo源码,开发者可以参考源码;更多的时候是需要开发者自己本地调试代码,可以通过断点跟踪等基本且有效的方法来定位和排查问题。官网demo安装包,demo源码及SDK开发手册查看地址。
这篇文档有帮助吗?