Android IPC 通信方式介绍

01.使用Intent

  • 1.Activity,Service,Receiver 都支持在 Intent 中传递 Bundle 数据,而 Bundle 实现了 Parcelable 接口,可以在不同的进程间进行传输。
  • 2.在一个进程中启动了另一个进程的 Activity,Service 和 Receiver ,可以在 Bundle 中附加要传递的数据通过 Intent 发送出去。

02.使用文件共享

  • 1.Windows 上,一个文件如果被加了排斥锁会导致其他线程无法对其进行访问,包括读和写;而 Android 系统基于 Linux ,使得其并发读取文件没有限制地进行,甚至允许两个线程同时对一个文件进行读写操作,尽管这样可能会出问题。
  • 2.可以在一个进程中序列化一个对象到文件系统中,在另一个进程中反序列化恢复这个对象(注意:并不是同一个对象,只是内容相同。)。
  • 3.SharedPreferences 是个特例,系统对它的读 / 写有一定的缓存策略,即内存中会有一份 ShardPreferences 文件的缓存,系统对他的读 / 写就变得不可靠,当面对高并发的读写访问,SharedPreferences 有很多大的几率丢失数据。因此,IPC 不建议采用 SharedPreferences。

03.使用Messenger

  • Messenger 是一种轻量级的 IPC 方案,它的底层实现是 AIDL ,可以在不同进程中传递 Message 对象,它一次只处理一个请求,在服务端不需要考虑线程同步的问题,服务端不存在并发执行的情形。

  • 服务端进程:

    • 服务端创建一个 Service 来处理客户端请求,同时通过一个 Handler 对象来实例化一个 Messenger 对象,然后在 Service 的 onBind 中返回这个 Messenger 对象底层的 Binder 即可。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public class MessengerService extends Service {

    private static final String TAG = MessengerService.class.getSimpleName();

    private class MessengerHandler extends Handler {

    /**
    * @param msg
    */
    @Override
    public void handleMessage(Message msg) {

    switch (msg.what) {
    case Constants.MSG_FROM_CLIENT:
    Log.d(TAG, "receive msg from client: msg = [" + msg.getData().getString(Constants.MSG_KEY) + "]");
    Toast.makeText(MessengerService.this, "receive msg from client: msg = [" + msg.getData().getString(Constants.MSG_KEY) + "]", Toast.LENGTH_SHORT).show();
    Messenger client = msg.replyTo;
    Message replyMsg = Message.obtain(null, Constants.MSG_FROM_SERVICE);
    Bundle bundle = new Bundle();
    bundle.putString(Constants.MSG_KEY, "我已经收到你的消息,稍后回复你!");
    replyMsg.setData(bundle);
    try {
    client.send(replyMsg);
    } catch (RemoteException e) {
    e.printStackTrace();
    }
    break;
    default:
    super.handleMessage(msg);
    }
    }
    }

    private Messenger mMessenger = new Messenger(new MessengerHandler());


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
    return mMessenger.getBinder();
    }
    }
  • 客户端进程:

    • 首先绑定服务端 Service ,绑定成功之后用服务端的 IBinder 对象创建一个 Messenger ,通过这个 Messenger 就可以向服务端发送消息了,消息类型是 Message 。如果需要服务端响应,则需要创建一个 Handler 并通过它来创建一个 Messenger(和服务端一样),并通过 Message 的 replyTo 参数传递给服务端。服务端通过 Message 的 replyTo 参数就可以回应客户端了。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private Messenger mGetReplyMessenger = new Messenger(new MessageHandler());
    private Messenger mService;

    private class MessageHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
    switch (msg.what) {
    case Constants.MSG_FROM_SERVICE:
    Log.d(TAG, "received msg form service: msg = [" + msg.getData().getString(Constants.MSG_KEY) + "]");
    Toast.makeText(MainActivity.this, "received msg form service: msg = [" + msg.getData().getString(Constants.MSG_KEY) + "]", Toast.LENGTH_SHORT).show();
    break;
    default:
    super.handleMessage(msg);
    }
    }
    }



    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    }

    public void bindService(View v) {
    Intent mIntent = new Intent(this, MessengerService.class);
    bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    public void sendMessage(View v) {
    Message msg = Message.obtain(null,Constants.MSG_FROM_CLIENT);
    Bundle data = new Bundle();
    data.putString(Constants.MSG_KEY, "Hello! This is client.");
    msg.setData(data);
    msg.replyTo = mGetReplyMessenger;
    try {
    mService.send(msg);
    } catch (RemoteException e) {
    e.printStackTrace();
    }

    }

    @Override
    protected void onDestroy() {
    unbindService(mServiceConnection);
    super.onDestroy();
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() {
    /**
    * @param name
    * @param service
    */
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
    mService = new Messenger(service);
    Message msg = Message.obtain(null,Constants.MSG_FROM_CLIENT);
    Bundle data = new Bundle();
    data.putString(Constants.MSG_KEY, "Hello! This is client.");
    msg.setData(data);
    //
    msg.replyTo = mGetReplyMessenger;
    try {
    mService.send(msg);
    } catch (RemoteException e) {
    e.printStackTrace();
    }

    }

    /**
    * @param name
    */
    @Override
    public void onServiceDisconnected(ComponentName name) {


    }
    };
    }
  • 注意:客户端和服务端是通过拿到对方的 Messenger 来发送 Message 的。

    • 客户端通过 bindService onServiceConnected 而服务端通过 message.replyTo 来获得对方的 Messenger 。Messenger中有一个Hanlder以串行的方式处理队列中的消息。不存在并发执行,因此我们不用考虑线程同步的问题。

04.使用AIDL

  • Messenger 是以串行的方式处理客户端发来的消息,如果大量消息同时发送到服务端,服务端只能一个一个处理,所以大量并发请求就不适合用Messenger,而且Messenger只适合传递消息,不能跨进程调用服务端的方法。
  • AIDL 可以解决并发和跨进程调用方法的问题,要知道Messenger本质上也是AIDL,只不过系统做了封装方便上层的调用而已。

4.1 AIDL文件支持的数据类型

  • 基本数据类型
  • StringCharSequence
  • ArrayList ,里面的元素必须能够被 AIDL 支持;
  • HashMap ,里面的元素必须能够被 AIDL 支持;
  • Parcelable ,实现 Parcelable 接口的对象;
    注意:如果 AIDL 文件中用到了自定义的 Parcelable 对象,必须新建一个和它同名的 AIDL 文件。
  • AIDL ,AIDL 接口本身也可以在 AIDL 文件中使用。

4.2 服务端

  • 服务端创建一个 Service 用来监听客户端的连接请求,然后创建一个 AIDL 文件,将暴露给客户端的接口在这个 AIDL 文件中声明,最后在 Service 中实现这个 AIDL 接口即可。

4.3客户端

  • 绑定服务端的 Service ,绑定成功后,将服务端返回的 Binder 对象转成 AIDL 接口所属的类型,然后就可以调用 AIDL 中的方法了。客户端调用远程服务的方法,被调用的方法运行在服务端的 Binder 线程池中,同时客户端的线程会被挂起,如果服务端方法执行比较耗时,就会导致客户端线程长时间阻塞,导致 ANR 。客户端的 onServiceConnected 和 onServiceDisconnected 方法都在 UI 线程中。

05.使用ContentProvider

  • 用于不同应用间数据共享,和 Messenger 底层实现同样是 Binder 和 AIDL,系统做了封装,使用简单。

  • 系统预置了许多 ContentProvider ,如通讯录、日程表,需要跨进程访问。使用方法:继承 ContentProvider 类实现 6 个抽象方法,这六个方法均运行在 ContentProvider 进程中,除 onCreate 运行在主线程里,其他五个方法均由外界回调运行在 Binder 线程池中。ContentProvider 的底层数据,可以是 SQLite 数据库,可以是文件,也可以是内存中的数据。

  • 详见代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    public class BookProvider extends ContentProvider {
    private static final String TAG = "BookProvider";
    public static final String AUTHORITY = "com.jc.ipc.Book.Provider";

    public static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");
    public static final Uri USER_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");

    public static final int BOOK_URI_CODE = 0;
    public static final int USER_URI_CODE = 1;
    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
    sUriMatcher.addURI(AUTHORITY, "book", BOOK_URI_CODE);
    sUriMatcher.addURI(AUTHORITY, "user", USER_URI_CODE);
    }

    private Context mContext;
    private SQLiteDatabase mDB;

    @Override
    public boolean onCreate() {
    mContext = getContext();
    initProviderData();

    return true;
    }

    private void initProviderData() {
    //不建议在 UI 线程中执行耗时操作
    mDB = new DBOpenHelper(mContext).getWritableDatabase();
    mDB.execSQL("delete from " + DBOpenHelper.BOOK_TABLE_NAME);
    mDB.execSQL("delete from " + DBOpenHelper.USER_TABLE_NAME);
    mDB.execSQL("insert into book values(3,'Android');");
    mDB.execSQL("insert into book values(4,'iOS');");
    mDB.execSQL("insert into book values(5,'Html5');");
    mDB.execSQL("insert into user values(1,'haohao',1);");
    mDB.execSQL("insert into user values(2,'nannan',0);");

    }

    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    Log.d(TAG, "query, current thread"+ Thread.currentThread());
    String table = getTableName(uri);
    if (table == null) {
    throw new IllegalArgumentException("Unsupported URI" + uri);
    }

    return mDB.query(table, projection, selection, selectionArgs, null, null, sortOrder, null);
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
    Log.d(TAG, "getType");
    return null;
    }

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
    Log.d(TAG, "insert");
    String table = getTableName(uri);
    if (table == null) {
    throw new IllegalArgumentException("Unsupported URI" + uri);
    }
    mDB.insert(table, null, values);
    // 通知外界 ContentProvider 中的数据发生变化
    mContext.getContentResolver().notifyChange(uri, null);
    return uri;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
    Log.d(TAG, "delete");
    String table = getTableName(uri);
    if (table == null) {
    throw new IllegalArgumentException("Unsupported URI" + uri);
    }
    int count = mDB.delete(table, selection, selectionArgs);
    if (count > 0) {
    mContext.getContentResolver().notifyChange(uri, null);
    }

    return count;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    Log.d(TAG, "update");
    String table = getTableName(uri);
    if (table == null) {
    throw new IllegalArgumentException("Unsupported URI" + uri);
    }
    int row = mDB.update(table, values, selection, selectionArgs);
    if (row > 0) {
    getContext().getContentResolver().notifyChange(uri, null);
    }
    return row;
    }

    private String getTableName(Uri uri) {
    String tableName = null;
    switch (sUriMatcher.match(uri)) {
    case BOOK_URI_CODE:
    tableName = DBOpenHelper.BOOK_TABLE_NAME;
    break;
    case USER_URI_CODE:
    tableName = DBOpenHelper.USER_TABLE_NAME;
    break;
    default:
    break;
    }

    return tableName;

    }
    }
  • DBOpenHelper代码如下所示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public class DBOpenHelper extends SQLiteOpenHelper {

    private static final String DB_NAME = "book_provider.db";
    public static final String BOOK_TABLE_NAME = "book";
    public static final String USER_TABLE_NAME = "user";

    private static final int DB_VERSION = 1;

    private String CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS "
    + BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";

    private String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS "
    + USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT,"
    + "sex INT)";



    public DBOpenHelper(Context context) {
    super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    db.execSQL(CREATE_BOOK_TABLE);
    db.execSQL(CREATE_USER_TABLE);

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
    }
  • 测试代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    public class ProviderActivity extends AppCompatActivity {
    private static final String TAG = ProviderActivity.class.getSimpleName();
    private TextView displayTextView;
    private Handler mHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_provider);
    displayTextView = (TextView) findViewById(R.id.displayTextView);
    mHandler = new Handler();

    getContentResolver().registerContentObserver(BookProvider.BOOK_CONTENT_URI, true, new ContentObserver(mHandler) {
    @Override
    public boolean deliverSelfNotifications() {
    return super.deliverSelfNotifications();
    }

    @Override
    public void onChange(boolean selfChange) {
    super.onChange(selfChange);
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
    Toast.makeText(ProviderActivity.this, uri.toString(), Toast.LENGTH_SHORT).show();
    super.onChange(selfChange, uri);
    }
    });




    }

    public void insert(View v) {
    ContentValues values = new ContentValues();
    values.put("_id",1123);
    values.put("name", "三国演义");
    getContentResolver().insert(BookProvider.BOOK_CONTENT_URI, values);

    }
    public void delete(View v) {
    getContentResolver().delete(BookProvider.BOOK_CONTENT_URI, "_id = 4", null);


    }
    public void update(View v) {
    ContentValues values = new ContentValues();
    values.put("_id",1123);
    values.put("name", "三国演义新版");
    getContentResolver().update(BookProvider.BOOK_CONTENT_URI, values , "_id = 1123", null);


    }
    public void query(View v) {
    Cursor bookCursor = getContentResolver().query(BookProvider.BOOK_CONTENT_URI, new String[]{"_id", "name"}, null, null, null);
    StringBuilder sb = new StringBuilder();
    while (bookCursor.moveToNext()) {
    Book book = new Book(bookCursor.getInt(0),bookCursor.getString(1));
    sb.append(book.toString()).append("\n");
    }
    sb.append("--------------------------------").append("\n");
    bookCursor.close();

    Cursor userCursor = getContentResolver().query(BookProvider.USER_CONTENT_URI, new String[]{"_id", "name", "sex"}, null, null, null);
    while (userCursor.moveToNext()) {
    sb.append(userCursor.getInt(0))
    .append(userCursor.getString(1)).append(" ,")
    .append(userCursor.getInt(2)).append(" ,")
    .append("\n");
    }
    sb.append("--------------------------------");
    userCursor.close();
    displayTextView.setText(sb.toString());
    }
    }

06.使用Socket

Socket起源于 Unix,而 Unix 基本哲学之一就是“一切皆文件”,都可以用“打开 open –读写 write/read –关闭 close ”模式来操作。Socket 就是该模式的一个实现,网络的 Socket 数据传输是一种特殊的 I/O,Socket 也是一种文件描述符。Socket 也具有一个类似于打开文件的函数调用: Socket(),该函数返回一个整型的Socket 描述符,随后的连接建立、数据传输等操作都是通过该 Socket 实现的。
常用的 Socket 类型有两种:流式 Socket(SOCK_STREAM)和数据报式 Socket(SOCK_DGRAM)。流式是一种面向连接的 Socket,针对于面向连接的 TCP 服务应用;数据报式 Socket 是一种无连接的 Socket ,对应于无连接的 UDP 服务应用。

  • Socket 本身可以传输任意字节流。谈到Socket,就必须要说一说 TCP/IP 五层网络模型:
    • 应用层:规定应用程序的数据格式,主要的协议 HTTP,FTP,WebSocket,POP3 等;
    • 传输层:建立“端口到端口” 的通信,主要的协议:TCP,UDP;
    • 网络层:建立”主机到主机”的通信,主要的协议:IP,ARP ,IP 协议的主要作用:一个是为每一台计算机分配 IP 地址,另一个是确定哪些地址在同一子网;
    • 数据链路层:确定电信号的分组方式,主要的协议:以太网协议;
    • 物理层:负责电信号的传输。
  • Socket 是连接应用层与传输层之间接口(API)。
    • 网络模型

5.1Client 端代码:

  • 代码如下所示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    public class TCPClientActivity extends AppCompatActivity implements View.OnClickListener{

    private static final String TAG = "TCPClientActivity";
    public static final int MSG_RECEIVED = 0x10;
    public static final int MSG_READY = 0x11;
    private EditText editText;
    private TextView textView;
    private PrintWriter mPrintWriter;
    private Socket mClientSocket;
    private Button sendBtn;
    private StringBuilder stringBuilder;
    private Handler mHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
    switch (msg.what) {
    case MSG_READY:
    sendBtn.setEnabled(true);
    break;
    case MSG_RECEIVED:
    stringBuilder.append(msg.obj).append("\n");
    textView.setText(stringBuilder.toString());
    break;
    default:
    super.handleMessage(msg);
    }

    }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.tcp_client_activity);
    editText = (EditText) findViewById(R.id.editText);
    textView = (TextView) findViewById(R.id.displayTextView);
    sendBtn = (Button) findViewById(R.id.sendBtn);
    sendBtn.setOnClickListener(this);
    sendBtn.setEnabled(false);
    stringBuilder = new StringBuilder();

    Intent intent = new Intent(TCPClientActivity.this, TCPServerService.class);
    startService(intent);

    new Thread(){
    @Override
    public void run() {
    connectTcpServer();
    }
    }.start();
    }


    private String formatDateTime(long time) {
    return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));
    }

    private void connectTcpServer() {
    Socket socket = null;
    while (socket == null) {
    try {
    socket = new Socket("localhost", 8888);
    mClientSocket = socket;
    mPrintWriter = new PrintWriter(new BufferedWriter(
    new OutputStreamWriter(socket.getOutputStream())
    ), true);
    mHandler.sendEmptyMessage(MSG_READY);
    } catch (IOException e) {
    e.printStackTrace();
    }
    }

    // receive message
    BufferedReader bufferedReader = null;
    try {
    bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    } catch (IOException e) {
    e.printStackTrace();
    }
    while (!isFinishing()) {
    try {
    String msg = bufferedReader.readLine();
    if (msg != null) {
    String time = formatDateTime(System.currentTimeMillis());
    String showedMsg = "server " + time + ":" + msg
    + "\n";
    mHandler.obtainMessage(MSG_RECEIVED, showedMsg).sendToTarget();
    }
    } catch (IOException e) {
    e.printStackTrace();
    }

    }
    }

    @Override
    public void onClick(View v) {
    if (mPrintWriter != null) {
    String msg = editText.getText().toString();
    mPrintWriter.println(msg);
    editText.setText("");
    String time = formatDateTime(System.currentTimeMillis());
    String showedMsg = "self " + time + ":" + msg + "\n";
    stringBuilder.append(showedMsg);

    }

    }

    @Override
    protected void onDestroy() {
    if (mClientSocket != null) {
    try {
    mClientSocket.shutdownInput();
    mClientSocket.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    super.onDestroy();
    }
    }

5.2 Server 端代码

  • 代码如下所示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    public class TCPServerService extends Service {
    private static final String TAG = "TCPServerService";
    private boolean isServiceDestroyed = false;
    private String[] mMessages = new String[]{
    "Hello! Body!",
    "用户不在线!请稍后再联系!",
    "请问你叫什么名字呀?",
    "厉害了,我的哥!",
    "Google 不需要科学上网是真的吗?",
    "扎心了,老铁!!!"
    };


    @Override
    public void onCreate() {
    new Thread(new TCPServer()).start();
    super.onCreate();
    }

    @Override
    public void onDestroy() {
    isServiceDestroyed = true;
    super.onDestroy();
    }

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

    private class TCPServer implements Runnable {

    @Override
    public void run() {
    ServerSocket serverSocket = null;
    try {
    serverSocket = new ServerSocket(8888);
    } catch (IOException e) {
    e.printStackTrace();
    return;
    }
    while (!isServiceDestroyed) {
    // receive request from client
    try {
    final Socket client = serverSocket.accept();
    Log.d(TAG, "=============== accept ==================");
    new Thread(){
    @Override
    public void run() {
    try {
    responseClient(client);
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }.start();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }

    }
    }


    private void responseClient(Socket client) throws IOException {
    //receive message
    BufferedReader in = new BufferedReader(
    new InputStreamReader(client.getInputStream()));
    //send message
    PrintWriter out = new PrintWriter(
    new BufferedWriter(
    new OutputStreamWriter(
    client.getOutputStream())),true);
    out.println("欢迎来到聊天室!");

    while (!isServiceDestroyed) {
    String str = in.readLine();
    Log.d(TAG, "message from client: " + str);
    if (str == null) {
    return;
    }
    Random random = new Random();
    int index = random.nextInt(mMessages.length);
    String msg = mMessages[index];
    out.println(msg);
    Log.d(TAG, "send Message: " + msg);
    }
    out.close();
    in.close();
    client.close();

    }
    }