Reputation: 1837
I am building a simple android app but I keep getting this exception NetworkOnMainThreadException. I have researched a lot about this, I understand that on android3.0(honeycomb) os and above, it is stricter, and should use asyncTask (on background thread), and I have done everything that I found, but still getting this exception, could someone guide on how to fix this ?
EditProductActivity.java :
package com.example.test;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class EditProductActivity extends Activity {
EditText txtName;
EditText txtPrice;
EditText txtDesc;
EditText txtCreatedAt;
Button btnSave;
Button btnDelete;
String pid;
//progress Dialog
private ProgressDialog pDialog;
//JSON parser class
JSONParser jsonParser = new JSONParser();
//single product url
private static final String url_product_details = "http://api.kerjaapa.com/android_connect/get_product_details.php";
//url to update product
private static final String url_update_product = "http://api.kerjaapa.com/android_connect/update_product.php";
//url to delete product
private static final String url_delete_product = "http://api.kerjaapa.com/android_connect/delete_product.php";
//JSON Node names
private static final String TAG_SUCCESS = "success";
private static final String TAG_PRODUCT = "product";
private static final String TAG_PID = "pid";
private static final String TAG_NAME = "name";
private static final String TAG_PRICE = "price";
private static final String TAG_DESCRIPTION = "description";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_product);
//save button
btnSave = (Button) findViewById(R.id.btnSave);
btnDelete = (Button) findViewById(R.id.btnDelete);
//getting product details from intent
Intent i = getIntent();
//getting product id(pid) from intent
pid = i.getStringExtra(TAG_PID);
//getting complete product details in background thread
new GetProductDetails().execute();
//save button click event
btnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
//starting background task to update product
new SaveProductDetails().execute();
}
});
//Delete button click event
btnDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
//deleting product in background thread
new DeleteProduct().execute();
}
});
}
//Background Async Task to Get Complete Product Details
class GetProductDetails extends AsyncTask<String, String, String> {
//Before starting background thread show progress dialog
@Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(EditProductActivity.this);
pDialog.setMessage("Loading product details. Please wait...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(true);
pDialog.show();
}
//getting product details in background thread
protected String doInBackground(String... params) {
//updating UI from Background Thread
runOnUiThread(new Runnable() {
public void run() {
//Check for success tag
int success;
try {
//Building Parameters
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("pid", pid));
//getting product details by making HTTP request
//Note that product details url will use GET request
JSONObject json = jsonParser.makeHttpRequest(
url_product_details, "GET", params);
//check your log for json response
Log.d("Single Product Details", json.toString());
//json success tag
success = json.getInt(TAG_SUCCESS);
if (success == 1) {
//successfully received product details
JSONArray productObj = json
.getJSONArray(TAG_PRODUCT); //JSON Array
//get first product object from JSON Array
JSONObject product = productObj.getJSONObject(0);
//product with this pid found
//Edit text
txtName = (EditText) findViewById(R.id.inputName);
txtPrice = (EditText) findViewById(R.id.inputPrice);
txtDesc = (EditText) findViewById(R.id.inputDesc);
//display product data in editText
txtName.setText(product.getString(TAG_NAME));
txtPrice.setText(product.getString(TAG_PRICE));
txtDesc.setText(product.getString(TAG_DESCRIPTION));
} else {
//product with pid not found
}
} catch (JSONException e) {
e.printStackTrace();
}
}
});
return null;
}
//After completing background task dismiss the progress dialog
protected void onPostExecute(String file_url) {
//dismiss the dialog once got all details
pDialog.dismiss();
}
}
//Background Async Task to Save product details
class SaveProductDetails extends AsyncTask<String, String, String> {
//Before starting background thread, show dialog
@Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(EditProductActivity.this);
pDialog.setMessage("Saving product ...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(true);
pDialog.show();
}
//Saving product
protected String doInBackground(String... args) {
//getting updated data from EditTexts
String name = txtName.getText().toString();
String price = txtPrice.getText().toString();
String description = txtDesc.getText().toString();
//Building parameters
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair(TAG_PID, pid));
params.add(new BasicNameValuePair(TAG_NAME, name));
params.add(new BasicNameValuePair(TAG_PRICE, price));
params.add(new BasicNameValuePair(TAG_DESCRIPTION, description));
//sending modified data through http request
//Notice that update product url accepts POST method
JSONObject json = jsonParser.makeHttpRequest(url_update_product,
"POST", params);
//check json success tag
try {
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
//successfully updated
Intent i = getIntent();
//send result code 100 to notify about product update
setResult(100, i);
finish();
} else {
//failed to update product
}
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
//After completing background task, dismiss dialog
protected void onPostExecute(String file_url) {
//dismiss dialog
pDialog.dismiss();
}
}
//Background async task to delete product
class DeleteProduct extends AsyncTask<String, String, String> {
//Before starting, show progress dialog
@Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(EditProductActivity.this);
pDialog.setMessage("Deleting Product...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(true);
pDialog.show();
}
//Deleting product
protected String doInBackground(String... args) {
//check for success tag
int success;
try {
//Building parameters
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("pid", pid));
//getting product details by making HTTP request
JSONObject json = jsonParser.makeHttpRequest(
url_delete_product, "POST", params);
//check your log for json response
Log.d("Delete product", json.toString());
//json success tag
success = json.getInt(TAG_SUCCESS);
if (success == 1) {
//product successfully deleted
//notify previous activity by sending code 100
Intent i = getIntent();
//send result code 100 to notify about product deletion
setResult(100, i);
finish();
}
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
//After completing background task dismiss dialog
protected void onPostExecute(String file_url) {
//dismiss dialog once finish
pDialog.dismiss();
}
}
}
And for the layout xml:
edit_product.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<!-- Name Label -->
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Product Name"
android:paddingLeft="10dip"
android:paddingRight="10dip"
android:paddingTop="10dip"
android:textSize="17dip" />
<!-- Input Name -->
<EditText android:id="@+id/inputName"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_marginBottom="15dip"
android:singleLine="true" />
<!-- Price Label -->
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Price"
android:paddingLeft="10dip"
android:paddingRight="10dip"
android:paddingTop="10dip"
android:textSize="17dip" />
<!-- Input Price -->
<EditText android:id="@+id/inputPrice"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_marginBottom="15dip"
android:singleLine="true"
android:inputType="numberDecimal" />
<!-- Description Label -->
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Description"
android:paddingLeft="10dip"
android:paddingRight="10dip"
android:paddingTop="10dip"
android:textSize="17dip" />
<!-- Input Description -->
<EditText android:id="@+id/inputDesc"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_marginBottom="15dip"
android:lines="4"
android:gravity="top" />
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!-- Button Update Product -->
<Button android:id="@+id/btnSave"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Save Changes"
android:layout_weight="1" />
<!-- Button Delete Product -->
<Button android:id="@+id/btnDelete"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Delete"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>
Here is the debug error stack :
Thread [<1> main] (Suspended (exception NetworkOnMainThreadException))
DefaultRequestDirector.execute(HttpHost, HttpRequest, HttpContext) line: 531
DefaultHttpClient(AbstractHttpClient).execute(HttpHost, HttpRequest, HttpContext) line: 555 DefaultHttpClient(AbstractHttpClient).execute(HttpUriRequest, HttpContext) line: 487
DefaultHttpClient(AbstractHttpClient).execute(HttpUriRequest) line: 465 JSONParser.makeHttpRequest(String, String, List) line: 61
EditProductActivity$GetProductDetails$1.run() line: 120 Handler.handleCallback(Message) line: 733
Handler.dispatchMessage(Message) line: 95
Looper.loop() line: 136 ActivityThread.main(String[]) line: 5017
Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]
Method.invoke(Object, Object...) line: 515
ZygoteInit$MethodAndArgsCaller.run() line: 779
ZygoteInit.main(String[]) line: 595 NativeStart.main(String[]) line: not available [native method]
I think I have followed the rules in using asyncTask, doing the task on the background using onPreExecute(), onPostExecute(). I'm sorry I'm a newbie, any help is greatly appreciated. Thx in advance.
Upvotes: 0
Views: 253
Reputation: 5535
doInBackground does not interact with the main UI thread and you have mentioned Views in the doinBackground method of AsynTask
also remove runOnUIThread from doInBackground
also put these in onPostExecute method
//product with this pid found
//Edit text
txtName = (EditText) findViewById(R.id.inputName);
txtPrice = (EditText) findViewById(R.id.inputPrice);
txtDesc = (EditText) findViewById(R.id.inputDesc);
//display product data in editText
txtName.setText(product.getString(TAG_NAME));
txtPrice.setText(product.getString(TAG_PRICE));
txtDesc.setText(product.getString(TAG_DESCRIPTION));
Upvotes: 0
Reputation: 3257
your doInBackground method is calling runOnUIThread, this is nonsense.
You should do your background work on doInBackground, and then execute the UI work in the onPostExecute, for example:
package com.example.test;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class EditProductActivity extends Activity {
EditText txtName;
EditText txtPrice;
EditText txtDesc;
EditText txtCreatedAt;
Button btnSave;
Button btnDelete;
String pid;
//progress Dialog
private ProgressDialog pDialog;
//JSON parser class
JSONParser jsonParser = new JSONParser();
//single product url
private static final String url_product_details = "http://api.kerjaapa.com/android_connect/get_product_details.php";
//url to update product
private static final String url_update_product = "http://api.kerjaapa.com/android_connect/update_product.php";
//url to delete product
private static final String url_delete_product = "http://api.kerjaapa.com/android_connect/delete_product.php";
//JSON Node names
private static final String TAG_SUCCESS = "success";
private static final String TAG_PRODUCT = "product";
private static final String TAG_PID = "pid";
private static final String TAG_NAME = "name";
private static final String TAG_PRICE = "price";
private static final String TAG_DESCRIPTION = "description";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_product);
//save button
btnSave = (Button) findViewById(R.id.btnSave);
btnDelete = (Button) findViewById(R.id.btnDelete);
//getting product details from intent
Intent i = getIntent();
//getting product id(pid) from intent
pid = i.getStringExtra(TAG_PID);
//getting complete product details in background thread
new GetProductDetails().execute();
//save button click event
btnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
//starting background task to update product
new SaveProductDetails().execute();
}
});
//Delete button click event
btnDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
//deleting product in background thread
new DeleteProduct().execute();
}
});
}
//Background Async Task to Get Complete Product Details
class GetProductDetails extends AsyncTask<String, String, Integer> {
//Before starting background thread show progress dialog
@Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(EditProductActivity.this);
pDialog.setMessage("Loading product details. Please wait...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(true);
pDialog.show();
}
//getting product details in background thread
protected String doInBackground(String... params) {
//Check for success tag
int success = 0;
try {
//Building Parameters
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("pid", pid));
//getting product details by making HTTP request
//Note that product details url will use GET request
JSONObject json = jsonParser.makeHttpRequest(
url_product_details, "GET", params);
//check your log for json response
Log.d("Single Product Details", json.toString());
//json success tag
success = json.getInt(TAG_SUCCESS);
} catch (JSONException e) {
e.printStackTrace();
}
}
return success;
}
//After completing background task dismiss the progress dialog
protected void onPostExecute(Integer success) {
if (success == 1) {
//successfully received product details
JSONArray productObj = json
.getJSONArray(TAG_PRODUCT); //JSON Array
//get first product object from JSON Array
JSONObject product = productObj.getJSONObject(0);
//product with this pid found
//Edit text
txtName = (EditText) findViewById(R.id.inputName);
txtPrice = (EditText) findViewById(R.id.inputPrice);
txtDesc = (EditText) findViewById(R.id.inputDesc);
//display product data in editText
txtName.setText(product.getString(TAG_NAME));
txtPrice.setText(product.getString(TAG_PRICE));
txtDesc.setText(product.getString(TAG_DESCRIPTION));
} else {
//product with pid not found
}
//dismiss the dialog once got all details
pDialog.dismiss();
}
}
//Background Async Task to Save product details
class SaveProductDetails extends AsyncTask<String, String, String> {
//Before starting background thread, show dialog
@Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(EditProductActivity.this);
pDialog.setMessage("Saving product ...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(true);
pDialog.show();
}
//Saving product
protected String doInBackground(String... args) {
//getting updated data from EditTexts
String name = txtName.getText().toString();
String price = txtPrice.getText().toString();
String description = txtDesc.getText().toString();
//Building parameters
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair(TAG_PID, pid));
params.add(new BasicNameValuePair(TAG_NAME, name));
params.add(new BasicNameValuePair(TAG_PRICE, price));
params.add(new BasicNameValuePair(TAG_DESCRIPTION, description));
//sending modified data through http request
//Notice that update product url accepts POST method
JSONObject json = jsonParser.makeHttpRequest(url_update_product,
"POST", params);
//check json success tag
try {
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
//successfully updated
Intent i = getIntent();
//send result code 100 to notify about product update
setResult(100, i);
finish();
} else {
//failed to update product
}
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
//After completing background task, dismiss dialog
protected void onPostExecute(String file_url) {
//dismiss dialog
pDialog.dismiss();
}
}
//Background async task to delete product
class DeleteProduct extends AsyncTask<String, String, String> {
//Before starting, show progress dialog
@Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(EditProductActivity.this);
pDialog.setMessage("Deleting Product...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(true);
pDialog.show();
}
//Deleting product
protected String doInBackground(String... args) {
//check for success tag
int success;
try {
//Building parameters
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("pid", pid));
//getting product details by making HTTP request
JSONObject json = jsonParser.makeHttpRequest(
url_delete_product, "POST", params);
//check your log for json response
Log.d("Delete product", json.toString());
//json success tag
success = json.getInt(TAG_SUCCESS);
if (success == 1) {
//product successfully deleted
//notify previous activity by sending code 100
Intent i = getIntent();
//send result code 100 to notify about product deletion
setResult(100, i);
finish();
}
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
//After completing background task dismiss dialog
protected void onPostExecute(String file_url) {
//dismiss dialog once finish
pDialog.dismiss();
}
}
}
Upvotes: 0
Reputation: 2372
Your doInBackground should run the network operation and post the result to onPostExecute, where you can update the UI.
Upvotes: 0
Reputation: 93892
I think I have followed the rules in using asyncTask, doing the task on the background using onPreExecute(), onPostExecute().
Not really because in your doInBackground
you perform the HTTP request on the UI thread:
runOnUiThread(new Runnable() {
public void run() {
which completely defeats the purpose of using an async task.
So remove the runOnUiThread
, do your request in the background and when the task is completed use the onPostExecute
to update your fields.
Upvotes: 2