Android 高仿微信實時聊天 基于百度云推送

jopen 8年前發布 | 44K 次閱讀 Android開發 移動開發

轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/38799363 ,本文出自:【張鴻洋的博客】

一直在仿微信界面,今天終于有幸利用百度云推送仿一仿微信聊天了~~~

首先特別感謝:weidi1989分享的Android之基于百度云推送IM ,大家可以直接下載;省了很多事哈,本例中也使用了weidi的部分代碼,凡是@author way的就是weidi1989的代碼~~

1、效果圖

核心功能也就上面的兩張圖了~~~我拿著手機和模擬器聊天,同時感謝群里的兄弟姐妹幫忙測試(好友列表中)。

2、原理

下面通過幾個問題來說明下實現的原理:

1、如何實現給某個用戶發送消息呢?

其實就是利用百度云提供的REST API,直接通過發送Http請求的形式給某個用戶推送一條消息;

網址:http://developer.baidu.com/wiki/index.php?title=docs/cplat/push/api/list

實例:

push_msg
功能
推送消息,該接口可用于推送單個人、一群人、所有人以及固定設備的使用場景。
HTTP請求方式
POST
URL
http[s]://channel.api.duapp.com/rest/2.0/channel/channel

....

2、的確可以實現給某個用戶或者一群用戶推送消息,那么用戶的昵稱神馬的咋獲取的呢,我印象中百度云中沒法存這樣的數據?

嗯,其實用了一個比較巧妙的方式;在用戶首次安裝軟件的時候,會要求用戶填寫注冊信息,也就是昵稱等;當用戶填寫完畢時,點擊登錄的時候:

1、發送一條比較特殊的消息,比如這個消息攜帶一個hello的字段,廣播給所有用戶;

2、其他用戶在收到消息時,首先判斷hello這個字段是否有值,有值則說明新用戶加入,把該新用戶存入本地數據庫,然后更新用戶列表;

3、其他用戶給這個新用戶回一條消息,附帶一個特殊字段,比如welcome,當新用戶收到攜帶welcome字段的消息時,表明這是對新用戶hello的答復,然后將已存在用戶添加到該新用戶的用戶列表。

基本原理就這樣了,大家甚至可以利用上述的原理添加一些刪除好友的功能;比如當用戶點擊刪除好友,則發送一條特殊消息給被刪除的對象,然后對方收到該消息,也將發送發刪除。

相信大家在了解原理之后,這個例子瞬間從高大尚變成矮矬窮了,這尼瑪,so easy,誰不會啊~~~

 

3、核心代碼解析

由于代碼量還是相當大的,也不想拆成幾篇博客,所以準備將核心代碼進行講解下,其他的大家自己看源碼~

關于這個例子的主界面,可以參考: 高仿微信5.2.1主界面架構 包含消息通知

關于百度云推送的入門,可以參考:Android推送 百度云推送 入門篇

好了,最主要就是收到百度云推送的Receiver

1、onBind

首先是用戶第一次登錄時候綁定的回調

@Override
    public void onBind(final Context context, int errorCode, String appid,
            String userId, String channelId, String requestId)
    {
        String responseString = "onBind errorCode=" + errorCode + " appid="
                + appid + " userId=" + userId + " channelId=" + channelId
                + " requestId=" + requestId;
        Log.e(TAG, responseString);

        if (errorCode == 0)
        {
            SharePreferenceUtil util = PushApplication.getInstance()
                    .getSpUtil();
            util.setAppId(appid);
            util.setChannelId(channelId);
            util.setUserId(userId);

        } else
        // 如果網絡正常,則重試
        {
            if (NetUtil.isNetConnected(context))
            {

                T.showLong(context, "啟動失敗,正在重試...");
                new Handler().postDelayed(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        PushManager.startWork(context,
                                PushConstants.LOGIN_TYPE_API_KEY,
                                PushApplication.API_KEY);
                    }
                }, 2000);// 兩秒后重新開始驗證
            } else
            {
                T.showLong(context, R.string.net_error_tip);
            }
        }
        // 回調函數
        for (int i = 0; i < bindListeners.size(); i++)
            bindListeners.get(i).onBind(userId, errorCode);
    }
初次綁定的時候,如果綁定成功則使用SharedPreferences存儲userId,channelId等數據

然后回調:bindListeners.get(i).onBind(userId, errorCode);給所有注冊該事件的發出通知

下面看listener.onBind

/**
     * 收到通知
     */
    @Override
    public void onBind(String userId, int errorCode)
    {
        Log.e("TAG", "Login Activity onBind ");
        if (errorCode == 0)
        {
            Log.e("TAG", "Login Activity onBind success ");
            // 如果綁定賬號成功,由于第一次運行,給同一tag的人推送一條新人消息
            User u = new User(mSpUtil.getUserId(), mSpUtil.getChannelId(),
                    mSpUtil.getNick(), mSpUtil.getHeadIcon(), 0);
            mUserDB.addUser(u);// 把自己添加到數據庫
            Message firstSendMsg = new Message(System.currentTimeMillis(), "");
            firstSendMsg.setHello("hello");
            task = new SendMsgAsyncTask(mGson.toJson(firstSendMsg), "");
            task.setOnSendScuessListener(new OnSendScuessListener()
            {
                @Override
                public void sendScuess()
                {
                    if (mLoadingDialog != null && mLoadingDialog.isVisible())
                        mLoadingDialog.dismiss();
                    mHandler.removeCallbacks(mConnTimeoutCallback);
                    finish();
                    Intent intent = new Intent(LoginActivity.this,
                            MainActivity.class);
                    startActivity(intent);
                }
            });
            task.send();
        }
    }
首先將自己保存到本地數據庫,然后發送一個Message給所有的用戶,設置一個特殊字段hello;也就是上述的原理分析中的部分~

 

2、onMessage

收到百度云推送的Receiver中的onMessage

@Override
    public void onMessage(Context context, String message,
            String customContentString)
    {
        String messageString = "收到消息 message=\"" + message
                + "\" customContentString=" + customContentString;
        Log.e(TAG, messageString);
        Message receivedMsg = PushApplication.getInstance().getGson()
                .fromJson(message, Message.class);
        // 對接收到的消息進行處理
        parseMessage(receivedMsg);

    }

    /**
     * 消息:1、攜帶hello,表示新人加入,應該自動回復一個world 消息; 2、普通消息;
     * 
     * @param msg
     */
    private void parseMessage(Message msg)
    {
        String userId = msg.getUserId();
        // 自己的消息
        if (userId
                .equals(PushApplication.getInstance().getSpUtil().getUserId()))
            return;
        if (checkHasNewFriend(msg) || checkAutoResponse(msg))
            return;
        // 普通消息
        UserDB userDB = PushApplication.getInstance().getUserDB();
        User user = userDB.selectInfo(userId);
        // 漏網之魚
        if (user == null)
        {
            user = new User(userId, msg.getChannelId(), msg.getNickname(), 0, 0);
            userDB.addUser(user);
            // 通知監聽的面板
            for (onNewFriendListener listener : friendListeners)
                listener.onNewFriend(user);
        }
        if (msgListeners.size() > 0)
        {// 有監聽的時候,傳遞下去
            for (int i = 0; i < msgListeners.size(); i++)
                msgListeners.get(i).onNewMessage(msg);
        } else
        // 當前沒有任何監聽,即處理后臺狀態
        {
            // 將新來的消息進行存儲
            ChatMessage chatMessage = new ChatMessage(msg.getMessage(), true,
                    userId, msg.getHeadIcon(), msg.getNickname(), false,
                    TimeUtil.getTime(msg.getTimeSamp()));
            PushApplication.getInstance().getMessageDB()
                    .add(userId, chatMessage);
            showNotify(msg);
        }
    }

    /**
     * 檢測是否是自動回復
     * 
     * @param msg
     */
    private boolean checkAutoResponse(Message msg)
    {
        String world = msg.getWorld();
        String userId = msg.getUserId();
        if (!TextUtils.isEmpty(world))
        {
            User u = new User(userId, msg.getChannelId(), msg.getNickname(),
                    msg.getHeadIcon(), 0);
            PushApplication.getInstance().getUserDB().addUser(u);// 存入或更新好友
            // 通知監聽的面板
            for (onNewFriendListener listener : friendListeners)
                listener.onNewFriend(u);

            return true;
        }
        return false;
    }

    /**
     * 檢測是否是新人加入
     * 
     * @param msg
     */
    private boolean checkHasNewFriend(Message msg)
    {
        String userId = msg.getUserId();
        String hello = msg.getHello();
        // 新人發送的消息
        if (!TextUtils.isEmpty(hello))
        {
            Log.e("checkHasNewFriend", msg.getUserId());
            // 新人
            User u = new User(userId, msg.getChannelId(), msg.getNickname(),
                    msg.getHeadIcon(), 0);
            PushApplication.getInstance().getUserDB().addUser(u);// 存入或更新好友
            T.showShort(PushApplication.getInstance(), u.getNick() + "加入");

            // 給新人回復一個應答
            Message message = new Message(System.currentTimeMillis(), "");
            message.setWorld("world");
            new SendMsgAsyncTask(PushApplication.getInstance().getGson()
                    .toJson(message), userId);
            // 通知監聽的面板
            for (onNewFriendListener listener : friendListeners)
                listener.onNewFriend(u);

            return true;
        }

        return false;
    }

當收到一個新的消息:

1、首先判斷是不是自己發的,是,忽略;

2、判斷是否攜帶hello字段,代表新人加入,攜帶,則保存到好友列表,且回復一個攜帶welcome字段的消息;

3、判斷是否包含welcome字段,包含,存入好友列表

4、不是1、2、3則肯定是普通消息,將消息保存到本地數據庫,然后為所有設置消息監聽的發送回調即可~

 

MainTabFriends.java用戶列表中收到新消息的回調:

/**
     * 收到新消息時
     */
    @Override
    public void onNewMessage(Message message)
    {
        // 如果是自己發送的,則直接返回
        if (message.getUserId() == mSpUtils.getUserId())
            return;
        // 如果該用戶已經有未讀消息,更新未讀消息的個數,并通知更新未讀消息接口,最后notifyDataSetChanged
        String userId = message.getUserId();
        if (mUserMessages.containsKey(userId))
        {
            mUserMessages.put(userId, mUserMessages.get(userId) + 1);
        } else
        {
            mUserMessages.put(userId, 1);
        }
        mUnReadedMsgs++;
        notifyUnReadedMsg();
        // 將新來的消息進行存儲
        ChatMessage chatMessage = new ChatMessage(message.getMessage(), true,
                userId, message.getHeadIcon(), message.getNickname(), false,
                TimeUtil.getTime(message.getTimeSamp()));
        mApplication.getMessageDB().add(userId, chatMessage);
        // 通知listview數據改變
        mAdapter.notifyDataSetChanged();
    }

1、如果是自己發的不做任何處理

2、如果當前消息發送用戶已有未讀消息,則更新該用戶未讀消息個數(用戶Item上顯示未讀消息通知數),更新所有未讀消息總和(朋友Tab上顯示未讀消息總和),然后存儲該消息

3、刷新界面

 

ChattingActivity.java聊天界面的新消息回調

@Override
    public void onNewMessage(Message message)
    {

        // 獲得回復的消息
        if (mFromUser.getUserId().equals(message.getUserId()))
        {
            ChatMessage chatMessage = new ChatMessage();
            chatMessage.setComing(true);
            chatMessage.setDate(new Date(message.getTimeSamp()));
            chatMessage.setMessage(message.getMessage());
            chatMessage.setUserId(message.getUserId());
            chatMessage.setNickname(message.getNickname());
            chatMessage.setReaded(true);
            mDatas.add(chatMessage);
            mAdapter.notifyDataSetChanged();
            mChatMessagesListView.setSelection(mDatas.size() - 1);
            // 存入數據庫,當前聊天記錄
            mApplication.getMessageDB().add(mFromUser.getUserId(), chatMessage);
        }
    }

首先判斷是否是正在聊天的用戶發的消息,如果是,直接存入數據庫,刷新聊天界面;

 

 

好了,細節也不展開描述了,大家自己看源碼,bug肯定有,發現bug可以直接留言,方便其他學習的童鞋,也方便我了,多謝~~~~~~~~~~~~~~

ps:最近狀態不好,我要去喝脈動~~~~

 

源碼點擊下載  大家可以先安裝壓縮文件里面的apk測試下,這樣就可以發現別的伙伴了~~~

 

源碼點擊下載 補充了一下那個BadgeView的項目~~~

 

 

 

來自: http://blog.csdn.net//lmj623565791/article/details/38799363

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!