I got an error while running my Android project for RssReader.
Code:
URL url = new URL(urlToRssFeed);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLReader xmlreader = parser.getXMLReader();
RssHandler theRSSHandler = new RssHandler();
xmlreader.setContentHandler(theRSSHandler);
InputSource is = new InputSource(url.openStream());
xmlreader.parse(is);
return theRSSHandler.getFeed();
And it shows the below error:
android.os.NetworkOnMainThreadException
How can I fix this issue?
转载于:https://stackoverflow.com/questions/6343166/how-do-i-fix-android-os-networkonmainthreadexception
This exception is thrown when an application attempts to perform a networking operation on its main thread. Run your code in AsyncTask
:
class RetrieveFeedTask extends AsyncTask<String, Void, RSSFeed> {
private Exception exception;
protected RSSFeed doInBackground(String... urls) {
try {
URL url = new URL(urls[0]);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLReader xmlreader = parser.getXMLReader();
RssHandler theRSSHandler = new RssHandler();
xmlreader.setContentHandler(theRSSHandler);
InputSource is = new InputSource(url.openStream());
xmlreader.parse(is);
return theRSSHandler.getFeed();
} catch (Exception e) {
this.exception = e;
return null;
} finally {
is.close();
}
}
protected void onPostExecute(RSSFeed feed) {
// TODO: check this.exception
// TODO: do something with the feed
}
}
How to execute the task:
In MainActivity.java
file you can add this line within your oncreate()
method
new RetrieveFeedTask().execute(urlToRssFeed);
Don't forget to add this to AndroidManifest.xml
file:
<uses-permission android:name="android.permission.INTERNET"/>
You cannot perform network I/O on the UI thread on Honeycomb. Technically, it is possible on earlier versions of Android, but it is a really bad idea as it will cause your app to stop responding, and can result in the OS killing your app for being badly behaved. You'll need to run a background process or use AsyncTask to perform your network transaction on a background thread.
There is an article about Painless Threading on the Android developer site which is a good introduction to this, and it will provide you with a much better depth of an answer than can be realistically provided here.
You should almost always run network operations on a thread or as an asynchronous task.
But it is possible to remove this restriction and you override the default behavior, if you are willing to accept the consequences.
Add:
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
In your class,
and
ADD this permission in android manifest.xml file:
<uses-permission android:name="android.permission.INTERNET"/>
Consequences:
Your app will (in areas of spotty internet connection) become unresponsive and lock up, the user perceives slowness and has to do a force kill, and you risk the activity manager killing your app and telling the user that the app has stopped.
Android has some good tips on good programming practices to design for responsiveness: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html
You disable the strict mode using following code:
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy =
new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
This is not recommended: use the AsyncTask
interface.
I solved this problem using a new Thread
.
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
The top answer of spektom works perfect.
If you are writing the AsyncTask
inline and not extending as a class, and on top of this, if there is a need to get a response out of the AsyncTask
, one can use the get()
method as below.
RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get();
(From his example.)
Use Service or AsyncTask
See also Stack Overflow question:
android.os.NetworkOnMainThreadException sending an email from Android
This happens in Android 3.0 and above. From Android 3.0 and above, they have restricted using network operations (functions that access the Internet) from running in the main thread/UI thread (what spawns from your on create and on resume methods in the activity).
This is to encourage using separate threads for network operations. See AsyncTask for more details on how to perform network activities the right way.
For me it was this:
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="10" />
The device I was testing my app on was 4.1.2 which is SDK Version 16!
Make the sure the target version is the same as your Android Target Library. If you are unsure what your target library is, right click your Project -> Build Path -> Android, and it should be the one that is ticked.
Also, as others have mentioned, include the correct permissions to access the Internet:
<uses-permission android:name="android.permission.INTERNET"/>
Do the network actions on another thread
For Example:
new Thread(new Runnable(){
@Override
public void run() {
// Do network action in this function
}
}).start();
And add this to AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
You should not do any time-consuming task on the main thread (UI thread), like any network operation, file I/O, or SQLite database operations. So for this kind of operation, you should create a worker thread, but the problem is that you can not directly perform any UI related operation from your worker thread. For that, you have to use Handler
and pass the Message
.
To simplify all these things, Android provides various ways, like AsyncTask
, AsyncTaskLoader
, CursorLoader
or IntentService
. So you can use any of these according to your requirements.
Network-based operations cannot be run on the main thread. You need to run all network-based tasks on a child thread or implement AsyncTask.
This is how you run a task in a child thread:
new Thread(new Runnable(){
@Override
public void run() {
try {
// Your implementation goes here
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}).start();
The accepted answer has some significant down-sides. It is not advisable to use AsyncTask for networking unless you really know what you are doing. Some of the down-sides include:
executeOnExecutor
method and supply an alternative executor). Code that works fine when run serially on ICS may break when executed concurrently on Gingerbread, say, if you have inadvertent order-of-execution dependencies.If you want to avoid short-term memory leaks, have well defined execution characteristics across all platforms, and have a base to build really robust network handling, you might want to consider:
Service
or IntentService
instead, perhaps with a PendingIntent
to return the result via the Activity's onActivityResult
method.Down-sides:
AsyncTask
, though not as much as you might thinkIntentService
with an equivalent Service
implementation, perhaps like this one.Up-sides:
onActivityResult
methodAsyncTask
in an Activity
, but if the user context-switches out of the app to take a phone-call, the system may kill the app before the upload completes. It is less likely to kill an application with an active Service
.IntentService
(like the one I linked above) you can control the level of concurrency via the Executor
.You can implement an IntentService
to perform downloads on a single background thread quite easily.
Step 1: Create an IntentService
to perform the download. You can tell it what to download via Intent
extra's, and pass it a PendingIntent
to use to return the result to the Activity
:
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Intent;
import android.util.Log;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
public class DownloadIntentService extends IntentService {
private static final String TAG = DownloadIntentService.class.getSimpleName();
public static final String PENDING_RESULT_EXTRA = "pending_result";
public static final String URL_EXTRA = "url";
public static final String RSS_RESULT_EXTRA = "url";
public static final int RESULT_CODE = 0;
public static final int INVALID_URL_CODE = 1;
public static final int ERROR_CODE = 2;
private IllustrativeRSSParser parser;
public DownloadIntentService() {
super(TAG);
// make one and re-use, in the case where more than one intent is queued
parser = new IllustrativeRSSParser();
}
@Override
protected void onHandleIntent(Intent intent) {
PendingIntent reply = intent.getParcelableExtra(PENDING_RESULT_EXTRA);
InputStream in = null;
try {
try {
URL url = new URL(intent.getStringExtra(URL_EXTRA));
IllustrativeRSS rss = parser.parse(in = url.openStream());
Intent result = new Intent();
result.putExtra(RSS_RESULT_EXTRA, rss);
reply.send(this, RESULT_CODE, result);
} catch (MalformedURLException exc) {
reply.send(INVALID_URL_CODE);
} catch (Exception exc) {
// could do better by treating the different sax/xml exceptions individually
reply.send(ERROR_CODE);
}
} catch (PendingIntent.CanceledException exc) {
Log.i(TAG, "reply cancelled", exc);
}
}
}
Step 2: Register the service in the manifest:
<service
android:name=".DownloadIntentService"
android:exported="false"/>
Step 3: Invoke the service from the Activity, passing a PendingResult object which the Service will use to return the result:
PendingIntent pendingResult = createPendingResult(
RSS_DOWNLOAD_REQUEST_CODE, new Intent(), 0);
Intent intent = new Intent(getApplicationContext(), DownloadIntentService.class);
intent.putExtra(DownloadIntentService.URL_EXTRA, URL);
intent.putExtra(DownloadIntentService.PENDING_RESULT_EXTRA, pendingResult);
startService(intent);
Step 4: Handle the result in onActivityResult:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RSS_DOWNLOAD_REQUEST_CODE) {
switch (resultCode) {
case DownloadIntentService.INVALID_URL_CODE:
handleInvalidURL();
break;
case DownloadIntentService.ERROR_CODE:
handleError(data);
break;
case DownloadIntentService.RESULT_CODE:
handleRSS(data);
break;
}
handleRSS(data);
}
super.onActivityResult(requestCode, resultCode, data);
}
A github project containing a complete working Android-Studio/gradle project is available here.
This is only thrown for applications targeting the Honeycomb SDK or higher. Applications targeting earlier SDK versions are allowed to do networking on their main event loop threads.
**Use like this in Your Activity**
btnsub.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
//Initialize soap request + add parameters
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME1);
//Use this to add parameters
request.addProperty("pincode",txtpincode.getText().toString());
request.addProperty("bg",bloodgroup.getSelectedItem().toString());
//Declare the version of the SOAP request
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.setOutputSoapObject(request);
envelope.dotNet = true;
try {
HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
//this is the actual part that will call the webservice
androidHttpTransport.call(SOAP_ACTION1, envelope);
// Get the SoapResult from the envelope body.
SoapObject result = (SoapObject)envelope.getResponse();
Log.e("result data", "data"+result);
SoapObject root = (SoapObject) result.getProperty(0);
// SoapObject s_deals = (SoapObject) root.getProperty(0);
//SoapObject s_deals_1 = (SoapObject) s_deals.getProperty(0);
//
System.out.println("********Count : "+ root.getPropertyCount());
value=new ArrayList<Detailinfo>();
for (int i = 0; i < root.getPropertyCount(); i++)
{
SoapObject s_deals = (SoapObject) root.getProperty(i);
Detailinfo info=new Detailinfo();
info.setFirstName( s_deals.getProperty("Firstname").toString());
info.setLastName( s_deals.getProperty("Lastname").toString());
info.setDOB( s_deals.getProperty("DOB").toString());
info.setGender( s_deals.getProperty("Gender").toString());
info.setAddress( s_deals.getProperty("Address").toString());
info.setCity( s_deals.getProperty("City").toString());
info.setState( s_deals.getProperty("State").toString());
info.setPinecode( s_deals.getProperty("Pinecode").toString());
info.setMobile( s_deals.getProperty("Mobile").toString());
info.setEmail( s_deals.getProperty("Email").toString());
info.setBloodgroup( s_deals.getProperty("Bloodgroup").toString());
info.setAdddate( s_deals.getProperty("Adddate").toString());
info.setWaight(s_deals.getProperty("waight").toString());
value.add(info);
}
} catch (Exception e) {
e.printStackTrace();
}
Intent inten=new Intent(getApplicationContext(),ComposeMail.class);
//intent.putParcelableArrayListExtra("valuesList", value);
startActivity(inten);
}
}).start();
}
});
Using Android Annotations is an option. It will allow you to simply run any method in a background thread:
// normal method
private void normal() {
doSomething(); // do something in background
}
@Background
protected void doSomething()
// run your networking code here
}
Note, that although it provides benefits of simplicity and readability, it has its disadvantages.
Just to spell out something explicitly:
The main thread is basically the UI thread.
So saying that you cannot do networking operations in the main thread means you cannot do networking operations in the UI thread, which means you cannot do networking operations in a *runOnUiThread(new Runnable() { ... }*
block inside some other thread, either.
(I just had a long head-scratching moment trying to figure out why I was getting that error somewhere other than my main thread. This was why; this thread helped; and hopefully this comment will help someone else.)
The error is due to executing long running operations in main thread,You can easily rectify the problem by using AsynTask or Thread. You can checkout this library AsyncHTTPClient for better handling.
AsyncHttpClient client = new AsyncHttpClient();
client.get("http://www.google.com", new AsyncHttpResponseHandler() {
@Override
public void onStart() {
// Called before a request is started
}
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] response) {
// Called when response HTTP status is "200 OK"
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
// Called when response HTTP status is "4XX" (for example, 401, 403, 404)
}
@Override
public void onRetry(int retryNo) {
// Called when request is retried
}
});
This exception occurs due to any heavy task performed on the main thread if that performing task takes too much time.
To avoid this, we can handle it using threads or executers
Executors.newSingleThreadExecutor().submit(new Runnable() {
@Override
public void run() {
// You can perform your task here.
}
});
Put your code inside:
new Thread(new Runnable(){
@Override
public void run() {
try {
// Your implementation
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}).start();
Or:
class DemoTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... arg0) {
//Your implementation
}
protected void onPostExecute(Void result) {
// TODO: do something with the feed
}
}
This works. Just made Dr.Luiji's answer a little simpler.
new Thread() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
Although above there is a huge solution pool, no one mentioned com.koushikdutta.ion
: https://github.com/koush/ion
It's also asynchronous and very simple to use:
Ion.with(context)
.load("http://example.com/thing.json")
.asJsonObject()
.setCallback(new FutureCallback<JsonObject>() {
@Override
public void onCompleted(Exception e, JsonObject result) {
// do stuff with the result or error
}
});
On Android, network operations cannot be run on the main thread. You can use Thread, AsyncTask (short-running tasks), Service (long-running tasks) to do network operations.
In simple words,
DO NOT DO NETWORK WORK IN THE UI THREAD
For example, if you do an HTTP request, that is a network action.
Solution:
Way:
Put all your works inside
run()
method of new threaddoInBackground()
method of AsyncTask class.But:
When you get something from Network response and want to show it on your view (like display response message in TextView), you need to return back to the UI thread.
If you don't do it, you will get ViewRootImpl$CalledFromWrongThreadException
.
How to?
onPostExecute()
methodrunOnUiThread()
method and update view inside the run()
method.Accessing network resources from the main (UI) thread cause this exception. Use a separate thread or AsyncTask for accessing a network resource to avoid this problem.
You are not allowed to implement network operations on the UI thread on Android. You will have to use AsyncTask class to perform network related operations like sending API request, downloading image from a URL, etc. and using callback methods of AsyncTask, you can get you result in onPostExecute menthod and you will be in the UI thread and you can populate UI with data from web service or something like that.
Example: Suppose you want to download image from an URL: https://www.samplewebsite.com/sampleimage.jpg
Solution using AsyncTask: are respectively.
public class MyDownloader extends AsyncTask<String,Void,Bitmap>
{
@Override
protected void onPreExecute() {
// Show progress dialog
super.onPreExecute();
}
@Override
protected void onPostExecute(Bitmap bitmap) {
//Populate Ui
super.onPostExecute(bitmap);
}
@Override
protected Bitmap doInBackground(String... params) {
// Open URL connection read bitmaps and return form here
return result;
}
@Override
protected void onProgressUpdate(Void... values) {
// Show progress update
super.onProgressUpdate(values);
}
}
}
Note: Do not forget to add the Internet permission in the Android manifest file. It will work like a charm. :)
New Thread
and AsyncTask solutions have been explained already.
AsyncTask
should ideally be used for short operations. Normal Thread
is not preferable for Android.
Have a look at alternate solution using HandlerThread and Handler
HandlerThread
Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that
start()
must still be called.
Handler:
A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
Solution:
Create HandlerThread
Call start()
on HandlerThread
Create Handler
by getting Looper
from HanlerThread
Embed your Network operation related code in Runnable
object
Submit Runnable
task to Handler
Sample code snippet, which address NetworkOnMainThreadException
HandlerThread handlerThread = new HandlerThread("URLConnection");
handlerThread.start();
handler mainHandler = new Handler(handlerThread.getLooper());
Runnable myRunnable = new Runnable() {
@Override
public void run() {
try {
Log.d("Ravi", "Before IO call");
URL page = new URL("http://www.google.com");
StringBuffer text = new StringBuffer();
HttpURLConnection conn = (HttpURLConnection) page.openConnection();
conn.connect();
InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
BufferedReader buff = new BufferedReader(in);
String line;
while ( (line = buff.readLine()) != null) {
text.append(line + "\n");
}
Log.d("Ravi", "After IO call");
Log.d("Ravi",text.toString());
}catch( Exception err){
err.printStackTrace();
}
}
};
mainHandler.post(myRunnable);
Pros of using this approach:
Thread/AsyncTask
for each network operation is expensive. The Thread/AsyncTask
will be destroyed and re-created for next Network operations. But with Handler
and HandlerThread
approach, you can submit many network operations (as Runnable tasks) to single HandlerThread
by using Handler
.RxAndroid
is another better alternative to this problem and it saves us from hassles of creating threads and then posting results on Android UI thread. We just need to specify threads on which tasks need to be executed and everything is handled internally.
Observable<List<String>> musicShowsObservable = Observable.fromCallable(new Callable<List<String>>() {
@Override
public List<String> call() {
return mRestClient.getFavoriteMusicShows();
}
});
mMusicShowSubscription = musicShowsObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<String>>() {
@Override
public void onCompleted() { }
@Override
public void onError(Throwable e) { }
@Override
public void onNext(List<String> musicShows){
listMusicShows(musicShows);
}
});
By specifiying (Schedulers.io())
,RxAndroid will run getFavoriteMusicShows()
on a different thread.
By using AndroidSchedulers.mainThread()
we want to observe this Observable on the UI thread, i.e. we want our onNext()
callback to be called on the UI thread