NetworkOnMainThread error still - android

I am experimenting with firebase and want to read a textfile from the bucket. I can copy the file to local disk, which works fine. Now I want to read the textfile and copy the contents to an arraylist. This time I get the NetworkOnMainThread although I start a new thread to do the work. At least I think I am.I read about using Asynchtask, but would like to know WHY this is not working as expected. The code to get the InputstreamfromURL worked fine in the past.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_download);
downloadtext = (TextView) findViewById(R.id.downloadtext);
text = new ArrayList<>();
listViewText = (ListView) findViewById(R.id.listViewtext);
listViewText.setAdapter(new ArrayAdapter(getApplicationContext(), android.R.layout.simple_list_item_1, text));
Thread thread= new Thread(){
public void run() {
storage = FirebaseStorage.getInstance();
storageRef = storage.getReferenceFromUrl("gs://fir-test-68815.appspot.com");
filename = "testfile.txt";
StorageReference file = storageRef.child(filename);
file.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
Log.d(MainActivity.TAG, "URL =" + uri.toString());
try {
InputStream is = getInputStreamFromURL(uri);
text = getText(is);
textReady.post(new Runnable() {
#Override
public void run() {
((ArrayAdapter)listViewText.getAdapter()).notifyDataSetChanged();
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
private ArrayList<String> getText(InputStream is) throws IOException {
ArrayList<String> text = new ArrayList<>();
BufferedReader reader = null;
reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String line;
while ((line = reader.readLine()) != null) {
text.add(line);
}
return text;
}
private InputStream getInputStreamFromURL(Uri urlToGet) throws IOException {
InputStream is = null;
URL downloadURL = new URL(urlToGet.toString());
HttpURLConnection conn = (HttpURLConnection) downloadURL.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Starts the query
conn.connect();
// next two lines produce the error
int response = conn.getResponseCode();
is = conn.getInputStream();
return is;
}
};
thread.start();
textReady = new Handler();
}

Firebase event callbacks are by default invoked on the main UI thread. That also happens with your OnSuccessListener.
There are also other ways to download files with Firebase. But if you still want to use getDownloadUrl(), you'll need to implement the downloading on a separate thread (for example using AsyncTask) after getDownloadUrl() callback fires.

Related

Connection to server is ok, but I'm writing an empty string to the OutputStream

I can't figure out what is up with this code. First of all, as it is here, it works. But I'm afraid I have missed an important concept and I don't want it to come back and bite me later. My code below will write a blank line to a text log that I have created for testing purposes if I don't add the line "connection.getResponseMessage.' It will also work with 'getResponseCode.' Why?
Why does it write an empty buffer to the OutputStream without these codes?
public class AdminActivity extends AppCompatActivity {
Context mContext;
private static final String TAG = "AdminActivity";
EditText mSystemID, mSystemPassword;
RecyclerView mRecyclerView;
Button mUpdateButton, mDownloadButton;
FileItemAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_admin);
mContext = this;
mSystemID = findViewById(R.id.et_system_id);
mSystemPassword = findViewById(R.id.et_system_password);
mRecyclerView = findViewById(R.id.rv_file_names);
mUpdateButton = findViewById(R.id.bt_update_files_list);
mDownloadButton = findViewById(R.id.bt_download_file);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(layoutManager);
DividerItemDecoration divider = new DividerItemDecoration(mContext, layoutManager.getOrientation());
divider.setDrawable(ContextCompat.getDrawable(mContext, R.drawable.divider_dark));
// TESTING TESTING TESTING TESTING TESTING //
mUpdateButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new SendInfoToServer().execute("A new Test");
}
});
}
private static class SendInfoToServer extends AsyncTask<String, String, String> {
HttpURLConnection connection = null; //***** Should eventually change to https instead of http
OutputStream out = null;
#Override
protected String doInBackground(String... params) {
String parameters = params[0];
try {
URL url = new URL("http://www.example.com/login/webhook.php");
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.connect();
out = new DataOutputStream(connection.getOutputStream());
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
writer.write(parameters);
writer.flush();
writer.close();
Log.d(TAG, "response message: " + connection.getResponseMessage());
} catch (IOException e) {
Log.d(TAG, "an error occured");
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
}
return null;
}
}
}
I've tried to ask this question multiple different ways and nobody has given me any useful input. This, I guess, is my last attempt to simplify the question. Above is the entire Activity. It has already been suggested to remove 'writer.close()' but that didn't work.
Thanks in advance
HttpURLConnection has a terrible API and is very confusing to use. I would strongly recommend using OkHttp in its place. With OkHttp, you would make the request with the following code:
String parameters = params[0];
Response response;
try {
MediaType contentType = MediaType.parse("text/plain; charset=utf-8");
RequestBody body = RequestBody.create(contentType, parameters);
Request request = new Request.Builder()
.url("http://www.example.com/login/webhook.php")
.post(body)
.build();
response = client.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
}
} catch (IOException e) {
Log.d(TAG, "an error occured");
e.printStackTrace();
} finally {
if(response != null) {
response.close();
}
}
return null;
If you want to stick with HttpURLConnection, this answer explains the connection process pretty well.

HttpURLConnection crashes application

I want to receive and send data with a web server but the code does not work
What do I do for this code to work?
Note this code inside onCreate
try {
URL url = new URL("http://myweb.com/");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
InputStream Stream = connection.getInputStream();
InputStreamReader reader = new InputStreamReader(Stream);
BufferedReader b = new BufferedReader(reader);
StringBuilder s = new StringBuilder();
String str ="";
while ((str = b.readLine())!=null) {
s.append(str);
}
String data = s.toString();
TextView myText = (TextView) findViewById(R.id.Text);
myText.setText(data);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Make sure that you do network-related tasks on a separate thread in Android. Also, check that you have the INTERNET permission set.
If you want to then update the UI from another thread, you have to use
runOnUiThread (new Runnable () {
public void run() {
//update ui in here
}
}
All your code runs in Main thread which should be always used for setting up the UI and to listen for UI events such as on click listeners.
Network calls are not allowed on this thread as they might take long time. Use AsyncTask API of android which is designed for running code in separate thread.
Create a class like one below for all GET request tasks.
public class DownloadTask extends AsyncTask<String, Void, Integer> {
private String TAG = "InDownloadTask";
private DownloadCallback callback;
private String data;
public DownloadTask(DownloadCallback cb){
callback = cb;
}
#Override
protected Integer doInBackground(String... params) {
Integer result = 0;
HttpURLConnection urlConnection;
try {
URL url = new URL(params[0]);
urlConnection = (HttpURLConnection) url.openConnection();
int statusCode = urlConnection.getResponseCode();
if (statusCode == 200) {
BufferedReader r = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
StringBuilder response = new StringBuilder();
String line;
while ((line = r.readLine()) != null) {
response.append(line);
}
data = response.toString();
result = 1;
} else {
result = 0;
}
} catch (Exception e) {
Log.d(TAG, e.getLocalizedMessage());
}
return result;
}
#Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
callback.onFinishDownload(data, integer);
}
}
Create a callback interface that we use for the above class.
public interface DownloadCallback {
public void onFinishDownload(String data, Integer result);
}
Now from your activity onCreate
String url = "http://myweb.com/";
new DownloadTask(new DownloadCallback() {
public void onFinishDownload(String data, Integer result) {
if(result == 1)
myText.setText(data);
else
myText.setText("Error");
}
}).execute(url);
If you have many network related operations, use a Network library such as Volley which will take care of this.

How can I send data to WebServer in Android?

I'm trying to send data in Android Studio. I'm making a login function. So I need to send data to the Webserver. I already made a Webserver, but I can't because of this error:
Caused by: android.os.NetworkOnMainThreadException
I think that error is pointed at this line:
InputStream is = conn.getInputStream();
I've been struggling for 3 days. Please help.
public class MainActivity extends AppCompatActivity {
EditText id;
EditText pass;
String custid;
String password;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
id=(EditText)findViewById(R.id.idtext);
pass=(EditText)findViewById(R.id.passtext);
}
public void ButtonClick(View view){
custid=id.getText().toString();
password=pass.getText().toString();
Toast.makeText(this, "버튼접근."+custid+password, Toast.LENGTH_SHORT).show();
Properties prop = new Properties();
prop.setProperty("custid", custid);
prop.setProperty("password", password);
String encodedString = encodeString(prop);
URL url = null;
try {
url = new URL ("http://192.168.56.1:9999/SEBank/customer/login.action" + "?" + encodedString);
URLConnection conn = url.openConnection();
conn.setUseCaches(false);
InputStream is = conn.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
char[] buff = new char[512];
int len = -1;
while( (len = br.read(buff)) != -1) {
System.out.print(new String(buff, 0, len));
}
br.close();
} catch (java.io.IOException e) {
e.printStackTrace();
}
}
public String encodeString(Properties params) {
StringBuffer sb = new StringBuffer(256);
Enumeration names = params.propertyNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
String value = params.getProperty(name);
sb.append(URLEncoder.encode(name) + "=" + URLEncoder.encode(value) );
if (names.hasMoreElements()) sb.append("&");
}
return sb.toString();
}
}
You can put this on your code
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
but I think the best solution is using AsyncTasks or Volley
If the error is "android.os.NetworkOnMainThreadException ...", try enclose your code with below snippet:
AsyncTask.execute(new Runnable() {
#Override
public void run() {
InputStream is = conn.getInputStream();
// other code
}
});

Android HttpUrlConnection Url doesn't work on emulator

I am trying to get json object as string from this url http://digitalcollections.tcd.ie/home/getMeta.php?pid=MS4418_021. It doesn't work I get an error after downloadUrl function.
java.io.IOException: unexpected end of stream on Connection{digitalcollections.tcd.ie:80, proxy=DIRECT# hostAddress=134.226.115.12 cipherSuite=none protocol=http/1.1} (recycle count=0)
Although it does work for this androidhive url http://api.androidhive.info/volley/person_object.json.
I am new to httpconnection below is my download url function. Error seems to show in this line HttpURLConnection conn = (HttpURLConnection) url.openConnection(); In the debugger after that line conn.getInputStream() shows the IO exception and the cause java.io.EOFException: \n not found: size=0 content=...
// Given a string representation of a URL, sets up a connection and gets
// an input stream.
private InputStream downloadUrl(String urlString) throws IOException {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(20000 /* milliseconds */);
conn.setConnectTimeout(30000 /* milliseconds */);
conn.setRequestMethod("GET");
//conn.setDoInput(true);
// Starts the query
conn.connect();
InputStream stream = conn.getInputStream();
return stream;
}
Other functions.
// Uses AsyncTask to create a task away from the main UI thread. This task takes a
// URL string and uses it to create an HttpUrlConnection. Once the connection
// has been established, the AsyncTask downloads the contents of the webpage as
// an InputStream. Finally, the InputStream is converted into a string, which is
// displayed in the UI by the AsyncTask's onPostExecute method.
private class DownloadXMLTask extends AsyncTask<String, Void, List<Entry>> {
private String urlFront = "";
#Override
protected List<Entry> doInBackground(String... urls) {
// params comes from the execute() call: params[0] is the url.
try {
return loadJsonFromNetwork(urls[0]);
} catch (IOException e) {
Log.d(TAG, "Unable to retrieve web page. URL may be invalid.");
return null;
} catch (JSONException e) {
Log.d(TAG, "XMLPULLPARSER ERROR IN download json task function");
return null;
}
}
}
// onPostExecute displays the results of the AsyncTask.
#Override
protected void onPostExecute(List<Entry> result) {
//post execution stuff
}
}
Loading json and parser, the parser might not work haven't tested it yet.
private List<Entry> loadJsonFromNetwork(String urlString) throws IOException, JSONException {
InputStream stream = null;
int len = 20000; //max amount of characters to display in string
List<Entry> entries = new ArrayList<Entry>();
try {
stream = downloadUrl(urlString); //IOException
String jsonStr = readit(stream,len);
if(jsonStr.equals(null)){
Log.d(TAG, "ERROR json string returned null");
return entries;
}
JSONObject jsonObj = new JSONObject(jsonStr);
//Not sure if the json parser works yet haven't got that far
// Getting JSON Array node
identifier = jsonObj.getJSONArray("identifier");
// looping through All Contacts
for (int i = 0; i < identifier.length(); i++) {
JSONObject c = identifier.getJSONObject(i);
String id = c.getString("type");
if(id.equals("DRIS_FOLDER")) {
String folder = c.getString("$");
entries.add(new Entry(null,null,null,folder));
}
}
// Makes sure that the InputStream is closed after the app is
// finished using it.
//This is where IOexception is called and stream is null
} catch (IOException e) {
Log.d(TAG, "Unable to retrieve json web page. URL may be invalid."+ e.toString());
return entries;
}
finally {
if (stream != null) {
stream.close();
}
}
return entries;
}
I am running this on a Nexus_5_API_23 emulator.
Thanks in advance.
UPDATE:
Doesn't work on Nexus_5_API_23 emulator?? Although it works on a Samsung GT-ST7500 external phone. Want it to work for the emulator.
The problem was my antivirus/firewall on my computer. It was blocking my connection and that's why it was working on a external phone and not emulator. I disabled my antivirus/firewall and it worked. There is a list of network limitations here http://developer.android.com/tools/devices/emulator.html#networkinglimitations
I just tried that URL on my device and didn't get any errors. Here is the code I used.
An Interface to get back onto the UI Thread
public interface AsyncResponse<T> {
void onResponse(T response);
}
A generic AsyncTask that returns a String - Feel free to modify this to parse your JSON and return a List.
public class WebDownloadTask extends AsyncTask<String, Void, String> {
private AsyncResponse<String> callback;
public void setCallback(AsyncResponse<String> callback) {
this.callback = callback;
}
#Override
protected String doInBackground(String... params) {
String url = params[0];
return readFromUrl(url);
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if (callback != null) {
callback.onResponse(s);
} else {
Log.w(WebDownloadTask.class.getSimpleName(), "The response was ignored");
}
}
private String streamToString(InputStream is) throws IOException {
StringBuilder sb = new StringBuilder();
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = rd.readLine()) != null) {
sb.append(line);
}
return sb.toString();
}
private String readFromUrl(String myWebpage) {
String response = null;
HttpURLConnection urlConnection = null;
try {
URL url = new URL(myWebpage);
urlConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
if (inputStream != null) {
response = streamToString(inputStream);
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return response;
}
}
Section of my Activity to call the AsyncTask.
String url = "http://digitalcollections.tcd.ie/home/getMeta.php?pid=MS4418_021";
WebDownloadTask task = new WebDownloadTask();
task.setCallback(new AsyncResponse<String>() {
#Override
public void onResponse(String response) {
Toast.makeText(getApplicationContext(), response, Toast.LENGTH_SHORT).show();
}
});
task.execute(url);
Make sure to use https instead of http to avoid these kind of errors on your Android Emulators.
private static final String BASE_URL = "https://content.guardianapis.com/search?";

Create an array from a remote XML for android

I have a remote XML file I want to use to populate a listview.
I have the app set up currently to create a listview from a local array. How do I populate the array using an XML file stored online? The array is currently located in strings.xml
public class ArchiveListActivity extends ListActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(ArrayAdapter.createFromResource(getApplicationContext(),
R.array.archivetitle, R.layout.archiveitem));
final String[] links = getResources().getStringArray(R.array.archivelinks);
getListView().setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
String content = links[position];
Intent showContent = new Intent(getApplicationContext(),
ArchiveViewerActivity.class);
showContent.setData(Uri.parse(content));
startActivity(showContent);
}
});
}
}
You can probably do it in the below mentioned steps:
1> Prepare the request URI where the xml is present.
prepareRequestUrl();
2> Get the response from the web-server:
/**
* fetch the response for the request url
* #param request url string
* #return InputStream
*/
public InputStream getResponse(String reqUrl) throws AppException {
URL url = null;
URLConnection connection = null;
HttpURLConnection httpConnection = null;
int reponseCode = 0;
InputStream inputStream = null;
try {
url = new URL(reqUrl);
connection = url.openConnection();
httpConnection = (HttpURLConnection) connection;
reponseCode = httpConnection.getResponseCode();
} catch (MalformedURLException e) {
} catch (IOException e) {
}
if (reponseCode == HttpURLConnection.HTTP_OK) {
try {
inputStream = httpConnection.getInputStream();
} catch (IOException e) {
}
}
else {
throw new AppException(AppConstants.HTTP_RESPONSE_FAILURE);
}
return inputStream;
}
3> Parse the input stream xml received from the server:
inputStream = super.getResponse(requestUrl);
result= xmlParser.parseList(inputStream);
4> Show the corresponding result in a listview.
Note: Its always recommended to use an async task to do any network operation.here in this case invoking the we-server.
Hope this helps!

Categories

Resources