Android Studio 中的多线程多客户端

Posted

技术标签:

【中文标题】Android Studio 中的多线程多客户端【英文标题】:MultiClients with Multithreading on AndroidStudio 【发布时间】:2021-06-28 05:29:37 【问题描述】:

我正在开发一个包含三个 WiFi 模块“ESP8266-01”(它们代表三个服务器)的项目,我想同时读取所有这些模块。 我想通过在一个单独的线程上运行三个客户端来实现这一点。 我为此目的创建了一个特殊类(称为 IMU1)并在 PC 上运行它,它工作正常,但是当我尝试将此程序移动到 android 时,当我按下按钮时它给了我这个错误:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: client.app.recrive, PID: 4416
    java.lang.IllegalStateException: Could not execute method for android:onClick
        at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:414)
        at android.view.View.performClick(View.java:4438)
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119)
        at android.view.View$PerformClick.run(View.java:18422)
        at android.os.Handler.handleCallback(Handler.java:733)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:136)
        at android.app.ActivityThread.main(ActivityThread.java:5017)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
        at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:409)
        at android.view.View.performClick(View.java:4438) 
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119) 
        at android.view.View$PerformClick.run(View.java:18422) 
        at android.os.Handler.handleCallback(Handler.java:733) 
        at android.os.Handler.dispatchMessage(Handler.java:95) 
        at android.os.Looper.loop(Looper.java:136) 
        at android.app.ActivityThread.main(ActivityThread.java:5017) 
        at java.lang.reflect.Method.invokeNative(Native Method) 
        at java.lang.reflect.Method.invoke(Method.java:515) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595) 
        at dalvik.system.NativeStart.main(Native Method) 
     Caused by: android.os.NetworkOnMainThreadException
        at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1145)
        at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:163)
        at libcore.io.IoBridge.recvfrom(IoBridge.java:506)
        at java.net.PlainSocketImpl.read(PlainSocketImpl.java:488)
        at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:46)
        at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:240)
        at java.io.InputStreamReader.read(InputStreamReader.java:233)
        at java.io.BufferedReader.fillBuf(BufferedReader.java:145)
        at java.io.BufferedReader.readLine(BufferedReader.java:397)
        at client.app.recrive.IMU1.read(IMU1.java:45)
        at client.app.recrive.MainActivity.receiveData(MainActivity.java:27)
        at java.lang.reflect.Method.invokeNative(Native Method) 
        at java.lang.reflect.Method.invoke(Method.java:515) 
        at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:409) 
        at android.view.View.performClick(View.java:4438) 
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119) 
        at android.view.View$PerformClick.run(View.java:18422) 
        at android.os.Handler.handleCallback(Handler.java:733) 
        at android.os.Handler.dispatchMessage(Handler.java:95) 
        at android.os.Looper.loop(Looper.java:136) 
        at android.app.ActivityThread.main(ActivityThread.java:5017) 
        at java.lang.reflect.Method.invokeNative(Native Method) 
        at java.lang.reflect.Method.invoke(Method.java:515) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595) 
        at dalvik.system.NativeStart.main(Native Method)   

主活动:

package client.app.recrive;

import androidx.appcompat.app.AppCompatActivity;

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

import java.net.*;
import java.io.*;
import client.app.recrive.IMU1;

public class MainActivity extends AppCompatActivity 
    private Button btn;
    private TextView tv;
    private EditText txt;
    public void receiveData(View v) throws IOException , InterruptedException

       IMU1 imu = new IMU1("192.168.1.7", 8080, 1);
        imu.start();
        //wait until the connection is complete
        Thread.sleep(100);
        this.tv.setText(imu.read());
        imu.join();
        Log.d("wifi","finished");
    
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.btn = findViewById(R.id.button);
        this.txt = findViewById(R.id.text);
        this.tv = findViewById(R.id.textView);
        this.tv.setText("");
    


IMU1 类:

package client.app.recrive;

import java.net.*;
import java.io.*;
import android.util.Log;

class IMU1 extends Thread 
    private String ip = "";
    private int port = 0;
    private int id = 0;
    private float q1 = 0;
    private float q2 = 0;
    private float q3 = 0;
    private float q4 = 0;
    private Socket socket;
    private BufferedReader reader;
    private InputStream input;
    private String text;
    
    public IMU1(String ip, int port, int id) 
        this.ip = ip;
        this.port = port;
        this.id = id;
    
     

    public synchronized void initialize() 
        try 
            Socket s = new Socket(this.ip, this.port);
            this.socket = s;
            BufferedReader r = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
            this.reader = r;
             catch (IOException e)  e.printStackTrace();
        
    
    
    public String read() throws IOException  
        if ((this.reader.readLine()) == null)  ///////////////////////// The Error!
        return "Nothing!";
         else 
        return this.reader.readLine().toString();
        

    

    
    public void terminate() throws IOException 
        this.socket.close();
        this.reader.close();
        //stop Thread
        
    

    @Override
    public void run() 
        super.run();
        this.initialize();
    


布局:

<?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:id="@+id/msg"
    android:layout_
    android:layout_
    android:layout_centerHorizontal="false"
    android:layout_gravity="center"
    android:orientation="vertical"
    android:background="@color/gray"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/text"
        android:layout_
        android:layout_
        android:layout_gravity="center"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="200dp"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="30dp"
        android:ems="10"
        android:hint="Message"
        android:inputType="textPersonName"
        android:textColor="@color/purple_700" />

    <Button
        android:id="@+id/button"
        android:layout_
        android:layout_
        android:layout_gravity="center|center_horizontal"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="30dp"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="30dp"
        android:insetLeft="10dp"
        android:insetRight="10dp"
        android:onClick="receiveData"
        android:text="Recieve"
        app:backgroundTint="@color/purple_700" />

    <TextView
        android:id="@+id/textView"
        android:layout_
        android:layout_
        android:layout_gravity="center"
        android:gravity="center"
        android:text="TextView" />

</LinearLayout>

清单:

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

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

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

</manifest>

我用来测试程序的三台服务器之一:

package server;

import java.net.*;
import java.io.*;
import java.util.Date;

public class server2 

    static Socket csk;
    static byte[] data =  111 ;
    public static void main(String[] args) 
        try 
            ServerSocket server = new ServerSocket(8080);
            System.out.println("Listening for connection on port 8080 ....");
            while (true) 
                try (Socket socket = server.accept()) 
                
                    System.out.println("accepted");
                    
                    String text = "Hello! from server2\n";
                    while(true) 
                    socket.getOutputStream().write(text.getBytes("UTF-8"));
                    //Thread.sleep(100);
                    
                
            

         catch (Exception e2) 
            System.out.println("Error" + e2);
        

    


这表明错误出现在if ((this.reader.readLine()) == null) 语句中,所以我反转了条件,但它不起作用。

【问题讨论】:

您确定允许receiveData 抛出已检查的Exceptions 吗?我在问,因为它用于 onClick 属性。 @gthanop 是对的。因为您不能只向用作 onClick 侦听器的方法签名添加异常 【参考方案1】:

编辑:

不要像在 IMU1 的读取方法中那样使用 BufferedReader。你可能认为你会得到同一行两次,因为你首先检查读取的行是否为空,然后再次调用 readLine 将其转换为字符串。但是对 readLine 的第二次调用不会返回您之前检查为 null 的行。您的读取方法实际上是读取两行单独的行。 您可能想要达到以下目标:

String line = this.reader.readLine();
if (line == null) 
    return "Nothing!";
 else 
    return line;

你可以缩小到:

String line = this.reader.readLine();
return line != null ? line : "Nothing!";

上一个答案:

您通常会像这样从 BufferedReader 读取数据:

BufferedReader r = null;
try 
    r = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
    
    String line;
    while ((line = r.readLine()) != null) 
        // Do something with that line
    
 finally 
    if (r != null) r.close();

您永远不能在缓冲阅读器上调用 readLine 两次并期望总是得到相同的行。因为一旦您阅读了一行,它将转到下一行。 当没有更多要读取的行时,读取的行将返回 null(流已结束)


如果您想在字符串中获取所有行,请执行以下操作:

StringBuilder sb = new StringBuilder();
BufferedReader r = null;
try 
    r = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
    
    String line;
    while ((line = r.readLine()) != null) 
        if (sb.length() > 0) sb.append('\n');
        sb.append(line);
    
 finally 
    if (r != null) r.close();


String result = sb.toString(); // Now you read all lines into 'result'

【讨论】:

我的 read() 函数现在是这样的:` public String read() throws IOException try String line; while ((line = this.reader.readLine()) != null) return this.reader.readLine().toString(); finally if (this.reader != null) this.reader.close( ); return "something"; ` 错误消失了,但它总是给我“something”。 @MahmoudِِِAkhras Noooo @MahmoudِِِAkhras 那仍然有两行。只需在您的 read 方法中使用我在 EDIT 下编写的内容即可。这样做: String line = this.reader.readLine();返回行!= null ? line : "什么都没有!"; 这就是你所需要的 我使用的是EDIT版本,错误又回来了,但这次在线:String line = this.reader.readLine();【参考方案2】:

在你问这样的问题之前,请先了解线程和 Java 流,但这里是重新编写的代码:

这里是receiveData方法(不要只给onClick方法添加异常):

public void receiveData(View v) 
    IMU1 imu = new IMU1("192.168.1.7", 8080, 1) 
        @Override
        public void postRun(String text) 
            tv.setText(text);
            Log.d("wifi","finished");
        
    ;
    imu.start();

这是重新设计的 IMU1 类(请了解线程的工作原理):

public abstract class IMU1 extends Thread 
    private final String ip;
    private final int port;
    private final int id;

    private Socket socket;
    private BufferedReader reader;

    public IMU1(String ip, int port, int id) 
        this.ip = ip;
        this.port = port;
        this.id = id;
    

    private void initialize() throws IOException 
        socket = new Socket(ip, port);
        reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    

    private String read() throws IOException 
        String line = reader.readLine();
        return line != null ? line : "Nothing!";
    

    private void terminate() throws IOException 
        socket.close();
        reader.close();
    

    @Override
    public void run() 
        String readText;
        try 
            initialize();

            readText = read();
         catch (Throwable tr) 
            tr.printStackTrace();

            readText = "An error occurred";
         finally 
            try 
                terminate();
             catch (Throwable ignored) 
            
        

        String text = readText;

        new Handler(Looper.getMainLooper()).post(new Runnable() 
            @Override
            public void run() 
                postRun(text);
            
        );
    

    public abstract void postRun(String text);

【讨论】:

在 postRun 方法上它 syas:“方法不会从它的 supercalss 中覆盖方法” 很抱歉,我是 Java 新手(不到两个月)。 你是不是直接拷贝到你的项目中了。 (那么你在公共抽象类 IMU1 中也有 abstract 关键字吗?) 您是否将我的 IMU1 版本直接复制到您的项目中并用我的版本替换了您的 receiveData 方法?因为如果你这样做,它应该可以工作 这是 IMU1 中的导入问题,谢谢它的工作

以上是关于Android Studio 中的多线程多客户端的主要内容,如果未能解决你的问题,请参考以下文章

Android中的多线程(字节跳动)

Android 上 Unity 中的多线程

Android中的多线程开源框架

(四)Android基于UDP的多客户端语音通信

Android中的多线程编程附源代码

c++ windows中客户端服务器编程中的多线程