We are getting some very weird behavior in Android. Our network stack (that talks to a REST server) works fine in almost all situations, except when we do a GET shortly after doing a larger POST. What appears to be happening is that the Output stream is not flushing, and ends up sending the last line that was in there when the new socket is opened. Please note, each connection is a new object created, so this is unexpected behavior. First, the error code that seems to point me to the output stream, these are from the server logs.
10.1.8.195 - - [07/Nov/2012:13:36:28 -0700] "POST /iou/lender HTTP/1.1" 200 28 "-" "Android"
10.1.8.195 - - [07/Nov/2012:13:36:36 -0700] "------------V2ymHFg03ehbqgZCaKO6jy" 400 173 "-" "-"
That attempt after should be a GET that then pulls the data from the server that includes the new entry added via the POST. However, all we get is again what appears to be the last line from the output stream from the POST. Here is our core code for the network stack, if more of the surrounding code is needed, let me know.
public Object serverConnect(String url, String method,
Hashtable<String, Object> params) {
HttpConnection c = null;
InputStream is = null;
OutputStream out = null;
ByteArrayOutputStream postDataByteArrayImage = new ByteArrayOutputStream();
byte[] data;
String boundry = "----------V2ymHFg03ehbqgZCaKO6jy";
try {
if (!url.startsWith("/")) {
url = "/" + url;
}
String uri = Control.URL_Secure + Control.dtserver + ":"
+ Control.port + url;
ByteArrayOutputStream postDataByteArray = new ByteArrayOutputStream();
params.put("sessionId", Control.sessionId);
if (method.equals("GET")) {
uri = uri + "?";
Enumeration enumParams = params.keys();
while (enumParams.hasMoreElements()) {
if (!uri.endsWith("?")) {
uri = uri + "&";
}
String key = (String) enumParams.nextElement();
uri = uri
+ key
+ "="
+ java.net.URLEncoder.encode((String) params
.get(key));
}
} else if (method.equals("POST")) {
Enumeration enumParams = params.keys();
postDataByteArray.write(("--").getBytes());
postDataByteArray.write((boundry).getBytes());
postDataByteArray.write(("\r\n").getBytes());
while (enumParams.hasMoreElements()) {
String key = (String) enumParams.nextElement();
if (!key.equals("image")){
postDataByteArray
.write(("Content-Disposition: form-data; name=\"")
.getBytes());
postDataByteArray.write((key).getBytes());
postDataByteArray.write(("\"").getBytes());
postDataByteArray.write(("\r\n\r\n").getBytes());
postDataByteArray.write(((String) params.get(key))
.getBytes());
postDataByteArray.write(("\r\n").getBytes());
postDataByteArray.write(("--").getBytes());
postDataByteArray.write(boundry.getBytes());
postDataByteArray.write(("\r\n").getBytes());
}
}
postDataByteArray.close();
}
Log.i("URL", uri);
URL urltoConenct = new URL(uri);
URLConnection connection = urltoConenct.openConnection();
HttpURLConnection urlConnection = (HttpURLConnection) connection;
URLConnection.setDefaultRequestProperty("Method", method); // default
urlConnection.setRequestProperty("User-Agent", "Android");
if (method.equals("POST")) {
urlConnection.setDoOutput(true);
urlConnection.setFixedLengthStreamingMode(postDataByteArray.toByteArray().length + postDataByteArrayImage.toByteArray().length);
urlConnection.setRequestProperty("Content-Type",
"multipart/form-data; boundary=" + boundry);
out = urlConnection.getOutputStream();
out.write(postDataByteArray.toByteArray());
out.write(postDataByteArrayImage.toByteArray());
out.close();
}
int response = 0;
try {
response = urlConnection.getResponseCode();
} catch (IOException e) {
if (e.toString()
.equals("java.io.IOException: Received authentication challenge is null"))
throw new RESTException(401, "Invalid Phone or Pin");
else
throw e;
}
if (response == HttpURLConnection.HTTP_OK) {
is = urlConnection.getInputStream();
if (is == null) {
return new IOException(
"Cannot open HTTP InputStream, aborting");
}
ByteArrayOutputStream bo = new ByteArrayOutputStream();
int ch;
int count = 0;
while ((ch = is.read()) != -1) {
bo.write(ch);
count++;
}
data = bo.toByteArray();
return new String(data);
} else if (response == 500) {
return new RESTException(500, "Internal server error");
} else {
RESTException x = new RESTException();
x.setCode(response);
try {
is = urlConnection.getInputStream();
if (is == null) {
x.setMessage("Unable to retrieve message");
return x;
}
ByteArrayOutputStream bo = new ByteArrayOutputStream();
int ch;
int count = 0;
while ((ch = is.read()) != -1) {
bo.write(ch);
count++;
}
data = bo.toByteArray();
String output = new String(data);
JSONObject obj;
try {
obj = new JSONObject(output);
JSONObject err = obj.getJSONArray("errors")
.getJSONObject(0);
x.setMessage(err.getString("message"));
} catch (JSONException e) {
Log.e("stuff", output);
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e) {
// Damn you android! I'm using a REST service here, stop
// trying to interpret my errors!
x.setMessage("Unable to retrieve message");
}
return x;
}
} catch (Exception x) {
x.printStackTrace();
/*
* if (!retried && x.toString().equals(
* "java.io.IOException: Persistent connection dropped after first chunk sent, cannot retry"
* )) { retry = true; } if (!retry) { return x; }
*/
return x;
} finally {
try {
out.close();
} catch (Exception x) {
}
try {
is.close();
} catch (Exception x) {
}
try {
c.close();
} catch (Exception x) {
}
params.clear();
}
// return null;
}
After a very long time of frustration, we discovered that Android tries to keep a connection alive even if you manually call .close() on the connection. This worked fine for our GET methods, but POST methods left the socket in a state that it couldn't then process a GET. Adding the following fixed all our problems:
urlConnection.setRequestProperty("connection", "close");
Related
This question already has answers here:
The AsyncTask API is deprecated in Android 11. What are the alternatives?
(19 answers)
Closed 1 year ago.
I'm new to Android Java and I would really appreciate it I can get some references and/or examples to make some simple network calls without using AsyncTask.
I'm making a program to parse a simple JSON Object from a URL.
In Android 11 (API 30), All AsyncTask are going to be deprecated as shown here:
https://developer.android.com/reference/android/os/AsyncTask
This is an example of how to send a request without AsyncTask using Thread
void send_request(final String url) {
try {
Thread thread = new Thread() {
public void run() {
Looper.prepare();
final JSONObject[] maindata = {new JSONObject()};
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
String data = "";
String error_data = "";
HttpURLConnection httpURLConnection = null;
try {
httpURLConnection = (HttpURLConnection) new URL(url).openConnection();
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setRequestProperty("Content-Type", "application/json");
int status = httpURLConnection.getResponseCode();
Log.d("GET RX", " status=> " + status);
try {
InputStream in = httpURLConnection.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(in);
int inputStreamData = inputStreamReader.read();
while (inputStreamData != -1) {
char current = (char) inputStreamData;
inputStreamData = inputStreamReader.read();
data += current;
}
Log.d("GET RX =>", " " + data);
sdbw sd = new sdbw(act);
maindata[0] = new JSONObject(data);
} catch (Exception exx) {
InputStream error = httpURLConnection.getErrorStream();
InputStreamReader inputStreamReader2 = new InputStreamReader(error);
int inputStreamData2 = inputStreamReader2.read();
while (inputStreamData2 != -1) {
char current = (char) inputStreamData2;
inputStreamData2 = inputStreamReader2.read();
error_data += current;
}
Log.e("TX", "error => " + error_data);
}
} catch (Exception e) {
Log.e("TX", " error => " + e.getMessage());
e.printStackTrace();
} finally {
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
}
handler.removeCallbacks(this);
Looper.myLooper().quit();
}
}, 2000);
Looper.loop();
}
};
thread.start();
} catch (Exception ex) {
Log.e("ERROR =>", "" + ex.getMessage());
ex.printStackTrace();
}
}
Heres what i found for room query, works well
val x = Executors.newSingleThreadExecutor().submit(Callable { mDao.getX() }).get()
I am trying to download a video from a link, but it only downloads a small part of it, so it can't be watched at all. How would you download an entire video no matter how large the file is from a link?
try {
URL url;
byte[] buf;
int byteRead, byteWritten = 0;
url = new URL(fAddress);
outStream = new BufferedOutputStream(new FileOutputStream(destinationDir + "\\" + localFileName));
conn = url.openConnection();
is = conn.getInputStream();
buf = new byte[size];
while ((byteRead = is.read(buf)) != -1) {
outStream.write(buf, 0, byteRead);
byteWritten += byteRead;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
outStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
It looks like server may redirect you to other location which your code doesn't handle. To get final location you can try method like (based on: http://www.mkyong.com/java/java-httpurlconnection-follow-redirect-example/):
public static String getFinalLocation(String address) throws IOException{
URL url = new URL(address);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
int status = conn.getResponseCode();
if (status != HttpURLConnection.HTTP_OK)
{
if (status == HttpURLConnection.HTTP_MOVED_TEMP
|| status == HttpURLConnection.HTTP_MOVED_PERM
|| status == HttpURLConnection.HTTP_SEE_OTHER)
{
String newLocation = conn.getHeaderField("Location");
return getFinalLocation(newLocation);
}
}
return address;
}
Now you simply need to change
url = new URL(fAddress);
to
url = new URL(getFinalLocation(fAddress));
In the doInBackground method, which returns an array of YouTube links, I log the value of the array right before returning it to make sure that it is not null. However, when I try to use it in the onPostExecute method (using the 'result' variable), I get a null array error. I cannot for the life of me find an explanation. Here's my code:
doInBackground:
#Override
protected String[] doInBackground(String... params) {
// If there's no parameter, there's nothing to look up. Verify size of params.
if (params.length == 0) {
return null;
}
// These two need to be declared outside the try/catch
// so that they can be closed in the finally block.
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
// Will contain the raw JSON response as a string.
String rawJsonStr = null;
try {
// Construct the URL for the TMDb query
//Define strings for creating the url:
final String BASE_URL = "http://api.themoviedb.org/3/movie/";
final String PARAM_API_KEY = "?api_key=----"; //REMEMBER NOT TO LEAVE YOUR KEYS HERE!
final String TRAILER_REVIEWS_APPEND = "&append_to_response=trailers,reviews";
URL url = new URL(BASE_URL + movieId + PARAM_API_KEY + TRAILER_REVIEWS_APPEND);
// Create the request to TMDb, and open the connection
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// Read the input stream into a String
InputStream inputStream = urlConnection.getInputStream();
StringBuffer buffer = new StringBuffer();
if (inputStream == null) {
// Nothing to do.
return null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
// Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
// But it does make debugging a *lot* easier if you print out the completed
// buffer for debugging.
buffer.append(line + "\n");
}
if (buffer.length() == 0) {
// Stream was empty. No point in parsing.
return null;
}
rawJsonStr = buffer.toString();
} catch (IOException e) {
Log.e(LOG_TAG, "Error ", e);
return null;
} finally{
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (final IOException e) {
Log.e(LOG_TAG, "Error closing stream", e);
}
}
}
try{
getMovieDataFromJson(rawJsonStr);
Log.d(LOG_TAG, "Before returning: " + getTrailerDataFromJson(rawJsonStr)[0]);
return getTrailerDataFromJson(rawJsonStr);
} catch (JSONException e) {
e.printStackTrace();
Log.e(LOG_TAG, "Coudn't return trailer array");
}
return null;
}
Note: Yes, I'm using the necessary parameters when invoking the AsyncTask
onPostExecute:
#Override
protected void onPostExecute(String[] result) {
Log.d(LOG_TAG, "After returning: " + result[0]);
}
The app crashes, and on the logs, I get first an "attempted to read from null array" exception in the line that belongs to the onPostExecute method. Then, I get the correct "Before returning: YouTubeLink" log.
Any suggestions?
Edit:
getTrailerData method:
private String[] getTrailerDataFromJson(String rawJsonData)
throws JSONException {
final String TMDb_TRAILERS = "trailers";
//Get the object corresponding to the movie
JSONObject movieData = new JSONObject(rawJsonData);
//Create trailer objects
JSONObject trailerData = movieData.getJSONObject(TMDb_TRAILERS);
JSONArray youtubeTrailers = trailerData.getJSONArray("youtube");
String[] trailerLinks = new String[youtubeTrailers.length()];
//Get trailers
for (int i = 0; i < youtubeTrailers.length(); i++){
if (!youtubeTrailers.isNull(i)){
JSONObject trailer = youtubeTrailers.getJSONObject(i);
trailerLinks[i] = "https://www.youtube.com/watch?v=" + trailer.getString("source");
}
else{
noTrailers = true;
break;
}
}
return trailerLinks;
}
The log error is the following:
08-13 14:30:54.422 17240-17240/com.mightybarbet.quickmovieinfo E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.mightybarbet.quickmovieinfo, PID: 17240
java.lang.NullPointerException: Attempt to read from null array
at com.mightybarbet.quickmovieinfo.DetailActivityFragment$FetchMovieInfoTask.onPostExecute(DetailActivityFragment.java:258)
at com.mightybarbet.quickmovieinfo.DetailActivityFragment$FetchMovieInfoTask.onPostExecute(DetailActivityFragment.java:105)
at android.os.AsyncTask.finish(AsyncTask.java:632)
at android.os.AsyncTask.access$600(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:155)
at android.app.ActivityThread.main(ActivityThread.java:5696)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)
Sorry I took so long, my internet went out.
Line 105 is the declaring of the AsyncTask:
public class FetchMovieInfoTask extends AsyncTask<String, Void, String[]> {
And line 258 is the line inside onPostExecute:
Log.d(LOG_TAG, "After returning: " + result[0]);
Replace your method and look at the final log "final return trailer array"
#Override
protected String[] doInBackground(String... params)
{
// If there's no parameter, there's nothing to look up. Verify size of params.
if (params.length == 0) {
return null;
}
// These two need to be declared outside the try/catch
// so that they can be closed in the finally block.
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
// Will contain the raw JSON response as a string.
String rawJsonStr = null;
String[] resultArray = null;
try {
// Construct the URL for the TMDb query
//Define strings for creating the url:
final String BASE_URL = "http://api.themoviedb.org/3/movie/";
final String PARAM_API_KEY = "?api_key=----"; //REMEMBER NOT TO LEAVE YOUR KEYS HERE!
final String TRAILER_REVIEWS_APPEND = "&append_to_response=trailers,reviews";
URL url = new URL(BASE_URL + movieId + PARAM_API_KEY + TRAILER_REVIEWS_APPEND);
// Create the request to TMDb, and open the connection
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// Read the input stream into a String
InputStream inputStream = urlConnection.getInputStream();
StringBuffer buffer = new StringBuffer();
if (inputStream == null) {
// Nothing to do.
return null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
// Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
// But it does make debugging a *lot* easier if you print out the completed
// buffer for debugging.
buffer.append(line + "\n");
}
if (buffer.length() == 0) {
// Stream was empty. No point in parsing.
return null;
}
rawJsonStr = buffer.toString();
} catch (IOException e) {
Log.e(LOG_TAG, "Error ", e);
return null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (final IOException e) {
Log.e(LOG_TAG, "Error closing stream", e);
}
}
}
try{
getMovieDataFromJson(rawJsonStr);
Log.d(LOG_TAG, "Before returning: " + getTrailerDataFromJson(rawJsonStr)[0]);
resultArray = getTrailerDataFromJson(rawJsonStr);
} catch (JSONException e) {
e.printStackTrace();
Log.e(LOG_TAG, "Couldn't return trailer array");
}
Log.e(LOG_TAG, "final return trailer array"+resultArray;);
return resultArray;
}
I'm writing one application in which I'm sending post request to server. The problem is that Nokia S40 series phone sends 2 request if it doesn't get response within 30 sec. I have checked the same with s60 series phone in my app is working fine.
One solution I found is to start timer while opening connection and terminate the connection after 25sec if connection not getting response. But it is also worthless. Here I'm attaching my code. Please review it and help me if possible.
try
{
param="function=CloseRecharge&LoginId="+SharedVariable.getUserInfo().getLoginID()
+"&BatchId="+SharedVariable.getSelectedProduct().getBatchID()
+"&SystemServiceID="+SharedVariable.getSelectedProduct().getSystemServiceID()
+"&ReferalNumber="+strMobileNo
+"&FromANI="+fromMoNo
+"&Email="+""
+"&Checksum="+Checksum;
connection = (HttpConnection) Connector.open(url);
timeout=0;
t.schedule(new TimerTask()
{
public void run()
{
try
{
timeout+=1;
System.out.println("Timeout value:"+timeout);
if(timeout>=25)
{
//timeout every 1 munite
timeout=0;
System.out.print("Connection is closing");
connection.close();
connection=null;
if(is!=null)
is.close();
Alert alert=new Alert("HttpConn Closed", "connection closed",null, AlertType.WARNING);
alert.setTimeout(Alert.FOREVER);
display.setCurrent(alert,new MainMenu("Menu", parent));
this.cancel();
}
}
catch(Exception e)
{
System.out.println("========Exception occured in Timer Task");
e.printStackTrace();
}
}
}
, 0, 1000);
connection.setRequestMethod(HttpConnection.POST);
connection.setRequestProperty("User-Agent", "Profile/MIDP-1.0 Configuration/CLDC-1.0");
connection.setRequestProperty("Accept_Language","en-US");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Content-length", ""+param.getBytes().length);
out = connection.openOutputStream();
out.write(param.getBytes());
out.flush();
param=null;
if (connection.getResponseCode() == HttpConnection.HTTP_OK )
{
t.cancel();
int len = (int)connection.getLength();
InputStream istrm = connection.openInputStream();
if (istrm == null)
{
throw new IOException("Cannot open HTTP InputStream, aborting");
}
if (len != -1)
{
data = new byte[len];
int bytesRead = istrm.read(data);
}
else
{
ByteArrayOutputStream bo = new ByteArrayOutputStream();
int ch;
int count = 0;
while ((ch = istrm.read()) != -1)
{
bo.write(ch);
count++;
}
data = bo.toByteArray();
bo.close();
}
String response = new String(data);
displayResponse();
}//if
else
{
Alert error=new Alert("ResponseCode:"+connection.getResponseCode(),
null,
null,AlertType.INFO);
error.setTimeout(Alert.FOREVER);
display.setCurrent(error,new MainMenu("Menu", parent));
}
catch(Exception exception)
{
displayErrorMessage(exception);
}
finally
{
try
{
if(connection!=null)
connection.close();
if(is!=null)
is.close();
}
catch (IOException ex)
{
System.out.println("Exception in Finally block....");
ex.printStackTrace();
}
}
I'm trying to download a large file from my Yahoo! web site server which apparently is setup (not by me) to disconnect downloads if they are not completed within 100 seconds. The file is small enough to usually successfully transfer. On the occasions when the data rate is slow and the download gets disconnected, is there a way to resume the URLConnection at the file offset where the disconnection occurred? Here's the code:
// Setup connection.
URL url = new URL(strUrl[0]);
URLConnection cx = url.openConnection();
cx.connect();
// Setup streams and buffers.
int lengthFile = cx.getContentLength();
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(strUrl[1]);
byte data[] = new byte[1024];
// Download file.
for (total=0; (count=input.read(data, 0, 1024)) != -1; total+=count) {
publishProgress((int)(total*100/lengthFile));
output.write(data, 0, count);
Log.d("AsyncDownloadFile", "bytes: " + total);
}
// Close streams.
output.flush();
output.close();
input.close();
Try using a "Range" request header:
// Open connection to URL.
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// Specify what portion of file to download.
connection.setRequestProperty("Range", "bytes=" + downloaded + "-");
// here "downloaded" is the data length already previously downloaded.
// Connect to server.
connection.connect();
Having done that, you can seek at a given point (just before the length of your download data, say X) and start writing the newly downloaded data there. Be sure to use the same value X for the range header.
Details about 14.35.2 Range Retrieval Requests
More details and source code can be found here
Here's an example code that you can use:
import java.io.*;
import java.net.*;
public class HttpUrlDownload {
public static void main(String[] args) {
String strUrl = "http://VRSDLSCEN001:80//DLS//lib//clics.jar";
String DESTINATION_PATH = "clics.jar";
int count = 0;
while (true) {
count++;
if (download(strUrl, DESTINATION_PATH) == true || count > 20) {
break;
}
}
}
public static boolean download(String strUrl, String DESTINATION_PATH) {
BufferedInputStream in = null;
FileOutputStream fos = null;
BufferedOutputStream bout = null;
URLConnection connection = null;
int downloaded = 0;
try {
System.out.println("mark ... download start");
URL url = new URL(strUrl);
connection = url.openConnection();
File file=new File(DESTINATION_PATH);
if(file.exists()){
downloaded = (int) file.length();
}
if (downloaded == 0) {
connection.connect();
}
else {
connection.setRequestProperty("Range", "bytes=" + downloaded + "-");
connection.connect();
}
try {
in = new BufferedInputStream(connection.getInputStream());
} catch (IOException e) {
int responseCode = 0;
try {
responseCode = ((HttpURLConnection)connection).getResponseCode();
} catch (IOException e1) {
e1.printStackTrace();
}
if (responseCode == 416) {
return true;
} else {
e.printStackTrace();
return false;
}
}
fos=(downloaded==0)? new FileOutputStream(DESTINATION_PATH): new FileOutputStream(DESTINATION_PATH,true);
bout = new BufferedOutputStream(fos, 1024);
byte[] data = new byte[1024];
int x = 0;
while ((x = in.read(data, 0, 1024)) >= 0) {
bout.write(data, 0, x);
}
in.close();
bout.flush();
bout.close();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
}
}
if (bout != null) {
try {
bout.close();
} catch (IOException e) {
}
}
if (connection != null) {
((HttpURLConnection)connection).disconnect();
}
}
}
}