Reputation: 983
Have no network call on main thread, but I still get the same error: NetworkOnMainThreadException.
MainActivity.class:
public class MainActivity extends Activity
{
private TextView tv1,
tv2,
tv3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv1 = (TextView) findViewById(R.id.tv1);
tv2 = (TextView) findViewById(R.id.tv2);
tv3 = (TextView) findViewById(R.id.tv3);
String xmlUrl = "https://dl.dropboxusercontent.com/s/wbuxa7cutb6/update.xml";
new DownloadData().execute(xmlUrl);
}
public class DownloadData extends AsyncTask<String, Void, InputStream>
{
@Override
protected InputStream doInBackground(String... urls) {
try {
URL url = new URL(urls[0]);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setReadTimeout(10000);
connection.setConnectTimeout(15000);
connection.setRequestMethod("GET");
connection.setDoInput(true);
connection.connect();
int response = connection.getResponseCode();
Log.d("", "The response is: " + response);
return connection.getInputStream();
//return new Scanner(connection.getInputStream(),"UTF-8").useDelimiter("\\A").next();
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(InputStream is)
{
List<List<String>> lists = new XmlParser().parse(is);
tv1.setText(lists.get(0).get(0));
tv2.setText(lists.get(0).get(1));
tv3.setText(lists.get(0).get(2));
}
}
}
How can you see, the need for internet is only in doInBackground(), but the error persists.
Log:
01-13 03:18:37.668: E/AndroidRuntime(5679): FATAL EXCEPTION: main
01-13 03:18:37.668: E/AndroidRuntime(5679): android.os.NetworkOnMainThreadException
01-13 03:18:37.668: E/AndroidRuntime(5679): at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1117)
01-13 03:18:37.668: E/AndroidRuntime(5679): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLInputStream.read(OpenSSLSocketImpl.java:657)
01-13 03:18:37.668: E/AndroidRuntime(5679): at libcore.net.http.FixedLengthInputStream.read(FixedLengthInputStream.java:45)
01-13 03:18:37.668: E/AndroidRuntime(5679): at libcore.io.Streams.readSingleByte(Streams.java:41)
01-13 03:18:37.668: E/AndroidRuntime(5679): at libcore.net.http.AbstractHttpInputStream.read(AbstractHttpInputStream.java:63)
01-13 03:18:37.668: E/AndroidRuntime(5679): at org.kxml2.io.KXmlParser.setInput(KXmlParser.java:1623)
01-13 03:18:37.668: E/AndroidRuntime(5679): at org.apache.harmony.xml.parsers.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:111)
01-13 03:18:37.668: E/AndroidRuntime(5679): at javax.xml.parsers.DocumentBuilder.parse(DocumentBuilder.java:132)
01-13 03:18:37.668: E/AndroidRuntime(5679): at neviat.test.downloaddata.XmlParser.parse(XmlParser.java:39)
01-13 03:18:37.668: E/AndroidRuntime(5679): at neviat.test.downloaddata.MainActivity$DownloadData.onPostExecute(MainActivity.java:70)
01-13 03:18:37.668: E/AndroidRuntime(5679): at neviat.test.downloaddata.MainActivity$DownloadData.onPostExecute(MainActivity.java:1)
01-13 03:18:37.668: E/AndroidRuntime(5679): at android.os.AsyncTask.finish(AsyncTask.java:631)
01-13 03:18:37.668: E/AndroidRuntime(5679): at android.os.AsyncTask.access$600(AsyncTask.java:177)
01-13 03:18:37.668: E/AndroidRuntime(5679): at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
01-13 03:18:37.668: E/AndroidRuntime(5679): at android.os.Handler.dispatchMessage(Handler.java:99)
01-13 03:18:37.668: E/AndroidRuntime(5679): at android.os.Looper.loop(Looper.java:137)
01-13 03:18:37.668: E/AndroidRuntime(5679): at android.app.ActivityThread.main(ActivityThread.java:5041)
01-13 03:18:37.668: E/AndroidRuntime(5679): at java.lang.reflect.Method.invokeNative(Native Method)
01-13 03:18:37.668: E/AndroidRuntime(5679): at java.lang.reflect.Method.invoke(Method.java:511)
01-13 03:18:37.668: E/AndroidRuntime(5679): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
01-13 03:18:37.668: E/AndroidRuntime(5679): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
01-13 03:18:37.668: E/AndroidRuntime(5679): at dalvik.system.NativeStart.main(Native Method)
XmlParser:
public class XmlParser
{
private DocumentBuilderFactory factory;
private DocumentBuilder builder;
private String getNodeValue(NamedNodeMap map, String key) {
String nodeValue = null;
Node node = map.getNamedItem(key);
if (node != null)
nodeValue = node.getNodeValue();
return nodeValue;
}
public List<List<String>> parse(InputStream inStream)
{
List<List<String>> lists = new ArrayList<List<String>>();
try {
factory = DocumentBuilderFactory.newInstance();
builder = factory.newDocumentBuilder();
builder.isValidating();
Document doc = builder.parse(inStream, null);
doc.getDocumentElement().normalize();
NodeList itemList = doc.getElementsByTagName("item");
final int length = itemList.getLength();
for (int i = 0; i < length; i++) {
final NamedNodeMap attr = itemList.item(i).getAttributes();
final String dataId = getNodeValue(attr, "id");
final String dataName = getNodeValue(attr, "name");
final String dataUrl = getNodeValue(attr, "url");
List<String> list = new ArrayList<String>();
list.add(dataId);
list.add(dataName);
list.add(dataUrl);
lists.add(list);
}
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
return lists;
}
}
Upvotes: 1
Views: 862
Reputation: 11741
You are returning the InputStream to the Main Thread, which means you will be doing network operations in the onPostExecute method (which runs on the Main Thread). You must get all the data in the background thread. You can parse the data on the UI Thread from an InputStream only if that data does not come from the network, which is not your case.
In short, put your List<List<String>> lists = new XmlParser().parse(is);
code inside the doInBackground method and return your list instead.
CLARIFICATION: What you are missing is what Streamings are for. They are meant to be used so you do not need to load all the data at once (making resources usage, especially memory consumption, low). When you pass the InputStream to the UI Thread, you don't know how much data has been downloaded yet (if any at all). Imagine you are downloading a huge XML file for instance (30MB) and you need to parse it. Using InputStream avoids the need to download the whole thing, put it into memory (which would crash the app) and then parse it. With Streams the method will download a little bit of data, parse it and free the used memory, so the memory consumption always stays low.
Upvotes: 1
Reputation: 44571
The first line which references your project tells you where the error is.
at neviat.test.downloaddata.XmlParser.parse(XmlParser.java:39)
So the error is actually in XmlParser.java
at line 39, not in MainActivity
. However, the stacktrace shows us where it is called from by looking at the next line which references your project
at neviat.test.downloaddata.MainActivity$DownloadData.onPostExecute(MainActivity.java:70)
so we can see this is called in onPostExecute()
in MainActivity
which runs on the UI Thread
. So the solution is to run that line in doInBackground()
.
List<List<String>> lists = new XmlParser().parse(is);
However, depending on where else you will use lists
, you will need to declare it as a member variable of either the AsyncTask
or of the Activity
(whatever scope you need for it). Then you can initialize it in doInBackground()
.
Upvotes: 2