欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android Socket接口實現(xiàn)即時通訊實例代碼

 更新時間:2016年12月15日 09:44:18   投稿:lqh  
這篇文章主要介紹了Android Socket接口實現(xiàn)即時通訊實例代碼的相關(guān)資料,這里對通訊知識進行了詳細(xì)介紹,并用Socket 接口實現(xiàn)通訊實例,需要的朋友可以參考下

Android Socket接口實現(xiàn)即時通訊

             最近學(xué)習(xí)Android 通信的知識,做一個小實例,鞏固下學(xué)習(xí)內(nèi)容,以下內(nèi)容是網(wǎng)上找的資料,覺得很不錯,知識比較全面,大家看下。

 首先了解一下即時通信的概念。通過消息通道 傳輸消息對象,一個賬號發(fā)往另外一賬號,只要賬號在線,可以即時獲取到消息,這就是最簡單的即使通訊。消息通道可由TCP/IP UDP實現(xiàn)。通俗講就是把一個人要發(fā)送給另外一個人的消息對象(文字,音視頻,文件)通過消息通道(C/S實時通信)進行傳輸?shù)姆?wù)。即時通訊應(yīng)該包括四種形式,在線直傳、在線代理、離線代理、離線擴展。在線直傳指不經(jīng)過服務(wù)器,直接實現(xiàn)點對點傳輸。在線代理指消息經(jīng)過服務(wù)器,在服務(wù)器實現(xiàn)中轉(zhuǎn),最后到達目標(biāo)賬號。離線代理指消息經(jīng)過服務(wù)器中轉(zhuǎn)到達目標(biāo)賬號,對方不在線時消息暫存服務(wù)器的數(shù)據(jù)庫,在其上線再傳發(fā)。離線擴展指將暫存消息以其它形式,例如郵件、短信等轉(zhuǎn)發(fā)給目標(biāo)賬號。

       此外,我們還需要認(rèn)識一下計算機網(wǎng)絡(luò)相關(guān)的概念。經(jīng)典的計算機網(wǎng)絡(luò)四層模型中,TCP和UDP是傳輸層協(xié)議,包含著消息通信內(nèi)容。ip為網(wǎng)絡(luò)層協(xié)議,是一種網(wǎng)絡(luò)地址。TCP/IP,即傳輸控制協(xié)議/網(wǎng)間協(xié)議,定義了主機如何連入因特網(wǎng)及數(shù)據(jù)如何在它們之間傳輸?shù)臉?biāo)準(zhǔn)。Socket,又稱“套接字”, 在應(yīng)用層和傳輸層之間的一個抽象層,用于描述 IP 地址和端口,是一個通信連的句柄,應(yīng)用程序通常通過“套接字”向網(wǎng)絡(luò)發(fā)送請求或者應(yīng)答網(wǎng)絡(luò)請求,它就是網(wǎng)絡(luò)通信過程中端點的抽象表示。它把TCP/IP層復(fù)雜的操作抽象為幾個簡單的接口供應(yīng)用層調(diào)用已實現(xiàn)進程在網(wǎng)絡(luò)中通信。XMPP(可擴展消息處理現(xiàn)場協(xié)議)是基于可擴展標(biāo)記語言(XML)的協(xié)議,應(yīng)用于即時通訊場景的應(yīng)用層協(xié)議,底層通過Socket實現(xiàn)。它用于即時消息(IM)以及在線現(xiàn)場探測。它在促進服務(wù)器之間的準(zhǔn)即時操作。這個協(xié)議可能最終允許因特網(wǎng)用戶向因特網(wǎng)上的其他任何人發(fā)送即時消息, 即使其操作系統(tǒng)和瀏覽器不同。這樣實現(xiàn)即時通訊就有兩種方案,一是從套接字入手,直接利用socket提供的接口進行數(shù)據(jù)的傳送。二是借助開源工具(服務(wù)器openfire),用XMPPConnection創(chuàng)建連接。

     XMPP是實現(xiàn)即時通訊使用較為普遍的做法。XMPP中,各項工作都是通過在一個 XMPP 流上發(fā)送和接收 XMPP 節(jié)來完成的。核心 XMPP 工具集由三種基本節(jié)組成,這三種節(jié)分別為<presence>、出席<message>、<iq>。XMPP 流由兩份 XML 文檔組成,通信的每個方向均有一份文檔。這份文檔有一個根元素<stream:stream>,這個根元素的子元素由可路由的節(jié)以及與流相關(guān)的頂級子元素構(gòu)成。xmpp協(xié)議同樣包括客戶端和服務(wù)器。客戶端基于 Android 平臺進行開發(fā)。負(fù)責(zé)初始化通信過程,進行即時通信時,由客戶端負(fù)責(zé)向服務(wù)器發(fā)起創(chuàng)建連接請求。系統(tǒng)通過 GPRS 無線網(wǎng)絡(luò)與Internet 網(wǎng)絡(luò)建立連接,通過服務(wù)器實現(xiàn)與 Android 客戶端的即時通信腳。服務(wù)器端則采用 Openfire 作為服務(wù)器。 允許多個客戶端同時登錄并且并發(fā)的連接到一個服務(wù)器上。服務(wù)器對每個客戶端的連接進行認(rèn)證,對認(rèn)證通過的客戶端創(chuàng)建會話,客戶端與服務(wù)器端之間的通信就在該會話的上下文中進行。使用了 asmark 開源框架實現(xiàn)的即時通訊功能.該框架基于開源的 XMPP 即時通信協(xié)議,采用 C/S 體系結(jié)構(gòu),通過 GPRS 無線網(wǎng)絡(luò)用TCP 協(xié)議連接到服務(wù)器,以架設(shè)開源的 Openfn'e 服務(wù)器作為即時通訊平臺。xmpp消息通道的創(chuàng)建:

          先配置通道信息進行連接

  ConnectionConfiguration configuration = new ConnectionConfiguration(HOST, PORT),

          設(shè)置Debug信息和安全模式

      configuration.setDebuggerEnabled(true);

      configuration.setSecurityMode(SecurityMode.disabled),

          最后才是建立連接

      conn.connect();

         在ContentObserver的實現(xiàn)類中觀察消息變化。XMPPConnection.getRoster()獲取聯(lián)系人列表對象。用xmpp協(xié)議編寫通訊協(xié)議的大致思路可以如下。進入登陸界面,通過xmppconnection的login方法實現(xiàn)登陸,登陸成功進入主界面。主界面包含兩個Fragment,分別用來顯示聯(lián)系人和聊天記錄。創(chuàng)建聯(lián)系人和短信的數(shù)據(jù)觀察者,在聯(lián)系人、短信服務(wù)中分別設(shè)定監(jiān)聽RosterListener()、ChatManagerListener(),接受聯(lián)系人和短信信息,同時將相關(guān)信息添加到內(nèi)容提供者中。在內(nèi)容提供者中設(shè)定一個內(nèi)容觀察者,當(dāng)數(shù)據(jù)發(fā)生變化時通知界面更新。

        本文的重點是利用Socket的接口實現(xiàn)即時通訊,因為絕大多數(shù)即時通訊的底層都是通過Socket實現(xiàn)的。其基本的業(yè)務(wù)邏輯可描述如下。用戶進入登陸界面后,提交賬號密碼 經(jīng)服務(wù)端確定,返回相關(guān)參數(shù)用于確定連接成功。進入聊天界面或好友界面。點擊聯(lián)系人或聊天記錄的條目,進入聊天界面。當(dāng)移動端再次向服務(wù)器發(fā)送消息時,由服務(wù)器轉(zhuǎn)發(fā)消息內(nèi)容給目標(biāo)賬號。同時更新界面顯示。這樣就完成即時通訊的基本功能。當(dāng)然,也可以添加一個后臺服務(wù),當(dāng)用戶推出程序時,在后臺接受消息。不難看出,對于即時通訊來講,有三個關(guān)注點:消息通道、消息內(nèi)容、消息對象。因此,主要邏輯也是圍繞這三個點展開。消息通道實現(xiàn)傳輸消息對象的發(fā)送和接收。為Socket(String host, int port)傳入服務(wù)其地址和端口號,即可創(chuàng)建連接。消息內(nèi)容的格式應(yīng)該與服務(wù)器保持一致。接受數(shù)據(jù)時,獲取輸入流并用DataInputStream包裝,通過輸入流讀取server發(fā)來的數(shù)據(jù)。發(fā)送數(shù)據(jù)時,獲取輸出流并用DataOutputStream包裝,通過輸出流往server發(fā)送數(shù)據(jù)。消息內(nèi)容中應(yīng)該包括發(fā)送者、接受者信息、數(shù)據(jù)類型等。消息對象就是消息的發(fā)送者和消息的接受者。接下來在代碼中進行詳細(xì)的講解。

         創(chuàng)建一個消息的基類,實現(xiàn)xml文件和字符串的轉(zhuǎn)換,用到Xsream第三方j(luò)ar包。這樣當(dāng)創(chuàng)建消息類時,繼承該方法,就可以直接在類中實現(xiàn)數(shù)據(jù)的轉(zhuǎn)換。

/**
 * Created by huang on 2016/12/3.
 */
public class ProtacolObjc implements Serializable {
  public String toXml() {
    XStream stream = new XStream();
    //將根節(jié)點轉(zhuǎn)換為類名
    stream.alias(this.getClass().getSimpleName(), this.getClass());
    return stream.toXML(this);
  }

  public Object fromXml(String xml) {
    XStream x = new XStream();
    x.alias(this.getClass().getSimpleName(), this.getClass());
    return x.fromXML(xml);
  }
  
  //創(chuàng)建Gson數(shù)據(jù)和字符串之間轉(zhuǎn)換的方法,適應(yīng)多種數(shù)據(jù)
  public String toGson() {
    Gson gson = new Gson();
    return toGson();
  }

  public Object fromGson(String result) {
    Gson gson = new Gson();
    return gson.fromJson(result, this.getClass());
  }
}

         創(chuàng)建線程工具,指定方法運行在子線程和主線程中。由于網(wǎng)絡(luò)操作需要在子線程中,界面更新需要在主線程中,創(chuàng)建線程工具可以方便選擇線程。

import android.os.Handler;
/**
 * Created by huang on 2016/12/5.
 */
public class ThreadUtils {
  private static Handler handler = new Handler();
  public static void runUIThread(Runnable r){
    handler.post(r);
  }
  public static void runINThread(Runnable r){
    new Thread(r).start();
  }
}

          創(chuàng)建消息的工具類,包括消息內(nèi)容、消息類型、消息本省等。由于服務(wù)器返回的內(nèi)容中包含消息的包名信息所以消息本身的包名應(yīng)該于服務(wù)其保持一直。

/**
 * Created by huang on 2016/12/3.
 * 消息內(nèi)容
 */
public class QQMessage extends ProtacolObjc {
  public String type = QQmessageType.MSG_TYPE_CHAT_P2P;// 類型的數(shù)據(jù) chat login
  public long from = 0;// 發(fā)送者 account
  public String fromNick = "";// 昵稱
  public int fromAvatar = 1;// 頭像
  public long to = 0; // 接收者 account
  public String content = ""; // 消息的內(nèi)容 約不?
  public String sendTime = getTime(); // 發(fā)送時間

  public String getTime() {
    Date date = new Date(System.currentTimeMillis());
    java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("mm-DD HH:mm:ss");
    return format.format(date);
  }

  public String getTime(Long time) {
    Date date = new Date(time);
    java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("mm-DD HH:mm:ss");
    return format.format(date);
  }
}

/**
 * Created by huang on 2016/12/3.
 * 消息類型
 */
public class QQmessageType {
  public static final String MSG_TYPE_REGISTER = "register";// 注冊
  public static final String MSG_TYPE_LOGIN = "login";// 登錄
  public static final String MSG_TYPE_LOGIN_OUT = "loginout";// 登出
  public static final String MSG_TYPE_CHAT_P2P = "chatp2p";// 聊天
  public static final String MSG_TYPE_CHAT_ROOM = "chatroom";// 群聊
  public static final String MSG_TYPE_OFFLINE = "offline";// 下線
  public static final String MSG_TYPE_SUCCESS = "success";//成功
  public static final String MSG_TYPE_BUDDY_LIST = "buddylist";// 好友
  public static final String MSG_TYPE_FAILURE = "failure";// 失敗
}

import com.example.huang.imsocket.bean.ProtacolObjc;
/*
 *消息本身 包括 賬號、頭像和昵稱
 *
 */
public class QQBuddy extends ProtacolObjc {
  public long account;
  public String nick;
  public int avatar;
}

/**
 * Created by huang on 2016/12/3.
 */

public class QQBuddyList extends ProtacolObjc {
  public ArrayList<QQBuddy> buddyList = new ArrayList<>();
}

           關(guān)于socket的創(chuàng)建連接和發(fā)送消息、接受消息。

import android.util.Log;
import com.example.huang.imsocket.bean.QQMessage;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by huang on 2016/12/3.
 * 連接 服務(wù)器
 */
public class QQConnection extends Thread {
  private static final String TAG = "QQConnection";
  private Socket client;
  private DataOutputStream write;
  private DataInputStream read;
  public static final String HOST = "192.168.23.48";
  public static final int POST = 5225;

  private boolean flag = true;

  private List<OnQQmwssagereceiveLisener> mOnQQmwssagereceiveLisener = new ArrayList<>();

  public void addOnQQmwssagereceiveLisener(OnQQmwssagereceiveLisener lisener) {
    mOnQQmwssagereceiveLisener.add(lisener);
  }

  public void removeOnQQmwssagereceiveLisener(OnQQmwssagereceiveLisener lisener) {
    mOnQQmwssagereceiveLisener.remove(lisener);
  }

  public interface OnQQmwssagereceiveLisener {
    public void onReiceive(QQMessage qq);
  }

  @Override
  public void run() {
    super.run();
    while (flag) {
      try {
        String utf = read.readUTF();
        QQMessage message = new QQMessage();
        QQMessage msg = (QQMessage) message.fromXml(utf);
        if (msg != null) {
          for (OnQQmwssagereceiveLisener lisner : mOnQQmwssagereceiveLisener)
            lisner.onReiceive(msg);
        }
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }

  public void connect() {
      try {
        if (client == null) {
          client = new Socket(HOST, POST);
          write = new DataOutputStream(client.getOutputStream());
          read = new DataInputStream(client.getInputStream());
          flag = true;
          this.start();
          Log.e(TAG, "connect: "+(write==null)+"---"+ (read == null));
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
  }

  public void disconnect() {
    if (client != null) {
      flag = false;
      this.stop();
      try {
        read.close();
      } catch (IOException e) {
        e.printStackTrace();
      }

      try {
        write.close();
      } catch (IOException e) {
        e.printStackTrace();
      }

      try {
        client.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }

  public void send(String xml) throws IOException {
    write.writeUTF(xml);
    write.flush();
  }

  public void send(QQMessage qq) throws IOException {
    write.writeUTF(qq.toXml());
    write.flush();
  }
}

         閃屏界面的布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:id="@+id/activity_splash"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="@mipmap/splash_bg">

  <ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:src="@mipmap/conversation_bg_logo" />
</RelativeLayout>

                閃屏界面,保持4秒鐘進入登陸界面。一般來見,閃屏界面可以加載數(shù)據(jù)、獲取版本號、更新版本等操作。這里沒有做的那么復(fù)雜。

import com.example.huang.imsocket.R;

public class SplashActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getSupportActionBar().hide();  //隱藏標(biāo)欄
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); //全屏顯示
    setContentView(R.layout.activity_splash);

    new Handler().postDelayed(new Runnable() {
      @Override
      public void run() {
        startActivity(new Intent(SplashActivity.this, LoginActivity.class));
        finish();
      }
    }, 4000);
  }
}

        登陸界面的布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#aabbdd"
  android:gravity="center"
  android:orientation="vertical">

  <TableLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@mipmap/conversation_bg_logo" />

    <TableRow
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginLeft="20dp"
      android:layout_marginRight="20dp"
      android:layout_marginTop="8dp"
      android:gravity="center_horizontal">

      <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:text="賬號:"
        android:textColor="#000" />

      <EditText
        android:id="@+id/et_accoun"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="3"
        android:gravity="center"
        android:hint="輸入賬號" />
    </TableRow>

    <TableRow
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginLeft="20dp"
      android:layout_marginRight="20dp"
      android:layout_marginTop="4dp"
      android:gravity="center_horizontal">

      <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:text="密碼:"
        android:textColor="#000" />

      <EditText
        android:id="@+id/et_pwd"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="3"
        android:gravity="center"
        android:hint="輸入密碼" />
    </TableRow>

    <Button
      android:id="@+id/btn_login"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginLeft="80dp"
      android:layout_marginRight="80dp"
      android:layout_marginTop="8dp"
      android:onClick="sendmessage"
      android:text="登錄" />

  </TableLayout>
</LinearLayout>

     登陸界面,創(chuàng)建和服務(wù)器的連接,向服務(wù)器發(fā)送登陸信息,接受服務(wù)器返回的信息。

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import com.example.huang.imsocket.R;
import com.example.huang.imsocket.bean.Myapp;
import com.example.huang.imsocket.bean.QQBuddyList;
import com.example.huang.imsocket.bean.QQMessage;
import com.example.huang.imsocket.bean.QQmessageType;
import com.example.huang.imsocket.core.QQConnection;
import com.example.huang.imsocket.service.IMService;
import com.example.huang.imsocket.util.ThreadUtils;

import java.io.IOException;

/**
 * Created by huang on 2016/12/3.
 */
public class LoginActivity extends Activity {
  private static final String TAG = "LoginActivity";

  private EditText et_accoun;
  private EditText et_pwd;

  private String accoun;
  private QQConnection conn;
  private QQConnection.OnQQmwssagereceiveLisener lisener = new QQConnection.OnQQmwssagereceiveLisener() {
    @Override
    public void onReiceive(final QQMessage qq) {

      final QQBuddyList list = new QQBuddyList();
      final QQBuddyList list2 = (QQBuddyList) list.fromXml(qq.content);
      if (QQmessageType.MSG_TYPE_BUDDY_LIST.equals(qq.type)) {
        ThreadUtils.runUIThread(new Runnable() {
          @Override
          public void run() {
            Toast.makeText(getBaseContext(), "成功", Toast.LENGTH_SHORT).show();

            Myapp.me = conn;
            Myapp.username = accoun;
            Myapp.account = accoun + "@qq.com";

            Intent intent = new Intent(LoginActivity.this, contactActivity.class);
            intent.putExtra("list", list2);
            startActivity(intent);

            Intent data = new Intent(LoginActivity.this, IMService.class);
            startService(data);
            finish();
          }
        });
      } else {
        ThreadUtils.runUIThread(new Runnable() {
          @Override
          public void run() {
            Toast.makeText(getBaseContext(), "登陸失敗", Toast.LENGTH_SHORT).show();
          }
        });
      }
    }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);
    et_accoun = (EditText) findViewById(R.id.et_accoun);
    et_pwd = (EditText) findViewById(R.id.et_pwd);
    ThreadUtils.runINThread(new Runnable() {
      @Override
      public void run() {
        try {
          conn = new QQConnection();
          conn.addOnQQmwssagereceiveLisener(lisener);
          conn.connect();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    });
  }

  public void sendmessage(View view) {
    accoun = et_accoun.getText().toString().trim();
    final String password = et_pwd.getText().toString().trim();
    Log.i(TAG, "sendmessage: " + accoun + "#" + password);
    ThreadUtils.runINThread(new Runnable() {
      @Override
      public void run() {
        QQMessage message = new QQMessage();
        message.type = QQmessageType.MSG_TYPE_LOGIN;
        message.content = accoun + "#" + password;
        String xml = message.toXml();
        if (conn != null) {
          try {
            conn.send(xml);
          } catch (IOException e) {
            e.printStackTrace();
          }
        }
      }
    });
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    conn.removeOnQQmwssagereceiveLisener(lisener);
  }
}

         好友列表界面

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#aabbcc"
  android:orientation="vertical">

  <TextView
    android:id="@+id/tv_title"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:gravity="center"
    android:text="聯(lián)系人列表"
    android:textColor="#6d00"
    android:textSize="23dp" />

  <ListView
    android:id="@+id/lv_contact"
    android:layout_width="match_parent"
    android:layout_height="match_parent"></ListView>
</LinearLayout>

         好友列表及時收到從哪個服務(wù)其發(fā)揮的好友更新信息,點擊條目跳到聊天界面。

import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.example.huang.imsocket.R;
import com.example.huang.imsocket.bean.Myapp;
import com.example.huang.imsocket.bean.QQBuddyList;
import com.example.huang.imsocket.bean.QQMessage;
import com.example.huang.imsocket.bean.QQmessageType;
import com.example.huang.imsocket.core.QQConnection;
import com.example.huang.imsocket.util.ThreadUtils;

import java.util.ArrayList;

import butterknife.Bind;
import butterknife.ButterKnife;
import cn.itcast.server.bean.QQBuddy;

/**
 * Created by huang on 2016/12/5.
 */

public class contactActivity extends Activity {
  private static final String TAG = "contactActivity";
  @Bind(R.id.tv_title)
  TextView tv_title;
  @Bind(R.id.lv_contact)
  ListView lv_contact;
  private QQBuddyList list;
  private ArrayList<QQBuddy> BuddyList = new ArrayList<>();
  private ArrayAdapter adapter = null;

  private QQConnection.OnQQmwssagereceiveLisener listener = new QQConnection.OnQQmwssagereceiveLisener() {
    @Override
    public void onReiceive(QQMessage qq) {
      if (QQmessageType.MSG_TYPE_BUDDY_LIST.equals(qq.type)) {
        QQBuddyList qqlist = new QQBuddyList();
        QQBuddyList qqm = (QQBuddyList) qqlist.fromXml(qq.content);
        BuddyList.clear();
        BuddyList.addAll(qqm.buddyList);
        ThreadUtils.runUIThread(new Runnable() {
          @Override
          public void run() {
            saveAndNotify();
          }
        });
      }
    }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_contact);
    ButterKnife.bind(this);
    Myapp.me.addOnQQmwssagereceiveLisener(listener);
    Intent intent = getIntent();
    list = (QQBuddyList) intent.getSerializableExtra("list");
    BuddyList.clear();
    BuddyList.addAll(list.buddyList);
    saveAndNotify();
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    Myapp.me.removeOnQQmwssagereceiveLisener(listener);
  }

  private void saveAndNotify() {
    if (BuddyList.size() < 1) {
      return;
    }
    if (adapter == null) {
      adapter = new ArrayAdapter<QQBuddy>(getBaseContext(), 0, BuddyList) {
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
          viewHolder holder;
          if (convertView == null) {
            convertView = View.inflate(getContext(), R.layout.item_contacts, null);
            holder = new viewHolder(convertView);
            convertView.setTag(holder);
          } else {
            holder = (viewHolder) convertView.getTag();
          }
          QQBuddy qqBuddy = BuddyList.get(position);
          holder.tv_nick.setText(qqBuddy.nick);
          holder.tv_account.setText(qqBuddy.account + "@qq.com");

          if (Myapp.username.equals(qqBuddy.account + "")) {
            holder.tv_nick.setText("[自己]");
            holder.tv_nick.setTextColor(Color.GRAY);
          } else {
            holder.tv_nick.setTextColor(Color.RED);
          }
          return convertView;
        }
      };
      lv_contact.setAdapter(adapter);

      lv_contact.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
          QQBuddy qqbuddy = BuddyList.get(position);
          if (Myapp.username.equals(qqbuddy.account + "")) {
            Toast.makeText(getBaseContext(), "不能和自己聊天", Toast.LENGTH_SHORT).show();
          } else {
            Intent intent = new Intent(contactActivity.this, ChatActivity.class);
            intent.putExtra("account", qqbuddy.account + "");
            intent.putExtra("nick", qqbuddy.nick + "");
            startActivity(intent);
          }
        }
      });
    } else {
      adapter.notifyDataSetChanged();
    }
  }

  static class viewHolder {
    @Bind(R.id.iv_contact)
    ImageView iv_contact;
    @Bind(R.id.tv_nick)
    TextView tv_nick;
    @Bind(R.id.tv_account)
    TextView tv_account;
    public viewHolder(View view) {
      ButterKnife.bind(this, view);
    }
  }
}

        聊天界面

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">

  <TextView
    android:id="@+id/tv_name"
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:background="#aa119988"
    android:gravity="center"
    android:text="和誰誰聊天中........."
    android:textSize="19dp" />

  <ListView
    android:id="@+id/lv_chat"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1" />

  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <EditText
      android:id="@+id/et_sms"
      android:layout_width="0dp"
      android:layout_height="40dp"
      android:layout_weight="1"
      android:hint="輸入聊天" />

    <Button
      android:id="@+id/btn_send"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="發(fā)送" />
  </LinearLayout>
</LinearLayout>

         聊天界面中消息接收和消息發(fā)送都需要及時更新列表。

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.example.huang.imsocket.R;
import com.example.huang.imsocket.bean.Myapp;
import com.example.huang.imsocket.bean.QQMessage;
import com.example.huang.imsocket.bean.QQmessageType;
import com.example.huang.imsocket.core.QQConnection;
import com.example.huang.imsocket.util.ThreadUtils;

import java.io.IOException;
import java.util.ArrayList;

import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;

/**
 * Created by huang on 2016/12/3.
 */
public class ChatActivity extends Activity {
  private static final String TAG = "ChatActivity";
  @Bind(R.id.tv_name)
  TextView tv_name;
  @Bind(R.id.lv_chat)
  ListView lv_chat;
  @Bind(R.id.et_sms)
  EditText et_sms;

  private ArrayAdapter<QQMessage> adapter = null;
  private ArrayList<QQMessage> list = new ArrayList<>();
  private String account;

  @OnClick(R.id.btn_send)
  public void send(View view) {
    String sendsms = et_sms.getText().toString().trim();
    if (TextUtils.isEmpty(sendsms)) {
      Toast.makeText(this, "消息不能為空", Toast.LENGTH_SHORT).show();
      return;
    }
    et_sms.setText("");
    final QQMessage qq = new QQMessage();
    qq.type = QQmessageType.MSG_TYPE_CHAT_P2P;
    qq.content = sendsms;
    qq.from = Long.parseLong(Myapp.username);
    qq.to = Long.parseLong(account);
    list.add(qq);
    setAdapteORNotify();
    ThreadUtils.runINThread(new Runnable() {
      @Override
      public void run() {
        try {
          Myapp.me.send(qq);
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    });
  }

  private QQConnection.OnQQmwssagereceiveLisener listener = new QQConnection.OnQQmwssagereceiveLisener() {
    @Override
    public void onReiceive(final QQMessage qq) {
      if (QQmessageType.MSG_TYPE_CHAT_P2P.equals(qq.type)) {
        ThreadUtils.runUIThread(new Runnable() {
          @Override
          public void run() {
            list.add(qq);
            setAdapteORNotify();
          }
        });
      }
    }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_chat);
    ButterKnife.bind(this);
    Myapp.me.addOnQQmwssagereceiveLisener(listener);
    Intent intent = getIntent();
    account = intent.getStringExtra("account");
    String nick = intent.getStringExtra("nick");
    tv_name.setText("和" + nick + "聊天中......");
    setAdapteORNotify();
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    Myapp.me.removeOnQQmwssagereceiveLisener(listener);
  }

  private void setAdapteORNotify() {
    if (list.size() < 1) {
      return;
    }
    if (adapter == null) {
      adapter = new ArrayAdapter<QQMessage>(this, 0, list) {

        @Override
        public int getViewTypeCount() {
          return 2;
        }

        @Override
        public int getItemViewType(int position) {
          QQMessage msg = list.get(position);
          long fromId = Long.parseLong(Myapp.username);
          if (fromId == msg.from) {
            return 0;
          }
          return 1;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
          int type = getItemViewType(position);
          if (type == 0) {
            viewHolder holder1 = null;
            if (convertView == null) {
              holder1 = new viewHolder();
              convertView = View.inflate(getBaseContext(), R.layout.item_sms_send, null);
              holder1.tv_send_time = (TextView) convertView.findViewById(R.id.tv_send_time);
              holder1.tv_send = (TextView) convertView.findViewById(R.id.tv_send);
              convertView.setTag(holder1);
            } else {
              holder1 = (viewHolder) convertView.getTag();
            }
            QQMessage qqMessage = list.get(position);
            holder1.tv_send_time.setText(qqMessage.sendTime);
            holder1.tv_send.setText(qqMessage.content);
            return convertView;
          } else if (type == 1) {
            viewHolder holder2 = null;
            if (convertView == null) {
              holder2 = new viewHolder();
              convertView = View.inflate(getBaseContext(), R.layout.item_sms_receive, null);
              holder2.tv_receive_time = (TextView) convertView.findViewById(R.id.tv_receive_time);
              holder2.tv_receive = (TextView) convertView.findViewById(R.id.tv_receive);
              convertView.setTag(holder2);
            } else {
              holder2 = (viewHolder) convertView.getTag();
            }
            QQMessage qqMessage = list.get(position);
            holder2.tv_receive_time.setText(qqMessage.sendTime);
            holder2.tv_receive.setText(qqMessage.content);
            return convertView;
          }
          return convertView;
        }
      };
      lv_chat.setAdapter(adapter);
    } else {
      adapter.notifyDataSetChanged();
    }

    if (lv_chat.getCount() > 0) {
      lv_chat.setSelection(lv_chat.getCount() - 1);
    }
  }

  class viewHolder {
    TextView tv_send_time;
    TextView tv_send;
    TextView tv_receive_time;
    TextView tv_receive;
  }
}

          最后可以添加一個服務(wù)當(dāng)程序退出時,接受消息。

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;

import com.example.huang.imsocket.bean.Myapp;
import com.example.huang.imsocket.bean.QQMessage;
import com.example.huang.imsocket.core.QQConnection;
import com.example.huang.imsocket.util.ThreadUtils;

/**
 * Created by huang on 2016/12/7.
 */

public class IMService extends Service {
  private QQConnection.OnQQmwssagereceiveLisener lisener = new QQConnection.OnQQmwssagereceiveLisener() {
    @Override
    public void onReiceive(final QQMessage qq) {
      ThreadUtils.runUIThread(new Runnable() {
        @Override
        public void run() {
          Toast.makeText(getBaseContext(), "收到好友消息: " + qq.content, Toast.LENGTH_SHORT).show();
        }
      });
    }
  };

  @Override
  public IBinder onBind(Intent intent) {
    return null;
  }

  @Override
  public void onCreate() {
    super.onCreate();
    Toast.makeText(getBaseContext(), "服務(wù)開啟", Toast.LENGTH_SHORT).show();
    Myapp.me.addOnQQmwssagereceiveLisener(lisener);
  }

  @Override
  public void onDestroy() {
    Myapp.me.removeOnQQmwssagereceiveLisener(lisener);
    super.onDestroy();
  }
}

      Activity和Service節(jié)點配置,以及相應(yīng)的權(quán)限。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.example.huang.imsocket">

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

    <activity android:name="com.example.huang.imsocket.activity.SplashActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    <activity
      android:name="com.example.huang.imsocket.activity.LoginActivity"
      android:theme="@android:style/Theme.NoTitleBar"></activity>
    <activity
      android:name="com.example.huang.imsocket.activity.ChatActivity"
      android:theme="@android:style/Theme.NoTitleBar"></activity>
    <activity
      android:name="com.example.huang.imsocket.activity.contactActivity"
      android:theme="@android:style/Theme.NoTitleBar"></activity>

    <service android:name=".service.IMService" />
  </application>

</manifest>

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

相關(guān)文章

  • Android自定義ProgressDialog進度等待框

    Android自定義ProgressDialog進度等待框

    這篇文章主要介紹了Android自定義ProgressDialog進度等待框,通過本文大家可以嘗試?yán)肁ndroid自定義ProgressDialog,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-01-01
  • Android 開發(fā)實現(xiàn)EditText 光標(biāo)居右顯示

    Android 開發(fā)實現(xiàn)EditText 光標(biāo)居右顯示

    這篇文章主要介紹了Android 開發(fā)實現(xiàn)EditText 光標(biāo)居右顯示的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Android 7.0中拍照和圖片裁剪適配的問題詳解

    Android 7.0中拍照和圖片裁剪適配的問題詳解

    這篇文章主要介紹了Android 7.0中拍照和圖片裁剪適配的相關(guān)問題,文中通過示例代碼介紹的很詳細(xì),對大家具有一定的參考價值,有需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-02-02
  • Android實現(xiàn)文件按時間先后順序排列顯示的示例代碼

    Android實現(xiàn)文件按時間先后順序排列顯示的示例代碼

    在很多Android應(yīng)用中,需要管理和展示本地文件,對文件按最后修改時間排序展示,能讓用戶直觀地了解文件的創(chuàng)建或修改順序,從而更方便地查找最新或最舊的文件,本文將介紹如何在Android平臺上獲取指定目錄下的文件列表,并按照時間先后排序,需要的朋友可以參考下
    2025-04-04
  • Android中Notification的用法匯總

    Android中Notification的用法匯總

    這篇文章主要介紹了Android中Notification的用法匯總的相關(guān)資料,需要的朋友可以參考下
    2016-01-01
  • 利用Flutter制作經(jīng)典貪吃蛇游戲

    利用Flutter制作經(jīng)典貪吃蛇游戲

    Flutter框架可以使用單一代碼庫為 Android、iOS、Web ,桌面平臺構(gòu)建應(yīng)用程序。本文將利用它制作經(jīng)典的貪吃蛇游戲,感興趣的小伙伴可以了解一下
    2022-04-04
  • Android仿微信清理內(nèi)存圖表動畫(解決surfaceView屏幕閃爍問題)demo實例詳解

    Android仿微信清理內(nèi)存圖表動畫(解決surfaceView屏幕閃爍問題)demo實例詳解

    本文通過實例代碼給大家講解android仿微信清理內(nèi)存圖表動畫(解決surfaceView屏幕閃爍問題)的相關(guān)資料,本文介紹的非常詳細(xì),具有參考借鑒價值,需要的朋友可以參考下
    2016-09-09
  • Android中SurfaceFlinger工作原理

    Android中SurfaceFlinger工作原理

    這篇文章介紹了Android中SurfaceFlinger的工作原理,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • Android中獲取狀態(tài)欄高度的兩種方法分享

    Android中獲取狀態(tài)欄高度的兩種方法分享

    在android應(yīng)用中,有時需要計算個View的位置,導(dǎo)致需要計算狀態(tài)欄高度。為以后方便,在此做個簡單記錄。下面這篇文章主要介紹了Android中獲取狀態(tài)欄高度的兩種方法,兩種方法分別給出了示例代碼,有需要的朋友可以參考借鑒。
    2017-02-02
  • Android編程實現(xiàn)播放音頻的方法示例

    Android編程實現(xiàn)播放音頻的方法示例

    這篇文章主要介紹了Android編程實現(xiàn)播放音頻的方法,結(jié)合實例形式分析了Android使用MediaPlayer類播放音頻的相關(guān)操作技巧,需要的朋友可以參考下
    2017-08-08

最新評論