“21 天好习惯”第一期-12

前言

今天学习了android socket 两个app间的通信,相信很多人在学习的过程中遇到了很多问题,比如程序闪退是最为常见的,还有就是网络请求失败,聊天窗口不显示消息记录等,下面就让我与大家分享我所遇到的吧

先来看我的运行结果

android socket 实现简单聊天室

如何实现呢?

开始我是以菜鸟教程来学习的,然后我就准备去实现两个app通信的小实例,然后发现程序运行闪退,后来我发现是我忘了加网络请求的权限(注意,现在的android版本和以前的是有较大区别的,所以在以一些较旧的学习资料来学习时,不要忘了你现在使用的android版本的一些新特性),但是加了权限后还是会闪退,于是我又去查找资料发现是从Honeycomb SDK(3.0)开始,google不再允许网络请求(HTTP、Socket)等相关操作直接在Main Thread类中,所以一个APP如果在主线程中请求网络操作,将会抛出异常。Android这个设计是为了防止网络请求时间过长而导致界面假死的情况发生。那么如何解决呢?我知道的就三种方法

第一种方法

在MainActivity文件的setContentView(R.layout.activity_main)下面加上如下代码添加以下代码实现的(不推荐)

if (android.os.Build.VERSION.SDK_INT > 9) {
    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
}

第二种方法

换一个低版本的模拟器,比如我用API 30的就会闪退,我换成API 22的就不会闪退,正常运行(不推荐,还是得尝试解决问题)

第三种方法

在子线程中进行网络请求实现

看我项目结构

其中MainActivity是第三种方法实现,MainActivity2是第一种方法实现(这里我就不介绍了,很简单,就是加上面那几行代码)

注意要先运行服务端得代码,再运行客户端代码,我是在IDEA中运行的服务端代码,Android Studio中分别用两个模拟器来运行客户端代码,还有就是两个模拟器要连接同一个网络才能实现通信

服务端

Server代码如下


import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Server {
    //定义相关的参数,端口,存储Socket连接的集合,ServerSocket对象
    //以及线程池
    private static final int PORT = 12345;
    private List<Socket> mList = new ArrayList<Socket>();
    private ServerSocket server = null;
    private ExecutorService myExecutorService = null;


    public static void main(String[] args) {
        new Server();
    }

    public Server()
    {
        try
        {
            server = new ServerSocket(PORT);
            //创建线程池
            myExecutorService = Executors.newCachedThreadPool();
            System.out.println("服务端运行中...\n");
            Socket client = null;
            while(true)
            {
                client = server.accept();
                mList.add(client);
                myExecutorService.execute(new Service(client));
            }

        }catch(Exception e){e.printStackTrace();}
    }

    class Service implements Runnable
    {
        private Socket socket;
        private BufferedReader in = null;
        private String msg = "";

        public Service(Socket socket) {
            this.socket = socket;
            try
            {
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                msg = "用户:" +this.socket.getInetAddress() + "~加入了聊天室"
                        +"当前在线人数:" +mList.size();
                this.sendmsg();
            }catch(IOException e){e.printStackTrace();}
        }



        @Override
        public void run() {
            try{
                while(true)
                {
                    if((msg = in.readLine()) != null)
                    {
                        if(msg.equals("bye"))
                        {
                            System.out.println("~~~~~~~~~~~~~");
                            mList.remove(socket);
                            in.close();
                            msg = "用户:" + socket.getInetAddress()
                                    + "退出:" +"当前在线人数:"+mList.size();
                            socket.close();
                            this.sendmsg();
                            break;
                        }else{
                            msg = socket.getInetAddress() + "   说: " + msg;
                            this.sendmsg();
                        }
                    }
                }
            }catch(Exception e){e.printStackTrace();}
        }

        //为连接上服务端的每个客户端发送信息
        public void sendmsg()
        {
            System.out.println(msg);
            int num = mList.size();
            for(int index = 0;index < num;index++)
            {
                Socket mSocket = mList.get(index);
                PrintWriter pout = null;
                try {
                    pout = new PrintWriter(new BufferedWriter(
                            new OutputStreamWriter(mSocket.getOutputStream(),"UTF-8")),true);
                    pout.println(msg);
                }catch (IOException e) {e.printStackTrace();}
            }
        }

    }
}

客户端

MainActivity代码如下


import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

public class MainActivity extends AppCompatActivity{

    //定义相关变量,完成初始化
    private TextView txtshow;
    private EditText editsend;
    private Button btnsend;
    private static final String HOST = "10.128.126.170";
    private static final int PORT = 12345;
    private Socket socket = null;
    private BufferedReader in = null;
    private PrintWriter out = null;
    private String content = "";
    private StringBuilder sb = null;
    private String msg;


    //定义一个handler对象,用来刷新界面
    public Handler handler2 = new Handler() {
        public void handleMessage(Message msg) {
            if (msg.what == 0x123) {
                sb.append(content);
                txtshow.setText(sb.toString());
            }
        }

    };
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.activity_main);
        sb = new StringBuilder();
        txtshow = (TextView) findViewById(R.id.txtshow);
        editsend = (EditText) findViewById(R.id.editsend);
        btnsend = (Button) findViewById(R.id.btnsend);

        final LooperThread looperThread = new LooperThread();

        looperThread.start();

        btnsend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Message message = Message.obtain();
                String msg = editsend.getText().toString();
                message.obj = msg;
                looperThread.handler.sendMessage(message);

                new Thread() {
                    //覆写Run方法
                    @Override
                    public void run() {

                            try {
                                while (true) {
                                    if (socket.isConnected()) {
                                        if (!socket.isInputShutdown()) {
                                            if ((content = in.readLine()) != null) {
                                                content += "\n";
                                                handler2.sendEmptyMessage(0x123);
                                            }
                                        }
                                    }
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                }.start();
            }
        });
    }

    class LooperThread extends Thread {

        public Handler handler;

        @Override
        public void run() {
            super.run();
            try {
                socket = new Socket(HOST, PORT);
                in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
                Log.i("TAG", "run: "+in.readLine());
                out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
                        socket.getOutputStream())), true);
            } catch (IOException e) {
                e.printStackTrace();
            }

            Looper.prepare();

            handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    out.println(msg.obj);
                }
            };

            Looper.loop();//loop()会调用到handler的handleMessage(Message msg)方法,所以,写在下面;
        }
    }



}

MainActivity的布局文件activity.xml

<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:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="小猪简易聊天室" />
    <TextView
        android:id="@+id/txtshow"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />
    <EditText
        android:id="@+id/editsend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />
    <Button
        android:id="@+id/btnsend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="发送"
        />
</LinearLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myappserver2">
    <uses-permission android:name="android.permission.INTERNET"/>

    <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/Theme.MyApplication1">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

参考文献

菜鸟教程:7.6.2 基于TCP协议的Socket通信(1) | 菜鸟教程 (runoob.com)

大佬博客:(18条消息) Android之NetworkOnMainThreadException异常_其实并不难,是你太悲观-CSDN博客_networkonmainthreadexception

(18条消息) [Android开发那点破事]解决android.os.NetworkOnMainThreadException_llixiangjian的博客-CSDN博客

(18条消息) Android主线程发消息给子线程_iblade的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值