IPC跨进程交互(3)Socket的使用

来源:互联网 发布:域名价格查询 编辑:程序博客网 时间:2024/06/04 18:19

前两篇说了两种交互了,这次来说socket,socket其实在通信交互上面很常用,虽然我目前没用到过实现项目。不过也是一种常用的跨进程交互,这边就来讲解下做一个聊天室!本篇也是参考了任玉刚老师的Android艺术


开始,还是从看效果开始。


Service


Client



下面开始讲解代码。

1.Service

先创建一个SocketService的服务,代码如下

SocketService.java

package com.gjn.myservice;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.util.Log;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.io.Reader;import java.net.ServerSocket;import java.net.Socket;import java.util.ArrayList;import java.util.List;import java.util.Random;import java.util.concurrent.atomic.AtomicBoolean;public class SocketService extends Service {    private static final String TAG = "SocketService";    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);    private List<String> mDefinedMsg;    @Override    public void onCreate() {        mDefinedMsg = new ArrayList<>();        for (int i = 0; i < 10; i++) {            mDefinedMsg.add("msg_"+i);        }        new Thread(new TcpServer()).start();        super.onCreate();    }    @Override    public IBinder onBind(Intent intent) {        return null;    }    @Override    public void onDestroy() {        mIsServiceDestoryed.set(true);        super.onDestroy();    }    private class TcpServer implements Runnable {        @Override        public void run() {            ServerSocket serverSocket = null;            try {                serverSocket = new ServerSocket(8888);            } catch (IOException e) {                Log.e(TAG, "端口:8888 出错");                e.printStackTrace();                return;            }            while (!mIsServiceDestoryed.get()) {                try {                    final Socket client = serverSocket.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 {        //接受消息        InputStream is = client.getInputStream();        Reader reader = new InputStreamReader(is);        BufferedReader in = new BufferedReader(reader);        //发送消息        OutputStream os = client.getOutputStream();        OutputStreamWriter osw = new OutputStreamWriter(os);        BufferedWriter bw = new BufferedWriter(osw);        PrintWriter out = new PrintWriter(bw, true);        //循环交互中        while (!mIsServiceDestoryed.get()) {            //获取的消息            String str = in.readLine();            Log.e(TAG, "client: "+str);            if (str == null){                //离开socket                break;            }            if (str.equals("我要离开socket")) {                //离开socket                break;            }            //发送的消息            int i = new Random().nextInt(mDefinedMsg.size());            String msg = mDefinedMsg.get(i);            out.println(msg);            Log.e(TAG, "service: "+msg);        }        //离开        Log.e(TAG, "responseClient: 退出socket");        //关闭流        in.close();        out.close();        client.close();    }}


这边来说下代码

开始的onCreate创建线程和onDestroy停止交互这边就不讲解了。

只来看下如何交互的吧!

            ServerSocket serverSocket = null;            try {                serverSocket = new ServerSocket(8888);            } catch (IOException e) {                Log.e(TAG, "端口:8888 出错");                e.printStackTrace();                return;            }
创建一个ServerSocket 对象 占用8888端口

之后再开启一个新线程循环获取端口传来的BufferReader

发送消息用的是PrintWriter发送

最后交互结束后关闭使用的流


然后说下跨进程交互需要设置xml文件

Service的AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.gjn.myservice">    <permission        android:name="com.gjn.permission.A"        android:protectionLevel="normal" />    <uses-permission android:name="com.gjn.permission.A" />    <uses-permission android:name="android.permission.INTERNET" />    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity android:name=".MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <service            android:name=".MessengerService"            android:enabled="true"            android:exported="true">            <intent-filter>                <action android:name="com.gjn.myservice.Messenger_Service" />            </intent-filter>        </service>        <service            android:name=".AIDLService"            android:enabled="true"            android:exported="true">            <intent-filter>                <action android:name="com.gjn.myservice.AIDLService" />            </intent-filter>        </service>        <service            android:name=".SocketService"            android:enabled="true"            android:exported="true">            <intent-filter>                <action android:name="com.gjn.myservice.SocketService" />            </intent-filter>        </service>    </application></manifest>
设置了Action


2.Client

在来看下Client的代码吧!

首先要说下。因为用到端口,所以AndroidManifest.xml文件中必须加入权限

    <uses-permission android:name="android.permission.INTERNET"/>    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
下面是完整的AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.gjn.myclient">    <uses-permission android:name="com.gjn.permission.A" />    <uses-permission android:name="android.permission.INTERNET"/>    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>    <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.gjn.myclient.MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <activity android:name="com.gjn.myclient.MessengerActivity" />        <activity android:name="com.gjn.myclient.AIDLActivity" />        <activity android:name="com.gjn.myclient.SocketActivity" />        <activity android:name="com.gjn.myclient.BundleActivity" />    </application></manifest>

然后来创建一个SocketActivity,代码如下

布局设置


代码

<?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:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="com.gjn.myclient.SocketActivity">    <Button        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="连接服务器"        android:id="@+id/btn_link"        android:layout_centerVertical="true"        android:layout_centerHorizontal="true" />    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:id="@+id/tv_socket"        android:layout_alignParentTop="true"        android:layout_alignParentStart="true"        android:layout_alignParentEnd="true"        android:layout_above="@+id/btn_link" />    <Button        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="发送消息"        android:id="@+id/btn_send"        android:layout_below="@+id/btn_link"        android:layout_alignEnd="@+id/tv_socket" />    <Button        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="离开聊天室"        android:id="@+id/btn_quit"        android:layout_below="@+id/btn_send"        android:layout_centerHorizontal="true" />    <EditText        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:id="@+id/et_msg"        android:layout_above="@+id/btn_quit"        android:layout_alignParentStart="true"        android:layout_toStartOf="@+id/btn_send"        android:hint="输入聊天内容" /></RelativeLayout>

SocketActivity.java

package com.gjn.myclient;import android.content.Intent;import android.os.Handler;import android.os.Message;import android.os.SystemClock;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.text.TextUtils;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import android.widget.Toast;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.io.Reader;import java.net.Socket;public class SocketActivity extends AppCompatActivity {    private static final String TAG = "SocketActivity";    private TextView tv_socket;    private EditText et_msg;    private Button btn_link;    private Button btn_send;    private Button btn_quit;    private Socket mClientSocket;    private PrintWriter mPrintWriter;    private Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case 1:                    tv_socket.setText(tv_socket.getText() + (String) msg.obj);                    break;                case 2:                    tv_socket.setText("欢迎来到聊天室");                    Toast.makeText(SocketActivity.this, "link susuccess", Toast.LENGTH_SHORT).show();                    break;                default:                    super.handleMessage(msg);            }        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_socket);        findview();        onclick();        Intent intent = new Intent();        intent.setAction("com.gjn.myservice.SocketService");        intent.setPackage("com.gjn.myservice");        startService(intent);    }    private void findview() {        tv_socket = (TextView) findViewById(R.id.tv_socket);        et_msg = (EditText) findViewById(R.id.et_msg);        btn_link = (Button) findViewById(R.id.btn_link);        btn_send = (Button) findViewById(R.id.btn_send);        btn_quit = (Button) findViewById(R.id.btn_quit);    }    private void onclick() {        btn_link.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                new Thread(new Runnable() {                    @Override                    public void run() {                        connectService();                    }                }).start();            }        });        btn_send.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                final String send = et_msg.getText().toString();                if (!TextUtils.isEmpty(send) && mPrintWriter != null) {                    mPrintWriter.println(send);                    et_msg.setText("");                    tv_socket.setText(tv_socket.getText() + "\nself: " + send);                }            }        });        btn_quit.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                if (mPrintWriter != null) {                    close();                    tv_socket.setText("");                    Log.e(TAG, "link disconnect");                    Toast.makeText(SocketActivity.this, "link disconnect", Toast.LENGTH_SHORT).show();                }            }        });    }    private void close() {        mPrintWriter.println("我要离开socket");        mPrintWriter.close();        mPrintWriter = null;        try {            mClientSocket.shutdownInput();            mClientSocket.close();        } catch (IOException e) {            e.printStackTrace();        }        mClientSocket = null;    }    private Socket link() {        while (mClientSocket == null) {            try {                mClientSocket = new Socket("localhost", 8888);                OutputStream os = mClientSocket.getOutputStream();                OutputStreamWriter osw = new OutputStreamWriter(os);                BufferedWriter bw = new BufferedWriter(osw);                mPrintWriter = new PrintWriter(bw, true);                mHandler.sendEmptyMessage(2);                Log.e(TAG, "link susuccess");                return mClientSocket;            } catch (IOException e) {                SystemClock.sleep(1000);                e.printStackTrace();            }        }        return null;    }    private void connectService() {        link();        try {            //获取服务器的返回消息            InputStream is = mClientSocket.getInputStream();            Reader reader = new InputStreamReader(is);            BufferedReader in = new BufferedReader(reader);            while (!SocketActivity.this.isFinishing()) {                String msg = in.readLine();                if (msg != null) {                    mHandler.obtainMessage(1, "\nservice: " + msg).sendToTarget();                }            }            if (mPrintWriter != null){                mPrintWriter.close();            }            in.close();            if (mClientSocket != null){                mClientSocket.close();            }        } catch (IOException e) {            e.printStackTrace();        }    }    @Override    protected void onDestroy() {        if (mClientSocket != null) {            try {                mClientSocket.shutdownInput();                mClientSocket.close();                Log.e(TAG, "onDestroy: mClientSocket disconnect");            } catch (IOException e) {                e.printStackTrace();            }        }        super.onDestroy();    }}

下面也来讲解下代码

首先是连接

    private Socket link() {        while (mClientSocket == null) {            try {                mClientSocket = new Socket("localhost", 8888);                OutputStream os = mClientSocket.getOutputStream();                OutputStreamWriter osw = new OutputStreamWriter(os);                BufferedWriter bw = new BufferedWriter(osw);                mPrintWriter = new PrintWriter(bw, true);                mHandler.sendEmptyMessage(2);                Log.e(TAG, "link susuccess");                return mClientSocket;            } catch (IOException e) {                SystemClock.sleep(1000);                e.printStackTrace();            }        }        return null;    }
每1s一次的重新创建Socket,当然是如果没有创建成功的话!


这边也来说下交互,发现在Client中获取消息也变成了BufferReader,发送消息也是PrintWriter

        try {            //获取服务器的返回消息            InputStream is = mClientSocket.getInputStream();            Reader reader = new InputStreamReader(is);            BufferedReader in = new BufferedReader(reader);            while (!SocketActivity.this.isFinishing()) {                String msg = in.readLine();                if (msg != null) {                    mHandler.obtainMessage(1, "\nservice: " + msg).sendToTarget();                }            }            if (mPrintWriter != null){                mPrintWriter.close();            }            in.close();            if (mClientSocket != null){                mClientSocket.close();            }        } catch (IOException e) {            e.printStackTrace();        }

        btn_send.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                final String send = et_msg.getText().toString();                if (!TextUtils.isEmpty(send) && mPrintWriter != null) {                    mPrintWriter.println(send);                    et_msg.setText("");                    tv_socket.setText(tv_socket.getText() + "\nself: " + send);                }            }        });

说明了,交互的时候接收方都是用BufferReader接收消息,用PrintWriter发送消息

这边因为不是在主线程,所以修改UI的东西需要转回到UI线程,所以使用了mHandler来实现修改UI


在看下断开连接

        btn_quit.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                if (mPrintWriter != null) {                    close();                    tv_socket.setText("");                    Log.e(TAG, "link disconnect");                    Toast.makeText(SocketActivity.this, "link disconnect", Toast.LENGTH_SHORT).show();                }            }        });
    private void close() {        mPrintWriter.println("我要离开socket");        mPrintWriter.close();        mPrintWriter = null;        try {            mClientSocket.shutdownInput();            mClientSocket.close();        } catch (IOException e) {            e.printStackTrace();        }        mClientSocket = null;    }
这边是发送了一个消息“我要离开socket”,这是因为上面的Service有做判断,可以翻上去看下。这个是跳出交互循环的一个条件

其他就是初始化了一下Socket和PrintWriter。



至此Socket也算讲解完了。

上面其实只要Service中的循环反馈消息中,加个UI发送消息就可以实现Service自己返回消息而不是自动随即生成返回消息

//循环交互中        while (!mIsServiceDestoryed.get()) {            //获取的消息            String str = in.readLine();            Log.e(TAG, "client: "+str);            if (str == null){                //离开socket                break;            }            if (str.equals("我要离开socket")) {                //离开socket                break;            }            //发送的消息            int i = new Random().nextInt(mDefinedMsg.size());            String msg = mDefinedMsg.get(i);            out.println(msg);            Log.e(TAG, "service: "+msg);        }
这边的发送消息下面就是循环反馈。只需要改成自己输入反馈就好了!





0 0
原创粉丝点击