IPC process communication mode in Android Part 5

Time:2021-8-6

This article is a reprint of the article. You can get the source code by reading the original text. There is a link to the original text at the end of the article

PS: This article is about inter process communication using socket, and the demo is written in kotlin language

1. Using socket

Socket is called “socket” in Chinese. It is an intermediate software abstraction layer for the communication between the application layer and the TCP / IP protocol family. It is expressed as a programming interface (API) encapsulating the TCP / IP protocol family;

It is divided into streaming socket and user datagram socket, which correspond to TCP and UDP protocols in the transmission control layer of the network respectively.

TCP protocol is a connection oriented protocol, which provides stable two-way communication function. The establishment of connection can be completed only after “three handshakes”, in order to provide stable data transmission function; In order to ensure the reliable transmission of data packets, TCP will give each packet a sequence number. At the same time, this sequence number also ensures that the host sent to the receiving end can receive in order, and then the host at the receiving end sends back a corresponding confirmation character for the successfully received data packets. If the host at the sending end does not receive the confirmation character within a reasonable round-trip delay, Then the corresponding packet is considered lost and will be retransmitted; UDP is a connectionless protocol that does not guarantee reliability. UDP does not provide packet Grouping, assembly and sorting, that is, it is impossible to know whether it arrives safely and completely, but UDP has better efficiency in performance.

When we use socket for IPC communication, it also belongs to network operation, which is likely to be time-consuming. Therefore, when receiving or sending data, we should try to use sub threads to operate, because placing it in the main thread will affect the response efficiency of the program, and we should not access the network in the main thread in terms of performance; Let’s write a demo;

(1) On the server side, create a KT class myservice (package name com. Xe. Ipcservice) and inherit the service:

class MyService: Service() {

private var mIsServiceDestoryed = false
private val TAG = "MyService"
override fun onBind(intent: Intent?): IBinder {
    return null!!
}

override fun onDestroy() {
    super.onDestroy()
    mIsServiceDestoryed = true
}

override fun onCreate() {
    super.onCreate()
    Thread(TcpServer()).start()
}

private fun recevi(client: Socket) {
    var inB: BufferedReader? = null
    var out: PrintWriter? = null
    try {
        inB = BufferedReader(InputStreamReader(client.getInputStream()))
        out = PrintWriter(BufferedWriter(OutputStreamWriter(client.getOutputStream())), true)
        var msg: String = ""
        while (!mIsServiceDestoryed) {
            Thread.sleep(50)
            msg = inB!!.readLine()
            if (msg != null) {
                Var s: String = "the server has received a message and is preparing to send it back ------" + MSG
                Log.d(TAG, s)
                out.println(s)
            } else {
                Log.d(TAG, "msg == null")
            }
        }
    } catch (e: IOException) {
        e.printStackTrace()
    } catch (e: InterruptedException) {
        e.printStackTrace()
    } finally {
        if (out != null) {
            out.close()
        }
        if (inB != null) {
            try {
                inB.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }

        }
    }
}

internal inner class TcpServer : Runnable {
    override fun run() {
        var serverSocket: ServerSocket? = null
        try {
            serverSocket = ServerSocket(8083)
        } catch (e: IOException) {
            e.printStackTrace()
        }

        while (!mIsServiceDestoryed) {
            try {
                val client = serverSocket!!.accept()
                recevi(client)
            } catch (e: IOException) {
                e.printStackTrace()
            }

        }
    }
}

}
Here, our server uses port 8083. At the beginning, we start a sub thread, create a ServerSocket object, and wait for the client to connect. When the client connects successfully, we obtain the input stream BufferedReader and output stream printwriter through the ServerSocket object; After receiving the data sent by the client through BufferedReader, the modified content is sent to the client by printwriter.

(2) Client, create a KT type activity named clientactivity (package name com. Xe. Ipcdemo5):

class ClientActivity : AppCompatActivity() {

var mBtnConnect: Button? = null
var mTvMessage: TextView? = null
var mBtnSend: Button? = null
var mH: Handler? = null
var mReceiveThread: Thread? = null
var mPrintWriter: PrintWriter? = null
var mClientSocket: Socket? = null
var isThreadActive: Boolean = true
Var mdefinedmessages = arrayof ("Hello!"“ What's your name "," nice weather today "," tell you a joke "," you can chat with many people ")
companion object {
    var MESSAGE_SOCKET_CONNECTED: Int = 1
    var UPDATE_VIEW: Int = 2
    var TAG: String = "ClientActivity"
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_client)
    init();
    startMyService()
}

fun startMyService() {
    startService(Intent(this, MyService::class.java))
}

fun init() {
    mBtnConnect = findViewById(R.id.btn_connect)
    mTvMessage = findViewById(R.id.tv_message)
    mBtnSend = findViewById(R.id.btn_send);
    mH = MyHandler();
    mReceiveThread = ReceiveThread();
}

fun onClick(v: View) {
    if (v.id == R.id.btn_connect) {
        connect(v)
    } else if (v.id == R.id.btn_send) {
        sendMessage(v)
    }
}

fun sendMessage(v: View) {
    mBtnSend!!.isEnabled = false
    var t: Thread = SendThread()
    t.start()

}

inner class SendThread : Thread() {
    override fun run() {
        super.run()
        try {
            var index: Int = Random().nextInt(mDefinedMessages.size)
            if (mPrintWriter != null) {
                mPrintWriter!!.println(mDefinedMessages[index])
            } else {
                Log.d(TAG,"mPrintWriter == null")
            }
        } catch (e: Exception) {

        } finally {
            mH!!.sendEmptyMessage(UPDATE_VIEW)
        }
    }
}

fun connect(v: View) {
    mReceiveThread!!.start()
    v.isEnabled = false
}

inner class MyHandler : Handler() {
    override fun handleMessage(msg: Message?) {
        super.handleMessage(msg)
        if (msg!!.what == MESSAGE_SOCKET_CONNECTED) {
            var message: String = msg!!.obj as String
            var mTvContent: String = mTvMessage!!.text.toString()
            mTvContent = mTvContent + "\n" + message
            mTvMessage!!.setText(mTvContent)
        } else if (msg!!.what == UPDATE_VIEW){
            mBtnSend!!.isEnabled = true
        }
    }
}

inner class ReceiveThread : Thread() {
    override fun run() {
        super.run()
        var socket: Socket? = null
        while (socket == null) {
            try {
                socket = Socket("127.0.0.1", 8083);
                mClientSocket = socket;
                mPrintWriter = PrintWriter(BufferedWriter(OutputStreamWriter(socket.getOutputStream())), true);
            } catch (e: IOException) {
                e.printStackTrace();
            }
        }
        var br: BufferedReader? = null
        try {
            br = BufferedReader(InputStreamReader(socket.getInputStream()));
            while (isThreadActive) {
                var msg = br!!.readLine()
                Thread.sleep(500);
                if (msg != null) {
                    var message: Message = Message.obtain();
                    message.what = MESSAGE_SOCKET_CONNECTED
                    message.obj = msg
                    mH!!.sendMessage(message);
                }
            }

        } catch (e: IOException) {
            e.printStackTrace();
        } catch (e: InterruptedException) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (e: IOException) {
                    e.printStackTrace();
                }
            }
        }
        try {
            socket.close();
        } catch (e: IOException) {
            e.printStackTrace();
        }
    }
}

override fun onDestroy() {
    super.onDestroy()
    isThreadActive = false;
    mReceiveThread = null
    if (mPrintWriter != null) {
        mPrintWriter!!.close()
        mPrintWriter = null
    }
    if (mClientSocket != null) {
        try {
            mClientSocket!!.shutdownInput()
            mClientSocket!!.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }

    }
}

}

Layout file activity corresponding to clientactivity_ The client is as follows:

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.xe.ipcdemo5.ClientActivity">

<Button
    android:id="@+id/btn_connect"
    android:layout_width="match_parent"
    Android: text = "connect to server"
    android:onClick="onClick"
    android:layout_height="wrap_content" />
<Button
    android:id="@+id/btn_send"
    android:layout_width="match_parent"
    Android: text = "send a message"
    android:onClick="onClick"
    android:layout_height="wrap_content" />
<TextView
    android:id="@+id/tv_message"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

</LinearLayout>
First, the client starts myservice and a process. Click the “connect to server” button to start a sub thread receivethread. The main task of the sub thread is to create a socket object through port number 8083 and obtain an input stream BufferedReader and an output stream printwriter through the socket object, Then wait to receive data through BufferedReader, switch the received data to the main thread and display it with textview; Click the “send a message” button. The main thing is to start a sub thread to send data with printwriter.

(3) Configure the androidmanifest.xml file accordingly:

<?xml version=”1.0″ encoding=”utf-8″?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android”

package="com.xe.ipcdemo5">
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".ClientActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <service android:name="com.xe.ipcservice.MyService"
        android:process=":remote">
    </service>
</application>

</manifest>

The interface at the beginning of the program is as follows:

picture

Click the “connect to server” button, and then click the “send a message” button continuously. The interface changes as follows:

picture

The log printing of the console is as follows:

picture