亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

android-使用環(huán)信SDK開(kāi)發(fā)即時(shí)通信功能(附源碼下載)

 更新時(shí)間:2016年12月02日 14:38:15   作者:龍吟在天  
本篇文章主要介紹了android-使用環(huán)信SDK開(kāi)發(fā)即時(shí)通信功能,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。

最近項(xiàng)目中集成即時(shí)聊天功能,挑來(lái)揀去,最終選擇環(huán)信SDK來(lái)進(jìn)行開(kāi)發(fā),選擇環(huán)信的主要原因是接口方便、簡(jiǎn)潔,說(shuō)明文檔清晰易懂。文檔有Android、iOS、和后臺(tái)服務(wù)器端,還是非常全的。 

環(huán)信官網(wǎng):http://www.easemob.com/

本篇文章目的主要在于說(shuō)明環(huán)信Demo如何實(shí)現(xiàn)即時(shí)通信的。我在集成環(huán)信SDK到我們自己開(kāi)發(fā)的app之前,研究了一下環(huán)信demo的代碼,看了兩三天的樣子,基本搞清楚來(lái)龍去脈,但是只是清楚來(lái)龍去脈,要說(shuō)到里面的細(xì)節(jié)可能得深一步研究,但是這就夠了,已經(jīng)可以把demo里面的功能集成到我們自己的app中了。所以本篇文章就說(shuō)明一下如何集成環(huán)信到自己的app中。

集成起來(lái)還是比較快的,最多一周時(shí)間集成就搞定了。我們是有自己的用戶(hù)體系的,所以我們采用的是將環(huán)信與現(xiàn)有的APP用戶(hù)體系集成。

這里寫(xiě)圖片描述

集成之前,必然要到上面這個(gè)頁(yè)面進(jìn)行了解,如何集成,在這里說(shuō)明了如何集成的方案,這個(gè)方案的選擇就需要你自己根據(jù)已有的需求進(jìn)行選擇了。這個(gè)就不多說(shuō)了,應(yīng)該都明白。

登 錄 原 理 

我們的方案是將環(huán)信與現(xiàn)有的APP用戶(hù)體系集成!也就是說(shuō)我們的服務(wù)器需要把現(xiàn)有的用戶(hù)在后臺(tái)注冊(cè)到環(huán)信服務(wù)器中,然后app登錄的時(shí)候自動(dòng)登錄環(huán)信服務(wù)器,然后使用環(huán)信的即時(shí)通信功能。 

這就意味著用戶(hù)登錄app的時(shí)候,需要登錄兩次,一次是我們的應(yīng)用服務(wù)器,一次是環(huán)信服務(wù)器,只不過(guò)給用戶(hù)的感覺(jué)是登錄了一次,而環(huán)信服務(wù)器的登錄是代碼中控制的,用戶(hù)看不到也感覺(jué)不到。

好友體系原理 

登錄之后,就是獲取好友和群組了,環(huán)信增加了聊天室的功能,有點(diǎn)類(lèi)似于松群組的功能,只不過(guò)聊天室更加隨意些。群組大家都明白,不多說(shuō),聊天室呢不同,開(kāi)放的公共的聊天室,成員可以隨時(shí)進(jìn)入聊天隨時(shí)離開(kāi),離開(kāi)之后自動(dòng)不再收到聊天信息。 

好友體系中環(huán)信是可以進(jìn)行管理的,當(dāng)然也可以不使用環(huán)信的好友管理體系,而使用應(yīng)用服務(wù)器來(lái)進(jìn)行好友的管理工作。我們項(xiàng)目中使用的是環(huán)信的好友管理體系,主要是方便,不過(guò)也不見(jiàn)得省了多少事兒,因?yàn)閼?yīng)用服務(wù)器用戶(hù)體系的變更,都要由服務(wù)器把該用戶(hù)體系的關(guān)系的變更通知環(huán)信服務(wù)器,然環(huán)信服務(wù)器也進(jìn)行更改,從而保持應(yīng)用服務(wù)器和環(huán)信服務(wù)器用戶(hù)體系的一致性。所以大家集成過(guò)程中需要自己考慮代價(jià)。我們項(xiàng)目中使用環(huán)信管理好友體系主要在于app端方便,app端也不進(jìn)行用戶(hù)體系的變更,復(fù)雜的操作都在服務(wù)器端實(shí)現(xiàn),所以app端方便實(shí)現(xiàn)、開(kāi)發(fā)簡(jiǎn)單。

用戶(hù)昵稱(chēng)、頭像 

環(huán)信服務(wù)器采用了低浸入的方式開(kāi)發(fā)即時(shí)通信,也就是說(shuō)它不保存用戶(hù)的信息,也不訪問(wèn)用戶(hù)的信息,這就意味著用戶(hù)的昵稱(chēng)、頭像等等信息環(huán)信是沒(méi)有保存的,開(kāi)發(fā)者無(wú)法通過(guò)環(huán)信獲取用戶(hù)信息。所以環(huán)信專(zhuān)門(mén)對(duì)與用戶(hù)的昵稱(chēng)、頭像信息給出了解決方案。

 這里寫(xiě)圖片描述

方法一 從APP服務(wù)器獲取昵稱(chēng)和頭像 

方法二 從消息擴(kuò)展中獲取昵稱(chēng)和頭像 

昵稱(chēng)或頭像處理的方法一和方法二區(qū)別: 

方法一:在發(fā)送消息時(shí)不含有任何擴(kuò)展,收消息時(shí)如果本地不存在發(fā)送人的用戶(hù)信息則需要從APP服務(wù)器查詢(xún)發(fā)送人的昵稱(chēng)和頭像的URL。 

方法二:在發(fā)送消息時(shí)帶有包含昵稱(chēng)和頭像URL的消息擴(kuò)展,收到消息時(shí)即可從消息擴(kuò)展中取出,不需要再去APP服務(wù)器獲取, 方法二和方法一相比 

優(yōu)點(diǎn):收到消息立即顯示昵稱(chēng)不用等待APP服務(wù)器返回?cái)?shù)據(jù)后顯示。 

缺點(diǎn):每條消息都要帶有擴(kuò)展,增加消息體積,每次發(fā)消息都有一些不必要的數(shù)據(jù)。 

上面是環(huán)信給出的用戶(hù)昵稱(chēng)和頭像的兩種解決方案。這兩種解決方案大家一看就應(yīng)用明白了,不多說(shuō)。主要說(shuō)說(shuō)我們項(xiàng)目中的解決方案,采用第一種方案,從應(yīng)用服務(wù)器獲取,保存本地?cái)?shù)據(jù)庫(kù),之后,查詢(xún)操作就是本地操作,那就會(huì)有問(wèn)題了,用戶(hù)關(guān)系更新或者信息更新呢?這個(gè)問(wèn)題主要解決方法是用戶(hù)好友體系的每次更新都會(huì)同時(shí)更新用戶(hù)昵稱(chēng)和頭像,然后更新本地?cái)?shù)據(jù)庫(kù)來(lái)解決這個(gè)問(wèn)題。

到此,這三個(gè)問(wèn)題明白之后,基本就可以開(kāi)始進(jìn)行開(kāi)發(fā)了,你可能會(huì)說(shuō),還沒(méi)有說(shuō)明即時(shí)通信呢?最主要的就是即時(shí)通信怎么沒(méi)有說(shuō)明呢?這個(gè)問(wèn)題大家勿急,后面會(huì)有!^_^

開(kāi) 發(fā) 

開(kāi)發(fā)過(guò)程,首先就是要研究一下環(huán)信demo的代碼,里面已經(jīng)進(jìn)行了封裝,所以把環(huán)信demo的代碼看懂,利用的好的代碼完全可以應(yīng)用到現(xiàn)有的app中。

這里寫(xiě)圖片描述 

這個(gè)環(huán)信demo的代碼,導(dǎo)入手機(jī)直接運(yùn)行,注冊(cè),用著非常好,代碼運(yùn)行正常,功能也正常,所以研究這個(gè)代碼之后,再集成到自己的app中那就so easy!!

demo里面用到了幾個(gè)jar包,主要是環(huán)信的sdk、百度地圖、友盟數(shù)據(jù)分析、百度地圖定位、圖片加載等這幾個(gè)jar包,百度地圖這個(gè)應(yīng)該沒(méi)什么說(shuō)的,之前我們app里面集成過(guò),不過(guò)有點(diǎn)舊,這次順帶著把百度地圖也更新成最新的了,目前百度地圖最新的挺好用的。也算是教訓(xùn),就是實(shí)時(shí)更新所應(yīng)用的第三方的jar!別的jar就沒(méi)什么說(shuō)的了。 

下面就是demo里面的分包了,demo里面的分包比較多,不過(guò)從分包的名字可以看出每個(gè)包下面的代碼是什么作用了。我主要看的是activity包下面的每個(gè)類(lèi),因?yàn)閍ctivity類(lèi)就是一個(gè)個(gè)的界面,其他的都是為這個(gè)activity類(lèi)服務(wù)的代碼工具類(lèi),所以主要看這個(gè)就可以了。

這里寫(xiě)圖片描述 

activity包下面的類(lèi)比較多,不過(guò)我們關(guān)心的類(lèi)只有幾個(gè)而已,ChatActivity.Java類(lèi)就是即時(shí)聊天的界面,這個(gè)一定是要集成到自己的app當(dāng)中的。其他的三個(gè)ContactlistFragment.java、ChatAllHistoryFragment.java、GroupsActivity.java這三個(gè)類(lèi)分別是聯(lián)系人界面、回話歷史界面、群組界面。這三個(gè)需要根據(jù)自己app的需求進(jìn)行集成。所以主要研究的工作就是放在這幾個(gè)類(lèi)上。

MainActivity.java就是主界面,主界面集成了上面三個(gè)界面,由主界面進(jìn)行管理界面的顯示。

剩下的工作沒(méi)什么特別的了,搞不明白代碼的可以給我留言,相互交流一下。 

特別提一下下面的幾個(gè)類(lèi)

這里寫(xiě)圖片描述 

這個(gè)幾個(gè)類(lèi)有點(diǎn)繞!剛開(kāi)始著實(shí)弄混了。現(xiàn)在看來(lái)demo里面代碼也是用心良苦呀??!

1、先看controller包下面的HXSDKHelper.java類(lèi),再看chatuidemo包下面的DemoHXSDKHelper.java類(lèi),明顯是繼承關(guān)系!后者才是demo中使用的對(duì)象類(lèi)。并且該父類(lèi)在controller包下,明顯是控制信息管理類(lèi),打開(kāi)該類(lèi)查看代碼

這里寫(xiě)圖片描述 

從說(shuō)明可以看出該類(lèi)的作用了。

2、再看HXSDKModel.java類(lèi),這類(lèi)名字就是模版類(lèi),還有DefaultHXSDKModel.java類(lèi)和DemoSDKModel.java類(lèi),也很明顯存在繼承關(guān)系。完成的功能主要是app當(dāng)中即時(shí)通信的一些數(shù)據(jù)的保存和控制信息顯示信息等。
 這幾個(gè)類(lèi)搞清楚之后基本就沒(méi)有什么打的問(wèn)題了。

主要代碼講解

1、主類(lèi)MainActivity.java

public class MainActivity extends BaseActivity implements EMEventListener

該類(lèi)實(shí)現(xiàn)了EMEventListener 接口,就一個(gè)方法如下:

/**
   * 監(jiān)聽(tīng)事件
   */
  @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;
    }
  }

主要就是監(jiān)聽(tīng)新消息、離線消息、回話消息變化等,然后更新界面refreshUI(),更新界面就是刷新未讀消息數(shù)、刷新聯(lián)系人列表,回話列表等。

在主界面初始化中注冊(cè)了三個(gè)監(jiān)聽(tīng)器,如下代碼:

private void init() {
    // setContactListener監(jiān)聽(tīng)聯(lián)系人的變化等
    EMContactManager.getInstance().setContactListener(new MyContactListener());
    // 注冊(cè)一個(gè)監(jiān)聽(tīng)連接狀態(tài)的listener
    connectionListener = new MyConnectionListener();
    EMChatManager.getInstance().addConnectionListener(connectionListener);

    groupChangeListener = new MyGroupChangeListener();
    // 注冊(cè)群聊相關(guān)的listener
  }

 這三個(gè)監(jiān)聽(tīng)器就是監(jiān)聽(tīng)聯(lián)系人變化、群組變化、與環(huán)信服務(wù)器鏈接變化的監(jiān)聽(tīng)器,這三者的變化都會(huì)回調(diào)這三個(gè)監(jiān)聽(tīng)器里面的相應(yīng)的方法,方便開(kāi)發(fā)者通過(guò)相應(yīng)的方法采取相應(yīng)的措施。 

 這三個(gè)監(jiān)聽(tīng)器demo中代碼比較詳細(xì),在此就不多說(shuō)了。

2 聯(lián)系人列表ContactlistFragment.java類(lèi)

/**
 * 聯(lián)系人列表頁(yè)
 */
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;

  /**
   * 這里注冊(cè)了兩個(gè)監(jiān)聽(tīng)器,目的在于同步聯(lián)系人信息
   * 當(dāng)聯(lián)系人發(fā)生變化、黑名單發(fā)生變化,通知這里注冊(cè)的監(jiān)聽(tīng)器
   * 進(jìn)而刷新界面
   *
   */
  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后,沒(méi)點(diǎn)確定按鈕然后按了home鍵,長(zhǎng)期在后臺(tái)又進(jìn)app導(dǎo)致的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>();
    // 獲取設(shè)置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();
      }
    });

    // 設(shè)置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)) {
          // 進(jìn)入申請(qǐng)與通知頁(yè)面
          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)) {
          // 進(jìn)入群聊列表頁(yè)面
          startActivity(new Intent(getActivity(), GroupsActivity.class));
        } else if(Constant.CHAT_ROOM.equals(username)){
          //進(jìn)入聊天室列表頁(yè)面
          startActivity(new Intent(getActivity(), PublicChatRoomsActivity.class));
        }else {
          // demo中直接進(jìn)入聊天頁(yè)面,實(shí)際一般是進(jìn)入用戶(hù)詳情頁(yè)
          startActivity(new Intent(getActivity(), ChatActivity.class).putExtra("userId", adapter.getItem(position).getUsername()));
        }
      }
    });
    listView.setOnTouchListener(new OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent event) {
        // 隱藏軟鍵盤(pán)
        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);
    // 進(jìn)入添加好友頁(yè)
    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 {
        // 刪除此聯(lián)系人
        deleteContact(toBeProcessUser);
        // 刪除相關(guān)的邀請(qǐng)消息
        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);
  }

  /**
   * 當(dāng)該Fragment對(duì)象改變了隱藏狀態(tài)(由isHidden()方法返回)時(shí),系統(tǒng)會(huì)調(diào)用這個(gè)方法。
   * Fragment初始是不隱藏的,只要Fragment對(duì)象改變了它的顯示狀態(tài),就會(huì)調(diào)用該方法。
   * 參數(shù)hidden 如果該Fragment對(duì)象現(xiàn)在是隱藏的,則該參數(shù)是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();
    }
  }

  /**
   * 刪除聯(lián)系人
   * 
   * @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和內(nèi)存中此用戶(hù)的數(shù)據(jù)
          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 {
      // 可能會(huì)在子線程中調(diào)到這方法
      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);
      }
    }
  }

  /**
   * 獲取聯(lián)系人列表,并過(guò)濾掉黑名單和排序
   */
  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, "獲取聯(lián)系人="+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));

    // 把"申請(qǐng)與通知"添加到首位
    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);
    }

  }
}

上面聯(lián)系人類(lèi)中的注冊(cè)的監(jiān)聽(tīng)器使用的就是觀察者模式,先看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);
    }
  }

這部分代碼控制著觀察者,添加、刪除、通知每一個(gè)觀察者,當(dāng)群組、好友、黑名單 通過(guò)環(huán)信服務(wù)器同步到客戶(hù)端之后,notify每個(gè)觀察者,然后觀察者接收到之后,刷新UI。這里就是觀察者模式的經(jīng)典應(yīng)用?。?!
 聯(lián)系人列表看懂之后,其他的群組界面和回話歷史界面就不多說(shuō)了。

3 聊天界面ChatActivity.java

這個(gè)類(lèi)比較龐大,因?yàn)閐emo里面把單聊和群聊、聊天室都集成到這一個(gè)界面中完成,代碼很龐大,但是不影響最終的集成,直接集成該類(lèi)就可以實(shí)現(xiàn)功能。不多說(shuō)。

附上界面:

這里寫(xiě)圖片描述

圖一 回話歷史界面

這里寫(xiě)圖片描述 

圖二 通訊錄界面好友

這里寫(xiě)圖片描述 

圖三 設(shè)置界面

這里寫(xiě)圖片描述 

圖四 聊天界面

最后附上源碼下載

補(bǔ)充:

環(huán)信官方網(wǎng)站已經(jīng)發(fā)布IM3.0版本。目前開(kāi)發(fā)的一個(gè)app采用的就是IM3.0版本。 

整體界面沒(méi)發(fā)生大的變化,功能也都一樣。但是在官方給的demo代碼上優(yōu)化很多,方便很多。不過(guò)得大概看懂里面的代碼。如果是高手的話,半天就應(yīng)該能集成好環(huán)信的即時(shí)通信功能。 

本文給出的下載鏈接,是IM2.0版本。所以如果想要使用IM3.0的版本的,需要到官網(wǎng)下載。

對(duì)于新手來(lái)說(shuō),環(huán)信官網(wǎng)給出的demo是可以直接使用的。人家給出的是完整的app代碼。新手就疑惑,不知道該如何入手集成即時(shí)通信功能? 

其實(shí)很簡(jiǎn)單! 

首先,把環(huán)信官網(wǎng)給出的依賴(lài)包和動(dòng)態(tài)庫(kù)添加到自己的工程中。目前官網(wǎng)給出的依賴(lài)包和動(dòng)態(tài)庫(kù)分為包含語(yǔ)音視頻通話功能的和不包括語(yǔ)音視頻通話功能的。大家根據(jù)自己的APP的功能添加。 

然后,把demo里面的聊天界面直接復(fù)制到自己的功能里面,此時(shí)復(fù)制進(jìn)去以后,會(huì)出現(xiàn)大量的錯(cuò)誤!因?yàn)榱奶旖缑骊P(guān)聯(lián)了很多demo中的其他類(lèi),所以,要把其他類(lèi)復(fù)制到自己的工程中。記得不要忘記布局文件、資源圖片文件、字符串等等資源文件! 建議:在自己的工程中,新建一個(gè)包專(zhuān)門(mén)放環(huán)信的類(lèi)。因?yàn)槟阋獜?fù)制的類(lèi)非常多!大概有二三十個(gè)! 

最后,向即時(shí)通信代碼填充數(shù)據(jù)。主要有幾部分:

1)application類(lèi)中環(huán)信helper類(lèi)完成初始化操作。 

2)登錄app界面做好登錄環(huán)信服務(wù)器操作。需要登錄環(huán)信的登錄名和密碼。這里的環(huán)信登錄與登錄app 不同。APP登錄是應(yīng)用服務(wù)器的用戶(hù),用戶(hù)名和密碼在應(yīng)用服務(wù)器。而登錄環(huán)信是環(huán)信的登錄名和密碼, 需要先注冊(cè)到環(huán)信服務(wù)器才行。注冊(cè)操作可以在應(yīng)用服務(wù)器提前做好。APP登錄應(yīng)用服務(wù)器的時(shí)候順帶著登錄環(huán)信服務(wù)器即可。 

3)獲取好友信息。這里要分為好友信息的維護(hù)是應(yīng)用服務(wù)器維護(hù)還是環(huán)信幫助你維護(hù)。這個(gè)我就不多說(shuō) 了。環(huán)信官網(wǎng)有說(shuō)明。 

4)本地維護(hù)好友列表和聊天信息里列表。聊天信息列表在環(huán)信中已經(jīng)不讓開(kāi)發(fā)者編輯和改變了。該功能 已經(jīng)集成在了依賴(lài)的環(huán)信的包中了。好友列表在demo中給出了簡(jiǎn)單的數(shù)據(jù)表。開(kāi)發(fā)者可以自己根據(jù)APP 需要開(kāi)發(fā)和擴(kuò)展。 

5)退出APP。退出APP時(shí)務(wù)必調(diào)用helper類(lèi)的logout方法。這樣以后,先前登錄的用戶(hù)就從APP上退 出了環(huán)信的服務(wù)器。開(kāi)發(fā)者要注意,我這里說(shuō)的退出時(shí)指APP用戶(hù)手動(dòng)退出,不是用戶(hù)按手機(jī)返回按鈕或者返回主界面按鈕導(dǎo)致APP退出。而是APP中的退出按鈕,當(dāng)前登錄用戶(hù)退出APP。如果是用戶(hù)按返回按鈕或回主界面按鈕,返回到手機(jī)桌面的,沒(méi)必要調(diào)用helper類(lèi)的logout方法。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • android ViewPager實(shí)現(xiàn)滑動(dòng)翻頁(yè)效果實(shí)例代碼

    android ViewPager實(shí)現(xiàn)滑動(dòng)翻頁(yè)效果實(shí)例代碼

    本篇文章主要介紹了android ViewPager實(shí)現(xiàn)滑動(dòng)翻頁(yè)效果實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-03-03
  • Android?WebView軟鍵盤(pán)遮擋輸入框方案詳解

    Android?WebView軟鍵盤(pán)遮擋輸入框方案詳解

    這篇文章主要介紹了Android?WebView軟鍵盤(pán)遮擋輸入框方案詳解,本文提供了一種新的解決?WebView?輸入框被軟鍵盤(pán)遮擋的思路,不過(guò)這種思路也有它的局限性,目前來(lái)看僅適用于全屏的?WebView?中,需要的朋友可以參考下
    2022-06-06
  • 基于Android實(shí)現(xiàn)仿QQ5.0側(cè)滑

    基于Android實(shí)現(xiàn)仿QQ5.0側(cè)滑

    本課程將帶領(lǐng)大家通過(guò)自定義控件實(shí)現(xiàn)QQ5.0側(cè)滑菜單,課程將循序漸進(jìn),首先實(shí)現(xiàn)最普通的側(cè)滑菜單,然后引入屬性動(dòng)畫(huà)與拖動(dòng)菜單效果相結(jié)合,最終實(shí)現(xiàn)QQ5.0側(cè)滑菜單效果。通過(guò)本課程大家會(huì)對(duì)側(cè)滑菜單有更深層次的了解,通過(guò)自定義控件和屬性動(dòng)畫(huà)打造千變?nèi)f化的側(cè)滑菜單效果
    2015-12-12
  • flutter FadeTransition實(shí)現(xiàn)透明度漸變動(dòng)畫(huà)

    flutter FadeTransition實(shí)現(xiàn)透明度漸變動(dòng)畫(huà)

    這篇文章主要為大家詳細(xì)介紹了flutter FadeTransition實(shí)現(xiàn)透明度漸變動(dòng)畫(huà),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • 淺談Android硬件加速原理與實(shí)現(xiàn)簡(jiǎn)介

    淺談Android硬件加速原理與實(shí)現(xiàn)簡(jiǎn)介

    這篇文章主要介紹了淺談Android硬件加速原理與實(shí)現(xiàn)簡(jiǎn)介,本文嘗試從底層硬件原理,一直到上層代碼實(shí)現(xiàn),對(duì)硬件加速技術(shù)進(jìn)行簡(jiǎn)單介紹,感興趣的小伙伴們可以參考一下
    2018-07-07
  • Android邊框裁切的正確姿勢(shì)實(shí)現(xiàn)示例

    Android邊框裁切的正確姿勢(shì)實(shí)現(xiàn)示例

    這篇文章主要為大家介紹了Android邊框裁切的正確姿勢(shì)實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • Android中SharedPreferences簡(jiǎn)單使用實(shí)例

    Android中SharedPreferences簡(jiǎn)單使用實(shí)例

    這篇文章主要介紹了Android中SharedPreferences簡(jiǎn)單使用案例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • Kotlin?ContentProvider使用方法詳解

    Kotlin?ContentProvider使用方法詳解

    ContentProvider內(nèi)容提供者,主要用于再不同的應(yīng)用程序之前實(shí)現(xiàn)數(shù)據(jù)共享的功能,它提供了一套完整的機(jī)制,允許一個(gè)程序訪問(wèn)另外一個(gè)程序的數(shù)據(jù),同時(shí)還能保證數(shù)據(jù)的安全性
    2022-12-12
  • Android的Activity跳轉(zhuǎn)動(dòng)畫(huà)各種效果整理

    Android的Activity跳轉(zhuǎn)動(dòng)畫(huà)各種效果整理

    Android的Activity跳轉(zhuǎn)就是很生硬的切換界面。其實(shí)Android的Activity跳轉(zhuǎn)可以設(shè)置各種動(dòng)畫(huà),本文整理了一些,還有很多動(dòng)畫(huà)效果,就要靠我們發(fā)揮自己的想象力
    2013-06-06
  • Android第三方控件PhotoView使用方法詳解

    Android第三方控件PhotoView使用方法詳解

    這篇文章主要為大家詳細(xì)介紹了Android第三方控件PhotoView的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12

最新評(píng)論