我创建了一个简单的Android应用程序来控制具有WiFi接入点的机器人。该应用程序创建一个UDP套接字和一个侦听来自机器人的消息的线程。收到消息后,将记录机器人的IP地址和端口,并设置一个标志,指示机器人已连接。连接后,应用程序可以向机器人发送命令。
当应用程序收到消息(即UdpServer类中的socket.receive返回)时,我观察到isConnected标志被设置为true(在调试器中运行)。但是当我按下应用程序上的按钮向机器人发送命令消息时,isConnected标志现在为false。为什么线程内部的这个变量的值和另一个类方法之间存在差异?
源代码如下所示:
MainActivity
package com.test.robotremote;
import android.os.Bundle;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.view.Menu;
import android.view.View;
public class MainActivity extends Activity {
UdpServer server = null;
enum Directions
{
STOP,
FORWARD,
BACKWARD,
LEFT,
SHARP_LEFT,
RIGHT,
SHARP_RIGHT
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Force activity to landscape orientation
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
//Create the UDP server
server = new UdpServer(49999);
//Start the RX thread
Thread serverThread = new Thread(server);
serverThread.start();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void sendCommand(Directions cmd){
byte[] msg = new byte[2];
//Set direction
msg[0] = (byte) cmd.ordinal();
//Set speed
if(cmd == Directions.STOP)
{
msg[1] = 0;
}
else
{
msg[1] = 100;
}
server.sendUdpMessage(msg, msg.length);
}
public void handleForwardButton(View view){
sendCommand(Directions.FORWARD);
}
public void handleBackwardButton(View view){
sendCommand(Directions.BACKWARD);
}
public void handleRightButton(View view){
sendCommand(Directions.RIGHT);
}
public void handleLeftButton(View view){
sendCommand(Directions.LEFT);
}
public void handleStopButton(View view){
sendCommand(Directions.STOP);
}
}
UdpServer
package com.test.robotremote;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UdpServer implements Runnable {
boolean isRunning = false;
boolean isConnected = false;
int localPort = 0;
int remotePort = 0;
InetAddress remoteIp;
byte[] rxBuffer = new byte[1500];
DatagramSocket socket = null;
public UdpServer(int port) {
localPort = port;
}
@Override
public void run() {
//Run the server receive in the background
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
try
{
//Create an RX packet buffer
DatagramPacket packet = new DatagramPacket(rxBuffer, rxBuffer.length);
//Create our servers socket
socket = new DatagramSocket(localPort);
isRunning = true;
while(isRunning)
{
//Wait for received data
socket.receive(packet);
isConnected = true;
//Record sender's IP address and port
remoteIp = packet.getAddress();
remotePort = packet.getPort();
}
//Close the socket when thread exits
if (socket != null)
{
socket.close();
}
}
catch (Throwable e)
{
e.printStackTrace();
}
}
public int sendUdpMessage(byte[] msg, int length) {
try
{
if(isConnected)
{
DatagramPacket p = new DatagramPacket(msg, length, remoteIp, remotePort);
socket.send(p);
return 1;
}
}
catch (Throwable e)
{
e.printStackTrace();
}
return 0;
}
}
答案 0 :(得分:1)
Android将终止使用while(true)或递归线程/任务的任何进程,这就是您的连接中断的原因。我使用从前台开始的Intent服务。这是您的通知中的小图标,例如当您启动Pandora时。这是Android执行长时间运行流程的最佳方式。
答案 1 :(得分:0)
经过一些实验,我发现了问题。我希望这个应用程序始终处于横向方向,我认为以编程方式设置方向看起来最好。但是,有一些不必要的副作用。问题是以下应用OnCreate
方法中的声明:
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setRequestedOrientation
的文档说明此调用可能可能导致重新启动活动。如果清单文件未明确强制方向为横向,则活动将重新启动。
如果重新启动活动,则会多次调用OnCreate
。在这个应用程序中,UdpServer
对象和接收线程是在OnCreate
方法中构造的。因此,第一次OnCreate
被称为UdpServer
对象被创建并且线程被启动。第一个线程打开套接字。接下来,当重新启动活动并第二次调用OnCreate
时,将创建第二个UdpServer
对象并启动第二个线程。现在第二个UdpServer
将无法打开套接字,因为第一个已打开套接字。因此,当收到消息时,它们将在创建的第一个UdpServer
实例中接收。但是,由于活动重新启动,UI按钮按下(调用UdpServer
类方法)正在使用第二个UdpServer
实例。因此,当在isConnected
接收线程(第一个实例)中查看时UdpServer
变量与在UI上按下按钮以使用UdpServer
对象发送消息时的原因不同(第二个实例) )。
对此的简单(临时)修复是强制清单中的方向为横向,并从setRequestedOrientation
方法中删除对OnCreate
的调用。由于可以出于其他原因重新创建活动,因此仍然存在问题。更理想的解决方案是将持久数据(即UDP服务器对象)移动到应用程序类,该应用程序类将仅为应用程序生存期创建一次。