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里面把单聊和群聊、聊天室都集成到这一个界面中完成,代码很庞大,但是不影响最终的集成,直接集成该类就可以实现功能。不多说。

附上界面:

图一 回话历史界面

图二 通讯录界面好友

图三 设置界面

图四 聊天界面

最后附上源码下载

补充:

环信官方网站已经发布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方法。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android Easeui 3.0 即时通讯的问题汇总

    0.关于注册账号就不用说了. 1.创建应用.获取appkey 0.创建应用 1.填写信息 2.获取appkey 2.集成 0.首先新建一个工程 1.这里主要介绍使用easeui来集成环信的即时通讯功能,需要下载sdk 2.把easeui当做依赖导入到工程当中,然后建立依赖关系 3在导入之后可能会出现的问题: 3.0 <uses-permission android:name="android.permission.ACCESS_MOCK_LACATTON> 这个权限报错,在咨询官方技

  • android 仿微信聊天气泡效果实现思路

    微信聊天窗口的信息效果类似iphone上的短信效果,以气泡的形式展现,在Android上,实现这种效果主要用到ListView和BaseAdapter,配合布局以及相关素材,就可以自己做出这个效果,素材可以下一个微信的APK,然后把后缀名改成zip,直接解压,就可以得到微信里面的所有素材了.首先看一下我实现的效果: 以下是工程目录结构: 接下来就是如何实现这个效果的代码: main.xml,这个是主布局文件,显示listview和上下两部分内容. 复制代码 代码如下: <?xml version

  • Android RichText 让Textview轻松的支持富文本(图像ImageSpan、点击效果等等类似QQ微信聊天)

    AndroidRichText帮助实现像QQ,微信一样的,一个TextView里既有文字又有表情又有图片的效果,采用插件化的框架,代码简单,可拓展性强. 基础框架包只有四个java文件, RichTextWrapper :TextView的包裹类,实现支持富文本,通过new RichTextWrapper(TextView v)来构造. RTMovementMethod: 继承自Android原生的LinkMovementMethod,重写onTouchEvent方法,优化了ClickSpan(

  • Android仿QQ、微信聊天界面长按提示框效果

    先来看看效果图 如何使用 示例代码 PromptViewHelper pvHelper = new PromptViewHelper(mActivity); pvHelper.setPromptViewManager(new ChatPromptViewManager(mActivity)); pvHelper.addPrompt(holder.itemView.findViewById(R.id.textview_content)); 使用起来还是很简单的 首先new一个PromptViewH

  • Android高仿微信聊天界面代码分享

    微信聊天现在非常火,是因其界面漂亮吗,哈哈,也许吧.微信每条消息都带有一个气泡,非常迷人,看起来感觉实现起来非常难,其实并不难.下面小编给大家分享实现代码. 先给大家展示下实现效果图: OK,下面我们来看一下整个小项目的主体结构: 下面是Activity的代码: package com.way.demo; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import jav

  • Android编程中聊天页面背景图片、标题栏由于键盘引起问题的解决方法

    本文实例讲述了Android编程中聊天页面背景图片.标题栏由于键盘引起问题的解决方法.分享给大家供大家参考,具体如下: 在一个群里面有人问到 聊天页面由于键盘弹出来,导致自定义的标题栏不见和背景图片都变形了,然后自己也折腾了一下,在stackOverFlow上面找到了一个解决方法. 解决方法很简单: 1.在AndroidManifest.xml文件里面的Activity配置: 复制代码 代码如下: android:windowSoftInputMode="adjustResize|stateAl

  • 适用于Android开发的简单聊天软件

    适用于android 开发.是一个简单的聊天软件,包括知识点,各个控件的运用(ExpandableListView,ViewPager,Spinner,LinearLayout,RelativeLayot),自定义的ViaImageView(自定义xml属性),sql 的写入,读取等操作. 1. ViaImageView.java    package com.farina.farinaimagelib; import android.content.Context; import androi

  • Android之ProgressBar即时显示下载进度详解

    这里利用 ProgressBar 即时显示下载进度. 途中碰到的问题: 1.主线程中不能打开 URL,和只能在主线程中使用 Toast 等 2.子线程不能修改 UI 3.允许网络协议 4.暂停下载和继续下载   ........ fragment_main 布局文件 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.

  • Android仿微信语音聊天功能

    本文实例讲述了Android仿微信语音聊天功能代码.分享给大家供大家参考.具体如下: 项目效果如下: 具体代码如下: AudioManager.java package com.xuliugen.weichat; import java.io.File; import java.io.IOException; import java.util.UUID; import android.media.MediaRecorder; public class AudioManager { private

  • android-使用环信SDK开发即时通信功能(附源码下载)

    最近项目中集成即时聊天功能,挑来拣去,最终选择环信SDK来进行开发,选择环信的主要原因是接口方便.简洁,说明文档清晰易懂.文档有Android.iOS.和后台服务器端,还是非常全的. 环信官网:http://www.easemob.com/ 本篇文章目的主要在于说明环信Demo如何实现即时通信的.我在集成环信SDK到我们自己开发的app之前,研究了一下环信demo的代码,看了两三天的样子,基本搞清楚来龙去脉,但是只是清楚来龙去脉,要说到里面的细节可能得深一步研究,但是这就够了,已经可以把demo

  • Android编程实现画板功能的方法总结【附源码下载】

    本文实例讲述了Android编程实现画板功能的方法.分享给大家供大家参考,具体如下: Android实现画板主要有2种方式,一种是用自定义View实现,另一种是通过Canvas类实现.当然自定义View内部也是用的Canvas.第一种方式的思路是,创建一个自定义View(推荐SurfaceView),在自定义View里通过Path对象记录手指滑动的路径调用lineTo()绘制:第二种方式的思路是,先用Canvas绘制一张空的Bitmap,通过ImageView的setImageBitmap()方

  • Android实现画板、写字板功能(附源码下载)

    前言 本文给大家分享一个使用Android开发写字板功能Dem.简单操作内存中的图像.对图像进行简单的处理.绘制直线.以达到写字板的效果 效果图如下 XML布局代码 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="

  • Android编程实现滑动开关组件功能【附源码下载】

    本文实例讲述了Android编程实现滑动开关组件功能.分享给大家供大家参考,具体如下: 由于Android并未提供滑动开关之类的组件,所以我们需要自己去实现一个自定义的视图组件来实现滑动开关效果. 这里有一个示例代码,它包括三个类:开关组件视图.状态监听接口.MainActivity 我们先来看看整个demo的效果图: 我们先来看看视图组件的完整代码,代码都已经注释: package com.bear.swtichbuttondemo; import java.util.ArrayList; i

  • Android仿腾讯QQ实现滑动删除 附源码下载

    看了很多大神们的文章,感觉受益良多,也非常欣赏大家的分享态度,所以决定开始写Blog,给大家分享自己的心得. 先看看效果图: 本来准备在ListView的每个Item的布局上设置一个隐藏的Button,当滑动的时候显示.但是因为每次只要存在一个Button,发现每个Item上的Button相互间不好控制.所以决定继承ListView然后结合PopupWindow. 首先是布局文件: delete_btn.xml:这里只需要一个Button <?xml version="1.0"

  • Hook实现Android 微信、陌陌 、探探位置模拟(附源码下载)

    Hook实现Android 微信.陌陌 .探探位置模拟 最近需要对微信,陌陌等程序进行位置模拟 实现世界各地发朋友圈,搜索附近人的功能,本着站在巨人肩膀上的原则 爱网上搜索一番. 也找到一些 代码和文章,但是代码大都雷同而且都有一个弊端 比如说 微信 对目标函数实现hook之后第一次打开微信 第一次定位是可以改变的 但是 我如果想更换地址的话 就需要重启手机了,重新加载hook了,试了很多次都是这样满足不了需求. 为了改进这个地方我们从gps定义的源代码流程开始看寻找hook系统函数的突破口 我

  • Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能(附源码)

    最近项目中需要用到ListView下拉刷新的功能,一开始想图省事,在网上直接找一个现成的,可是尝试了网上多个版本的下拉刷新之后发现效果都不怎么理想.有些是因为功能不完整或有Bug,有些是因为使用起来太复杂,十全十美的还真没找到.因此我也是放弃了在网上找现成代码的想法,自己花功夫编写了一种非常简单的下拉刷新实现方案,现在拿出来和大家分享一下.相信在阅读完本篇文章之后,大家都可以在自己的项目中一分钟引入下拉刷新功能. 首先讲一下实现原理.这里我们将采取的方案是使用组合View的方式,先自定义一个布局

  • Android开发实现可拖动排序的ListView功能【附源码下载】

    本文实例讲述了Android开发实现可拖动排序的ListView功能.分享给大家供大家参考,具体如下: 一.上图 二.简述 1.需要实现的效果是长按右侧可拖动部分布局实现列表项的拖动排序 2.当点击列表项前面的单选按钮时,在该条目右侧显示删除图标,点击该图标删除当前条目. 三.实现思路 借助github上的开源代码drag-sort-listview-master加以改造. 四.主要源码展示 1.Activity代码 package com.gengducun.dslvdemo; import

  • Android开发实现仿QQ消息SwipeMenuListView滑动删除置顶功能【附源码下载】

    本文实例讲述了Android开发实现仿QQ消息SwipeMenuListView滑动删除置顶功能.分享给大家供大家参考,具体如下: 一.先来效果图 二.实现步骤: 1. 在项目build.gradle里面添加包 compile 'com.baoyz.swipemenulistview:library:1.3.0' 2. xml布局文件 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  • Android开发实现横向列表GridView横向滚动的方法【附源码下载】

    本文实例讲述了Android开发实现横向列表GridView横向滚动的方法.分享给大家供大家参考,具体如下: Android 横向列表实现,可左右滑动,如下图 1. 主界面布局代码:activity_main.xml a.包裹HorizontalScrollView控件是GirdView横向滚动的基本条件 b.GirdView外包裹LinearLayout是java代码中参数设置的必要条件 <?xml version="1.0" encoding="utf-8"

随机推荐