我有一台蓝牙服务器从客户端,手机接收数据。我正在使用的代码如下所示
@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();
}
}
正如你所看到的,我有一个不定式循环接收消息,直到它被告知停止。此时循环接收所有消息。这有一个问题。使用代码的类是MVC中的模型类。在课堂上我也有一个名为getContacts()
的方法。它用于通过蓝牙从手机接收联系人。当服务器发送--#do:getcontacts
时,系统会通知电话发送联系人。
我需要做的是在getContacts()
方法中获取ArrayList中的联系人,并将其作为方法的返回值返回,以便控制器可以处理联系人。
public ArrayList<Contact> getContacts() {
ArrayList<Contact> contacts = new ArrayList<>();
// How do I get the contacts in the ArrayList?
return contacts;
}
答案 0 :(得分:2)
我会给你一些建议。我的例子不是工作代码,只是你的工作基础。
首先,我强烈建议您在服务器中使用线程。每次客户端连接到服务器时,都会创建一个新线程,其中的参数包含启动它所需的所有数据:
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's 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...
}
}
在ServerThread类中,您实现了处理客户端的这些行(非编译代码,无需异常处理!):
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");
}
}
此代码的优点是什么?您的服务器现在可以同时处理一千个客户端。你有并行化,这就是服务器通常的工作方式!没有线程的服务器就像没有鞋子的袜子......
其次,如果您有Java客户端和Java服务器,则可以使用更简单的方法将对象发送到服务器:ObjectOutputStream / ObjectInputStream。您只需将包含联系人的数组(我将使用通常的ArraList)发送到服务器,然后您将读取该数组。这是服务器的代码(再次未编译且没有任何异常处理):
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?
}
}
在Java中,每个类都是一个对象,包括ArrayList。并且由于对象的结尾将被视为断开连接,因此您无需执行任何其他操作。
第三:您使用上述服务器不仅用于蓝牙连接,还用于WLAN连接,aso。然后您可以轻松地启动不同的线程,例如伪代码if(connection.isBluetooth()){//create a thread from BluetoothThread} else if(connection.isWLAN()){//create a thread from WLANsThread}
。我不知道你的应用程序是什么,但也许有一天你想将它扩展到台式PC,所以使用WLAN是正确的。另外因为你无论如何需要在客户端建立验证(“哪些联系人将被发送到哪个服务器?”),无论是蓝牙还是WLAN,因为低范围的蓝牙无法给你任何安全保障。 ;)
第四,最后关于你的问题:要获得某些东西,你需要有一个数据源和/或一个类变量。这里有一个简短的例子,里面有一个存储联系人的文件(但它也可以是一个数据库......本地或其他地方!):
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...
}
}
现在在服务器中,您刚刚读完了上面描述的contacts数组。你对这些联系人做了什么,仍然取决于你。
希望这可以帮助您了解您的问题并找到解决方案。编程完全是关于试验和错误......并改进您的代码。
修改强>
在我们讨论之后,我终于找到了你需要的东西:你需要一个名为BluetoothManager的单线程服务器,它与另一个名为GUIController的线程交互。既然我无论如何都在我脑海中实现,我可以为你发布,并附上一些解释。请注意,在这种情况下,您不需要初始化服务器中的另一个线程,因为BluetoothManager已经是一个线程,并且您无论如何只需要一个连接(问题仍然存在,如果这是一个“服务器”) ,我宁愿称之为“接收者”):
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!!
GUIController首先使用startBluetoothManager()
运行蓝牙管理器,除了显示通知“接收联系人”和“中止重新启动”按钮外,不执行任何其他操作。当BluetoothManager完成后,他只需通过调用refreshContacts(...)
将新联系人添加到GUIController内的现有contacts-array中。如果按下“Abort Reveiving”按钮,则立即调用abortBluetoothManager()
方法,该方法在BluetoothManager中设置running=false
以结束服务器并完成线程。
此解决方案解决的主要问题是:两个线程不可能直接相互通信!一旦你致电thread.start()
,每个线程都是独立的。这就是为什么BluetoothManager线程不可能告诉GUIController线程“我已经完成了!”。这些线程唯一能做的就是共享相同的资源,并通过这个资源进行通信。在我们的例子中,它是GUIController中的contacts
- ArrayList,我认为首先需要同步并且可以由两个线程更新(但不能同时更新)。而且 - 有点滑稽 - 还有第二个共享资源,它实际上是BluetoothManager类中的running
标志可以关闭它(但是从来没有任何同步running
需要,这个变量只是由GUIController改变。)
现在关于同步:我更多地考虑了这个问题并理解,你也可以在没有任何“synchronized(...)”调用的情况下解决你的问题。因此,如果您不想同步ArrayList,则必须执行以下操作:在服务器运行时,您只显示“接收联系人”弹出窗口和“中止重新启动”按钮。发生这种情况时,您永远不会访问GUIController中的contact-ArrayList。这在某种程度上是一种“内在同步”,它不需要真正的Java同步。您仍然可以实现同步,只是为了100%确保在将来扩展应用程序时没有任何反应。
答案 1 :(得分:2)
首先,您需要审核/修复代码中的一些内容
1- ArrayList<Contact> contacts
应在您的班级中定义,以便线程可以访问它并将其作为<{1}}方法中的局部变量而不是填充
getContacts()
2-你应该避免在run方法中使用无限循环,以便能够在你想要的时候停止线程。
public ArrayList<Contact> getContacts() {
//ArrayList<Contact> contacts = new ArrayList<>();
return contacts;
}
3-检查连接是否等于而在断开连接后将其设置为空意味着将从第一个客户端接受仅连接(假设最初设置了连接)为null)之后你的意志只会有一个无限循环,但代码//while(true)
while(isRunning) { // a flag that is set to true by default
}
将无法再访问
this.connection = this.server.acceptAndOpen();
或者只是完全删除此检查,我认为它没用。
现在回到你的问题:
您可以将联系人列表定义为类成员,以便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;
}
}
}
和run()
方法都可以访问。如果需要,您可以将其设为最终版。然后在getContacts()
方法中填充此列表;就是这样。
e.g。
run()
您不必使用对象序列化,您可以构建一个简单的协议来将联系人从手机发送到PC,类似于您发送的命令,例如 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();
}
}
}