I'm developing an Android app that requires me to get some data from the server, this data is comes as JSON data, I have to receive like 7 JSON Objects, I'm using regular socket programming to get this data, and I get it by launching a thread that will wait for a data to come from the server.
I'm using the following method:
public String getServerRespons() throws JSONException {
String responseLine, server_response = null_string;
try {
input = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
} catch (IOException e) {
}
int count = 0;
boolean first = true;
try {
while (true)
{
if((responseLine = input.readLine()) == null){
break;
}
first = false;
server_response = server_response + responseLine;
//
// some processing to make sure it's a valid JSON
//
if(count == 0){ // related to the Processing Lines result
System.out.println(server_response);
return response; // when commenting that line everything is ok
}
}
} catch (IOException e) {
Login.errorMessage.setText(conn_err);
}
return null;
}
With that way i got like only two or three JSON Objects of the seven ones. BUT, when commenting the return Line and let it completes with the receiving process I got all the seven Objects efficiently and each Object is separated which makes me make sure that the processing i made to validate the JSON is going so well.
I think int count is always 0 in your example so the return statement is always hit.
Related
Driving myself crazy over the simplest thing. I have a JSON file called config.txt. The file is shown below.
{
"UsePipesInGuestData": true
}
All I want to do is get a 2 dimensional array such that:
Array[0] = UsePipesInGuestData and
Array[1] = true
I have been trying for 4 hours with various attempts, my most recent is shown below:
private void getConfig(){
//Function to read the config information from config.txt
FileInputStream is;
BufferedReader reader;
try {
final File configFile = new File(Environment.getExternalStorageDirectory().getPath() + "/guestlink/config.txt");
if (configFile.exists()) {
is = new FileInputStream(configFile);
reader = new BufferedReader(new InputStreamReader(is));
String line = reader.readLine();
while (line != null) {
line = reader.readLine();
if(line!= null) {
line = line.replace("\"", ""); //Strip out Quotes
line = line.replace(" ", ""); //Strip out Spaces
if ((!line.equals("{")) || (!line.equals("}"))) {
} else {
String[] configValue = line.split(":");
switch (configValue[0]) {
case "UsePipesInGuestData":
if (configValue[1].equals("true")) {
sharedPreferences.edit().putString("UsePipes", "true").apply();
} else {
sharedPreferences.edit().putString("UsePipes", "false").apply();
}
break;
}
}
}
}
reader.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
I cannot seem to ignore the lines with the { and } in them.
Clearly there MUST be an easier way. JAVA just seems to take an extremely large amount of code to do the simplest thing. Any help is greatly appreciated.
I believe your condition is incorrect. You try to read the file in the else of the condition (!line.equals("{")) || (!line.equals("}")). Simplifying, your code will run when the following happens:
!(!{ || !}) => { && } (applying De Morgan's law)
This means you will only run your code when the line is "{" AND it is "}" which is a contradiction. Try using simple conditions, like (!line.equals("{")) && (!line.equals("}")) (this is when you want to execute your code).
Additionally, you may be getting end of line characters (\n) in your string, which will make your condition fail ("{\n" != "{"). I suggest you debug and see the actual values you're getting in those lines.
I'm modifying an app and wondering if someone can point me in the right direction. Basically, I want the app to poll a URL and in the json response, if a value is true, then continue on.
The current code has this:
return prepareForUpgrade() &&
uploadImage(firmware) &&
checkUpgradeStatus() &&
rebootDevice();
private boolean prepareForUpgrade() {
Timber.i("Preparing...");
try {
Response<ResponseBody> prepare = DeviceRestApi.prepareFirmwareUpgrade(
Constants.Device.DEFAULT_USERNAME,
Constants.Device.DEFAULT_PASSWORD,
DeviceRestApi.COMMAND_PREPARE
).execute();
boolean result = prepare.isSuccessful();
Timber.i("Result: %s", result);
return result;
} catch (IOException e) {
Timber.e(e, "Preparation failed");
return false;
}
}
Basically, the process continues on if the network request was successful, but I want to replace that behavior with something that polls a URL and the URL says when its successful.
Don't need a solution exactly, just wondering which way to go. Should I try a Service? An IntentService? Something else?
My code is starting to get a bit hard to debug which leads me to believe that my design choices are not ideal. I am a novice Android programming and would love some help with streamlining the design for optimum operation.
Intro
I am writing an application that uses rfcomm interface to transfer data between a client and server device. The client needs to request certain things from the server using a specific key, then it needs to wait until the server sends the result back.
Current Design
A button press triggers a request for information from the server.
A new thread is started which performs the request.
A key which is a unique integer is converted to a byte array and sent to the server.
Thread has a while loop that is waiting for a specific boolean to flip from false to true indicating a response back from the server.
Information is received on the server side. Server uses key to identify what to do next.
server starts a thread to run some query and gets a jsonString back as a result.
Server sends jsonstring converted to byte array prepended with the same identifying key back to the client.
Client reads message, and sends the byte array to a handling method based on the identifying key.
Handling method stores jsonString to a class variable and then flips the boolean to let the other thread know that the value it was waiting on has been set.
Json string is converted to object on the client side. Something is done with that object.
This code currently correctly sends info to the server, server correctly does search and gets a valid json string result. However, the issue occurs when the server writes its results make to the client. I am getting 20 messages instead of one and none match the search key...
My questions
Am I doing things in an efficient way design wise?
Can I benefit from using synchronized keyword or and Atomic Boolean to make my code more thread safe? How would I go about implementing it?
Is there a max length for converting strings to byte array? Maybe the code is trying to break up the sending for me and that's why I'm getting 20 different results?
Relevant code
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);
}
}
Take a look at https://github.com/gettyimages/connect_sdk_java. Specifically, at the test application. It performs a search using an AsyncTask and the private class notifies the UI via onPostExecute. Hopefully, this will get you further along.
I am having a bit of an issue with my app. I receive a data through a socket, via a BufferedReader. I loop round with while ((sLine = reader.readLine ()) != null) and append the sLine to a StringBuilder object. I also spend a new line \n to the builder.
The plan is that once the builder is all finished, String sTotal = builder.toString()is called and a total is passed to the next routine.
However, the next routine is instead being called once for each line rather than with the string as a whole. The routine call is outside the loop above so I really don't know why!
Hope someone can help...
Edit: Code extract below.
public void run() {
try {
oServerSocket = new ServerSocket(iPort);
while ((!Thread.currentThread().isInterrupted()) && (!bStopThread)) {
try {
oSocket = oServerSocket.accept();
this.brInput = new BufferedReader(new InputStreamReader(this.oSocket.getInputStream()));
StringBuilder sbReadTotal = new StringBuilder();
String sReadXML = "";
while ((sReadXML = brInput.readLine()) != null) {
sbReadTotal.append("\n");
sbReadTotal.append(sReadXML);
}
sReadXML = sbReadTotal.toString();
Log.d("XMLDATA", sReadXML);
processXML(sReadXML);
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
/* Nothing Yet */
e.printStackTrace();
}
}
If you're exiting your internal while loop, it means you reached the end of your input stream (that's when readLine() returns null according to the docs).
You should be looking into the client, and not the server. What's establishing the client socket? Are you sure it's not establishing a separate connection for each line it sends?
I am desperatly trying to fix a bug that:
always happens in my emulator for Android versions 2.2, 2.3
never happens in emulator android versions 4.*
never happens in a real device (android 4.*)
It is the following IndexOutOfBoundsException exception:
java.lang.RuntimeException: Unable to start activity
ComponentInfo{<myapppackage>}: java.lang.IndexOutOfBoundsException:
Invalid index 39, size is 0
In my app I am fecthing data from a json file that I am displaying as text. I've isoleted where the bug is coming from, it is when I call this method:
public String getItemValue(int id, String s) {
List<JsonItems> list = new ArrayList<JsonItems>();
try {
// CONVERT RESPONSE STRING TO JSON ARRAY
JSONArray ja = new JSONArray(s);
// ITERATE THROUGH AND RETRIEVE
int n = ja.length();
for (int i = 0; i < n; i++) {
// GET INDIVIDUAL JSON OBJECT FROM JSON ARRAY
JSONObject jo = ja.getJSONObject(i);
// RETRIEVE EACH JSON OBJECT'S FIELDS
JsonItems ji = new JsonItems();
ji.id = jo.getInt("id");
ji.text= jo.getString("text");
list.add(ji);
}
} catch (Exception e) {
e.printStackTrace();
}
return list.get(id).text;
}
My class JsonItems is very basic:
public class JsonItems{
int id;
String text;
}
Sample from my json file:
[
{"id":0,"text":"some text 0"},
{"id":1,"text":"some text 1"},
{"id":2,"text":"some text 2"}
]
Here is how I process content of my json file into a String
public static String fromJsonFileToString(String fileName, Context c) {
//JSONArray jArray = null;
String text = "";
try {
InputStream is = c.getAssets().open(fileName);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
text = new String(buffer);
} catch (IOException e) {
throw new RuntimeException(e);
}
return text;
}
Once again I repeat: the IndexOutOfBoundsException NEVER happens on a device with Android 4.* , it only happens when I test the app on emulators with Android 2.*
Any idea where it is coming from?
Thanks
First problem:
You are not reading your input stream correctly. Calling available literally does just that - it returns you the amount of data that is available to be read, when the call is made. This number may, or may not represent the entire content of the file.
Reading material for you:
How to Read a File in Java.
Writing and Creating Files
Note that there are helper libraries like Apache Commons IO that make it possible to read file contents in a single line of code (IOUtils.toString(inputStream)). Android doesn't support Java 7 yet but a noteworthy alternative is available in that release, with the Files.readAllLines method. In any case, you can make the below shown changes to your file reading code and it should work better:
public static String fromFileToString(String fileName, Context context) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
context.getAssets().open(fileName)));
String line = null;
StringBuilder builder = new StringBuilder(1024);
while ((line = reader.readLine()) != null) {
builder.append(line);
}
return builder.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Second problem:
You do not do any bound check, to make sure that the argument you pass into your 'search' method:
public String getItemValue(int id, String s)
Does not exceed the length of the list of items you eventually calculate:
return list.get(id).text;
// ^
// -------------
// 'id' could be larger than the list size!
In any case, your current design doesn't at all match what you are really trying to do, aka, to determine the element in the JSON array that has an 'id' field matching what you supply to the method. You need to process the JSON data as a map, in order to be able to do this.
public String getItemValue(int id, String json) {
if(json == null || json.trim().equals("")) return null;
Map<Integer, JsonItems> map = new HashMap<Integer, JsonItems>(4);
try {
JSONArray ja = new JSONArray(json);
int n = ja.length();
for (int i = 0; i < n; i++) {
JSONObject jo = ja.getJSONObject(i);
JsonItems ji = new JsonItems();
ji.id = jo.getInt("id");
ji.text = jo.getString("text");
map.put(Integer.valueOf(ji.id, ji);
}
} catch(NumberFormatException nfe) {
throw new IllegalArgumentException("Invalid JSON format");
} catch (Exception e) {
throw new RuntimeException(e);
}
JsonItems item = map.get(id);
return item != null ? item.text : null;
}
Some quick notes:
JsonItems should be called JsonItem, to conform to good Java naming standards
You should really parse and store your JSON just once, to improve performance
You are really only using a minimal subset of your JSON data, you could actually determine the matching node within your for loop and directly return its value, without having to use an intermedia Java bean object