Потоки, атомарные логические, синхронизированные соображения проектирования для метода, который должен ждать результата
Мой код начинает становиться немного сложным для отладки, что приводит меня к мысли, что мой выбор дизайна не идеален. Я новичок в программировании на Android и хотел бы помочь с оптимизацией дизайна для оптимальной работы.
вступление
Я пишу приложение, которое использует интерфейс rfcomm для передачи данных между клиентом и устройством сервера. Клиент должен запросить определенные вещи с сервера, используя определенный ключ, затем он должен ждать, пока сервер не отправит результат обратно.
Текущий дизайн
- Нажатие кнопки запускает запрос информации с сервера.
- Запущен новый поток, который выполняет запрос.
- Ключ, который является уникальным целым числом, преобразуется в байтовый массив и отправляется на сервер.
- В потоке есть цикл while, который ожидает, чтобы конкретное логическое значение перешло с false на true, что указывает на ответ от сервера.
- Информация получена на стороне сервера. Сервер использует ключ, чтобы определить, что делать дальше.
- Сервер запускает поток для выполнения какого-либо запроса и в результате возвращает jsonString.
- Сервер отправляет jsonstring, преобразованный в байтовый массив, которому предшествует тот же идентификационный ключ, обратно клиенту.
- Клиент читает сообщение и отправляет байтовый массив в метод обработки на основе идентифицирующего ключа.
- Метод обработки сохраняет jsonString в переменной класса и затем переворачивает логическое значение, чтобы сообщить другому потоку, что значение, которое он ожидал, было установлено.
- Строка Json преобразуется в объект на стороне клиента. Что-то сделано с этим объектом.
Этот код в настоящее время корректно отправляет информацию на сервер, сервер правильно выполняет поиск и получает действительный результат строки json. Однако проблема возникает, когда сервер записывает свои результаты в клиент. Я получаю 20 сообщений вместо одного, и ни одно не соответствует ключу поиска...
Мои вопросы
- Я делаю вещи эффективным способом в дизайне?
- Могу ли я извлечь выгоду из использования
synchronized
ключевое слово или и Atomic Boolean, чтобы сделать мой код более потокобезопасным? Как бы я пошел на реализацию этого? - Существует ли максимальная длина для преобразования строк в байтовый массив? Может быть, код пытается разбить посылку для меня, и поэтому я получаю 20 разных результатов?
Соответствующий код
public class ClientSpokesmanClass {
private final int searchKey = 2222222; //set the key to some int.
private boolean pendingSearchResults = false;
List<Place> places = new ArrayList<Place>();
private final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
switch(msg.what) {
...
case MESSAGE_READ:
//Message received from server
readAndDistribute(msg.arg1, msg.obj);
break;
...
}
}
};
public List<Place> getPlacesFromServer(String query){
//ask server for search results
requestSearchFromServer(query);
//just wait for them...
while (pendingSearchResults){
//just waiting
}
return places;
}
private void requestSearchFromConnectedDevice(String query) {
if (mBluetoothState == STATE_CONNECTED){
byte[] bites = new byte[4];
bites = ByteBuffer.wrap(bites).putInt(searchKey).array();
byte[] stringBytes = null;
try {
stringBytes = query.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
Log.e(TAG, "unsupported encoding", e);
}
int keyLength = bites.length;
int stringLength = stringBytes.length;
byte[] combined = new byte[keyLength+stringLength];
System.arraycopy(bites, 0, combined, 0, keyLength);
System.arraycopy(stringBytes, 0, combined, keyLength, stringLength);
mBluetoothService.write(combined);
}
pendingSearchResults = true;
}
private void receiveSearchResults(byte[] bites){
String jsonString = "";
PlacesJSONParser parser = new PlacesJSONParser();
try {
jsonString = new String(bites, "UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
Log.e(TAG, "unsupported encoding", e);
}
if (D) Log.d(TAG, "Json string we got is "+jsonString);
try {
places = parser.parse(new JSONObject(jsonString));
} catch (JSONException e) {
// TODO Auto-generated catch block
Log.e(TAG, "JSON exception", e);
}
pendingSearchResults = false;
}
/**
* Reads come here first. Then, based on the key prepended to them,
* they then go to other methods for further work.
* @param bytes
* @param buffer
*/
private synchronized void readAndDistribute(int bytes, Object buffer){
byte[] buff = (byte[]) buffer;
int key = ByteBuffer.wrap(Arrays.copyOfRange(buff, 0, 4)).getInt();
if (key == searchKey){
receiveSearchResults(Arrays.copyOfRange(buff, 4, bytes));
}else{
//do something else
}
}
}
,
public class ClientUI extends Activity {
...
onQueryTextSubmit(String query){
final String queryFinal = query;
Thread thread = new Thread(){
public void run() {
places = ClientSpokesmanClass.getPlacesFromServer(query);
doSomethingWithPlaces();
}
};
thread.start();
}
}
,
public class ServerReceive {
private searchKey = 2222222;
...
//code that handles messages, reads key, and then runs doSearchAndWriteResults()
...
private synchronized void doSearchAndWriteResults(byte[] bites){
if (D) Log.d(TAG, "+++writeSearchResults");
//Initialize query and placesString
String query = null;
String placesString;
//Convert byte array to the query string
try {
query = new String(bites, "UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
Log.e(TAG, "unsupported encoding",e);
}
//if the string was converted successfully...
if (query != null){
//Run the places query and set the json string to placesString
if (D) Log.d(TAG, "query is "+query);
PlacesProvider placeProvider = new PlacesProvider();
placesString = placeProvider.getPlacesString(query);
}
//initialize a bite array
byte[] stringBytes = null;
try {
//convert jsonString to byte array
stringBytes = placesString.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
Log.e(TAG, "unsupported encoding",e);
}
//Put the search key to a byte array. I am using this key on the client side
//to confirm that we are reading searchResults and not some other type of write.
byte[] bite = new byte[4];
bite = ByteBuffer.wrap(bite).putInt(searchKey).array();
//Get the lengths of the two byte arrays
int keyLength = bite.length;
int stringLength = stringBytes.length;
//combine the byte arrays for sending
byte[] combined = new byte[keyLength+stringLength];
System.arraycopy(bite, 0, combined, 0, keyLength);
System.arraycopy(stringBytes, 0, combined, keyLength, stringLength);
if (D) Log.d(TAG, "Actually writing things here...");
//send the byte arrrays over rfcomm
mBluetoothService.write(combined);
}
}
1 ответ
Посмотрите на https://github.com/gettyimages/connect_sdk_java. Именно на тестовом приложении. Он выполняет поиск, используя AsyncTask, и закрытый класс уведомляет пользовательский интерфейс через onPostExecute. Надеюсь, это продвинет вас дальше.