Having some issues with a custom class that extends AsyncTask. My app is Targeting Android 4.0.3 and the below code works fine for 30+ people testing it. However there are two users that are seeing the app crash when I call new AsyncRequest like below.
I've got a working logger that is recording to a text file on the users storage and doesn't record the entry that is in the AsyncRequest constructor. So I have to assume that the crash is happening before the constructor is called.
One of the two devices that are experiencing this crash is running Android 4.0.4 apparently. Not sure what the other device is running. Unfortunately I dont' have access to the two devices so can't see a logcat output.
Any input as to why the object creation is causing a crash would be greatly appreciated.
String url = "www.google.com";
new AsyncRequest(callback, context).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url);
And here is the full AsyncRequest class
public class AsyncRequest extends AsyncTask<String, String, String>{
HttpURLConnection connection;
InputStream inStream;
IApiCallback callback;
Context context_;
public AsyncRequest(IApiCallback callback, Context context) {
// Log entry added for testing. Never gets called.
FileLogger.getFileLogger(context).ReportInfo("Enter AsyncRequest Constructor");
this.callback = callback;
context_ = context;
}
#Override
protected String doInBackground(String... uri) {
try {
URL url = new URL(uri[0] + "?format=json");
FileLogger.getFileLogger(context_).ReportInfo("Async Request: Sending HTTP GET to " + url);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.addRequestProperty("Accept-Encoding", "gzip");
connection.addRequestProperty("Cache-Control", "no-cache");
connection.connect();
String encoding = connection.getContentEncoding();
// Determine if the stream is compressed and uncompress it if needed.
if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
inStream = new GZIPInputStream(connection.getInputStream());
} else {
inStream = connection.getInputStream();
}
if (inStream != null) {
// process response
BufferedReader br = new BufferedReader(new InputStreamReader(inStream));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
return sb.toString();
}
} catch (SocketTimeoutException e) {
FileLogger.getFileLogger(context_).ReportException("Async Request: SocketTimeoutException", e);
Log.i("AsyncRequest", "Socket Timeout occured");
} catch (MalformedURLException e) {
FileLogger.getFileLogger(context_).ReportException("Async Request: MalformedUrlException", e);
} catch (IOException e) {
FileLogger.getFileLogger(context_).ReportException("Async Request: IOException", e);
Log.i("doInBackground:","IOException");
if (e != null && e.getMessage() != null) {
Log.i("doInBackground:",e.getMessage());
}
} catch (Exception e) {
FileLogger.getFileLogger(context_).ReportException("Async Request: Exception", e);
} finally {
if (connection != null)
connection.disconnect();
}
return null;
}
#Override
protected void onPostExecute(String result) {
if (result != null)
FileLogger.getFileLogger(context_).ReportInfo("Async Request: Response is valid");
else
FileLogger.getFileLogger(context_).ReportInfo("Async Request: Invalid response");
callback.Execute(result);
}
}
EDIT: As per comments below.
Here is the full method that I call my custom AsyncTask from. All the logging messages I have up to creating the AsyncTask are showing in the log. None of the exceptions are.
The logging displays the url value just before creating my AsyncRequest and the URL is not malformed at all. It's what I'm expecting.
public void GetServerInfoAsync(IApiCallback callback, Context context) throws IllegalArgumentException, Exception {
if (callback == null)
throw new IllegalArgumentException("callback");
if (context == null)
throw new IllegalArgumentException("context");
try {
FileLogger.getFileLogger(context).ReportInfo("Build URL");
String url = GetApiUrl("System/Info");
FileLogger.getFileLogger(context).ReportInfo("Finished building URL");
if (url != null) {
FileLogger.getFileLogger(context).ReportInfo("GetServerInfoAsync: url is " + url);
new AsyncRequest(callback, context).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url);
} else {
FileLogger.getFileLogger(context).ReportError("GetServerInfoAsync: url is null");
}
} catch (IllegalArgumentException iae) {
FileLogger.getFileLogger(context).ReportException("GetServerInfoAsync: IllegalArgumentException", iae);
throw iae;
} catch (Exception e) {
FileLogger.getFileLogger(context).ReportException("GetServerInfoAsync: Exception", e);
throw e;
}
}
First of all, just keep in mind that executeOnExecutor() is not available prior to Api 11. You have already said the issue is with a 4.0.4 device, but just keep this in mind.
Here are the steps I would take in order to troubleshoot what the problem is. It seems as if you have already done a few of these with all those ReportInfo() statements.
First, I assume your call to GetServerInfoAsync is within a try...catch, correct? I am checking because of your use of Throw. Also, you have already added logging to check for errors with the url. Since the errors occur before you actually use it, the error cannot be with the url, or any internet permissions.
You call the AsyncTask generation with references to callback and context. You have added logging via ReportInfo() which references context, and those work, yes? Therefore, context is not your issue. However, you never check what callback is. You throw an error if it is null, but you never do anything with it before you call AsyncRequest. Try a ReportInfo(callback.toString()) to see what it is.
If all else fails, it would seem to be an error with threading. Why not try using just AsyncTask, instead of executeOnExecutor(). Do you really need more than 1 background thread?
Sorry for not getting back to this sooner. There were numerous issues here.
First off... Thanks to Wolfram's suggestion I was able to catch the exception and diagnose that the issue was that my FileLogger (and another class) was a static reference and these two tablets couldn't find the reference at runtime. So I ended up removing Logging from my async methods.
Secondly, after making the above changes there was another issue which was that a looper had to be called from the main thread. It turned out that these two tablets weren't calling my async task from the main thread. So I had to enclose the async call in a new Handler using the MainLooper.
Related
I'm developing an Android application for in-house of a certain company, and it needs to log the working time of employees. Therefore, the work with system time is crucial. My application badly needs to know when the user changes the system time. Big deal, you say, see this: Is there a way to detect when the user has changed the clock time on their device?
The problem is that the user may circumvent that solution by doing Force Stop of the application prior to changing the time. The application then won't receive system notification, which is brilliantly described here: https://stackoverflow.com/a/19856367/1309803
I don't mind checking that upon the next launch of the application, but how can I possibly know if the user has changed the time? I'm aware about SystemClock.elapsedRealtime(). I could figure time shift based on delta of those values provided that the user hasn't reboot the device, but this is what I'm unsure of. My application is subscribed to BOOT_COMPLETED event, but that one won't be received either while the application is in stopped state.
And, to cap it all, employees of that company are supposed to work in condition of having no network access, so I can't rely on Web servers. So is there any other possible approach?
Getting the time from the third-party servers is not reliable most of the times and some of them are paid services.
If you want to get the exact time and check with the phone whether it is correct or not, irrespective of the proper way, you can use the following simple trick to get the actual time.
private class GetActualTime extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
try {
HttpURLConnection urlConnection = null;
StringBuilder result = new StringBuilder();
try {
URL url = new URL(urls[0]);
urlConnection = (HttpURLConnection) url.openConnection();
int code = urlConnection.getResponseCode();
if (code == 200) {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
String line = "";
while ((line = bufferedReader.readLine()) != null)
result.append(line);
in.close();
}
else {
return "error on fetching";
}
return result.toString();
} catch (MalformedURLException e) {
return "malformed URL";
} catch (IOException e) {
return "io exception";
} finally {
if (urlConnection != null) {urlConnection.disconnect();
}
}
} catch (Exception e) { return "null"; }
}
#Override
protected void onPostExecute(String time) {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat mdformat = new SimpleDateFormat("h:mm");
String times = mdformat.format(calendar.getTime());
try {
String areatime = time.substring(time.indexOf(String.valueOf(times)), time.indexOf(String.valueOf(times)) + 5).trim();
Toast.makeText(this, "The actual time is " + areatime, Toast.LENGTH_SHORT).show();
}
catch(IndexOutOfBoundsException e){
Toast.makeText(this, "Mobile time is not same as Internet time", Toast.LENGTH_SHORT).show();
}
}
}
}
Call the class in the onCreate();
new GetActualTime().execute("https://www.google.com/search?q=time");
So this is actually getting the time from Google. This works pretty awesomely in my projects. In order to check whether the system time is wrong, you can use this trick. Instead of depending on the time servers, you can trust Google.
As it is more sensitive in checking, even a minute ahead or lag will catch the exception. You can customise the code if you want to handle that.
I'm programming an app with my brother, and today unfortunately, I encountered with a problem.
When the app load a php page via my asynctask class it works fine. but I would like to program this situation: if the remote server is down, or crash, and doesnt display the right page, the application will show error message. but instead, the app crashes =[
I tried to load this page, for example:
http://alonadoni.com/sql3.php
(I want to simulate that there is a problem with the server. the regular page is sql2.php and it works fine when the server works)
When the app try to load this page (sql3.php) , the app crashes.
I did another experiment : I created a file sql3.php, and wrote "aaaaaaaa" in the page, the app doesn't crash in this situation. it downloaded the data "aaaaa". in this case, the app show jsonexecption error.
Unfortunately, I can't get logcat because my old computer can't run emulators, and my phone also can't connect to my computer on developer mode =[ When I try application I create an apk then transfer the file to my phone and install.
my code is:
in OnCreate:
String serverURL = sss() + "sql3.php?imei=" + imei;
new LongOperation().execute(serverURL);
outside OnCreate:
private class LongOperation extends AsyncTask<String, Void, Void> {
private final HttpClient Client = new DefaultHttpClient();
private String Error = null;
protected void onPreExecute() {
}
protected Void doInBackground(String... urls) {
try {
HttpGet httpget = new HttpGet(urls[0]);
ResponseHandler<String> responseHandler = new BasicResponseHandler();
data[x] = Client.execute(httpget, responseHandler);
} catch (ClientProtocolException e) {
Error = e.getMessage();
Toast.makeText(getApplicationContext(),"error2" , Toast.LENGTH_LONG).show();
cancel(true);
} catch (IOException e) {
Error = e.getMessage();
Toast.makeText(getApplicationContext(),"error34" , Toast.LENGTH_LONG).show();
cancel(true);
}
return null;
}
public void onPostExecute(Void unused) {
if (Error != null) {
} else {
try {
JSONObject json = new JSONObject(data[x]);
name = json.getString("name");
} catch (JSONException e) {
Toast.makeText(getApplicationContext(),"e" + e, Toast.LENGTH_LONG).show();
}
}
x++;
}
}
DoInBackground of asynctask needs to contain only NON UI work , hence referring to context and performing UI operations in UI thread may cause crash.
You can perform UI operations in postexecute of asynctask.
Hence Removing toast from above code which refers to UI operation will solve your issue
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?
In my app I have a facility to download reference data updates. The user can modify base url in a PreferenceActivity - and then I append the actual file name to the base URL. When I attempt to download the file, an exception may be thrown if something went wrong. I'd like to present the user with the most appropriate error message rather than simply "error occurred". To do this, I want to catch individual exception and format messages accordingly. So, what exceptions can be thrown when downloading a file? For the reference, here's my downloading code (simplified):
int msgId;
try {
String url = props.getProperty(Constants.SETTINGS_REFDATA_SOURCE);
if(!url.endsWith("/")) {
url += "/";
}
url += Constants.UPDATE_CUSTOMER_FILE;
CSVReader in = new CSVReader(new InputStreamReader(url.openStream()));
...//read and parse file here
}
catch(MalformedURLException e) {
msgId = R.string.error_invalid_base_url;
}
catch(UnknownHostException e) {
msgId = R.string.error_unknown_host;
}
catch(FileNotFoundException e) {
msgId = R.string.error_file_not_found;
}
catch(IOException e) {
msgId = R.string.error_reading_data;
}
catch(MyParseException e) {
msgId = R.string.error_invalid_file_format;
}
catch(Exception e) {
msgId = R.string.error_other_error;
}
finally {
try { in.close(); } catch(Exception e2) {}
}
// then I display AlertDialog using msgId as the message
As you can see, I'm already catching several exception - some that I know can be thrown, some that I encountered in testing. What other exceptions do I need to cater for? Note that the amount of data being download is quite small (at most 15-20 Kb) so things like OutOfMemoryError shouldn't be applicable.
There is the ConnectException due to connection timeout or connection refused (HTTP 403)
//edit: I just read, that "Most applications should not catch the ConnectException; it is more robust to catch the superclass SocketException."
Furthermore you could test if there is an active Internet connection:
private boolean checkCon() {
ConnectivityManager conMgr = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
if ( conMgr.getNetworkInfo(0).getState() == NetworkInfo.State.DISCONNECTED
&& conMgr.getNetworkInfo(1).getState() == NetworkInfo.State.DISCONNECTED) {
return false;
}
return true;
}
In my application I download and parse a html page. However, I want to be able to stop the download in its tracks (i.e. when the user hits cancel).
This is the code I use now, which is being called from doInBackground from ASyncTask.
How do I cancel this request from outside of the ASyncTask?
I currently use htmlcleaner
HtmlCleaner cleaner = new HtmlCleaner();
CleanerProperties props = cleaner.getProperties();
props.setAllowHtmlInsideAttributes(true);
props.setAllowMultiWordAttributes(true);
props.setRecognizeUnicodeChars(true);
props.setOmitComments(true);
try {
URL url = new URL(urlstring);
URLConnection conn = url.openConnection();
TagNode node = cleaner.clean(new InputStreamReader(conn.getInputStream()));
return node;
} catch (Exception e) {
failed = true;
return;
}
Can't you use AsyncTask.cancel()? You should be able to then use the onCancelled callback to return to the main activity..
Ok, I believe I've solved this.
In my Activity class I have a variable (boolean) failed. Also, I have a private Downloader class within the activity which extends ASyncTask. This way, the Downloader class has access to the failed boolean. When the Activity launches, it starts the Downloader task and a progress dialog pops up. When the task finishes, it closes the dialog and then goes on processing the downloaded content.
However, when the user cancels the progress dialog, failed is set to true, and the user is sent back to the previous activity by a call to finished. In the meantime, Downloader is still busy downloading. Because the results are now unneccessary, we want it to stop using resources asap. In order to accomplish this, I have broken up the doInBackground method in as much steps as possible. After each step I check if failed is still false, when it is set to true, it simply doesn't go to the next step. See it in action below. Furthemore, the BufferedReader reader is public, and in the onCancelled method I execute reader.close(). This will throw all sorts of exceptions, but these are properly caught.
public void DoInBackground(.........) {
try {
URL url = new URL(uri);
URLConnection conn = url.openConnection();
if (!failed) {
isr = new InputStreamReader(conn.getInputStream());
if (!failed) {
reader = new BufferedReader(isr);
publishProgress(1);
if (!failed) {
TagNode node = cleaner.clean(reader);
publishProgress(2);
return node;
}
}
}
} catch (Exception e) {
failed = true;
Log.v("error",""+e);
}
}
#Override
protected void onCancelled() {
failed = true;
if (reader != null)
try {
reader.close();
} catch (IOException e) {
failed = true;
}
if (isr != null)
try {
isr.close();
} catch (IOException e) {
}
}
I know that I could have broken up the downloading process in even tinier bits, but I am downloading very small files, so it's not that important.