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
抛出已检查的Exception
s 吗?我在问,因为它用于 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 中的多线程多客户端的主要内容,如果未能解决你的问题,请参考以下文章