1. 程式人生 > >android-使用環信SDK開發即時通訊功能及原始碼下載

android-使用環信SDK開發即時通訊功能及原始碼下載

最近專案中整合即時聊天功能,挑來揀去,最終選擇環信SDK來進行開發,選擇環信的主要原因是介面方便、簡潔,說明文件清晰易懂。文件有android、ios、和後臺伺服器端,還是非常全的。
環信官網:http://www.easemob.com/

本篇文章目的主要在於說明環信Demo如何實現即時通訊的。我在整合環信SDK到我們自己開發的app之前,研究了一下環信demo的程式碼,看了兩三天的樣子,基本搞清楚來龍去脈,但是隻是清楚來龍去脈,要說到裡面的細節可能得深一步研究,但是這就夠了,已經可以把demo裡面的功能整合到我們自己的app中了。所以本篇文章就說明一下如何整合環信到自己的app中。

整合起來還是比較快的,最多一週時間整合就搞定了。我們是有自己的使用者體系的,所以我們採用的是將環信與現有的APP使用者體系整合。
這裡寫圖片描述

整合之前,必然要到上面這個頁面進行了解,如何整合,在這裡說明了如何整合的方案,這個方案的選擇就需要你自己根據已有的需求進行選擇了。這個就不多說了,應該都明白。

登 錄 原 理
我們的方案是將環信與現有的APP使用者體系整合!也就是說我們的伺服器需要把現有的使用者在後臺註冊到環信伺服器中,然後app登入的時候自動登入環信伺服器,然後使用環信的即時通訊功能。
這就意味著使用者登入app的時候,需要登入兩次,一次是我們的應用伺服器,一次是環信伺服器,只不過給使用者的感覺是登入了一次,而環信伺服器的登入是程式碼中控制的,使用者看不到也感覺不到。

好友體系原理
登入之後,就是獲取好友和群組了,環信增加了聊天室的功能,有點類似於鬆群組的功能,只不過聊天室更加隨意些。群組大家都明白,不多說,聊天室呢不同,開放的公共的聊天室,成員可以隨時進入聊天隨時離開,離開之後自動不再收到聊天資訊。
好友體系中環信是可以進行管理的,當然也可以不使用環信的好友管理體系,而使用應用伺服器來進行好友的管理工作。我們專案中使用的是環信的好友管理體系,主要是方便,不過也不見得省了多少事兒,因為應用伺服器使用者體系的變更,都要由伺服器把該使用者體系的關係的變更通知環信伺服器,然環信伺服器也進行更改,從而保持應用伺服器和環信伺服器使用者體系的一致性。所以大家整合過程中需要自己考慮代價。我們專案中使用環信管理好友體系主要在於app端方便,app端也不進行使用者體系的變更,複雜的操作都在伺服器端實現,所以app端方便實現、開發簡單。

使用者暱稱、頭像
環信伺服器採用了低浸入的方式開發即時通訊,也就是說它不儲存使用者的資訊,也不訪問使用者的資訊,這就意味著使用者的暱稱、頭像等等資訊環信是沒有儲存的,開發者無法通過環信獲取使用者資訊。所以環信專門對與使用者的暱稱、頭像資訊給出瞭解決方案。
這裡寫圖片描述

方法一 從APP伺服器獲取暱稱和頭像
方法二 從訊息擴充套件中獲取暱稱和頭像
暱稱或頭像處理的方法一和方法二區別:
方法一:在傳送訊息時不含有任何擴充套件,收訊息時如果本地不存在傳送人的使用者資訊則需要從APP伺服器查詢傳送人的暱稱和頭像的URL。
方法二:在傳送訊息時帶有包含暱稱和頭像URL的訊息擴充套件,收到訊息時即可從訊息擴充套件中取出,不需要再去APP伺服器獲取, 方法二和方法一相比
優點:收到訊息立即顯示暱稱不用等待APP伺服器返回資料後顯示。
缺點:每條訊息都要帶有擴充套件,增加訊息體積,每次發訊息都有一些不必要的資料。
上面是環信給出的使用者暱稱和頭像的兩種解決方案。這兩種解決方案大家一看就應用明白了,不多說。主要說說我們專案中的解決方案,採用第一種方案,從應用伺服器獲取,儲存本地資料庫,之後,查詢操作就是本地操作,那就會有問題了,使用者關係更新或者資訊更新呢?這個問題主要解決方法是使用者好友體系的每次更新都會同時更新使用者暱稱和頭像,然後更新本地資料庫來解決這個問題。

到此,這三個問題明白之後,基本就可以開始進行開發了,你可能會說,還沒有說明即時通訊呢?最主要的就是即時通訊怎麼沒有說明呢?這個問題大家勿急,後面會有!^_^

開 發
開發過程,首先就是要研究一下環信demo的程式碼,裡面已經進行了封裝,所以把環信demo的程式碼看懂,利用的好的程式碼完全可以應用到現有的app中。
這裡寫圖片描述
這個環信demo的程式碼,匯入手機直接執行,註冊,用著非常好,程式碼執行正常,功能也正常,所以研究這個程式碼之後,再整合到自己的app中那就so easy!!

demo裡面用到了幾個jar包,主要是環信的sdk、百度地圖、友盟資料分析、百度地圖定位、圖片載入等這幾個jar包,百度地圖這個應該沒什麼說的,之前我們app裡面整合過,不過有點舊,這次順帶著把百度地圖也更新成最新的了,目前百度地圖最新的挺好用的。也算是教訓,就是實時更新所應用的第三方的jar!別的jar就沒什麼說的了。
下面就是demo裡面的分包了,demo裡面的分包比較多,不過從分包的名字可以看出每個包下面的程式碼是什麼作用了。我主要看的是activity包下面的每個類,因為activity類就是一個個的介面,其他的都是為這個activity類服務的程式碼工具類,所以主要看這個就可以了。
這裡寫圖片描述
activity包下面的類比較多,不過我們關心的類只有幾個而已,ChatActivity.java類就是即時聊天的介面,這個一定是要整合到自己的app當中的。其他的三個ContactlistFragment.java、ChatAllHistoryFragment.java、GroupsActivity.java這三個類分別是聯絡人介面、回話歷史介面、群組介面。這三個需要根據自己app的需求進行整合。所以主要研究的工作就是放在這幾個類上。

MainActivity.java就是主介面,主介面集成了上面三個介面,由主介面進行管理介面的顯示。

剩下的工作沒什麼特別的了,搞不明白程式碼的可以給我留言,相互交流一下。
特別提一下下面的幾個類
這裡寫圖片描述
這個幾個類有點繞!剛開始著實弄混了。現在看來demo裡面程式碼也是用心良苦呀!!
1、先看controller包下面的HXSDKHelper.java類,再看chatuidemo包下面的DemoHXSDKHelper.java類,明顯是繼承關係!後者才是demo中使用的物件類。並且該父類在controller包下,明顯是控制資訊管理類,開啟該類檢視程式碼
這裡寫圖片描述
從說明可以看出該類的作用了。

2、再看HXSDKModel.java類,這類名字就是模版類,還有DefaultHXSDKModel.java類和DemoSDKModel.java類,也很明視訊記憶體在繼承關係。完成的功能主要是app當中即時通訊的一些資料的儲存和控制資訊顯示資訊等。
這幾個類搞清楚之後基本就沒有什麼打的問題了。

主要程式碼講解
1、主類MainActivity.java

public class MainActivity extends BaseActivity implements EMEventListener 

該類實現了EMEventListener 介面,就一個方法如下:

/**
     * 監聽事件
     */
    @Override
    public void onEvent(EMNotifierEvent event) {
        switch (event.getEvent()) {
        case EventNewMessage: // 普通訊息
        {
            EMMessage message = (EMMessage) event.getData();
            // 提示新訊息
            HXSDKHelper.getInstance().getNotifier().onNewMsg(message);
            refreshUI();
            break;
        }
        case EventOfflineMessage: {
            refreshUI();
            break;
        }
        case EventConversationListChanged: {
            refreshUI();
            break;
        }

        default:
            break;
        }
    }

主要就是監聽新訊息、離線訊息、回話訊息變化等,然後更新介面refreshUI(),更新介面就是重新整理未讀訊息數、重新整理聯絡人列表,回話列表等。

在主介面初始化中註冊了三個監聽器,如下程式碼:

private void init() {
        // setContactListener監聽聯絡人的變化等
        EMContactManager.getInstance().setContactListener(new MyContactListener());
        // 註冊一個監聽連線狀態的listener
        connectionListener = new MyConnectionListener();
        EMChatManager.getInstance().addConnectionListener(connectionListener);

        groupChangeListener = new MyGroupChangeListener();
        // 註冊群聊相關的listener
    }

這三個監聽器就是監聽聯絡人變化、群組變化、與環信伺服器連結變化的監聽器,這三者的變化都會回撥這三個監聽器裡面的相應的方法,方便開發者通過相應的方法採取相應的措施。
這三個監聽器demo中程式碼比較詳細,在此就不多說了。

2 聯絡人列表ContactlistFragment.java類

/**
 * 聯絡人列表頁
 */
public class ContactlistFragment extends Fragment {
    public static final String TAG = "ContactlistFragment";
    private ContactAdapter adapter;
    private List<User> contactList;
    private ListView listView;
    private boolean hidden;
    private Sidebar sidebar;
    private InputMethodManager inputMethodManager;
    private List<String> blackList;
    ImageButton clearSearch;
    EditText query;
    HXContactSyncListener contactSyncListener;
    HXBlackListSyncListener blackListSyncListener;
    View progressBar;
    Handler handler = new Handler();
    private User toBeProcessUser;
    private String toBeProcessUsername;

    /**
     * 這裡註冊了兩個監聽器,目的在於同步聯絡人資訊
     * 當聯絡人發生變化、黑名單發生變化,通知這裡註冊的監聽器
     * 進而重新整理介面
     *
     */
    class HXContactSyncListener implements HXSDKHelper.HXSyncListener {
        @Override
        public void onSyncSucess(final boolean success) {
            EMLog.d(TAG, "on contact list sync success:" + success);
            ContactlistFragment.this.getActivity().runOnUiThread(new Runnable() {
                public void run() {
                    getActivity().runOnUiThread(new Runnable(){
                        @Override
                        public void run() {
                            if(success){
                                progressBar.setVisibility(View.GONE);
                                refresh();
                            }else{
                                String s1 = getResources().getString(R.string.get_failed_please_check);
                                Toast.makeText(getActivity(), s1, 1).show();
                                progressBar.setVisibility(View.GONE);
                            }
                        }

                    });
                }
            });
        }
    }

    class HXBlackListSyncListener implements HXSyncListener{
        @Override
        public void onSyncSucess(boolean success) {
            getActivity().runOnUiThread(new Runnable(){
                @Override
                public void run() {
                    blackList = EMContactManager.getInstance().getBlackListUsernames();
                    refresh();
                }
            });
        }

    };

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_contact_list, container, false);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        //防止被T後,沒點確定按鈕然後按了home鍵,長期在後臺又進app導致的crash
        if(savedInstanceState != null && savedInstanceState.getBoolean("isConflict", false))
            return;
        inputMethodManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
        listView = (ListView) getView().findViewById(R.id.list);
        sidebar = (Sidebar) getView().findViewById(R.id.sidebar);
        sidebar.setListView(listView);

        //黑名單列表
        blackList = EMContactManager.getInstance().getBlackListUsernames();
        contactList = new ArrayList<User>();
        // 獲取設定contactlist
        getContactList();
        //搜尋框
        query = (EditText) getView().findViewById(R.id.query);
        query.setHint(R.string.search);
        clearSearch = (ImageButton) getView().findViewById(R.id.search_clear);
        query.addTextChangedListener(new TextWatcher() {
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                adapter.getFilter().filter(s);
                if (s.length() > 0) {
                    clearSearch.setVisibility(View.VISIBLE);
                } else {
                    clearSearch.setVisibility(View.INVISIBLE);
                }
            }
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }
            public void afterTextChanged(Editable s) {
            }
        });
        clearSearch.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                query.getText().clear();
                hideSoftKeyboard();
            }
        });

        // 設定adapter
        adapter = new ContactAdapter(getActivity(), R.layout.row_contact, contactList);
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                String username = adapter.getItem(position).getUsername();
                if (Constant.NEW_FRIENDS_USERNAME.equals(username)) {
                    // 進入申請與通知頁面
                    User user = DemoApplication.getInstance().getContactList().get(Constant.NEW_FRIENDS_USERNAME);
                    user.setUnreadMsgCount(0);
                    startActivity(new Intent(getActivity(), NewFriendsMsgActivity.class));
                } else if (Constant.GROUP_USERNAME.equals(username)) {
                    // 進入群聊列表頁面
                    startActivity(new Intent(getActivity(), GroupsActivity.class));
                } else if(Constant.CHAT_ROOM.equals(username)){
                    //進入聊天室列表頁面
                    startActivity(new Intent(getActivity(), PublicChatRoomsActivity.class));
                }else {
                    // demo中直接進入聊天頁面,實際一般是進入使用者詳情頁
                    startActivity(new Intent(getActivity(), ChatActivity.class).putExtra("userId", adapter.getItem(position).getUsername()));
                }
            }
        });
        listView.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // 隱藏軟鍵盤
                if (getActivity().getWindow().getAttributes().softInputMode
                        != WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) {
                    if (getActivity().getCurrentFocus() != null)
                        inputMethodManager.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(),
                                InputMethodManager.HIDE_NOT_ALWAYS);
                }
                return false;
            }
        });

        ImageView addContactView = (ImageView) getView().findViewById(R.id.iv_new_contact);
        // 進入新增好友頁
        addContactView.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                startActivity(new Intent(getActivity(), AddContactActivity.class));
            }
        });
        registerForContextMenu(listView);

        progressBar = (View) getView().findViewById(R.id.progress_bar);

        contactSyncListener = new HXContactSyncListener();
        HXSDKHelper.getInstance().addSyncContactListener(contactSyncListener);

        blackListSyncListener = new HXBlackListSyncListener();
        HXSDKHelper.getInstance().addSyncBlackListListener(blackListSyncListener);

        if (!HXSDKHelper.getInstance().isContactsSyncedWithServer()) {
            progressBar.setVisibility(View.VISIBLE);
        } else {
            progressBar.setVisibility(View.GONE);
        }
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        if (((AdapterContextMenuInfo) menuInfo).position > 2) {
            toBeProcessUser = adapter.getItem(((AdapterContextMenuInfo) menuInfo).position);
            toBeProcessUsername = toBeProcessUser.getUsername();
            getActivity().getMenuInflater().inflate(R.menu.context_contact_list, menu);
        }
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.delete_contact) {
            try {
                // 刪除此聯絡人
                deleteContact(toBeProcessUser);
                // 刪除相關的邀請訊息
                InviteMessgeDao dao = new InviteMessgeDao(getActivity());
                dao.deleteMessage(toBeProcessUser.getUsername());
            } catch (Exception e) {
                e.printStackTrace();
            }
            return true;
        }else if(item.getItemId() == R.id.add_to_blacklist){
            moveToBlacklist(toBeProcessUsername);
            return true;
        }
        return super.onContextItemSelected(item);
    }

    /**
     * 當該Fragment物件改變了隱藏狀態(由isHidden()方法返回)時,系統會呼叫這個方法。
     * Fragment初始是不隱藏的,只要Fragment物件改變了它的顯示狀態,就會呼叫該方法。
     * 引數hidden 如果該Fragment物件現在是隱藏的,則該引數是true,否則是false。
     */
    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        this.hidden = hidden;
        if (!hidden) {
            refresh();
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (!hidden) {
            refresh();
        }
    }

    /**
     * 刪除聯絡人
     * 
     * @param toDeleteUser
     */
    public void deleteContact(final User tobeDeleteUser) {
        String st1 = getResources().getString(R.string.deleting);
        final String st2 = getResources().getString(R.string.Delete_failed);
        final ProgressDialog pd = new ProgressDialog(getActivity());
        pd.setMessage(st1);
        pd.setCanceledOnTouchOutside(false);
        pd.show();
        new Thread(new Runnable() {
            public void run() {
                try {
                    EMContactManager.getInstance().deleteContact(tobeDeleteUser.getUsername());
                    // 刪除db和記憶體中此使用者的資料
                    UserDao dao = new UserDao(getActivity());
                    dao.deleteContact(tobeDeleteUser.getUsername());
                    DemoApplication.getInstance().getContactList().remove(tobeDeleteUser.getUsername());
                    getActivity().runOnUiThread(new Runnable() {
                        public void run() {
                            pd.dismiss();
                            adapter.remove(tobeDeleteUser);
                            adapter.notifyDataSetChanged();

                        }
                    });
                } catch (final Exception e) {
                    getActivity().runOnUiThread(new Runnable() {
                        public void run() {
                            pd.dismiss();
                            Toast.makeText(getActivity(), st2 + e.getMessage(), 1).show();
                        }
                    });

                }

            }
        }).start();

    }

    /**
     * 把user移入到黑名單
     */
    private void moveToBlacklist(final String username){
        final ProgressDialog pd = new ProgressDialog(getActivity());
        String st1 = getResources().getString(R.string.Is_moved_into_blacklist);
        final String st2 = getResources().getString(R.string.Move_into_blacklist_success);
        final String st3 = getResources().getString(R.string.Move_into_blacklist_failure);
        pd.setMessage(st1);
        pd.setCanceledOnTouchOutside(false);
        pd.show();
        new Thread(new Runnable() {
            public void run() {
                try {
                    //加入到黑名單
                    EMContactManager.getInstance().addUserToBlackList(username,false);
                    getActivity().runOnUiThread(new Runnable() {
                        public void run() {
                            pd.dismiss();
                            Toast.makeText(getActivity(), st2, 0).show();
                            refresh();
                        }
                    });
                } catch (EaseMobException e) {
                    e.printStackTrace();
                    getActivity().runOnUiThread(new Runnable() {
                        public void run() {
                            pd.dismiss();
                            Toast.makeText(getActivity(), st3, 0).show();
                        }
                    });
                }
            }
        }).start();

    }

    // 重新整理ui
    public void refresh() {
        try {
            // 可能會在子執行緒中調到這方法
            getActivity().runOnUiThread(new Runnable() {
                public void run() {
                    getContactList();
                    adapter.notifyDataSetChanged();
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onDestroy() {
        if (contactSyncListener != null) {
            HXSDKHelper.getInstance().removeSyncContactListener(contactSyncListener);
            contactSyncListener = null;
        }
        if(blackListSyncListener != null){
            HXSDKHelper.getInstance().removeSyncBlackListListener(blackListSyncListener);
        }
        super.onDestroy();
    }

    public void showProgressBar(boolean show) {
        if (progressBar != null) {
            if (show) {
                progressBar.setVisibility(View.VISIBLE);
            } else {
                progressBar.setVisibility(View.GONE);
            }
        }
    }

    /**
     * 獲取聯絡人列表,並過濾掉黑名單和排序
     */
    private void getContactList() {
        contactList.clear();
        //獲取本地好友列表
        Map<String, User> users = DemoApplication.getInstance().getContactList();
        Iterator<Entry<String, User>> iterator = users.entrySet().iterator();
        while (iterator.hasNext()) {
            Entry<String, User> entry = iterator.next();
            if (!entry.getKey().equals(Constant.NEW_FRIENDS_USERNAME)
                    && !entry.getKey().equals(Constant.GROUP_USERNAME)
                    && !entry.getKey().equals(Constant.CHAT_ROOM)
                    && !blackList.contains(entry.getKey())){
                EMLog.i(TAG, "獲取聯絡人="+entry.getValue());
                contactList.add(entry.getValue());
            }
        }
        // 排序
        Collections.sort(contactList, new Comparator<User>() {

            @Override
            public int compare(User lhs, User rhs) {
                return lhs.getUsername().compareTo(rhs.getUsername());
            }
        });

        // 加入"群聊"和"聊天室"
        if(users.get(Constant.CHAT_ROOM) != null)
            contactList.add(0, users.get(Constant.CHAT_ROOM));
        if(users.get(Constant.GROUP_USERNAME) != null)
            contactList.add(0, users.get(Constant.GROUP_USERNAME));

        // 把"申請與通知"新增到首位
        if(users.get(Constant.NEW_FRIENDS_USERNAME) != null)
            contactList.add(0, users.get(Constant.NEW_FRIENDS_USERNAME));
    }

    void hideSoftKeyboard() {
        if (getActivity().getWindow().getAttributes().softInputMode != 
                WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) {
            if (getActivity().getCurrentFocus() != null)
                inputMethodManager.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(),
                        InputMethodManager.HIDE_NOT_ALWAYS);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if(((MainActivity)getActivity()).isConflict){
            outState.putBoolean("isConflict", true);
        }else if(((MainActivity)getActivity()).getCurrentAccountRemoved()){
            outState.putBoolean(Constant.ACCOUNT_REMOVED, true);
        }

    }
}

上面聯絡人類中的註冊的監聽器使用的就是觀察者模式,先看HXSDKHelper.java中的部分程式碼

public void addSyncGroupListener(HXSyncListener listener) {
        if (listener == null) {
            return;
        }
        if (!syncGroupsListeners.contains(listener)) {
            syncGroupsListeners.add(listener);
        }
    }

    public void removeSyncGroupListener(HXSyncListener listener) {
        if (listener == null) {
            return;
        }
        if (syncGroupsListeners.contains(listener)) {
            syncGroupsListeners.remove(listener);
        }
    }

    public void addSyncContactListener(HXSyncListener listener) {
        if (listener == null) {
            return;
        }
        if (!syncContactsListeners.contains(listener)) {
            syncContactsListeners.add(listener);
        }
    }

    public void removeSyncContactListener(HXSyncListener listener) {
        if (listener == null) {
            return;
        }
        if (syncContactsListeners.contains(listener)) {
            syncContactsListeners.remove(listener);
        }
    }

    public void addSyncBlackListListener(HXSyncListener listener) {
        if (listener == null) {
            return;
        }
        if (!syncBlackListListeners.contains(listener)) {
            syncBlackListListeners.add(listener);
        }
    }

    public void removeSyncBlackListListener(HXSyncListener listener) {
        if (listener == null) {
            return;
        }
        if (syncBlackListListeners.contains(listener)) {
            syncBlackListListeners.remove(listener);
        }
    }
    public void noitifyGroupSyncListeners(boolean success){
        for (HXSyncListener listener : syncGroupsListeners) {
            listener.onSyncSucess(success);
        }
    }
   public void notifyContactsSyncListener(boolean success){
        for (HXSyncListener listener : syncContactsListeners) {
            listener.onSyncSucess(success);
        }
    }
     public void notifyBlackListSyncListener(boolean success){
        for (HXSyncListener listener : syncBlackListListeners) {
            listener.onSyncSucess(success);
        }
    }

這部分程式碼控制著觀察者,新增、刪除、通知每一個觀察者,當群組、好友、黑名單 通過環信伺服器同步到客戶端之後,notify每個觀察者,然後觀察者接收到之後,重新整理UI。這裡就是觀察者模式的經典應用!!!
聯絡人列表看懂之後,其他的群組介面和回話歷史介面就不多說了。

3 聊天介面ChatActivity.java
這個類比較龐大,因為demo裡面把單聊和群聊、聊天室都整合到這一個介面中完成,程式碼很龐大,但是不影響最終的整合,直接整合該類就可以實現功能。不多說。

附上介面:
這裡寫圖片描述
圖一 回話歷史介面

這裡寫圖片描述
圖二 通訊錄介面好友

這裡寫圖片描述
圖三 設定介面

這裡寫圖片描述
圖四 聊天介面

最後附上原始碼下載
其他的就沒有什麼特別需要指出的地方了,大家如果有什麼問題或者疑問,都可以留言交流!【握手】!

2016年5月17日更新
環信官方網站已經發布IM3.0版本。目前開發的一個app採用的就是IM3.0版本。
整體介面沒發生大的變化,功能也都一樣。但是在官方給的demo程式碼上優化很多,方便很多。不過得大概看懂裡面的程式碼。如果是高手的話,半天就應該能整合好環信的即時通訊功能。
本文給出的下載連結,是IM2.0版本。所以如果想要使用IM3.0的版本的,需要到官網下載。

對於新手來說,環信官網給出的demo是可以直接使用的。人家給出的是完整的app程式碼。新手就疑惑,不知道該如何入手整合即時通訊功能?
其實很簡單!
首先,把環信官網給出的依賴包和動態庫新增到自己的工程中。目前官網給出的依賴包和動態庫分為包含語音視訊通話功能的和不包括語音視訊通話功能的。大家根據自己的APP的功能新增。
然後,把demo裡面的聊天介面直接複製到自己的功能裡面,此時複製進去以後,會出現大量的錯誤!因為聊天介面關聯了很多demo中的其他類,所以,要把其他類複製到自己的工程中。記得不要忘記佈局檔案、資源圖片檔案、字串等等資原始檔! 建議在自己的工程中,新建一個包專門放環信的類。因為你要複製的類非常多!大概有二三十個!
最後,向即時通訊程式碼填充資料。主要有幾部分:

    1)application類中環信helper類完成初始化操作。 
    2)登入app介面做好登入環信伺服器操作。需要登入環信的登入名和密碼。這裡的環信登入與登入app 
    不同。APP登入是應用伺服器的使用者,使用者名稱和密碼在應用伺服器。而登入環信是環信的登入名和密碼, 
    需要先註冊到環信伺服器才行。註冊操作可以在應用伺服器提前做好。APP登入應用伺服器的時候順帶著 
    登入環信伺服器即可。 
    3)獲取好友資訊。這裡要分為好友資訊的維護是應用伺服器維護還是環信幫助你維護。這個我就不多說 
    了。環信官網有說明。 
    4)本地維護好友列表和聊天資訊裡列表。聊天資訊列表在環信中已經不讓開發者編輯和改變了。該功能 
    已經整合在了依賴的環信的包中了。好友列表在demo中給出了簡單的資料表。開發者可以自己根據APP 
    需要開發和擴充套件。 
    5)退出APP。退出APP時務必呼叫helper類的logout方法。這樣以後,先前登入的使用者就從APP上退 
    出了環信的伺服器。開發者要注意,我這裡說的退出時指APP使用者手動退出,不是使用者按手機返回按鈕或 
    者返回主介面按鈕導致APP退出。而是APP中的退出按鈕,當前登入使用者退出APP。如果是使用者按返回按 
    鈕或回主介面按鈕,返回到手機桌面的,沒必要呼叫helper類的logout方法。

先說這麼多,大家還有什麼問題,大家留言交流~~