Java 在Android线程内部(和外部)使用类变量

Java 在Android线程内部(和外部)使用类变量,java,android,multithreading,Java,Android,Multithreading,我创建了一个简单的android应用程序来控制一个有WiFi接入点的机器人。该应用程序创建一个UDP套接字和一个线程,用于侦听来自机器人的消息。收到消息后,记录机器人的IP地址和端口,并设置标志,指示机器人已连接。一旦连接,应用程序可以向机器人发送命令 当应用程序收到消息(即UdpServer类返回的socket.receive)时,我观察到isConnected标志被设置为true(在调试器中运行)。但是,当我按下应用程序上的按钮向机器人发送命令消息时,断开连接的标志现在为false。为什么线

我创建了一个简单的android应用程序来控制一个有WiFi接入点的机器人。该应用程序创建一个UDP套接字和一个线程,用于侦听来自机器人的消息。收到消息后,记录机器人的IP地址和端口,并设置标志,指示机器人已连接。一旦连接,应用程序可以向机器人发送命令

当应用程序收到消息(即UdpServer类返回的socket.receive)时,我观察到isConnected标志被设置为true(在调试器中运行)。但是,当我按下应用程序上的按钮向机器人发送命令消息时,断开连接的标志现在为false。为什么线程中的这个变量的值与另一个类方法中的变量的值不同

源代码如下所示:

main活动

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;
}
}

Android将杀死任何使用while(true)或递归线程/任务的进程,这就是连接中断的原因。我会使用从前台开始的Intent服务。这是通知中的小图标,就像启动潘多拉一样。这是Android执行长时间运行进程的最佳方式


经过一些实验,我已经解决了这个问题。我希望这个应用程序始终是横向的,我认为以编程方式设置方向看起来是最好的。然而,也有一些不必要的副作用。问题是以下apps
OnCreate
方法中的声明:

this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setRequestedOrientation
的文档说明此调用可能导致活动重新启动。如果清单文件没有显式强制方向为横向,则活动将重新启动

如果活动重新启动,则会多次调用
OnCreate
。在这个应用程序中,
UdpServer
对象和接收线程是在
OnCreate
方法中构造的。因此,第一次创建
OnCreate
被称为
UdpServer
对象,并启动线程。第一个线程打开套接字。接下来,当活动重新启动并且第二次调用
OnCreate
时,将创建第二个
UdpServer
对象并启动第二个线程。现在,第二个
UdpServer
将无法打开套接字,因为第一个已打开套接字。因此,当接收到消息时,它们将在创建的第一个
UdpServer
实例中接收。但是,由于活动已重新启动,UI按钮按下(调用
UdpServer
class方法)将使用第二个
UdpServer
实例。因此,在
UdpServer
接收线程(第一个实例)中查看
isConnected
变量与在UI上按下按钮以使用
UdpServer
对象(第二个实例)发送消息时查看变量不同的原因


对此的简单(临时)修复方法是强制在清单中横向定位,并从
OnCreate
方法中删除对
setRequestedOrientation
的调用。这仍然存在问题,因为活动可能由于其他原因而重新创建。更理想的解决方案是将持久性数据(即UDP服务器对象)移动到应用程序类,该类在应用程序生命周期内只创建一次

我明白了。但是在这种情况下,线程仍在运行。我继续从线程内的UDP套接字接收消息。问题是“isConnected”变量在线程中的值与在sendUdpMessage方法中的值不同。