How to override a loop getting messages in a method

I have a bluetooth server receiving data from a client, a mobile phone. The code I am using is as follows

@Override
public void run() {
    try {
        this.localDevice = LocalDevice.getLocalDevice();
        this.localDevice.setDiscoverable(DiscoveryAgent.GIAC);

        this.server = (StreamConnectionNotifier) Connector.open(URL);

        while(true) {
            if(this.connection == null) {
                this.connection = this.server.acceptAndOpen();

                System.out.println("INFO: Bluetooth client connected");

                BufferedReader reader = new BufferedReader(new InputStreamReader(connection.openInputStream()));
                this.writer = new BufferedWriter(new OutputStreamWriter(connection.openOutputStream()));

                String line;
                while((line = reader.readLine()) != null) {
                    if(line.equals("--#do:disconnect")) {
                        break;
                    }

                    System.out.println("INFO: Received from Bluetooth: " + line);
                }

                System.out.println("INFO: Client disconnected");
            }
        }
    } catch(BluetoothStateException ex) {
        ex.printStackTrace();
    } catch(IOException ex) {
        ex.printStackTrace();
    }
}

      

As you can see, I have an endless loop receiving messages until it is told to stop. The loop is currently receiving all messages. There is a problem with this. The class the code is used in is the model class in MVC. In the class, I also have a method called getContacts()

. It is used to receive contacts from a phone via Bluetooth. The phone should send contacts when the server sends --#do:getcontacts

.

What I need to do is get the contacts into an ArrayList in the method getContacts()

and return it as the return value of the method so that the controller can handle the contacts.

public ArrayList<Contact> getContacts() {
    ArrayList<Contact> contacts = new ArrayList<>();

    // How do I get the contacts in the ArrayList?

    return contacts;
}

      

+3


source to share


2 answers


I will have suggestions. My examples are not working code, just a working base for you.

First, I highly recommend that you use server-side streams. Every time clients connect to the server, you create a new thread with parameters containing all the data needed to start it:

boolean running = true;    //this class variable will allow you to shut down the server correctly

public void stopServer(){    //this method will shut down the server
    this.running = false;
}

public void run() {
    ...

    while(running) {
        // if(this.connection == null) {  // I removed this line since it unnecessary, or even harmful!
        StreamConnection connection = this.server.acceptAndOpen();  //This line will block until a connection is made...
        System.out.println("INFO: Bluetooth client connected");
        Thread thread = new ServerThread(connection);
        thread.start()              //don't forget exception handling...
    } 
}

      

And in the ServerThread class, you implement these lines handling clients (not compiled code without exception handling!):

Class ServerThread extends Thread {
    StreamConnection connection;

    public ServerThread(StreamConnection connection){
        this.connection = connection;
    }

    public void run() {
        ...

        connection.close();     //closing the connection...don't forget exception handling!
        System.out.println("INFO: Client disconnected");
    }
}

      

What is the advantage of this code? Now your server can handle thousands of clients simultaneously. You've got parallelization, and how the server works! A threadless server is like socks without shoes ...

Second, if you have a Java client and a Java server, you can use a much simpler way to send your objects to the server: ObjectOutputStream / ObjectInputStream. You just send an array (I'll use ArraList as usual) containing the contacts on the server, and then you read the array. Here is the code for the server (again, no compilation and no exception handling):

Class ServerThread extends Thread {
    StreamConnection connection;

    public ServerThread(StreamConnection connection){
        this.connection = connection;
    }

    public void run() {

        BufferedInputStream bis = new BufferedInputStream(this.connection.openInputStream());
        ObjectInputStream ois = new ObjectInputStream(bis);

        ArrayList contacts = (ArrayList) ois.readObject();  //this is a cast: don't forget exception handling!
        //You could also try the method ois.readUTF(); especially if you wanna use other non-Java clients

        System.out.println("INFO: Received from Bluetooth: " + contacts);
        this.connection.close();    //closing the connection...don't forget exception handling!
        //ois.close();      //do this instead of "this.connection.close()" if you want the connection to be open...i.e. to receive more data

        System.out.println("INFO: Client disconnected");

        //here you do whatever you wanna do with the contacts array, maybe add to your other contacts?
    }
}

      

In Java, every class is an object, including ArrayList. And since the end of the object will be treated as a disconnect, you don't need to do anything else.

Third: you are using the server above not only for Bluetooth connections, but also for WLAN connections, aso. Then you can easily start different threads, for example in pseudocode if(connection.isBluetooth()){//create a thread from BluetoothThread} else if(connection.isWLAN()){//create a thread from WLANsThread}

. I don't know what your application is, but perhaps one day you would like to extend it to a desktop PC, so using a WLAN would be correct. Also, since you still need to create a check on the client ("which contacts will be sent to which server?"), No matter if it is bluetooth or WLAN, since the low range of bluetooth functions cannot give you any security;)



Fourth, finally about your question: in order to get something, you need to have a data source and / or a class variable. Here's a short example with a file that stores contacts (but it could also be a database ... local or somewhere else!):

public class MyApp(){
    ArrayList contacts;
    ...

    public void run(){                  //this happens when we start our app
        this.contacts = new ArrayList();
        FileReader fr = new FileReader ("C:\WhereverYourFileIs\Contacts.file");
        BufferedReader br = new BufferedReader(fr);
        //here you use a loop to read the contacts via "br" from the file and fill them into your array...I can't provide you more code, since the exact data structure is up to you.
    }

    //now we want to send our contacts array to the already connected server:
    public sendArrayToServer() {
        BufferedOutputStream bos = new BufferedOutputStream (this.connection.openOutputStream());
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this.contacts);
         //If you use readUTF() in the server, you need to call here something like oos.writeUTF(this.contacts.toString()); or even need to use another parser method which exactly creates the string you want.
        this.connection.close();    //closing the connection...don't forget exception handling!
        //oos.close();      //do this instead of "this.connection.close()" if you want the connection to stay open...
    }
}

      

Now on the server you have just read the contacts array as described above. What you do with these contacts is yours.

Hope this helps you understand your problems and find a solution. Programming is all about trial and error and improving your code.

EDIT:

After our discussion, I finally figured out what you want: you need a server with one thread called the BluetoothManager that communicates with another thread called the GUIController. Now that I have completed this task in my head after all, I can post it for you along with some explanations. Just keep in mind that in this case you don't need to initialize another thread on the server, since the BluetoothManager is already a thread, and in any case, you only need one connection at a time (the question remains if it is a "server", I'd rather called it "receiver"):

Public class BluetoothManager extends Thread{
    boolean running = true;    //this class variable will allow you to shut down the server correctly
    GUIController controller;

    public BluetoothManager(GUIController controller){
        this.controller = controller;  //this registers the GUIController in the BluetoothManager
    }

    public void stop(){    //this method will shut down the "server"
        this.running = false;
    }

    public void run() {
        this.localDevice = LocalDevice.getLocalDevice();
        this.localDevice.setDiscoverable(DiscoveryAgent.GIAC);
        this.server = (StreamConnectionNotifier) Connector.open(URL);
        while(running){
            StreamConnection connection = this.server.acceptAndOpen();  //This line will block until a connection is made...or running==false!
            System.out.println("INFO: Bluetooth client connected");
            BufferedInputStream bis = new BufferedInputStream(this.connection.openInputStream());
            ObjectInputStream ois = new ObjectInputStream(bis);
            ArrayList contacts = (ArrayList) ois.readObject();  //this is a cast: don't forget exception handling!
            System.out.println("INFO: Received from Bluetooth: " + contacts);
            this.connection.close();    //closing the connection...don't forget exception handling!
            System.out.println("INFO: Client disconnected");
            this.controller.refreshContacts(contacts);
        }
    } 
}

public class GUIController extends Thread implements Runnable {
    ArrayList contacts;   //also a HashMap may be appropriate
    BluetoothManager manager;

    public void run(){
        this.contacts = new ArrayList();
        FileReader fr = new FileReader ("C:\WhereverYourFileIs\Contacts.file");
        BufferedReader br = new BufferedReader(fr);
        //here you use a loop to read the contacts via "br" from the file and fill them into your array...I can't provide you more code, since the exact data structure is up to you.
    }

    public void startBluetoothManager(){    //starting the BluetoothManager
        this.manager = new BluetoothManager(this);
        this.manager.start();
    }

    public void abortBluetoothManager(){  //call this when clicking on the "Abort" button
        this.manager.stop();
        //now the next 2 lines you normally don't need...still may use it if you've problems shutting down the thread:
        // try{ this.manager.interrupt(); }   //we want to be 100% sure to shut down our thread!
        // catch(Exception e){}
        this.manager = null; //now the garbage collector can clean everything...byebye
    }

    public void refreshContacts(ArrayList contacts) {
        // synchronize(this.contactArray){  //no synchronisation needed if you have a GUI pop-up with an "Abort"-button!
        Iterator i = this.contacts.iterator();
        while(i.hasNext()){
            this.contacts.add(i.next());
        }
        //At the end you need remove the "Receiving message" pop-up together with the "Abort Receiving"-button, these are all class variables!
        // important note: If you have unique entries, you may need to replace them! In this case I suggest storing all contact objects better in a HashMap contacts, and use the unique ID as a key to find the element. And then you may prompt the user, if there are identical entries, to overwrite each entry or not. These things remain all up to you.
    }
}
//As always: This is no compiled code!!

      

The GUIController first launches the BluetoothManager with startBluetoothManager()

and does nothing other than displaying a "Receive Contacts" notification and a "Cancel Output" button. And when the BluetoothManager is complete, it just adds new contacts to the existing contacts array inside GUIController by calling refreshContacts(...)

. If you click the Abort Reveiving button, you will immediately call the method abortBluetoothManager()

that it sets running=false

in the BluetoothManager to shut down the server and end the stream.

The main problem solved by this solution is that it is not possible for two threads to communicate directly with each other! When you call thread.start()

, each thread is on its own. Therefore, there is no way for the BluetoothManager thread to describe the GUIController "I'm done!" The only thing these streams can do is use the same ressource (s) and communicate through that ressource (s). In our case, this is the contacts

-ArrayList in the GUIController, which I thought should be synchronized and could be updated by both threads (but not at the same time). And - kind of ridiculous - there is a second shared resource, this is actually a flag running

in the BluetoothManager class that can turn it off (but sync withrunning

never needed, this variable is only changed by GUIController).

Now about synchronization: I thought more about this problem and realized that you can solve your problem without the "synchronous (...)" call. So, if you don't want to sync the ArrayList, you should do it like this: while the server is running, you only see the Get Contacts popup and the Undo Checkout button. While this is happening, you just don't access the contact-ArrayList inside the GUIController. It's kind of "internal sync" that doesn't require actual Java sync. However, you can implement synchronization to be 100% sure nothing happens if you expand your application in the future.

+2


source


First of all, there are a few things in your code that need to be checked / fixed.

1- ArrayList<Contact> contacts

must be defined in your class, so a thread can access it and fill it not as a local variable in the getContacts()

method

public ArrayList<Contact> getContacts() {

    //ArrayList<Contact> contacts = new ArrayList<>();    

    return contacts;
}

      

2. You should avoid using an infinite loop in the run method to be able to stop the thread whenever you want.

//while(true)
while(isRunning) { // a flag that is set to true by default

}

      

3- Checking that the connection is equal without setting it to zero after disconnecting means that the connection will only be accepted from the first client (assuming the connection is initially set to null) and then yours just has an infinite loop, but the code is this.connection = this.server.acceptAndOpen();

bigger will not be available

if(this.connection == null) {               
  while((line = reader.readLine()) != null) {
     if(line.equals("--#do:disconnect")) {
           // You have to set it to null if you want to continue listening after disconnecting
           this.connection = null
           break;
      }
   }
 }

      



Or just remove this check altogether , I see it is useless.

Now, back to your question:

You can define your contact list as a class member for access using the run()

and methods getContacts()

. You can make it final if needed. Then fill in this list in the method run()

; what all.

eg.

 class MyServerThread implements Runnable {
    private boolean isRunning = true;
    ArrayList<Contact> contacts = new ArrayList<>();

    public ArrayList<Contact> getContacts(){
        // Make sure that your not currently updating the contacts when this method is called
        // you can define a boolean flag and/or use synchronization
        return contacts;
    }

    public void run() {

     ...

    while(isRunning ) {
            this.connection = this.server.acceptAndOpen();

            System.out.println("INFO: Bluetooth client connected");

            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.openInputStream()));
            this.writer = new BufferedWriter(new OutputStreamWriter(connection.openOutputStream()));

            // You need to remove previously received contacts before issuing a new --#do:getcontacts command                
            contacts.clear();

            String line;
            while((line = reader.readLine()) != null) {
                if(line.equals("--#do:disconnect")) {
                    break;
                }

                // Here you can parse the contact information
                String contactName = ...
                String contactPhone = ...
                contacts.add(new Contact(contactName,contactPhone));
            }

            System.out.println("INFO: Client disconnected");            
    }
} catch(BluetoothStateException ex) {
    ex.printStackTrace();
} catch(IOException ex) {
    ex.printStackTrace();
}
  }

 }

      

You don't need to use object serialization, you can create a simple protocol to send contacts from phone to PC, something similar to the commands you send, for example. --#C:name$phone

+2


source







All Articles