Reputation: 311
I have a AsyncTask
in my app which downloads data from a webserver.
But when i rotate my screen, my app crashes. It's because the onPostExecute(where i storage the downloaded data) isn't executed the second time.
But after a screen rotation the onCreate()
method runs and so should the AsynchTask
(it does) but it doesn't call the onPostExecute
the second time. I hope you have a solution.
class ProductManager{
public ArrayList<ProductClass> productArray;
ProductManager(){
Log.d("LOGTAG","TAG2");
new DownloadProductTask().execute();
}
class DownloadProductTask extends AsyncTask<String,Integer,Void>
{
private ProgressDialog progressDialog = new ProgressDialog(ProductList.this);
InputStream is = null ;
String result = "";
@Override
protected void onPreExecute() {
progressDialog.setMessage("Download data...");
progressDialog.show();
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface arg0) {
DownloadProductTask.this.cancel(true);
}
});
}
@Override
protected Void doInBackground(String... params) {
Log.d("LOGTAG","TAG2.1");
String url_select = "http://jaspevj20.twenty.axc.nl/vhapp/displayproducts.php"; //Use http:// except for localhost & 127.0.0.1// jaspevj20.twenty.axc.nl for emulator local connection
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url_select);
ArrayList param = new ArrayList();
try {
httpPost.setEntity(new UrlEncodedFormEntity(param));
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
//read content
is = httpEntity.getContent();
} catch (Exception e) {
Log.e("log_tag", "Error in http connection "+e.toString());
}
try {
BufferedReader br = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = "";
while((line=br.readLine())!=null)
{
sb.append(line+"\n");
}
is.close();
result=sb.toString();
} catch (Exception e) {
// TODO: handle exception
Log.e("log_tag", "Error converting result "
+e.toString());
}
return null;
}
@Override
protected void onPostExecute(Void v) {
// ambil data dari Json database
try {
Log.d("LOGTAG","TAG3");
JSONArray Jarray = new JSONArray(result);
productArray = new ArrayList<ProductClass>();
int productid;
String productname;
double productprice;
int cookingtime;
String productoptionsstring;
List<String> productoptionslist;
String productflavoursstring;
List<String> productflavourslist;
String productdescription;
String category;
List<String> categorylist;
for(int i=0;i<Jarray.length();i++)
{
JSONObject Jasonobject = null;
Jasonobject = Jarray.getJSONObject(i);
productid = Integer.parseInt(Jasonobject.getString("ProductId"));
productname = Jasonobject.getString("ProductName");
productprice = Double.parseDouble(Jasonobject.getString("ProductPrice"));
cookingtime = Integer.parseInt(Jasonobject.getString("CookingTime"));
productoptionsstring = Jasonobject.getString("ProductOptions");
productoptionslist = Arrays.asList(productoptionsstring.split("\\s*,\\s*"));
productflavoursstring = Jasonobject.getString("ProductFlavours");
productflavourslist = Arrays.asList(productflavoursstring.split("\\s*,\\s*"));
productdescription = Jasonobject.getString("ProductDescription");
category = Jasonobject.getString("Category");
categorylist = Arrays.asList(category.split("\\s*,\\s*"));
Log.d("LOGTAG","TAG4");
productArray.add(new ProductClass(productid,productname,productprice,cookingtime));
ProductClass product = productArray.get(productArray.size()-1);
product.setProductOptions(productoptionslist);
product.setFlavours(productflavourslist);
product.setDescription(productdescription);
product.setCategories(categorylist);
}
mTitle = mDrawerTitle = getTitle();
mCategoryTitles = mProductManager.getAllCategories();
// set a custom shadow that overlays the main content when the drawer opens
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
mDrawerList.setAdapter(new ArrayAdapter<String>(ProductList.this,
R.layout.drawer_list_item, mCategoryTitles));
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
// Select the first category displaying as default
selectItem(0);
this.progressDialog.dismiss();
}
catch (Exception e)
{
Log.e("log_tag", "Error parsing data "+e.toString());
}
}
}
Upvotes: 0
Views: 445
Reputation: 49986
Actually whats more probable is that your AsyncTask actually does execute and in onPostExecute is using references to widgets (from previous Activity instance) that were destroyed by Android. Since your DownloadProductTask is internal to your Activity it hold implicit reference to your Activity and it disallows freeing your activity.
If you dont want to use android:configChanges hack - then make your AsyncTask static, then retain your AsyncTask instance using onRetainNonConfigurationInstance / getLastNonConfigurationInstance, and in onCreate update your AsyncTask with new widgets to update. Thats actually short story, its quite complicated. Other options - IMO a lot easier is to put your AsyncTask into retained fragment. Here are some info on this:
Android Fragments. Retaining an AsyncTask during screen rotation or configuration change
The problam with android:configChanges is that it does not protect you when your Activity is destroyed during normal LifeCycle changes, ie. you call you AsyncTask and then you hide your activity, and go to some other Activity - Android is now free to destroy your Activity - causing the same problems.
Upvotes: 2
Reputation: 7860
Use a Bundle to save your Data. Or edit the Manifest for the required Activity with
android:configChanges="orientation|keyboardHidden"
Introduction to Bundle:
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
// Save UI state changes to the savedInstanceState.
// This bundle will be passed to onCreate if the process is
// killed and restarted.
savedInstanceState.putBoolean("MyBoolean", true);
savedInstanceState.putDouble("myDouble", 1.9);
savedInstanceState.putInt("MyInt", 1);
savedInstanceState.putString("MyString", "Welcome back to Android");
// etc.
}
The Bundle is essentially a way of storing a NVP ("Name-Value Pair") map, and it will get passed in to onCreate
and also onRestoreInstanceState
where you'd extract the values like this:
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate.
boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
double myDouble = savedInstanceState.getDouble("myDouble");
int myInt = savedInstanceState.getInt("MyInt");
String myString = savedInstanceState.getString("MyString");
}
Upvotes: 0