Is there anything wrong with AsyncTask? - android

#Override
protected InputStream doInBackground(String... url){
try {
InputStream stream = downloadXml(url[0]);
new ParseXml(stream); //for testing porpuses: outputs ok to logcat
return stream;
} catch (IOException e) {
Log.d("dbg","exception");
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(InputStream result) {
if (result != null) {
new ParseXml(result); //crashes the app
}
Log.d("dbg","postexecute triggered ok");
}
Code is pretty self explanatory i think, i tried changing the passing type to just Object and type casted it where needed but it didn't worked either.
Is there anything undocumented in sdk that i should know of ?

obviously, Crash.. You are doing lengthy (also may be network related) operation in MainUI Thread. as onPostExecute() of AsyncTask runs on In MainUI Thread only. So always keep it in doInBackground().
This code line new ParseXml(result); should be in doInBackground() of AsyncTask.
Update:
So complete the Parsing of XML in doInBackground() and only pass the result in onPostExecute() if only you want to reflect the updation on Application UI.

Related

Updating TextView with setText() doesn't work in AsyncTask onProgressUpdate()

I'm trying to update my user interface with information about a background task that's going on with an AsyncTask. Here's my onProgressUpdate() method.
#Override
protected void onProgressUpdate(final String... params) {
Log.d(TAG, "onProgressUpdate()");
StringBuilder currentParam = new StringBuilder();
StringBuilder statusMessage = new StringBuilder();
txtInputFile.setText("Input: " + params[0]);
txtOutputFile.setText("Output: " + params[1]);
txtPercentDone.setText("" + MainActivity.getProgressStatus(statusMessage, currentParam) + "%");
txtStatusMessage.setText(statusMessage);
txtParamName.setText(currentParam);
}
However, none of the TextViews update their text. I am calling publishProgress() in my doInBackground() method. And the log shows that onProgressUpdate() is getting called correctly. I know that the TextViews have been instantiated and are not null. Does anyone know what's going on?
UPDATE:
This may have something to do with the fact that this code is in a nested fragment (the ui being updated). When I show this same fragment as the root, everything is working fine. But I need to have it nested in this situation. Why is that making a difference?
Pass your parameters and context from AsyncTask and then set them using runOnUiThread. If you can't then put your log here to know the issue.
Every publishProgress() are enqueue on the UI thread, so if your doInBackground ends before all yours onProgressUpdate(), you only see your last ui update.
Try to test adding Thread.sleep(MILLISECONDS) to check the concurrency of the ui thread.
#Override
protected Void doInBackground(Void... params) {
for (int i = 0; i < SOME_VALUE ;i++) {
publishProgress(""+i);
try {
Thread.sleep(MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}

Android AsyncTask getting NetworkOnMainThreadException when called a second time

I'm getting some data off Parse.com in my app
When it first starts up, it checks the cache if available and displays it, then the user can pull-to-refresh to get the latest data
When the first refresh action is called, everything works great, but when I try to refresh a second time I get the NetworkOnMainThreadException force close
This is the calling method (I'm using Android support's swipe-to-refresh view)
#Override
public void onRefresh() {
// this part is merely a check, crashes both with or without this
if(!runningTask.getClass().equals(AsyncTask.Status.FINISHED))
runningTask.cancel(true);
runningTask = new DownloadEvents(true).execute();
}
This is the async task
private class DownloadEvents extends AsyncTask<Void, Void, ArrayList<Event>> {
boolean forceDownload;
String errorString = "";
public DownloadEvents(boolean forceDownload) {
this.forceDownload = forceDownload;
Parse.initialize(getActivity(), #API KEY CENSORED#);
}
// Do the long-running work in here
protected ArrayList<Event> doInBackground(Void... params) {
List<ParseObject> events = null;
PreferencesHelper ph = new PreferencesHelper(getActivity());
ParseQuery<ParseObject> eventsQuery = new ParseQuery<ParseObject>("events");
try {
eventsQuery.setCachePolicy(ParseQuery.CachePolicy.CACHE_ONLY);
events = eventsQuery.find();
} catch (ParseException e1) {
e1.printStackTrace();
}
if(forceDownload || System.currentTimeMillis() - ph.getLastEventsDownloadTime() > 43200000 || events == null)
{
swipeLayout.setRefreshing(true);
try {
eventsQuery.setCachePolicy(ParseQuery.CachePolicy.NETWORK_ELSE_CACHE);
events = eventsQuery.find();
ph.setLastEventsDownloadTime();
} catch (ParseException e) {
e.printStackTrace();
}
}
// creating classes and sorting goes here
return eventsList;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(ArrayList<Event> events) {
if(events.size() > 0)
{
EventsAdapter adapter = new EventsAdapter(getActivity(), R.layout.events_card, events);
adapter.notifyDataSetChanged();
getListView().setAdapter(adapter);
}
if(errorString != "")
Toast.makeText(getActivity(), errorString, Toast.LENGTH_SHORT).show();
swipeLayout.setRefreshing(false);
}
}
Even if I force the download as the app start, it's always the second pulldown that crashes it, not the second time it downloads something
Please don't suggest turning off the NetworkOnMainThread strict rule, that's not a solution
Edit: here's the logcat
FATAL EXCEPTION: main
Process: com.mdk.test, PID: 24048
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1145)
at com.android.org.conscrypt.OpenSSLSocketImpl.shutdownAndFreeSslNative(OpenSSLSocketImpl.java:1102)
at com.android.org.conscrypt.OpenSSLSocketImpl.close(OpenSSLSocketImpl.java:1097)
at org.apache.http.impl.SocketHttpClientConnection.close(SocketHttpClientConnection.java:205)
at org.apache.http.impl.conn.DefaultClientConnection.close(DefaultClientConnection.java:161)
at org.apache.http.impl.conn.tsccm.AbstractConnPool.closeConnection(AbstractConnPool.java:320)
at org.apache.http.impl.conn.tsccm.ConnPoolByRoute.shutdown(ConnPoolByRoute.java:678)
at org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager.shutdown(ThreadSafeClientConnManager.java:256)
at com.parse.ParseRequest.initialize(ParseRequest.java:118)
at com.parse.Parse.initialize(Parse.java:109)
at com.mdk.test.EventsFragment$DownloadEvents.<init>(EventsFragment.java:136)
at com.mdk.test.EventsFragment.onRefresh(EventsFragment.java:223)
at android.support.v4.widget.SwipeRefreshLayout.startRefresh(SwipeRefreshLayout.java:441)
at android.support.v4.widget.SwipeRefreshLayout.onTouchEvent(SwipeRefreshLayout.java:399)
at android.view.View.dispatchTouchEvent(View.java:8073)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2253)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1987)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2259)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2001)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2259)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2001)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2259)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2001)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2259)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2001)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2259)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2001)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2198)
at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1649)
at android.app.Activity.dispatchTouchEvent(Activity.java:2717)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2159)
at android.view.View.dispatchPointerEvent(View.java:8263)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4013)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:3892)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3454)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3507)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3473)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3583)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3481)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3640)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3454)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3507)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3473)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3481)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3454)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5682)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5656)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5627)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:5761)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
at android.view.InputEv
Parse.initialize() is in the async task constructor that gets executed in the main thread and it invokes a network operation.
Move the call to doInBackground().
It's hard to tell exactly where the problem occurs, because you didn't post the stacktrace, but this line looks suspicious:
swipeLayout.setRefreshing(true);
You can't modify any views on your doInBackground, and it looks like this might be doing just that.
Another fishy line is:
PreferencesHelper ph = new PreferencesHelper(getActivity());
Apparantly this object needs the activity, which may or may not cause a NetworkOnMainThread exception later on. You can't touch the UI in your doInBackGround method.
Just keep the doInBackground clean and only do the network stuff there, don't entangle it with other things.
event.size()
For null data will throw an exception error, however not sure if that is the exact issue you are facing.

Class Asyntask error

I'm developing an android application , my problem is that I can't execute my asyntask class after clicking on a button but it works normally when I called it in my program
I have in logcat the error : "Only the original thread that created a view hierarchy can touch its views.”
here is my class :
ts.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
jr=2;
emp trs=new emp();
trs.execute();
}
});
emp t=new emp();
t.execute();
private class emp extends AsyncTask<Void,Void,Void>{
#Override
protected Void doInBackground(Void... params) {
try{
url = new URL("....");
HttpURLConnection httpconn = (HttpURLConnection)url.openConnection();
httpconn.connect();
if (httpconn.getResponseCode() == HttpURLConnection.HTTP_OK){
BufferedReader input = new BufferedReader(new InputStreamReader(httpconn.getInputStream()),8192);
while ((line = input.readLine()) != null) {
ja = new JSONArray(line);}
for (int i = 0; i < ja.length(); i++) {
JSONObject jo = null;j=0;
jo = (JSONObject) ja.getJSONObject(i);
ch = jo.getString("bgcolor");
ch1=jo.getString("duree_heure");
ch2=jo.getString("debut_heure");
ch4=jo.getString("matiere");
j=Integer.parseInt(ch2);
ch2=trans(j,ch1);
ch5=jo.getString("idsalle");
ch6=salle(ch5);
addvi(v,ch,ch6,ch2,ch4);
}
input.close();
}
}catch (JSONException e){
System.out.print("vérifier !");e.printStackTrace();} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}
return null;
}
}
so can anyone helps me please ?
It's caused by the fact that when you are inside doInBackground you are inside another thread too and since it's forbidden to edit/remove/etc views create from another thread (in this case UI thread) it throw this error.
Since you didn't posted the full code, the only thing which could case this problem is addvi(v,ch,ch6,ch2,ch4); so you should use runOnUiThread method of Activity to execute the method from the main thread.
But you should rethink your logic to work better with Asynctask methods onPreExecute / onPostExecute which is used to work with UI and are called and execute in the main thread (UI thread).
P.S To work better with the methods i said above, you should know what means the three generic in the extendsAsyncTask<Params, Progress, Result>
The three types used by an asynchronous task are the following:
Params, the type of the parameters sent to the task upon execution.
Progress, the type of the progress units published during the background computation.
Result, the type of the result of the background computation.
Edit: As other noticed in comments you have onProgressUpdate too which is invoked by you from doBackground method using publishProgress
you cannot touch or modify view in the doInBackground function of asynctask all UI work need to be done on UI thread or main thread.I think you are doing some UI work so do it in onPostExecute() method

continue ProgressDialog until onPostExecute finishes

I know that progress dialog will stop spinning if it's not terminated immediately inside onPostExecute(). However, I have some methods to call inside onPostExecute() and I cannot transfer them to doInBackground() because these methods should run on UI. Is there a possible way to continue the progressDialog after calling these methods without stopping it to spin?
Here is the onPostExecute of my AsyncTask:
protected String doInBackground(final String... strings) {
//Network activity here
}
protected void onPostExecute(String unused){
//progressdialog stops spinning here, cannot change the message also
try {
if(response.equals("HOST ERROR") || response.equals("CONNECTION ERROR") || response.equals("ERROR")){
new AlertDialog.Builder(context).setTitle("Error").setMessage("Cannot connect to the internet.").setNeutralButton("Close", null).setIcon(android.R.drawable.ic_delete).show();
}
else{
doc = Jsoup.parse(response);
Intent cc = new Intent(activity,com.sblive.aufschoolbliz.GradeBook.class);
subjectCodes = getSubjectCodes(); //this parsing method should run on UI
professors = getProfs(); //this parsing method should run on UI
grades = getGrades(); //this parsing method should run on UI
cc.putExtra("subjectCodes", subjectCodes);
cc.putExtra("professors", professors);
cc.putExtra("grades", grades);
if(this.pd.isShowing()) {
this.pd.dismiss();
}
context.startActivity(cc);
}
}
catch (NullPointerException e) {
}
}
<<< EDIT: >>>
Ok forget about what I posted, silly me. Of course anything that modifies user interface needs to be called/dismissed on the UI thread.
So what I would do is run everything possible during doInBackground(), and creating/dissmissing the dialog or anything that requires to be run on the UI thread explicitly like this:
this.runOnUiThread(new Runnable() {
public void run() {
/*Code required to run on UI thread*/ }
});
And im afraid that's as much as you can do without overdoing way too much.

Android thread sometimes does not start

I must use Thread in an Android project. Sometimes, it works corectly, however sometimes does not; it does not start (does not call SendToServer() method)or it starts but return to another function suddenly (return updated; line)before the thread does not finish.
Note: affected value is bigger than 0, it gives condition and it goes to if statement.
Here is the my code sample;
public static Boolean MyUpdateFunction(MyObject myobject){
Boolean updated=false;
//Code for updating local database
int affected= SqliteDb.update(....);
if(affected>0)
{
//Send updated data to server
//For this I must use Thread(I can't use AsyncThread)
updated=true;
SendToServer();
}
return updated;
}
public static void SendToServer()
{
try{
;
Thread th=new Thread(new Runnable() {
public void run() {
try {
//Create data and send it to server
//.......
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
th.start();
th.join();
}
catch(SQLException e)
{
Toast.makeText(myContext,"ERROR: "+e.getMessage(), Toast.LENGTH_LONG).show();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Other people are correct in that an AsyncTask is the way forward, but the direct problem due to what you're experiencing is this (and as such, I would recommend reading up on how Threading works):
When you start the thread, it begins a new process. The UI thread (which is generally where the majority of your code is) continues. So your code will fire the thread with SendToServer(), and then by definition will immediately return updated, as the UI thread immediately goes to the next line.
What you need is a callback from your Thread, which is handled in the onPostExecute() method of an AsyncTask. There's a good tutorial on how to use them and what they do here
Edit:
I've just seen from a comment above that you can't use Asynctasks, fair enough, but you still need a callback/event fired from your Thread to return any results
Instead of using threads and your variables (updated and affected), you can use AsyncTasks: see: http://developer.android.com/reference/android/os/AsyncTask.html
With AsyncTask, you have some methods which are doing exactly what you want:
onPreExecute
doInBackground
onPostExecute
So, what you can do is to check your condition in onPreExecute, then do your SendToServer in the doInBackground and onPostExecute do what you need.

Categories

Resources