Android 常见问题总结及解决方法

Android 常见问题

SDK 如何初始化

  • 在你的 application 类中 onCreate() 方法中调用初始化方法
    /**
     * 初始化 SDK,在整个应用程序全局,只需要调用一次。
     *
     * @param appKey  应用的app key.
     * @param context 应用上下文。
     */
    public static void init(Context context, String appKey)
  • 注意:SDK 在初始化成功后,会有至少两个进程:主进程,ipc;在不支持系统推送的机型上,还会有 push 进程。

SDK 初始化失败、应用启动不成功

缓存和刷新 token

  • 从 App Server 获取到 token 后,客户端应该做 token 的缓存(存储在 sharepreference 中)

  • token 的有效期是开发者后台设置的,可以永久有效,也可以定期失效

  • 当链接时,connect() 方法回调 onTokenIncorrect() 时,应从 App Server 上重新获取 token 并缓存,使用新获取的 token 再次链接

SDK 如何连接

  • 调用 SDK 的设置网络状态监听方法,之后所有的链接状态变化都会通过此方法回调通知
    /**
     * 设置连接状态变化的监听器。
     *
     * @param listener 连接状态变化的监听器。
     */
    public static void setConnectionStatusListener(final RongIMClient.ConnectionStatusListener listener)
  • 在你的登录界面类中调用连接方法
    /**
     * <p>连接服务器,在整个应用程序全局,只需要调用一次,需在 {@link #init(Context)} 之后调用。</p>
     * <p>如果调用此接口遇到连接失败,SDK 会自动启动重连机制进行最多10次重连,分别是1, 2, 4, 8, 16, 32, 64, 128, 256, 512秒后。
     * 在这之后如果仍没有连接成功,还会在当检测到设备网络状态变化时再次进行重连。</p>
     *
     * @param token    从服务端获取的用户身份令牌(Token)。
     * @param callback 连接回调。
     * @return RongIMClient IM 客户端核心类的实例。
     */
    public static RongIMClient connect(final String token, final ConnectCallback callback)

连接成功后收不到消息如何解决

如何动态获取 token

token 失效问题

  • token错误,请您检查客户端初始化使用的AppKey和您服务器获取token使用的AppKey是否一致

  • token过期,是因为您在开发者后台设置了token过期时间,您需要请求您的服务器重新获取token并再次用新的token建立连接。

  • 请确认一下 AppKey 和 token 是否匹配。

  • 请在开发者后台确认 token 是否在有效期。

  • 请使用工具:https://rongcloud.github.io/websdk-demo/api-test.html 来测试一下能否链接成功。

自定义的消息接收不到如何解决

获取不到消息中携带的用户信息

  • 首先检查数据库中 rct_message 表中,相应的消息中是否有用户信息:如果没有,可以就是发送端没有设置成功用户信息

  • 数据库位置:需要 root 手机或者模拟器

    老版本:/data/data/app包名/files/appkey/userId/storage

    新版本:包含三个文件,/data/data/app包名/files/appkey/userId/ 目录下的 “storage”、“storage-shm”、“storage-wal”。将这三个文件拷贝到统一路径下,打开 storage 文件。

  • 如果数据库消息表中包含用户信息,需要检查用户信息是否完整:比如 userId 是否正确

如何在会话列表、会话界面展示群组头像,个人头像

  • 在你的 application 类中设置用户、群组信息提供者,当 SDK 需要展示头像时,会回调提供者的回调方法

  • 如果是异步获取信息,你可以先在回调方法中先返回 null,再取到信息后,调用如下方法再次刷新:

    /**
     * 刷新用户缓存数据。
     *
     * @param userInfo 需要更新的用户缓存数据。
     */
    public void refreshUserInfoCache(UserInfo userInfo)

    /**
     * 刷新群组缓存数据。
     *
     * @param group 需要更新的群组缓存数据。
     */
    public void refreshGroupInfoCache(Group group)
  • 头像、名字信息第一次加载成功后,SDK 会做数据库存储。SDK 在展示头像、名字信息时如果能从数据库中取出,就不会再回调信息提供者

  • 所以,如果信息变更,需要你重新调用刷新接口,做强制刷新

IMKit 和 IMLib 发送消息接口有什么区别

  • kit 中的发消息接口在发送消息时,会同时将消息展示在界面上

如何解决 libsqlite.so 文件找不到的异常

问题表现:SDK 的 ipc 进程启动不起来,连接不成功

  • 方法一:在 build.gradle 中把 targetSdkVersion 改小于24

  • 方法二: 如果配置了 build.gradle 中的 sourceSets,请检查配置是否正确:

    sourceSets 正确的配置如下:

    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
    
  • 通过Android Studio的Analyze APK功能[Build → Analyze APK…], 或直接解开apk包,查看是apk的lib目录下是否已包含libsqlite.so文件 https://support.rongcloud.cn/ks/NzIw

点击通知栏后,如何跳转到会话、会话列表界面

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        ...
        Intent intent = getIntent();
        Uri data = intent != null ? intent.getData() : null;
        String pushParam = data != null ? data.getQueryParameter("isFromPush") : null;
        isPush = pushParam != null && pushParam.equals("true");
        boolean isBackgroundNotify = pushParam != null && pushParam.equals("false");
        //push 通知
        if (isPush
                //应用被杀死,后台通知依然存在
                || (isBackgroundNotify && RongIMClient.getCurrentConnectionStatus().equals(RongIMClient.ConnectionStatusListener.ConnectionStatus.DISCONNECTED))
                //界面被回收恢复
                || (savedInstanceState != null && !RongIMClient.getCurrentConnectionStatus().equals(RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTED))) {
            RongIM.connect(token, callback);
        }
        ...
    }

历史消息、历史消息云存储、消息补偿的解释

  • 历史消息:SDK 会对消息做本地存储,在会话中显示的都是本地存储的消息
    /**
     * <p>
     * 获取指定类型,targetId 的N条历史消息记录。通过此接口可以根据情况分段加载历史消息,节省网络资源,提高用户体验。
     * 该接口不支持拉取聊天室 {io.rong.imlib.model.Conversation.ConversationType#CHATROOM} 历史消息。
     * </p>
     *
     * @param conversationType 会话类型。
     * @param targetId         目标 Id。根据不同的 conversationType,可能是用户 Id、讨论组 Id、群组 Id。
     * @param oldestMessageId  最后一条消息的 Id,获取此消息之前的 count 条消息,没有消息第一次调用应设置为:-1。
     * @param count            要获取的消息数量。
     * @param callback         获取历史消息记录的回调,按照时间顺序从新到旧排列。
     */
    public void getHistoryMessages(final Conversation.ConversationType conversationType, final String targetId, final int oldestMessageId, final int count, final ResultCallback<List<Message>> callback)
  • 历史消息云存储:
  1. 当本地数据库的消息展示完成以后,如果你开通了历史消息云存储的服务,在会话中下拉,会继续加载消息,这部分消息就是从服务器端拉取的历史消息;

  2. 清空会话中的历史消息,仅仅清除的是本地存储的历史消息,如果你开通了历史消息云存储服务,在会话中下拉,依然能拉取历史消息,这部分消息就是从服务器端拉取的历史消息;

    /**
     * <p>获取融云服务器中暂存,特定类型,targetId 的N条(一次不超过40条)历史消息记录。通过此接口可以根据情况分段加载历史消息,节省网络资源,提高用户体验。</p>
     * <p>区别于 {@link #getHistoryMessages},该接口是从融云服务器中拉取。通常用于更换新设备后,拉取历史消息。
     * 公众服务会话 {@link io.rong.imlib.model.Conversation.ConversationType#APP_PUBLIC_SERVICE}
     * {@link io.rong.imlib.model.Conversation.ConversationType#PUBLIC_SERVICE} </p>
     *
     * @param conversationType 会话类型。
     * @param targetId         目标 Id。根据不同的 conversationType,可能是用户 Id、讨论组 Id、群组 Id。
     * @param dateTime         从该时间点开始获取消息。即:消息中的 sentTime;第一次可传 0,获取最新 count 条。
     * @param count            要获取的消息数量,最多 40 条。
     * @param callback         获取历史消息记录的回调,按照时间顺序从新到旧排列。
     */
    public void getRemoteHistoryMessages(final Conversation.ConversationType conversationType, final String targetId, final long dateTime, final int count, final ResultCallback<List<Message>> callback)
  • 消息补偿:是一项收费服务器,体现在多端登录的时候消息同步。消息补偿可以设置时间:3天或7天;当客户端第一次(仅仅是第一次)安装登录,就会把这段时间内的所有消息下发下来

+号扩展区域

如何自定义+号扩展区域

如何动态自定义会话中的+号扩展区域

  • 继承 ConversationFragment 然后根据布局中的 id 获取到 Extension 对象
public class ChatFragment extends ConversationFragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        containerView = (RelativeLayout) super.onCreateView(inflater, container, savedInstanceState);
        extension = (RongExtension) containerView.findViewById(io.rong.imkit.R.id.rc_extension);
        return containerView;
    }
}
  • 根据当前页面的 ConversationType 和 TargetId,调用 Extension 中的对应方法:
    /**
     * 动态在 plugin 区域的末尾增加一个 plugin 。
     *
     * @param pluginModule 增加的 plugin
     */
    public void addPlugin(IPluginModule pluginModule)
    
    /**
     * 移除指定的 plugin
     *
     * @param pluginModule 指定 plugin
     */
    public void removePlugin(IPluginModule pluginModule)
        
    /**
     * 获取当前所有 Plugin 列表
     *
     * @return 已加载的 plugin 列表
     */
    public List<IPluginModule> getPluginModules()