七鱼开发文档中心
总览服务端 APIWebiOSAndroid鸿蒙Uniapp微信小程序插件三方接入

接入说明

版本说明

本节说明 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)

参数说明

名称类型必填说明
contextContext是应用上下文
appKeyString是管理后台申请的 AppKey
optionsYSFOptions是选项配置
imageLoaderUnicornImageLoader是图片和表情加载器,开发者可根据自身项目实际实现,不可为空,且同步和异步方法都要实现

initSdk方法描述

在APP主界面调用Unicorn.initSdk方法,当下次打开APP的时候,若客服主动发送消息则可以立刻收到,或未读消息等功能也能及时收到通知。

public static boolean initSdk();
public static boolean initSdk(RequestCallbackWrapper<Boolean> callback);

参数说明

名称类型必填说明
callbackRequestCallbackWrapper否如果不想在主界面调用初始化方法,重载方法的参数初始化应在打开聊天界面前完成。具体实现可放在相关操作的成功回调函数中。

代码示例

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)

参数说明

名称类型必填说明
contextContext是应用上下文
titleString是打开的聊天窗口标题,以管理后台配置的导航栏信息优先
sourceConsultSource是发起咨询的来源信息

代码示例

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)

参数说明

名称类型必填说明
titleString是打开的聊天窗口标题,以管理后台配置的导航栏信息优先
sourceConsultSource是发起咨询的来源信息
actionMenuContainerViewGroup否放置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

  1. 在七鱼SDK的基础上,添加视频客服 SDK 到项目中。 在工程 build.gradle 文件中添加依赖。最新视频客服版本地址
// 最新视频客服版本可查看上面的链接地址
implementation 'com.qiyukf.unicorn:video:版本'
  1. 进行视频客服会话。 在 ConsultSource类增加了isEnableVideo参数,用于是否进行视频客服会话,默认为false。如果此参数设置为true,表示进行视频客服会话。

是否开启视频客服

APP同时集成了视频客服SDK,在进入会话时需要开启视频客服,则将该字段设置为 true

参数名类型参数说明取值说明
isEnableVideoboolean是否开启视频客服默认为false

App 侧配置示例

视频客服不是全局开关,而是进入某次会话时通过 ConsultSource.isEnableVideo 指定。App 可以在视频咨询入口单独设置为 true,普通在线客服入口保持默认 false。

ConsultSource source = new ConsultSource("video_service", "视频客服", null);
source.isEnableVideo = true;
Unicorn.openServiceActivity(context, "视频客服", source);

视频客服悬浮窗点击响应

接口描述

视频客服以悬浮窗展示或进行屏幕共享时,需要配置点击悬浮窗后的返回回调。建议返回客服聊天界面;如果未配置,SDK 无法确定需要返回的界面。

参数说明

参数名类型参数说明取值说明
onVideoFloatBackIntentOnVideoFloatBackIntent视频客服悬浮窗点击回调建议配置为返回客服聊天界面

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。

参数名类型参数说明取值说明
logSwitchbooleanSDK 的 log 是否要输出到 logcat默认为true,表示SDKlog输出到logcat

是否开启日志记录

是否开启日志记录,开启会生成日志文件,如果关闭,SDK运行将不会有任何日志记录,将影响问题排查。

参数名类型参数说明取值说明
openLogboolean是否开启日志记录默认为true,表示会开启日志记录

是否开启更多日志

是否开启更多日志,主要是数据库日志,开启后与数据库操作相关的会记录在本地日志,日志文件最大为16M,超过16M会进行截取,所以不用担心日志文件过大

参数名类型参数说明取值说明
isOpenMoreLogboolean是否开启更多日志默认为false,表示不开启

注意事项

不要调用 Unicorn.initSdk后立即进入客服界面,中间间隔至少2.5S,等七鱼SDK初始化完成,否则会出现界面样式与后台配置不一样的问题。

由于开发环境的不同,很多情况下会出现集成报错或者配置无效的问题。官网提供了demo源码,开发者可以参考源码;更多的时候是需要开发者自己本地调试代码,可以通过断点跟踪等基本且有效的方法来定位和排查问题。官网demo安装包,demo源码及SDK开发手册查看地址。

这篇文档有帮助吗?

目录