融雲 集成單聊與會話列表


寫在前面,因爲公司做的項目需要聊天功能,所以在網上找了下方案後,果斷選擇了融雲,因爲免費,有100個測試位。
本來沒想着寫這個文章,無奈融雲的文檔寫的相(fei)當(chang)簡(la)單(ji),在問客服問題,回覆太慢了,並且沒有解決問題。
所以記錄下來,防止大家繼續踩坑。
文章目錄

目錄

1.集成步驟

2.集成SDK

3.會話列表

1. 靜態註冊

2. 動態加載

4.會話

5.啓動會話和會話列表

6.用戶信息

7.退出融雲

8.通知

9.服務器連接狀態監聽

10.發送信息監聽

11.會話列表刷新頭像

12.會話列表界面操作監聽

13.會話界面操作監聽

14.會話中歷史消息頭像更新

15.問題處理



1.集成步驟


1.註冊
去融雲官方網站註冊,官方網址在這裏

登錄界面如下

2.獲取app key 和 app secert
登錄成功後創建應用後可以看到app key和 app secret。注意這個app key 很重要,在後面要用到!

查看app key 和 app secret

3.下載SDK
找到SDK進行下載,這裏需要根據開發功能進行下載,因爲我的項目只需要進行單聊和會話列表的功能,不需要其他功能(紅包,位置,視頻,語音),根據需求選擇sdk,注意,IMKit、IMLib 都要下載,IMkit是界面模塊開發,IMlib是通訊模塊開發。

查看app key 和 app secret

4.功能開發
到這一步就已經成功一大半了,功能開發可以參考融雲官方文檔,下面是針對我項目中涉及的功能開發進行介紹說明。


2.集成SDK


1.將下載好的sdk導入項目中

在這裏插入圖片描述
2.在項目中的build.gradle中添加imkit和imlib的依賴

然後在項目AndroidManifest.xml 文件中,添加 FileProvider 配置,修改 android:authorities 爲應用的 “ApplicationId”.FileProvider,如下:

<provider
       android:name="android.support.v4.content.FileProvider"
       android:authorities="項目的applicationid.FileProvider"
       android:exported="false"
       android:grantUriPermissions="true">
       <meta-data
           android:name="android.support.FILE_PROVIDER_PATHS"
           android:resource="@xml/rc_file_path" />
</provider>

 

3.設置App key

打開 IMLib Module 的 AndroidManifest.xml 文件,將meta-data中的RONG_CLOUD_APP_KEY 修改爲在融雲登錄成功後創建好的的app key。

4.初始化
融雲官方文檔中建議在application create的時候進行初始化,這裏按照建議進行初始化。
在這裏需要注意,所有的融雲請求或操作都是在init後才能操作的,如果沒有進行init直接請求或者操作必然失敗。

if (getApplicationInfo().packageName.equals(getCurProcessName(getApplicationContext()))){
    RongIM.init(this);
}


5.連接融雲服務器
在init成功後,需要進行服務器的鏈接,這個建議放在baseactivity中作爲公用方法,在登錄成功後主動調用,在進入會話列表界面主動調用,在進入會話界面主動調用,因爲在使用融雲服務之前要確保和融雲服務器的連接是存在且正常的。

在onSucceess的回調中,返回了userid,userid是我們在融雲的身份id,可以這樣理解:兩個用戶之間的聊天,就是用雲的兩個userid在互相發送消息。

/**
 * 連接服務器,在整個應用程序全局,只需要調用一次,需在 {@link #init(Context)} 之後調用。</p>
 * 如果調用此接口遇到連接失敗,SDK 會自動啓動重連機制進行最多10次重連,分別是1, 2, 4, 8, 16, 32, 64, 128, 256, 512秒後。
 * 在這之後如果仍沒有連接成功,還會在當檢測到設備網絡狀態變化時再次進行重連
 *
 * @param token    從服務端獲取的用戶身份令牌(Token)。
 * @param callback 連接回調。
 * @return RongIM  客戶端核心類的實例。
 */
private void connect(String token) {

    if (getApplicationInfo().packageName.equals(App.getCurProcessName(getApplicationContext()))) {

        RongIM.connect(token, new RongIMClient.ConnectCallback() {

            /**
             * Token 錯誤。可以從下面兩點檢查 1.  Token 是否過期,如果過期您需要向 App Server 重新請求一個新的 Token
             *                  2.  token 對應的 appKey 和工程裏設置的 appKey 是否一致
             */
            @Override
            public void onTokenIncorrect() {
                //在這裏處理鏈接失敗的問題,如果token錯誤,重新獲取token,進行連接
            }

            /**
             * 連接融雲成功
             * @param userid 當前 token 對應的用戶 id
             */
            @Override
            public void onSuccess(String userid) {
                Log.d("LoginActivity", "--onSuccess" + userid);
            }

            /**
             * 連接融雲失敗
             * @param errorCode 錯誤碼,可到官網 查看錯誤碼對應的註釋
             */
            @Override
            public void onError(RongIMClient.ErrorCode errorCode) {

            }
        });
    }
}


3.會話列表


按融雲的介紹,會話列表有兩種實現方式,一種是靜態註冊,一種是動態加載。

1. 靜態註冊


a. 會話列表所在的Activity 對應的佈局文件:conversationlist.xml。注意 android:name 固定爲融雲的 ConversationListFragment

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

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

</LinearLayout>


b.新建Activity

public class ConversationListActivity extends FragmentActivity {

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



c.Activity對應Intent-Filter配置

<!--會話列表-->
<activity
    android:name="io.rong.fast.activity.ConversationListActivity"
    android:screenOrientation="portrait"
    android:windowSoftInputMode="stateHidden|adjustResize">

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

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

        <data
            android:host="io.rong.fast"
            android:pathPrefix="/conversationlist"
            android:scheme="rong" />
    </intent-filter>
</activity>


在這裏需要注意的是host一定要設置成自己的application id,如果沒有特別設置,application id就是自己的包名。
但是,我們如果把會話列表放在fragment中如何弄呢?一般App都是用viewpage+fragment來進行多界面顯示,所以這就到了動態加載的時候了。

2. 動態加載


a. 所在fragment中的佈局文件

<!--會話列表-->
   <FrameLayout
       android:id="@+id/rong_container"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       />



b.配置Uri
可以根據我們要顯示的會話類型,進行配置

ConversationListFragment conversationListFragment = new ConversationListFragment();
Uri uri = Uri.parse("rong://" + getActivity().getApplicationInfo().packageName).buildUpon()
.appendPath("conversationlist")      
.appendQueryParameter(Conversation.ConversationType.PRIVATE.getName(), "false") //設置私聊會話,該會話聚合顯示
.appendQueryParameter(Conversation.ConversationType.SYSTEM.getName(), "true")//設置系統會話,該會話非聚合顯示
 .build();



c.將Uri設置給fragment

conversationListFragment.setUri(uri);


d.將會話列表加載到fragment中

 FragmentManager fragmentManager = getChildFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.add(R.id.rong_container,conversationListFragment);
        transaction.commit();


4.會話


會話 Fragment 跟會話列表是完全一致的,添加方式完全一致,這裏就不需要考慮加載在fragment中了,因爲基本上每個會話都是單獨的一個界面。

1.會話所在的Activity 對應的佈局文件:conversationlist.xml。注意 android:name 固定爲融雲的 ConversationFragment

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <fragment
        android:id="@+id/conversation"
        android:name="io.rong.imkit.fragment.ConversationFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>



2.新建Activity

public class ConversationActivity extends FragmentActivity {

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



3.Activity對應Intent-Filter配置

<!--會話列表-->
<activity
            android:name=".RongYun.ConversationActivity"
            android:screenOrientation="portrait"
            android:theme="@style/AppTheme.NoActionBar"
            android:windowSoftInputMode="stateHidden|adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />

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

                <data
                    android:host="io.rong.fast"
                    android:pathPrefix="/conversation/"
                    android:scheme="rong" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data
                    android:host="io.rong.fast"
                    android:path="/conversationlist"
                    android:scheme="rong" />
            </intent-filter>

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

                <data
                    android:host="io.rong.fast"
                android:pathPrefix="/push_message"
                android:scheme="rong" />
            </intent-filter>
        </activity>


這裏同樣和會話列表一樣,host一定要設置成自己的application id。

5.啓動會話和會話列表


1.啓動會話

/**
 *啓動會話界面
 * @param context          應用上下文。
 * @param conversationType 會話類型。
 * @param targetId         根據不同的 conversationType,可能是用戶 Id、羣組 Id 或聊天室 Id。
 * @param title            聊天的標題,開發者可以在聊天界面通過 intent.getData().getQueryParameter("title") 獲取該值, 再手動設置爲標題。
 */
public void startConversation(Context context, Conversation.ConversationType conversationType, String targetId, String title)

/**
 * 啓動單聊界面。
 *
 * @param context      應用上下文。
 * @param targetUserId 要與之聊天的用戶 Id。
 * @param title        聊天的標題,開發者需要在聊天界面通過 intent.getData().getQueryParameter("title")
 *                     獲取該值, 再手動設置爲聊天界面的標題。
 */
RongIM.getInstance().startPrivateChat(getActivity(), targetUserid, title);


2.啓動會話列表

/**
 * 啓動會話列表界面。
 *
 * @param context               應用上下文。
 * @param supportedConversation 定義會話列表支持顯示的會話類型,及對應的會話類型是否聚合顯示。
 *                              例如:supportedConversation.put(Conversation.ConversationType.PRIVATE.getName(), false) 非聚合式顯示 private 類型的會話。
 */
public void startConversationList(Context context, Map<String, Boolean> supportedConversation)


6.用戶信息


在融雲的官方介紹中,對用戶信息做了說明。

融雲認爲,每一個設計良好且功能健全的 App 都應該能夠在本地獲取、緩存並更新用戶信息。所以,融雲不維護用戶基本信息(用戶
Id、暱稱、頭像)。此外,App 提供用戶信息也避免了由於緩存導致的用戶信息更新不及時,App 中不同界面上的用戶信息不統一(比如:一部分
App 從 App 服務器上獲取並顯示,一部分由融雲服務器獲取並顯示),能夠獲得更好的用戶體驗。

融雲提供了兩種方式從 App 的數據源顯示用戶暱稱和頭像。

1.用戶信息提供者

2.發送消息的時候攜帶用戶信息
兩種方式實現任意一種就可以,安卓和IOS兩端必須保持統一,即選擇的實現方式必須一致,否則可能報錯
由於第一種實現方式比較麻煩,在項目中我選擇了第二種實現方式。

結果這裏埋了個坑,在更新頭像後發現會話中的歷史消息頭像並沒有更新,在後面講我是如何處理的

a.在init後設置發送消息的時候是否攜帶信息爲true,我是放在application的create中了

 

/**
* 設置消息體內是否攜帶用戶信息。
*
* @param state 是否攜帶用戶信息,true 攜帶,false 不攜帶。
*/
RongIM.getInstance().setMessageAttachedUserInfo(true);



b.設置用戶信息

/**
* @param id 用戶在融雲的id
* @param name 用戶在融雲的名稱
* @param uri 用戶的頭像
*/
RongIM.getInstance().setCurrentUserInfo(new io.rong.imlib.model.UserInfo(id, name, Uri.parse(img)));



c.啓動私聊或會話列表

RongIM.getInstance().startPrivateChat(context,userid ,title);



7.退出融雲


與融雲服務器斷開連接有兩種方式:

RongIM.logout()
RongIM.disconnect();


logout表示與融雲的服務器斷開連接後,有別人發過來的消息,不接收通知。
disconncet表示與融雲的服務器斷開連接後,有別人發送過來的消息,接受通知。

8.通知


應用切後臺,在有消息過來的時候,往往會有通知提示。
我們需要創建一個Receiver繼承PushMessageReceiver,如下:

public class SealNotificationReceiver extends PushMessageReceiver {
    @Override
    public boolean onNotificationMessageArrived(Context context, PushNotificationMessage message) {
        return false; // 返回 false, 會彈出融雲 SDK 默認通知; 返回 true, 融雲 SDK 不會彈通知, 通知需要由您自定義。
    }

    @Override
    public boolean onNotificationMessageClicked(Context context, PushNotificationMessage message) {
        return false; // 返回 false, 會走融雲 SDK 默認處理邏輯, 即點擊該通知會打開會話列表或會話界面; 返回 true, 則由您自定義處理邏輯。
    }

}



然後在Androidmanifest中靜態註冊接收器

<receiver
    android:exported="true"
    android:name=".RongYun.SealNotificationReceiver">
    <intent-filter>
        <action android:name="io.rong.push.intent.MESSAGE_ARRIVED" />
        <action android:name="io.rong.push.intent.MI_MESSAGE_ARRIVED" />
        <action android:name="io.rong.push.intent.MESSAGE_CLICKED" />
        <action android:name="io.rong.push.intent.MI_MESSAGE_CLICKED" />
    </intent-filter>
</receiver>



這樣,在融雲推送消息的時候,通知欄就能彈出消息,更多關於推送的內容可以去官網查看,點擊這裏

9.服務器連接狀態監聽


在進行融雲相關請求和操作的時候,我們往往需要知道與服務器的連接狀態,連接正常纔有必要進行下一步。

1.創建自己的狀態監聽文件,實現融雲的狀態監聽方法

public class RongYunConnectionStatusListener implements RongIMClient.ConnectionStatusListener {
    private Context context;
    public RongYunConnectionStatusListener(Context context){
        this.context = context;
    }
    @Override
    public void onChanged(ConnectionStatus connectionStatus) {

        switch (connectionStatus){

            case CONNECTED://連接成功。

                break;
            case DISCONNECTED://斷開連接。

                break;
            case CONNECTING://連接中。

                break;
            case NETWORK_UNAVAILABLE://網絡不可用。

                break;
            case KICKED_OFFLINE_BY_OTHER_CLIENT://用戶賬戶在其他設備登錄,本機會被踢掉線

                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {

                        /**
                         * 被踢掉後,彈框提示
                         */
                        ToastUtil.shortToast(context,"賬號在其他設備登錄,請重新登錄");

                        /**
                         * 被踢掉後,跳轉到登錄界面
                         */
                        Intent intent=new Intent(context,LoginActivity.class);
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                        context.startActivity(intent);
                    }
                });

                break;
        }
    }
}



2.在application的create中設置鏈接狀態監聽

/**
         * 融雲鏈接狀態監聽
         */
        RongIM.setConnectionStatusListener(new RongYunConnectionStatusListener(this));



這樣對設備與融雲服務器的鏈接狀態進行了監聽,當出現上面的幾種狀態,便對應進行邏輯處理。

10.發送信息監聽


在進行會話的過程中,有時候需要對發送消息的結果進行捕獲,這時候應該實現的是SendMessageListener

1.創建自己的信息發送監聽,實現融雲的監聽方法

public class RongYunSendMessageListener implements RongIM.OnSendMessageListener {

    private Context context;
    public RongYunSendMessageListener(BaseApplication context) {
        this.context = context;
    }

    @Override
    public Message onSend(Message message) {
        return message;
    }

    @Override
    public boolean onSent(Message message, RongIM.SentMessageErrorCode sentMessageErrorCode) {
        if(message.getSentStatus()== Message.SentStatus.FAILED){
            if(sentMessageErrorCode== RongIM.SentMessageErrorCode.NOT_IN_CHATROOM){
                //不在聊天室
            }else if(sentMessageErrorCode== RongIM.SentMessageErrorCode.NOT_IN_DISCUSSION){
                //不在討論組
            }else if(sentMessageErrorCode== RongIM.SentMessageErrorCode.NOT_IN_GROUP){
                //不在羣組
            }else if(sentMessageErrorCode== RongIM.SentMessageErrorCode.REJECTED_BY_BLACKLIST){
                //你在他的黑名單中
            }else{
                ToastUtil.shortToast(context,"與服務器失去鏈接,請重新登錄");
            }
        }
        return false;
    }
}



2.在application的create中設置消息發送 監聽
       

/**
         * 融雲發送消息監聽
         */
        RongIM.getInstance().setSendMessageListener(new RongYunSendMessageListener(this));


這樣對消息發送進行了監聽,當出現上面的幾種狀態,便對應進行邏輯處理。

11.會話列表刷新頭像


在跳轉會話列表界面時,可以先進行一次connect,因爲在刷新會話列表的時候需要保證與融雲服務器鏈接正常。a. 通過接口獲取會話列表數據
b.針對每個會話,通過userid在app服務器獲取用戶信息

/**
             * 更新會話列表頭像和標題
             */
            RongIM.getInstance().getConversationList(new RongIMClient.ResultCallback<List<Conversation>>() {
                @Override
                public void onSuccess(List<Conversation> conversations) {

                    if (conversations == null || conversations.size() <= 0){
                        return;
                    }
                    for (int i = 0 ; i < conversations.size() ; i ++){
                        Conversation conversation = conversations.get(i);
                        if (conversation != null){
                            //這裏通過token 和useid在我們自己服務器請求用戶信息
                            mPresenter.GetUserInfo(token,conversation.getTargetId());
                        }
                    }
                }

                @Override
                public void onError(RongIMClient.ErrorCode errorCode) {

                }
            });



c.獲取用戶信息成功後通過接口更新用戶信息
       

io.rong.imlib.model.UserInfo userInfo = new io.rong.imlib.model.UserInfo(
                result.getPhone(),
                result.getName(),
                Uri.parse(result.getImg()));
        RongIM.getInstance().refreshUserInfoCache(userInfo);



通過這樣設置後,會話列表會自動獲取最新的用戶信息,顯示在會話列表上。

12.會話列表界面操作監聽


1.定義自己的操作監聽類

private class MyConversationListBehaviorListener implements RongIM.ConversationListBehaviorListener{
      /**
     * 當點擊會話頭像後執行。
     *
     * @param context          上下文。
     * @param conversationType 會話類型。
     * @param targetId         被點擊的用戶id。
     * @return 如果用戶自己處理了點擊後的邏輯處理,則返回 true,否則返回 false,false 走融雲默認處理方式。
     */
    boolean onConversationPortraitClick(Context context, Conversation.ConversationType conversationType, String targetId){
        ToastUtils.shorttoast(context,"跳轉到用戶詳情");
    }

    /**
     * 當長按會話頭像後執行。
     *
     * @param context          上下文。
     * @param conversationType 會話類型。
     * @param targetId         被點擊的用戶id。
     * @return 如果用戶自己處理了點擊後的邏輯處理,則返回 true,否則返回 false,false 走融雲默認處理方式。
     */
    boolean onConversationPortraitLongClick(Context context, Conversation.ConversationType conversationType, String targetId){
      return false;
    }
    /**
     * 長按會話列表中的 item 時執行。
     *
     * @param context        上下文。
     * @param view           觸發點擊的 View。
     * @param uiConversation 長按時的會話條目。
     * @return 如果用戶自己處理了長按會話後的邏輯處理,則返回 true, 否則返回 false,false 走融雲默認處理方式。
     */
    @Override
    public boolean onConversationLongClick(Context context, View view, UIConversation uiConversation) {
        return false;
    }

    /**
     * 點擊會話列表中的 item 時執行。
     *
     * @param context        上下文。
     * @param view           觸發點擊的 View。
     * @param uiConversation 會話條目。
     * @return 如果用戶自己處理了點擊會話後的邏輯處理,則返回 true, 否則返回 false,false 走融雲默認處理方式。
     */
    @Override
    public boolean onConversationClick(Context context, View view, UIConversation uiConversation) {
        return false;
    }
}



2.在application的create中設置監聽

        /**
         * 融雲會話列表界面監聽
         */
        RongIM.setConversationListBehaviorListener(new MyConversationListBehaviorListener());


通過這樣設置後,針對在設置列表界面的不同操作可以進行不同的邏輯處理。

13.會話界面操作監聽


會話界面操作監聽和會話列表界面相同,都是對該界面的操作進行處理,實現自己的功能。1.定義自己的會話界面操作監聽類,實現融雲的方法

private class MyConversationClickListener implements RongIM.ConversationClickListener {

        /**
         * 當點擊用戶頭像後執行。
         *
         * @param context          上下文。
         * @param conversationType 會話類型。
         * @param user             被點擊的用戶的信息。
         * @param targetId         會話 id
         * @return 如果用戶自己處理了點擊後的邏輯處理,則返回 true,否則返回 false,false 走融雲默認處理方式。
         */
        @Override
        public boolean onUserPortraitClick(Context context, Conversation.ConversationType conversationType, UserInfo user, String targetId) {
            return false;
        }

        /**
         * 當長按用戶頭像後執行。
         *
         * @param context          上下文。
         * @param conversationType 會話類型。
         * @param user             被點擊的用戶的信息。
         * @param targetId         會話 id
         * @return 如果用戶自己處理了點擊後的邏輯處理,則返回 true,否則返回 false,false 走融雲默認處理方式。
         */
        @Override
        public boolean onUserPortraitLongClick(Context context, Conversation.ConversationType conversationType, UserInfo user, String targetId) {
            return false;
        }

        /**
         * 當點擊消息時執行。
         *
         * @param context 上下文。
         * @param view    觸發點擊的 View。
         * @param message 被點擊的消息的實體信息。
         * @return 如果用戶自己處理了點擊後的邏輯處理,則返回 true, 否則返回 false, false 走融雲默認處理方式。
         */
        @Override
        public boolean onMessageClick(Context context, View view, Message message) {
            return false;
        }

        /**
         * 當點擊鏈接消息時執行。
         *
         * @param context 上下文。
         * @param link    被點擊的鏈接。
         * @param message 被點擊的消息的實體信息
         * @return 如果用戶自己處理了點擊後的邏輯處理,則返回 true, 否則返回 false, false 走融雲默認處理方式。
         */
        @Override
        public boolean onMessageLinkClick(Context context, String link, Message message) {
            return false;
        }

        /**
         * 當長按消息時執行。
         *
         * @param context 上下文。
         * @param view    觸發點擊的 View。
         * @param message 被長按的消息的實體信息。
         * @return 如果用戶自己處理了長按後的邏輯處理,則返回 true,否則返回 false,false 走融雲默認處理方式。
         */
        @Override
        public boolean onMessageLongClick(Context context, View view, Message message) {
            return false;
        }
    }



2.在application的create中設置監聽
       

/**
         * 融雲會話界面監聽
         */
        RongIM.getInstance().setConversationClickListener(new MyConversationClickListener(this));



通過這樣設置後,針對在設置列表界面的不同操作可以進行不同的邏輯處理。

14.會話中歷史消息頭像更新


在本地更新頭像後,我們一般會refreshUserInfoCache來更新本地內存中保存的用戶信息,但是當我們進入單個會話後會發現,歷史消息中的頭像並沒有更新。
上面我們提過的,當你的用戶信息選擇第二種方式的時候出現的坑,這就是坑啊。
上面用戶信息的部分說過了,選擇第二種方式,那麼在發送消息的時候會攜帶用戶信息,這時候顯示的頭像是A,然後更新頭像爲B後,發送消息,攜帶的頭像是B,單個消息看,是沒有問題的,每個消息都顯示了正確的頭像,但是整體來看,歷史消息中應該顯示最新的頭像,這就是問題。
知道來源後,我們就要着手解決這個問題了,融雲API中提供了獲取歷史消息的接口

 

    public void getHistoryMessages(ConversationType conversationType, String targetId, int oldestMessageId
        ,int count
        ,ResultCallback<List<Message>> callback) {
            RongIMClient.getInstance().getHistoryMessages(conversationType, targetId, oldestMessageId, count, callback);
    }


conversationType表示會話類型,一般有私聊,聊天室等,我這邊是單聊。
targetId 是想獲取歷史消息攜帶的userinfo的userid。
oldestMessageId 是獲取時間距離現在最久的messageid,如果是第一次獲取的話,可以傳入-1。
count 是想獲取的message個數,我在這邊傳入的是Integer.Max_Value.
callback 中返回了獲取的所有消息。



通過這個接口,我們拿到所有的Message,然後在Message中的content中的Userinfo中修改portialUrl即可。
下面是我的項目中處理邏輯

RongIM.getInstance().getHistoryMessages(Conversation.ConversationType.PRIVATE,
                targetId,
                -1,
                Integer.MAX_VALUE,
                new RongIMClient.ResultCallback<List<Message>>() {
                    @Override
                    public void onSuccess(List<Message> messages) {
                        for (int i = 0 ; i < messages.size() ; i++){
                            UserInfo userInfo = messages.get(i).getContent().getUserInfo();
                            if (userInfo.getUserId().equals(targetId)){
                                userInfo.setPortraitUri(Uri.parse(targetImg));
                            }else if (userInfo.getUserId().equals(senderId)){
                                userInfo.setPortraitUri(Uri.parse(senderImg));
                            }
                        }
                    }

                    @Override
                    public void onError(RongIMClient.ErrorCode errorCode) {

                    }
                });



至此,算是填了上面的用戶信息選擇第二種方式的坑。

15.問題處理


賬號切換會話列表刷新會出現問題,針對我的項目,業務有三個:a.沒登錄情況下,不顯示會話列表界面
b.登錄情況下,有在融雲註冊,顯示會話列表界面
c.登錄情況下,沒在融雲註冊,不顯示會話列表界面
由於會話列表是動態加載的,如下:

ConversationListFragment conversationListFragment = new ConversationListFragment();
Uri uri = Uri.parse("rong://" + getActivity().getApplicationInfo().packageName).buildUpon()
    .appendPath("conversationlist")      
    .appendQueryParameter(Conversation.ConversationType.PRIVATE.getName(), "false") //設置私聊會話,該會話聚合顯示
    .appendQueryParameter(Conversation.ConversationType.SYSTEM.getName(), "true")//設置系統會話,該會話非聚合顯示
    .build();
    
conversationListFragment.setUri(uri);

FragmentManager fragmentManager = getChildFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.rong_container,conversationListFragment);
transaction.commit();



官方文檔中說,在退出賬號後,回到會話列表界面的時候重設Uri,然後將uri設置給fragment,再調用融雲接口onRefreshUI。

Uri uri = Uri.parse("rong://" + getActivity().getApplicationInfo().packageName).buildUpon()
    .appendPath("conversationlist")      
    .appendQueryParameter(Conversation.ConversationType.PRIVATE.getName(), "false") //設置私聊會話,該會話聚合顯示
    .appendQueryParameter(Conversation.ConversationType.SYSTEM.getName(), "true")//設置系統會話,該會話非聚合顯示
    .build();
    
conversationListFragment.setUri(uri);

conversationListFragment.onRefreshUI();



實際上並不能刷新,顯示的還是之前賬號的會話列表,或者我沒搞懂,有知道的小夥伴教我下。
在這裏我做如下處理,因爲我發現Uri創建的時候傳入的parameter可以爲空字符串,按我的理解,如果傳入空字符串,那查詢結果肯定沒有內容啊,這樣在沒登錄的時候不顯示會話列表。

ConversationListFragment conversationListFragment = new ConversationListFragment();
Uri uri = Uri.parse("rong://" + getActivity().getApplicationInfo().packageName).buildUpon()
    .appendPath("conversationlist")      
    .appendQueryParameter("", "false") 
    //本應該是私聊,這裏傳入空,那查詢肯定沒有結果,反應在界面上就是沒有內容,只有一個沒有會話的背景提示
    .build();
    
conversationListFragment.setUri(uri);

FragmentManager fragmentManager = getChildFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.rong_container,conversationListFragment);
transaction.commit();



然後在切換賬號刷新的時候做如下處理,即可實現強制刷新會話列表的效果:
可以看到會話列表界面實際上是加載到我們的frament中了,那我們可以這樣想,切換賬號後我能不能主動去判斷是否加載會話列表還是不加載會話列表或者remove掉會話列表,這樣在界面上顯示出來不就是界面更新了嗎。

if (!isLogged()){
    if (fragment != null){
        transaction.remove(fragment);//沒登錄狀態下,會話列表如果不是null,則remove掉會話列表界面
    }
}else{
    fragment = new ConversationListFragment();
     Uri uri = Uri.parse("rong://" + getContext().getApplicationInfo().packageName).buildUpon()
             .appendPath("conversationlist")
             .appendQueryParameter(Conversation.ConversationType.PRIVATE.getName(), "false") 
             .build();
     fragment.setUri(uri);  //設置 ConverssationListFragment 的顯示屬性
     transaction.add(R.id.rong_content, fragment);
}
//根據登錄狀態的不同,進行不同邏輯處理
transaction.commit();


這樣就實現了不同賬號登錄後,會話列表的強制刷新了。
不過這有個問題就是如果沒有登錄的情況下,整個會話列表的背景是空白的,這個需要改下。
通過閱讀demo源碼可知,融雲在demo中的處理是添加了個layout,在會話列表有內容的情況下是不顯示的狀態。
所以,在我們的代碼中也加上這部分代碼就可以了,如下:

<FrameLayout
        android:id="@+id/rong_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

        <LinearLayout
            android:id="@+id/conversation_empty"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:orientation="vertical"
            android:visibility="visible">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/rc_conversation_list_empty" />

            <TextView
                android:id="@+id/rc_empty_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/rc_conversation_list_empty_prompt"
                android:textColor="#999999"
                android:textSize="14sp" />
        </LinearLayout>
    </FrameLayout>


尊重作者勞動成果:https://blog.csdn.net/jinlu7611/article/details/90055868

發佈了56 篇原創文章 · 獲贊 20 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章