android项目实践之融云聊天服务端与客户端的实现

来源:互联网 发布:端口80被占用 编辑:程序博客网 时间:2024/06/06 00:50
   最近研究了即时通讯,当然用户是第三方IM。融云和网易云信在市场上的使用用户都挺多,但是我还是选择了融云并且研究了一番,也终于有些成果并跟大家分享。下面是效果图。

这里写图片描述
这里写图片描述

一、开发前的准备

首先我们肯定是要去登录融云的官网去下载相关的SDK,并且创建相应的应用获取APP Key 和 App Secret。
这里写图片描述

最简单的聊天功能下载SDK只需要IMKit 与 IMLib就能实现。同时可以选一些附加功能这个根据自己的需求选择就行。这里的操作都很简单,现在讲项目这一块。
1、我们最初把SDK下载完之后选择两个库导入as中。步骤:file -> new -> import Module ->找到两个库并导入。导入重新构建后一定要记得在app项目的build.gradle中加上compile project(‘:IMKit’) 进行关联。
2、把上面选择需要功能的jar 和 os文件 (下载的SDK中有) 复制到app的Lib中。
这里写图片描述

3、在IMLib中的AndroidManifest文件中填入自己创建应用的App Key。

<application>        <!-- imlib config begin -->        <meta-data            android:name="RONG_CLOUD_APP_KEY"            android:value="你的app key" />

4、初始化融云SDK。建一个类继承Application(android启动先走Application)在OnCreate()方法中初始化,初始化语句:RongIM.init(this);。同时AndroidManifest中application取名为App

<application        android:name=".App"        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">

二、服务端连接融云获取Token

1、编写一个简单的测试界面。
这里写图片描述

2、我们来梳理一下思路:随便输入一个id -> 点击返回值按钮 -> 请求自己的服务器并传入id -> 自己的服务器把请求融云服务器并传入id -> 融云服务器进行响应并返回id对应的Token值给自己的服务器 -> 自己的服务器把ToKen返回给客户端 -> 客户端得到Token使用RongIM.connect()方法就可以连接融云服务器。希望下面的图能够清晰的帮你理解。
这里写图片描述
3、点击按钮进行请求自己的服务器,同时服务端代码的实现。

客户端请求:
private void postId(final String id) {        //创建一个OkHttpClient对象        OkHttpClient okHttpClient = new OkHttpClient();        FormBody body = new FormBody.Builder()                .add("id", id)                .build();        Request request = new Request.Builder().post(body).url(API.GET_TOKEN).build();        Call call = okHttpClient.newCall(request);        call.enqueue(new Callback() {            //请求失败时调用            @Override            public void onFailure(Call call, IOException e) {                Log.i(TAG, "onFailure: " + e);            }            //请求成功时调用            @Override            public void onResponse(Call call, Response response) throws IOException {                final String str = response.body().string();                userInfo = JSON.parseObject(str, UserInfo.class);                runOnUiThread(new Runnable() {                    @Override                    public void run() {                        Toast.makeText(LoginActivity.this, "str" + str + ", token:" + userInfo.getToken(), Toast.LENGTH_SHORT).show();                        editor.putString("loginToken", userInfo.getToken());                        btnConn.setVisibility(View.VISIBLE);                    }                });            }        });    }
服务端相应:
        //获取用户token        @ResponseBody        @RequestMapping(value = "/getToken")        String getToken(UserItem uEntity, String id){            String token = RongUtils.getToken(id);            return token;        }

还有请求融云服务器的工具类在下面的Demo中会有就不一一贴出来。

4、通过获取的Token与融云服务器连接。

if (getApplicationInfo().packageName.equals(App.getCurProcessName(getApplicationContext()))) {                    RongIM.connect(userInfo.getToken(), new RongIMClient.ConnectCallback() {                        @Override                        public void onTokenIncorrect() {                        }                        @Override                        public void onSuccess(String userid) {                            //userid,是我们在申请token时填入的userid                            System.out.println("========userid" + userid);                            runOnUiThread(new Runnable() {                                @Override                                public void run() {                                    Toast.makeText(LoginActivity.this, "链接服务器成功!", Toast.LENGTH_SHORT).show();                                    editor.putString("loginToken", userInfo.getToken());                                }                            });                        }                        @Override                        public void onError(RongIMClient.ErrorCode errorCode) {                        }                    });                }

接下来就可以通过写一个界面以及功能,其实融云封装的很好,阅读文档基本就能成功。下面就贴代码吧。。。

主界面MainActivity:

package com.song.rongyundemo;import android.content.Context;import android.content.Intent;import android.graphics.Color;import android.net.Uri;import android.os.Bundle;import android.support.v4.app.Fragment;import android.support.v4.app.FragmentActivity;import android.support.v4.app.FragmentPagerAdapter;import android.support.v4.view.ViewPager;import android.view.KeyEvent;import android.view.MotionEvent;import android.view.View;import android.view.inputmethod.InputMethodManager;import android.widget.ImageView;import android.widget.RelativeLayout;import android.widget.TextView;import com.song.rongyundemo.adapter.ConversationListAdapterEx;import com.song.rongyundemo.fragment.FriendFragment;import com.song.rongyundemo.utils.DragPointView;import com.song.rongyundemo.utils.NToast;import java.util.ArrayList;import java.util.List;import io.rong.common.RLog;import io.rong.imkit.RongContext;import io.rong.imkit.RongIM;import io.rong.imkit.fragment.ConversationListFragment;import io.rong.imkit.manager.IUnReadMessageObserver;import io.rong.imlib.RongIMClient;import io.rong.imlib.model.Conversation;@SuppressWarnings("deprecation")public class MainActivity extends FragmentActivity implements        ViewPager.OnPageChangeListener,        View.OnClickListener,        DragPointView.OnDragListencer,        IUnReadMessageObserver {    public static ViewPager mViewPager;    private List<Fragment> mFragment = new ArrayList<>();    private ImageView mImageChats, mImageContact;    private TextView mTextChats, mTextContact;    private DragPointView mUnreadNumView;    /**     * 会话列表的fragment     */    private ConversationListFragment mConversationListFragment = null;    private boolean isDebug;    private Context mContext;    private Conversation.ConversationType[] mConversationsTypes = null;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mContext = this;        isDebug = getSharedPreferences("config", MODE_PRIVATE).getBoolean("isDebug", false);        initViews();        changeTextViewColor();        changeSelectedTabState(0);        initMainViewPager();    }    private void initViews() {        RelativeLayout chatRLayout = (RelativeLayout) findViewById(R.id.seal_chat);        RelativeLayout contactRLayout = (RelativeLayout) findViewById(R.id.seal_contact_list);        mImageChats = (ImageView) findViewById(R.id.tab_img_chats);        mImageContact = (ImageView) findViewById(R.id.tab_img_contact);        mTextChats = (TextView) findViewById(R.id.tab_text_chats);        mTextContact = (TextView) findViewById(R.id.tab_text_contact);        chatRLayout.setOnClickListener(this);        contactRLayout.setOnClickListener(this);    }    private void initMainViewPager() {        Fragment conversationList = initConversationList();        mViewPager = (ViewPager) findViewById(R.id.main_viewpager);        mUnreadNumView = (DragPointView) findViewById(R.id.seal_num);        mUnreadNumView.setOnClickListener(this);        mUnreadNumView.setDragListencer(this);        mFragment.add(conversationList);        mFragment.add(new FriendFragment());        FragmentPagerAdapter fragmentPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {            @Override            public Fragment getItem(int position) {                return mFragment.get(position);            }            @Override            public int getCount() {                return mFragment.size();            }        };        mViewPager.setAdapter(fragmentPagerAdapter);        mViewPager.setOnPageChangeListener(this);        initData();    }    private Fragment initConversationList() {        if (mConversationListFragment == null) {            ConversationListFragment listFragment = new ConversationListFragment();            listFragment.setAdapter(new ConversationListAdapterEx(RongContext.getInstance()));            Uri uri;                uri = Uri.parse("rong://" + getApplicationInfo().packageName).buildUpon()                        .appendPath("conversationlist")                        .appendQueryParameter(Conversation.ConversationType.PRIVATE.getName(), "false") //设置私聊会话是否聚合显示                        .appendQueryParameter(Conversation.ConversationType.GROUP.getName(), "true")//群组                        .appendQueryParameter(Conversation.ConversationType.PUBLIC_SERVICE.getName(), "false")//公共服务号                        .appendQueryParameter(Conversation.ConversationType.APP_PUBLIC_SERVICE.getName(), "false")//订阅号                        .appendQueryParameter(Conversation.ConversationType.SYSTEM.getName(), "true")//系统                        .appendQueryParameter(Conversation.ConversationType.DISCUSSION.getName(), "true")                        .build();                mConversationsTypes = new Conversation.ConversationType[]{Conversation.ConversationType.PRIVATE,                        Conversation.ConversationType.GROUP,                        Conversation.ConversationType.PUBLIC_SERVICE,                        Conversation.ConversationType.APP_PUBLIC_SERVICE,                        Conversation.ConversationType.SYSTEM,                        Conversation.ConversationType.DISCUSSION                };            listFragment.setUri(uri);            mConversationListFragment = listFragment;            return listFragment;        } else {            return mConversationListFragment;        }    }    @Override    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {    }    @Override    public void onPageSelected(int position) {        changeTextViewColor();        changeSelectedTabState(position);    }    private void changeTextViewColor() {        mTextChats.setTextColor(Color.parseColor("#abadbb"));        mTextContact.setTextColor(Color.parseColor("#abadbb"));    }    private void changeSelectedTabState(int position) {        switch (position) {            case 0:                mTextChats.setTextColor(Color.parseColor("#0099ff"));                break;            case 1:                mTextContact.setTextColor(Color.parseColor("#0099ff"));                break;        }    }    @Override    public void onPageScrollStateChanged(int state) {    }    long firstClick = 0;    long secondClick = 0;    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.seal_chat:                if (mViewPager.getCurrentItem() == 0) {                    if (firstClick == 0) {                        firstClick = System.currentTimeMillis();                    } else {                        secondClick = System.currentTimeMillis();                    }                    RLog.i("MainActivity", "time = " + (secondClick - firstClick));                    if (secondClick - firstClick > 0 && secondClick - firstClick <= 800) {                        mConversationListFragment.focusUnreadItem();                        firstClick = 0;                        secondClick = 0;                    } else if (firstClick != 0 && secondClick != 0) {                        firstClick = 0;                        secondClick = 0;                    }                }                mViewPager.setCurrentItem(0, false);                break;            case R.id.seal_contact_list:                mViewPager.setCurrentItem(1, false);                break;        }    }    @Override    protected void onNewIntent(Intent intent) {        super.onNewIntent(intent);        if (intent.getBooleanExtra("systemconversation", false)) {            mViewPager.setCurrentItem(0, false);        }    }    protected void initData() {        final Conversation.ConversationType[] conversationTypes = {                Conversation.ConversationType.PRIVATE,                Conversation.ConversationType.GROUP, Conversation.ConversationType.SYSTEM,                Conversation.ConversationType.PUBLIC_SERVICE, Conversation.ConversationType.APP_PUBLIC_SERVICE        };        //未读消息        RongIM.getInstance().addUnReadMessageCountChangedObserver(this, conversationTypes);        //用户头像        RongIM.setUserInfoProvider(new RongIM.UserInfoProvider() {            @Override            public io.rong.imlib.model.UserInfo getUserInfo(String userId) {                if (userId.equals("5")) {                    return new io.rong.imlib.model.UserInfo(userId, "宋泉柯", Uri.parse("http://192.168.191.1:8080/petServer/upload/head_bg.jpg"));                }                return null;            }        }, true);        //刷新用户//        RongIM.getInstance().refreshUserInfoCache(new UserInfo(connectResultId, nickName, Uri.parse(portraitUri)));    }    @Override    public void onCountChanged(int count) {        if (count == 0) {            mUnreadNumView.setVisibility(View.GONE);        } else if (count > 0 && count < 100) {            mUnreadNumView.setVisibility(View.VISIBLE);            mUnreadNumView.setText(String.valueOf(count));        } else {            mUnreadNumView.setVisibility(View.VISIBLE);            mUnreadNumView.setText("...");        }    }    @Override    public boolean onKeyDown(int keyCode, KeyEvent event) {        if (keyCode == KeyEvent.KEYCODE_BACK) {            moveTaskToBack(false);            return true;        }        return super.onKeyDown(keyCode, event);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        if (null != this.getCurrentFocus()) {            InputMethodManager mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);            return mInputMethodManager.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0);        }        return super.onTouchEvent(event);    }    @Override    public void onDragOut() {        mUnreadNumView.setVisibility(View.GONE);        NToast.shortToast(mContext, "清理成功");        RongIM.getInstance().getConversationList(new RongIMClient.ResultCallback<List<Conversation>>() {            @Override            public void onSuccess(List<Conversation> conversations) {                if (conversations != null && conversations.size() > 0) {                    for (Conversation c : conversations) {                        RongIM.getInstance().clearMessagesUnreadStatus(c.getConversationType(), c.getTargetId(), null);                    }                }            }            @Override            public void onError(RongIMClient.ErrorCode e) {            }        }, mConversationsTypes);    }}

需要注意的是很多人说头像昵称不知道怎么实现其实很简单,如下代码:

//用户头像        RongIM.setUserInfoProvider(new RongIM.UserInfoProvider() {            @Override            public io.rong.imlib.model.UserInfo getUserInfo(String userId) {                if (userId.equals("5")) {                    return new io.rong.imlib.model.UserInfo(userId, "宋泉柯", Uri.parse("http://192.168.191.1:8080/petServer/upload/head_bg.jpg"));                }                return null;            }        }, true);

会话界面ConversationActivity:

package com.song.rongyundemo;import android.annotation.TargetApi;import android.content.Intent;import android.content.SharedPreferences;import android.net.Uri;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.support.v4.app.FragmentActivity;import android.support.v4.app.FragmentTransaction;import android.text.TextUtils;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.view.inputmethod.InputMethodManager;import android.widget.Button;import android.widget.TextView;import com.song.rongyundemo.fragment.ConversationFragmentEx;import com.song.rongyundemo.utils.NToast;import java.util.Collection;import java.util.Iterator;import java.util.Locale;import io.rong.imkit.RongIM;import io.rong.imkit.fragment.UriFragment;import io.rong.imkit.userInfoCache.RongUserInfoManager;import io.rong.imlib.MessageTag;import io.rong.imlib.RongIMClient;import io.rong.imlib.TypingMessage.TypingStatus;import io.rong.imlib.model.Conversation;import io.rong.imlib.model.UserInfo;import io.rong.message.TextMessage;import io.rong.message.VoiceMessage;/** * 会话页面 * 1,设置 ActionBar title * 2,加载会话页面 * 3,push 和 通知 判断 */public class ConversationActivity extends FragmentActivity implements View.OnClickListener {    private String TAG = ConversationActivity.class.getSimpleName();    /**     * 对方id     */    private String mTargetId;    /**     * 会话类型     */    private Conversation.ConversationType mConversationType;    /**     * title     */    private String title;    /**     * 是否在讨论组内,如果不在讨论组内,则进入不到讨论组设置页面     */    private boolean isFromPush = false;    private Button btnBack;    private Button btnRight;    private TextView tvTitle;    private SharedPreferences sp;    private final String TextTypingTitle = "对方正在输入...";    private final String VoiceTypingTitle = "对方正在讲话...";    private Handler mHandler;    public static final int SET_TEXT_TYPING_TITLE = 1;    public static final int SET_VOICE_TYPING_TITLE = 2;    public static final int SET_TARGET_ID_TITLE = 0;    @Override    @TargetApi(23)    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.conversation);        sp = getSharedPreferences("config", MODE_PRIVATE);        btnBack = (Button) findViewById(R.id.btn_left);        btnBack.setOnClickListener(this);        btnRight = (Button) findViewById(R.id.btn_right);        btnRight.setOnClickListener(this);        tvTitle = (TextView) findViewById(R.id.tv_title);        Intent intent = getIntent();        if (intent == null || intent.getData() == null)            return;        mTargetId = intent.getData().getQueryParameter("targetId");        mConversationType = Conversation.ConversationType.valueOf(intent.getData()                .getLastPathSegment().toUpperCase(Locale.US));        title = intent.getData().getQueryParameter("title");        Log.e(TAG, "mConversationType: " + mConversationType + ", title: " + title +", mTargetId" + mTargetId);        NToast.shortToast(this,"mConversationType: " + mConversationType + ", title: " + title +", mTargetId: " + mTargetId);        setActionBarTitle(mConversationType, mTargetId);        if (mConversationType.equals(Conversation.ConversationType.PRIVATE) | mConversationType.equals(Conversation.ConversationType.PUBLIC_SERVICE) | mConversationType.equals(Conversation.ConversationType.DISCUSSION)) {            btnRight.setBackground(getResources().getDrawable(R.drawable.icon1_menu));        }        isPushMessage(intent);        mHandler = new Handler(new Handler.Callback() {            @Override            public boolean handleMessage(Message msg) {                switch (msg.what) {                    case SET_TEXT_TYPING_TITLE:                        tvTitle.setText(TextTypingTitle);                        break;                    case SET_VOICE_TYPING_TITLE:                        tvTitle.setText(VoiceTypingTitle);                        break;                    case SET_TARGET_ID_TITLE:                        setActionBarTitle(mConversationType, mTargetId);                        break;                    default:                        break;                }                return true;            }        });        RongIMClient.setTypingStatusListener(new RongIMClient.TypingStatusListener() {            @Override            public void onTypingStatusChanged(Conversation.ConversationType type, String targetId, Collection<TypingStatus> typingStatusSet) {                //当输入状态的会话类型和targetID与当前会话一致时,才需要显示                if (type.equals(mConversationType) && targetId.equals(mTargetId)) {                    int count = typingStatusSet.size();                    //count表示当前会话中正在输入的用户数量,目前只支持单聊,所以判断大于0就可以给予显示了                    if (count > 0) {                        Iterator iterator = typingStatusSet.iterator();                        TypingStatus status = (TypingStatus) iterator.next();                        String objectName = status.getTypingContentType();                        MessageTag textTag = TextMessage.class.getAnnotation(MessageTag.class);                        MessageTag voiceTag = VoiceMessage.class.getAnnotation(MessageTag.class);                        //匹配对方正在输入的是文本消息还是语音消息                        if (objectName.equals(textTag.value())) {                            mHandler.sendEmptyMessage(SET_TEXT_TYPING_TITLE);                        } else if (objectName.equals(voiceTag.value())) {                            mHandler.sendEmptyMessage(SET_VOICE_TYPING_TITLE);                        }                    } else {//当前会话没有用户正在输入,标题栏仍显示原来标题                        mHandler.sendEmptyMessage(SET_TARGET_ID_TITLE);                    }                }            }        });    }    /**     * 判断是否是 Push 消息,判断是否需要做 connect 操作     */    private void isPushMessage(Intent intent) {        if (intent == null || intent.getData() == null)            return;                enterFragment(mConversationType, mTargetId);        }    private ConversationFragmentEx fragment;    /**     * 加载会话页面 ConversationFragmentEx 继承自 ConversationFragment     *     * @param mConversationType 会话类型     * @param mTargetId         会话 Id     */    private void enterFragment(Conversation.ConversationType mConversationType, String mTargetId) {        fragment = new ConversationFragmentEx();        Uri uri = Uri.parse("rong://" + getApplicationInfo().packageName).buildUpon()                .appendPath("conversation").appendPath(mConversationType.getName().toLowerCase())                .appendQueryParameter("targetId", mTargetId).build();        fragment.setUri(uri);        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();        //xxx 为你要加载的 id        transaction.add(R.id.rong_content, fragment);        transaction.commitAllowingStateLoss();    }    /**     * 设置会话页面 Title     *     * @param conversationType 会话类型     * @param targetId         目标 Id     */    private void setActionBarTitle(Conversation.ConversationType conversationType, String targetId) {        if (conversationType == null)            return;        if (conversationType.equals(Conversation.ConversationType.PRIVATE)) {            setPrivateActionBar(targetId);        }  else if (conversationType.equals(Conversation.ConversationType.SYSTEM)) {            tvTitle.setText("系统消息");        } else {            setTitle("聊天");        }    }    /**     * 设置私聊界面 ActionBar     */    private void setPrivateActionBar(String targetId) {        if (!TextUtils.isEmpty(title)) {            if (title.equals("null")) {                if (!TextUtils.isEmpty(targetId)) {                    UserInfo userInfo = RongUserInfoManager.getInstance().getUserInfo(targetId);                    if (userInfo != null) {                        tvTitle.setText(userInfo.getName());                    }                }            } else {                tvTitle.setText(title);            }        } else {            tvTitle.setText(targetId);        }    }    @Override    protected void onDestroy() {        super.onDestroy();    }    @Override    public void onBackPressed() {        super.onBackPressed();        finish();    }    @Override    public boolean onTouchEvent(MotionEvent event) {        if (null != this.getCurrentFocus()) {            InputMethodManager mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);            return mInputMethodManager.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0);        }        return super.onTouchEvent(event);    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.btn_right:                if (mConversationType == Conversation.ConversationType.PUBLIC_SERVICE                        || mConversationType == Conversation.ConversationType.APP_PUBLIC_SERVICE) {                    RongIM.getInstance().startPublicServiceProfile(this, mConversationType, mTargetId);                } else {                    UriFragment fragment = (UriFragment) getSupportFragmentManager().getFragments().get(0);                    //得到讨论组的 targetId                    mTargetId = fragment.getUri().getQueryParameter("targetId");                    Intent intent = null;                    if (mConversationType == Conversation.ConversationType.PRIVATE) {//                intent = new Intent(this, PrivateChatDetailActivity.class);                        intent.putExtra("conversationType", Conversation.ConversationType.PRIVATE);                    }                    intent.putExtra("TargetId", mTargetId);                    if (intent != null) {                        startActivityForResult(intent, 500);                    }                }                break;            case R.id.btn_left:                finish();        }    }}

主要的代码已经在上面,最后可以通过融云控制台API调试与你输出的id进行聊天。具体demo请下载哦~

融云Demo下载

4 0
原创粉丝点击