融云IM接入记录(1)——单聊

前言

  • 本篇为开篇,会涉及很多基本的东西,所以篇幅较大,若是觉得行文啰嗦还请各位大佬不要打我。
  • 本篇目的,是从0开始到实现单聊则打住,后续的特定需求会新开特定篇来写。
  • 若是跟着文章走,遇见了坑请看文末的坑总结章节,相信很快你就能成功接入。
  • 去融云开发平台申请key什么的我就不说了,会申请QQ号就会申请这个
  • 有什么问题请直接留言指出,欢迎批评指正。

跑个官方Demo压压惊


不管怎样,接入三方SDK最快的方式——官方Demo
所以起手我们还是把Demo打开玩一玩

相关资料:
融云IM Demo
融云新手指导
融云Android SDK集成指南
融云Server开发指南
融云开发者平台
融云sdk下载地址
PS:API调试入口:我的控制台——>左侧栏目API调试

运行效果:

Demo在手,天下我有。

分析:

来一个感性的认知。
从这个Demo来看,它的使用场景基本是:
点击一个列表item ——> 进入点对点的聊天界面。
在这个聊天界面内有可以发送许多类型的消息。地图位置,图片,文本等等。

总结下特征就是:

  • 聊天模式(点对点)
  • 消息类型(多种)

信息很有限,不过也是最直观的东西,所以我准备以此作为一个切入点来进行聊天的学习。

知识点铺垫


虽然Demo已经跑起来了,但是我还是懵逼的,我们还需要了解下融云相关的知识点。

基本概念

基础概念篇幅太大,其中包含业务篇,开发篇,我准备划俩个相对重要的,其他的则不作赘述,具体参考:http://www.rongcloud.cn/docs/quick_start.html#basic_concept

App Key / Secret

App Key / Secret 相当于您的 App 在融云的帐号和密码。是融云 SDK 连接服务器所必需的标识,每一个 App 对应一套 App Key / Secret。

Token

  • Token 即用户令牌,相当于您 APP 上当前用户连接融云的身份凭证。每个用户连接服务器都需要一个 Token,用户更换即需要更换 Token。每次初始化连接服务器时,都需要向服务器提交 Token。
  • Token 称为用户令牌。
  • Token 则是您 App 上的每一个用户的身份授权象征。
  • 您可以通过提交 userId 等信息来获得一个该用户对应的 Token,并使用这个 Token 作为该用户的唯一身份凭证与其他用户进行通信。
  • Token 的主要作用是身份授权和安全,因此不能通过客户端直接访问融云服务器获取 Token,您必须通过 Server API 从融云服务器 获取 Token 返回给您的 App,并在之后连接时使用。详细描述请参考 Server开发指南中的用户服务和获取Token方法小节。

官方流程图:

分析:
我们的最终目的是通过connect方法去连接聊天服务器,但是官方服务器需要我们带Token,但是token我们没有并且也不能自己维护token,所以我们就需要去请求服务器拿token,然而要拿这个token我们就需要从本地拿到我们登录时得到的userId作为一个请求参数去哪token,这样一个流程走下来我们就能去连接聊天服务器了。

那么我们如何获取一个测试token呢?
这就要用到官方提供的API调试工具了,接着往下看。

Api调试的的使用


利用Api调试获取测试token

最开始我就PS了,API调试入口:我的控制台——>左侧栏目API调试
这个东西在我们初期调试时很重要,因为你也可以看到token是通信必须,而我们在前期服务器没调试完成的情况下,如何来拿这个token呢?这个时候就需要用到api调试这个功能。

如图:

可以看到这里我们获取到的信息是:

userId:zj666
name:测试用户呵
portraitUri:https://ss3.baidu.com/-rVXeDTa2gU2pMbgoY3K/it/u=225869644,1383753585&fm=202&mola=new&crop=v1

请求结果: 
{
    "code":200,
    "userId":"zj666",
    "token":"wxS1DLbYEHlYRvEGldlHlLv9HaIuPBiUqpl0jm19YznutxcjH0D1Z2Yo81ijEZhZLPWs7qwNNlTGuKduIiTDBg=="
}

一顿操作猛如虎


来不及解释了,带上你的老年卡赶紧上车吧,滴,滴滴。

SDK引入

SDK下载地址:http://www.rongcloud.cn/downloads

既然是学习,我就全勾选下载了。

汇总

Name Type Desc 引入姿势
IMKit 界面 融云 IM 界面组件 Import Moudle
CallKit 界面 融云音视频界面组件 Import Moudle
IMLib 融云 IM 通讯能力库 armeabi, armeabi-v7a, arm64-v8a, x86 Import Moudle
CallLib 融云音视频核心组件 armeabi-v7a, x86 Import Moudle
LocationLib 融云位置相关库,高德地图的低版本jar包。 copy jar int your libs direct
RedPacket 融云红包相关库 Import Moudle

另外一点疑问,如图:

一般来说Kit和Lib都下载,因为它lib里边的库不全,但你用的话可以选择性的用,但是一点,用Kit必用Lib,但Kit不是必须的。

引入后的项目目录结构

配置文件

gradle文件依赖

dependencies {
    //kit 内依赖了lib 所以不必再依赖lib 又因为第一行 fileTree ,所以不必再额外依赖location jar包
    compile project(':IMKit')
    compile project(':CallKit')
    compile project(':RedPacket')
}

IMLib Module # AndroidManifest.xml

<meta-data
    android:name="RONG_CLOUD_APP_KEY"
    android:value="您的应用 AppKey" />

App Module # AndroidManifest.xml (位置依赖相关)

<meta-data
    android:name="com.amap.api.v2.apikey"
    android:value="高德地图的 AppKey" />

App Module # AndroidManifest.xml (FileProvider)

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="您的应用包名.FileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/rc_file_path" />
</provider>
<meta-data
    android:name="com.amap.api.v2.apikey"
    android:value="高德地图的 AppKey" />

初始化

public class IMApplication extends Application {

    @Override
    public void onCreate() {

        super.onCreate();
        RongIM.init(this);
    }

}

配置会话列表

不啰嗦,直接看代码,默认为Main为消息页载体:

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.zj.imrydemo.MainActivity">

    <fragment
        android:id="@+id/conversationlist"
        android:name="io.rong.imkit.fragment.ConversationListFragment"
        android:layout_width="match_parent"
        android:layout_height="0px"
        android:layout_weight="1" />

</LinearLayout>

看见没,他这个是直接写死Fragment:

android:name=”io.rong.imkit.fragment.ConversationListFragment”

清单文件:

    <application>
        <!--会话列表-->
        <activity
            android:name=".MainActivity"
            android:screenOrientation="portrait"
            android:windowSoftInputMode="stateHidden|adjustResize">

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

            <intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />

                <data
                    android:host="com.zj.imrydemo"
                    android:pathPrefix="/conversationlist"
                    android:scheme="rong" />
            </intent-filter>

        </activity>

    </application>

这人注意下我是双Intent-Fillter,实际上只需要一个。另外看下注释,有个host得改成自己的包名。

    <intent-filter>
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />

        <data
            android:host="com.zj.imrydemo" //注意这儿喲,得写自己包名
            android:pathPrefix="/conversationlist"
            android:scheme="rong" />
    </intent-filter>

看下效果吧:

连接聊天服务器

从上面的图也看到了,”无法连接到服务器”,那么这里我们就来说说如何连接到服务器。

一般的,我们就在进入这个页面之前进行连接,比如我们的闪屏页。

这里我就不写闪屏页了,懒得搞,知道这个意思就行。

    String portraitUri = "https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=616492013,3957489063&fm=179&w=56&h=56&img.PNG";
        String nickName = "蛟龙87";
        String token = "wxS1DLbYEHlYRvEGldlHlLv9HaIuPBiUqpl0jm19YznutxcjH0D1Z2Yo81ijEZhZLPWs7qwNNlTGuKduIiTDBg==";

        RongIM.connect(token, new RongIMClient.ConnectCallback() {
            @Override
            public void onTokenIncorrect() {
                Log.d(TAG, "onTokenIncorrect");
            }

            @Override
            public void onSuccess(String userId) {
                Log.d(TAG, "ConnectCallback connect onSuccess=" + userId);
            }

            @Override
            public void onError(RongIMClient.ErrorCode errorCode) {
                Log.d(TAG, "ConnectCallback connect onError-ErrorCode=" + errorCode);
            }
        });

重新运行之后,观察输出结果:

04-18 17:46:31.908 27639-27639/com.zj.imrydemo D/RongYunAbout: ConnectCallback connect onSuccess=zj666

这只是连接上了服务器罢了,可以注意到返回的参数是我们的userId.
但这离我们实现单聊的目的还差一丢丢,咱接着往下走。

单聊实现

若你是一步步跟着坐过来的,你会发现你在api调试上发一条消息到本机,消息是能收到,但是呢,列表没动静。
言归正传,会产生上述问题的原因是,我们还少干了俩件事。

初始化ConversationListFragment

   private void initConversationList() {
        FragmentManager supportFragmentManager = getSupportFragmentManager();
        ConversationListFragment conversationlistFragment = (ConversationListFragment) supportFragmentManager.findFragmentById(R.id.conversationlist);
        conversationlistFragment.getActivity();
        Uri uri = Uri.parse("rong://" + getApplicationInfo().packageName).buildUpon()
                .appendPath("conversationlist")
                .appendQueryParameter(Conversation.ConversationType.PRIVATE.getName(), "false") //设置私聊会话非聚合显示
                .appendQueryParameter(Conversation.ConversationType.GROUP.getName(), "true")//设置群组会话聚合显示
                .appendQueryParameter(Conversation.ConversationType.DISCUSSION.getName(), "false")//设置讨论组会话非聚合显示
                .appendQueryParameter(Conversation.ConversationType.SYSTEM.getName(), "false")//设置系统会话非聚合显示
                .build();
        conversationlistFragment.setUri(uri);
    }

是不是这波去拿静态fragment实例的操作有点骚?是的,因为我们xml里是静态定义的fargment,所以我们就这么拿,但也可以动态的去定义,动态的定义也是我们平时项目中常用的fragment用法,所以这里我也不多说了,注意下Fragment是v4的fragment以及其manager就行。

定义聊天详情页面

这一步也不能省的,因为如果你不定义这个,那么你点击的消息列表内就无法进入聊天详情页面。

  • 新建一个Activity,(是FragmentActivity的子类)
  • 定义它的布局,类似于上面回话列表的布局内一个静态的fragment
  • 清单文件内进行配置

注意这三个点,然后我分别贴出相关代码:

activity_talk_detail.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.zj.imrydemo.TalkDetailActivity">

    <fragment
        android:id="@+id/conversation"
        android:name="io.rong.imkit.fragment.ConversationFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

</LinearLayout>

清单内

      <activity android:name=".TalkDetailActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />

                <data
                    android:host="com.zj.imrydemo"
                    android:pathPrefix="/conversation/"
                    android:scheme="rong" />
            </intent-filter>
        </activity>

TalkDetailActivity

package com.zj.imrydemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class TalkDetailActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_talk_detail);
    }
}

能够坚持到了这一步,说明骚年你是个有耐心的人,你的努力终将得到回报,现在就把我的力量赐予你吧,骚年,敞开双臂接受我仁慈的一击吧。
这里写图片描述

Holes (遇见的坑)


Hole #1:Failed to resolve: com.android.support:support-v4:26.0.0

解决:

allprojects {
    repositories {
        jcenter()
        mavenCentral()
        maven { url "https://jitpack.io" }
//        maven {
//            url 'https://maven.google.com/'
//            name 'Google'
//        }
        maven { url 'https://maven.google.com' }
        maven {url 'http://developer.huawei.com/repo/'}
    }
}

Hole #2:Installation failed with message INSTALL_FAILED_DUPLICATE_PERMISSION: Package cn.rongcloud.im attempting to redeclare permission cn.rongcloud.im.permission.C2D_MESSAGE already owned by com.aaa.bbb.

2个关键词:

  • INSTALL_FAILED_DUPLICATE_PERMISSION

android编译运行时的结果码。DUPLICATE_PERMISSION,意为权限重复,什么权限重复?看下一条。

  • Package cn.rongcloud.im attempting to redeclare permission cn.rongcloud.im.permission.C2D_MESSAGE already owned by com.aaa.bbb

Package cn.rongcloud.im尝试重新定义权限:cn.rongcloud.im.permission.C2D_MESSAG,但是它已经被com.aaa.bbb抢占了。但是这个权限究竟是用来干嘛的呢?
目前我了解是推送相关的需要这个权限。如果说它具备唯一性,那么可能会出现一个手机上,同时俩个app集成了融云的情况下(因为cn.rongcloud.im.permission是官方SDK自带的定义),有可能会导致后安装的app安装不上。如果是,那么这个问题是很严重的,本文着重走大流程,这个问题也就不展开了,所以这里暂时存疑,等后面测试的时候再来验证这个问题。

解决:

  • 将与之冲突的包删除。
  • DIY融云包内的权限名前缀(未验证可行与否)。

Hole #3:libSqlite

 FATAL EXCEPTION: main
 Process: com.zj.imrydemo:ipc, PID: 8201
 java.lang.UnsatisfiedLinkError: dlopen failed: library "libsqlite.so" not found

去下载这个sqlite库放在IMkit的lib下边。
这里我提供了下载地址:https://download.csdn.net/download/user11223344abc/10359170

Hole #4:No static method getFont(Landroid/content/Context;II)

 FATAL EXCEPTION: main
 Process: com.zj.imrydemo, PID: 5752
 java.lang.NoSuchMethodError: No static method getFont(Landroid/content/Context;II)
 Landroid/support/v4/graphics/TypefaceCompat$TypefaceHolder; in class Landroid/support/v4/content/res/ResourcesCompat;
 or its super classes (declaration of 'android.support.v4.content.res.ResourcesCompat' appears
 in /data/app/com.zj.imrydemo-1/base.apk)

解决这个坑的方法为:统一各项版本号,一般情况下是统一app/build.gradle。

compile_sdk_version = 27
build_tools_version = '27.0.2'
target_sdk_version = 27
support_version = '27.0.2'

Hole #5:app moudle 内的清单文件找不到 provider 标签。

解决方式:
gradle.properties

android.enableAapt2=false

信息备忘

本文Demo:https://github.com/zj614android/RongYunIM_single_talk

融云开放平台账号:
账号:410那个Q
密码:123那个密码

猜你喜欢

转载自blog.csdn.net/user11223344abc/article/details/79994307