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

时间:2015-08-28 19:31:23

标签: java android multithreading

我创建了一个简单的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;
}
}

2 个答案:

答案 0 :(得分:1)

Android将终止使用while(true)或递归线程/任务的任何进程,这就是您的连接中断的原因。我使用从前台开始的Intent服务。这是您的通知中的小图标,例如当您启动Pandora时。这是Android执行长时间运行流程的最佳方式。

http://developer.android.com/guide/components/services.html

答案 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服务器对象)移动到应用程序类,该应用程序类将仅为应用程序生存期创建一次。