SoFunction
Updated on 2025-03-11

Android Socket interface implements instant messaging instance code

Android Socket interface realizes instant messaging

Recently, I have learned the knowledge of Android communication. I have made a small example to consolidate the learning content. The following content is the information I found online. I think it is very good and the knowledge is relatively comprehensive. Please take a look.

First, let’s understand the concept of instant communication. Transfer the message object through the message channel, and one account is sent to another account. As long as the account is online, the message can be obtained instantly. This is the easiest way to communicate. The message channel can be implemented by TCP/IP UDP. In layman's terms, it is a service that transmits the message object (text, audio and video, and files) that one person wants to send to another person through the message channel (C/S real-time communication). Instant communication should include four forms: online direct transmission, online proxy, offline proxy, and offline expansion. Online direct transmission refers to directly implementing point-to-point transmission without passing through the server. Online agents refer to messages passing through the server, transiting them on the server, and finally reaching the target account. Offline agent refers to the message being transferred to the target account through the server. When the other party is not online, the message is temporarily stored in the server's database and then sent online. Offline expansion refers to forwarding temporary messages to the target account in other forms, such as emails, text messages, etc.

In addition, we also need to understand the concepts related to computer networks. In the classic computer network layer four model, TCP and UDP are transport layer protocols, including message communication content. IP is a network layer protocol and is a network address. TCP/IP, namely the transmission control protocol/internet protocol, defines the standard for how the host connects to the Internet and how data is transmitted between them. Socket, also known as "socket", is an abstract layer between the application layer and the transport layer. It is used to describe IP addresses and ports. It is a handle to a communication connection. Applications usually send requests to the network or answer network requests through "sockets". It is an abstract representation of the endpoint during network communication. It abstracts the complex operations of the TCP/IP layer into several simple interfaces. The layer calls the implemented processes to communicate in the network. XMPP (Extensible Message Processing Field Protocol) is an application-layer protocol based on an extensible markup language (XML) application-layer protocol applied to instant messaging scenarios, and the underlying layer is implemented through Socket. It is used for instant messaging (IM) as well as online live detection. It facilitates quasi-instant operation between servers. This protocol may eventually allow Internet users to send instant messages to anyone else on the Internet, even if their operating system and browser are different. There are two solutions to implement instant communication in this way. One is to start with sockets and directly use the interface provided by the socket to transmit data. The second is to use open source tools (server openfire) to create connections using XMPPConnection.

XMPP is a common practice to implement instant messaging. In XMPP, all work is done by sending and receiving XMPP sections on an XMPP stream. The core XMPP toolset consists of three basic sections, namely <presence>, attendance<message>, and <iq>. An XMPP stream consists of two XML documents, with one document in each direction of communication. This document has a root element <stream:stream>, whose child elements are composed of routable sections and top-level child elements associated with the stream. The xmpp protocol also includes both client and server. The client is developed based on the Android platform. Responsible for initializing the communication process. When performing instant communication, the client is responsible for initiating a connection creation request to the server. The system establishes a connection with the Internet network through a GPRS wireless network, and realizes instant communication pins with Android clients through the server. Openfire is used as the server. Allows multiple clients to log in at the same time and connect to one server concurrently. The server authenticates the connection of each client and creates a session for the authenticated clients. The communication between the client and the server is carried out in the context of the session. The instant messaging function implemented by the asmark open source framework is used. The framework is based on the open source XMPP instant messaging protocol, adopts the C/S architecture, and connects to the server through the GPRS wireless network using the TCP protocol to set up an open source Openfn'e server as the instant messaging platform. Creation of xmpp message channel:

Configure channel information to connect first

  ConnectionConfiguration configuration = new ConnectionConfiguration(HOST, PORT),

Set Debug information and security mode

      (true);

      (),

Finally, establish the connection

      ();

Observe message changes in ContentObserver implementation class. () Get the contact list object. The general idea of ​​using the xmpp protocol to write a communication protocol can be as follows. Enter the login interface, and log in through the login method of xmppconnection, and successfully enter the main interface. The main interface contains two Fragments, which are used to display contacts and chat history respectively. Create a data observer for contacts and text messages, set up monitoring RosterListener() and ChatManagerListener() in the contacts and text messages services, accept contacts and text messages, and add relevant information to the content provider. Set a content observer in the content provider to notify the interface to update when the data changes.

The focus of this article is to use the Socket interface to achieve instant communication, because the underlying layer of most instant communication is implemented through Socket. Its basic business logic can be described as follows. After the user enters the login interface, submits the account and password. After the server determines, the relevant parameters are returned to determine the connection is successful. Enter the chat interface or friend interface. Click on the contact or chat history entry to enter the chat interface. When the mobile terminal sends a message to the server again, the server forwards the message content to the target account. Update the interface display at the same time. This completes the basic functions of instant communication. Of course, you can also add a background service to accept messages in the background when the user launches the program. It is not difficult to see that for instant messaging, there are three concerns: message channel, message content, and message object. Therefore, the main logic also revolves around these three points. The message channel realizes the sending and receiving of transmission message objects. Pass the service's address and port number for Socket(String host, int port) to create a connection. The format of the message content should be consistent with the server. When accepting data, obtain the input stream and wrap it with DataInputStream, and read the data sent by the server through the input stream. When sending data, the output stream is obtained and wrapped with DataOutputStream, and the data is sent to the server through the output stream. The message content should include sender, recipient information, data type, etc. The message object is the sender of the message and the receiver of the message. Next, we will explain in detail in the code.

Create a base class for messages, implement the conversion of xml files and strings, and use Xsream third-party jar package. In this way, when creating a message class, inheriting the method can directly implement data conversion in the class.

/**
 * Created by huang on 2016/12/3.
 */
public class ProtacolObjc implements Serializable {
  public String toXml() {
    XStream stream = new XStream();
    //Convert the root node to class name    (().getSimpleName(), ());
    return (this);
  }

  public Object fromXml(String xml) {
    XStream x = new XStream();
    (().getSimpleName(), ());
    return (xml);
  }
  
  // Create a method of converting Gson data and strings to adapt to multiple data  public String toGson() {
    Gson gson = new Gson();
    return toGson();
  }

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

Create a thread tool to specify the method to run in the child thread and the main thread. Since network operations need to be in the child thread and interface updates need to be in the main thread, the creation of thread tools can facilitate the selection of threads.

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

Tools for creating messages, including message content, message type, message province, etc. Since the content returned by the server contains the message's package name information, the package name of the message itself should remain consistent with the service.

/**
  * Created by huang on 2016/12/3.
  * Message content
  */
public class QQMessage extends ProtacolObjc {
  public String type = QQmessageType.MSG_TYPE_CHAT_P2P;// Type of data chat login  public long from = 0;// Sender account  public String fromNick = "";// Nick name  public int fromAvatar = 1;// avatar  public long to = 0; // Recipient account  public String content = ""; // Is the content of the message arbitrarily?  public String sendTime = getTime(); // Send time
  public String getTime() {
    Date date = new Date(());
     format = new ("mm-DD HH:mm:ss");
    return (date);
  }

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

/**
  * Created by huang on 2016/12/3.
  * Message type
  */
public class QQmessageType {
  public static final String MSG_TYPE_REGISTER = "register";// register  public static final String MSG_TYPE_LOGIN = "login";// Log in  public static final String MSG_TYPE_LOGIN_OUT = "loginout";// Sign out  public static final String MSG_TYPE_CHAT_P2P = "chatp2p";// chat  public static final String MSG_TYPE_CHAT_ROOM = "chatroom";// Group chat  public static final String MSG_TYPE_OFFLINE = "offline";// Offline  public static final String MSG_TYPE_SUCCESS = "success";//success  public static final String MSG_TYPE_BUDDY_LIST = "buddylist";// Friends  public static final String MSG_TYPE_FAILURE = "failure";// fail}

import ;
/*
  *The message itself includes account, avatar and nickname
  *
  */
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&lt;QQBuddy&gt; buddyList = new ArrayList&lt;&gt;();
}

About the creation of connection, sending messages and accepting messages about socket.

import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;

/**
  * Created by huang on 2016/12/3.
  * Connect to the server
  */
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&lt;OnQQmwssagereceiveLisener&gt; mOnQQmwssagereceiveLisener = new ArrayList&lt;&gt;();

  public void addOnQQmwssagereceiveLisener(OnQQmwssagereceiveLisener lisener) {
    (lisener);
  }

  public void removeOnQQmwssagereceiveLisener(OnQQmwssagereceiveLisener lisener) {
    (lisener);
  }

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

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

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

  public void disconnect() {
    if (client != null) {
      flag = false;
      ();
      try {
        ();
      } catch (IOException e) {
        ();
      }

      try {
        ();
      } catch (IOException e) {
        ();
      }

      try {
        ();
      } catch (IOException e) {
        ();
      }
    }
  }

  public void send(String xml) throws IOException {
    (xml);
    ();
  }

  public void send(QQMessage qq) throws IOException {
    (());
    ();
  }
}

Layout of the splash screen interface

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:andro
  xmlns:tools="/tools"
  android:
  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>

Skim screen interface, keep it in the login interface for 4 seconds. Generally speaking, the splash screen interface can load data, obtain version numbers, update versions and other operations. It's not as complicated as it is done here.

import ;

public class SplashActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    (savedInstanceState);
    getSupportActionBar().hide();  //Hide the bar    getWindow().setFlags(.FLAG_FULLSCREEN, .FLAG_FULLSCREEN); //Full screen display    setContentView(.activity_splash);

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

Layout of login interface

&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;LinearLayout xmlns:andro
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#aabbdd"
  android:gravity="center"
  android:orientation="vertical"&gt;

  &lt;TableLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"&gt;

    &lt;ImageView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@mipmap/conversation_bg_logo" /&gt;

    &lt;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"&gt;

      &lt;TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:text="account:"
        android:textColor="#000" /&gt;

      &lt;EditText
        android:
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="3"
        android:gravity="center"
        android:hint="Enter an account" /&gt;
    &lt;/TableRow&gt;

    &lt;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"&gt;

      &lt;TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:text="password:"
        android:textColor="#000" /&gt;

      &lt;EditText
        android:
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="3"
        android:gravity="center"
        android:hint="Enter Password" /&gt;
    &lt;/TableRow&gt;

    &lt;Button
      android:
      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="Log in" /&gt;

  &lt;/TableLayout&gt;
&lt;/LinearLayout&gt;

Login interface, create a connection to the server, send login information to the server, and accept the information returned by the server.

import ;
import ;
import ;
import ;
import ;
import ;
import ;

import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;

import ;

/**
 * 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  lisener = new () {
    @Override
    public void onReiceive(final QQMessage qq) {

      final QQBuddyList list = new QQBuddyList();
      final QQBuddyList list2 = (QQBuddyList) ();
      if (QQmessageType.MSG_TYPE_BUDDY_LIST.equals()) {
        (new Runnable() {
          @Override
          public void run() {
            (getBaseContext(), "success", Toast.LENGTH_SHORT).show();

             = conn;
             = accoun;
             = accoun + "@";

            Intent intent = new Intent(, );
            ("list", list2);
            startActivity(intent);

            Intent data = new Intent(, );
            startService(data);
            finish();
          }
        });
      } else {
        (new Runnable() {
          @Override
          public void run() {
            (getBaseContext(), "Login failed", Toast.LENGTH_SHORT).show();
          }
        });
      }
    }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    (savedInstanceState);
    setContentView(.activity_login);
    et_accoun = (EditText) findViewById(.et_accoun);
    et_pwd = (EditText) findViewById(.et_pwd);
    (new Runnable() {
      @Override
      public void run() {
        try {
          conn = new QQConnection();
          (lisener);
          ();
        } catch (Exception e) {
          ();
        }
      }
    });
  }

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

  @Override
  protected void onDestroy() {
    ();
    (lisener);
  }
}

Friend List Interface

&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;LinearLayout xmlns:andro
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#aabbcc"
  android:orientation="vertical"&gt;

  &lt;TextView
    android:
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:gravity="center"
    android:text="Contact List"
    android:textColor="#6d00"
    android:textSize="23dp" /&gt;

  &lt;ListView
    android:
    android:layout_width="match_parent"
    android:layout_height="match_parent"&gt;&lt;/ListView&gt;
&lt;/LinearLayout&gt;

Friend list receives update information from which friends to perform in a timely manner, click the entry to jump to the chat interface.

import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;

import ;
import ;
import ;
import ;
import ;
import ;
import ;

import ;

import ;
import ;
import ;

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

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

  private  listener = new () {
    @Override
    public void onReiceive(QQMessage qq) {
      if (QQmessageType.MSG_TYPE_BUDDY_LIST.equals()) {
        QQBuddyList qqlist = new QQBuddyList();
        QQBuddyList qqm = (QQBuddyList) ();
        ();
        ();
        (new Runnable() {
          @Override
          public void run() {
            saveAndNotify();
          }
        });
      }
    }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    (savedInstanceState);
    setContentView(.activity_contact);
    (this);
    (listener);
    Intent intent = getIntent();
    list = (QQBuddyList) ("list");
    ();
    ();
    saveAndNotify();
  }

  @Override
  protected void onDestroy() {
    ();
    (listener);
  }

  private void saveAndNotify() {
    if (() &lt; 1) {
      return;
    }
    if (adapter == null) {
      adapter = new ArrayAdapter&lt;QQBuddy&gt;(getBaseContext(), 0, BuddyList) {
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
          viewHolder holder;
          if (convertView == null) {
            convertView = (getContext(), .item_contacts, null);
            holder = new viewHolder(convertView);
            (holder);
          } else {
            holder = (viewHolder) ();
          }
          QQBuddy qqBuddy = (position);
          holder.tv_nick.setText();
          holder.tv_account.setText( + "@");

          if (( + "")) {
            holder.tv_nick.setText("[Own]");
            holder.tv_nick.setTextColor();
          } else {
            holder.tv_nick.setTextColor();
          }
          return convertView;
        }
      };
      lv_contact.setAdapter(adapter);

      lv_contact.setOnItemClickListener(new () {
        @Override
        public void onItemClick(AdapterView&lt;?&gt; parent, View view, int position, long id) {
          QQBuddy qqbuddy = (position);
          if (( + "")) {
            (getBaseContext(), "Can't chat with yourself", Toast.LENGTH_SHORT).show();
          } else {
            Intent intent = new Intent(, );
            ("account",  + "");
            ("nick",  + "");
            startActivity(intent);
          }
        }
      });
    } else {
      ();
    }
  }

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

Chat interface

&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;LinearLayout xmlns:andro
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"&gt;

  &lt;TextView
    android:
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:background="#aa119988"
    android:gravity="center"
    android:text="Chamber with whom..."
    android:textSize="19dp" /&gt;

  &lt;ListView
    android:
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1" /&gt;

  &lt;LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"&gt;

    &lt;EditText
      android:
      android:layout_width="0dp"
      android:layout_height="40dp"
      android:layout_weight="1"
      android:hint="Enter Chat" /&gt;

    &lt;Button
      android:
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="send" /&gt;
  &lt;/LinearLayout&gt;
&lt;/LinearLayout&gt;

The list needs to be updated in time for both message reception and message sending in the chat interface.

import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;

import ;
import ;
import ;
import ;
import ;
import ;

import ;
import ;

import ;
import ;
import ;

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

  private ArrayAdapter&lt;QQMessage&gt; adapter = null;
  private ArrayList&lt;QQMessage&gt; list = new ArrayList&lt;&gt;();
  private String account;

  @OnClick(.btn_send)
  public void send(View view) {
    String sendsms = et_sms.getText().toString().trim();
    if ((sendsms)) {
      (this, "Message cannot be empty", Toast.LENGTH_SHORT).show();
      return;
    }
    et_sms.setText("");
    final QQMessage qq = new QQMessage();
     = QQmessageType.MSG_TYPE_CHAT_P2P;
     = sendsms;
     = ();
     = (account);
    (qq);
    setAdapteORNotify();
    (new Runnable() {
      @Override
      public void run() {
        try {
          (qq);
        } catch (IOException e) {
          ();
        }
      }
    });
  }

  private  listener = new () {
    @Override
    public void onReiceive(final QQMessage qq) {
      if (QQmessageType.MSG_TYPE_CHAT_P2P.equals()) {
        (new Runnable() {
          @Override
          public void run() {
            (qq);
            setAdapteORNotify();
          }
        });
      }
    }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    (savedInstanceState);
    setContentView(.activity_chat);
    (this);
    (listener);
    Intent intent = getIntent();
    account = ("account");
    String nick = ("nick");
    tv_name.setText("and" + nick + "Chain...");
    setAdapteORNotify();
  }

  @Override
  protected void onDestroy() {
    ();
    (listener);
  }

  private void setAdapteORNotify() {
    if (() &lt; 1) {
      return;
    }
    if (adapter == null) {
      adapter = new ArrayAdapter&lt;QQMessage&gt;(this, 0, list) {

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

        @Override
        public int getItemViewType(int position) {
          QQMessage msg = (position);
          long fromId = ();
          if (fromId == ) {
            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 = (getBaseContext(), .item_sms_send, null);
              holder1.tv_send_time = (TextView) (.tv_send_time);
              holder1.tv_send = (TextView) (.tv_send);
              (holder1);
            } else {
              holder1 = (viewHolder) ();
            }
            QQMessage qqMessage = (position);
            holder1.tv_send_time.setText();
            holder1.tv_send.setText();
            return convertView;
          } else if (type == 1) {
            viewHolder holder2 = null;
            if (convertView == null) {
              holder2 = new viewHolder();
              convertView = (getBaseContext(), .item_sms_receive, null);
              holder2.tv_receive_time = (TextView) (.tv_receive_time);
              holder2.tv_receive = (TextView) (.tv_receive);
              (holder2);
            } else {
              holder2 = (viewHolder) ();
            }
            QQMessage qqMessage = (position);
            holder2.tv_receive_time.setText();
            holder2.tv_receive.setText();
            return convertView;
          }
          return convertView;
        }
      };
      lv_chat.setAdapter(adapter);
    } else {
      ();
    }

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

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

Finally, you can add a service to accept messages when the program exits.

import ;
import ;
import ;
import ;

import ;
import ;
import ;
import ;

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

public class IMService extends Service {
  private  lisener = new () {
    @Override
    public void onReiceive(final QQMessage qq) {
      (new Runnable() {
        @Override
        public void run() {
          (getBaseContext(), "Received a friend's message: " + , Toast.LENGTH_SHORT).show();
        }
      });
    }
  };

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

  @Override
  public void onCreate() {
    ();
    (getBaseContext(), "Service is enabled", Toast.LENGTH_SHORT).show();
    (lisener);
  }

  @Override
  public void onDestroy() {
    (lisener);
    ();
  }
}

Activity and Service node configuration, as well as corresponding permissions.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:andro
  package="">

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

    <activity android:name="">
      <intent-filter>
        <action android:name="" />

        <category android:name="" />
      </intent-filter>
    </activity>
    <activity
      android:name=""
      android:theme="@android:style/"></activity>
    <activity
      android:name=""
      android:theme="@android:style/"></activity>
    <activity
      android:name=""
      android:theme="@android:style/"></activity>

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

</manifest>

Thank you for reading, I hope it can help you. Thank you for your support for this site!