【问题标题】:MultiClients with Multithreading on AndroidStudioAndroid Studio 中的多线程多客户端
【发布时间】: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_width="match_parent"
    android:layout_height="match_parent"
    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_width="match_parent"
        android:layout_height="wrap_content"
        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_width="match_parent"
        android:layout_height="wrap_content"
        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_width="350dp"
        android:layout_height="wrap_content"
        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 侦听器的方法签名添加异常

标签: java android multithreading android-studio sockets


【解决方案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 中的导入问题,谢谢它的工作
猜你喜欢
  • 2010-11-25
  • 1970-01-01
  • 2012-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-17
  • 1970-01-01
相关资源
最近更新 更多