I have a threading issue. When I try to write in TextView percentage of successful download, it doesn't update it at all, until the end (until all content received) and it just show 100 at the end.
Seems like it is not actually running in another thread...
I tried to use logging in console instead of using publishProgress, and it works. It seems like MainActivity is frozen until the download task is done.
public class DownloadTask extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... urls) {
String result = "";
URL url;
HttpURLConnection urlConnection = null;
try {
url = new URL(urls[0]);
urlConnection = (HttpURLConnection)url.openConnection();
InputStream in = urlConnection.getInputStream();
InputStreamReader reader = new InputStreamReader(in);
int count = 0;
int size = 0;
while (reader.read() != -1) {
size++;
}
urlConnection = (HttpURLConnection)url.openConnection();
in = urlConnection.getInputStream();
reader = new InputStreamReader(in);
int data = reader.read();
while (data != -1) {
char current = (char) data;
result += current;
int progress = (int) (((float) count++ / (float) size) * 100);
publishProgress(progress);
data = reader.read();
}
return result;
}
catch(Exception e) {
e.printStackTrace();
return "Failed";
}
}
#Override
protected void onProgressUpdate(Integer... values) {
textView.setText((values[0]).toString());
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text);
DownloadTask task = new DownloadTask();
String result = null;
try {
result = task.execute("https://stackoverflow.com/").get();
}
catch (Exception e) {
e.printStackTrace();
}
Log.i("Contents Of URL", result);
}
Any help please?
You're using the get method in the ASyncTask, as per the docs.
Waits if necessary for the computation to complete, and then retrieves
its result.
That means you told the async task to run sincronously.
To have it running async, don't use the get method.
task.execute("https://stackoverflow.com/")
And use the onPostExecute to retrieve the result:
protected void onPostExecute(String result) {
}
Related
I am trying to fetch data from thingspeak api and I am taking Input for channel id and passing it on URL. But I have to check if the url is responding or not, if it is responding than go ahead with the code else user have to change channel id.
Error I am getting is 'int java.lang.String.length()' on a null object reference
and
W/System.err: java.io.FileNotFoundException: https://api.thingspeak.com/channels/497971/feeds.json?results=1
This is invalid url if I change it to
https://api.thingspeak.com/channels/497970/feeds.json?results=1
This will work
Code I am trying is
public class MainActivity extends AppCompatActivity {
TextView a, b;
String result = "";
String field1,field2,field3;
private int count = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
a = (TextView) findViewById(R.id.a);
b = (TextView) findViewById(R.id.b);
new CountDownTimer(100000, 10000) {
#Override
public void onTick(long l) {
DownloadTask task = new DownloadTask();
task.execute("https://api.thingspeak.com/channels/497970/feeds.json?results=1");
}
#Override
public void onFinish() {
}
}.start();
}
public class DownloadTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
URL url;
result = "";
HttpURLConnection urlConnection = null;
try {
url = new URL(urls[0]);
urlConnection = (HttpURLConnection) url.openConnection();
InputStream in = urlConnection.getInputStream();
InputStreamReader reader = new InputStreamReader(in);
int data = reader.read();
while (data != -1) {
char current = (char) data;
result += current;
data = reader.read();
}
return result;
} catch (MalformedURLException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(final String result) {
super.onPostExecute(result);
search(result);
}
public void search(String result) {
try {
JSONObject jsonObject = new JSONObject(result);
JSONObject fieldinfo = jsonObject.getJSONObject("channel");
String ff1 = fieldinfo.optString("field1","No Value");
String ff2 = fieldinfo.optString("field2","No Value");
JSONArray weatherInfo = jsonObject.getJSONArray("feeds");
JSONObject legsobject = weatherInfo.getJSONObject(0);
field1 = legsobject.getString("field1");
field2 = legsobject.getString("field2");
a.setText(ff1);
c.setText(field1);
} catch (JSONException e1) {
e1.printStackTrace();
}
}
}
}
Here is the Image of error
The issue here is that you are not taking into account that HTTP connections sometimes do fail, like in this case. And it fails because the channel id does not exist.
When you set a correct channel id, the URL is also correct because the resource exists and therefore you get the desired results.
However, when you set a wrong channel id the HTTP request fails (because that URL does not exist). Regardless of this, you are attempting to read the response and that's when it all blows up.
When you make an HTTP request to a server, it responds with a status code indicating what happened with your request. You are completely ignoring this status code.
Look at the headers that a request to that URL throws using:
curl -i https://api.thingspeak.com/channels/497971/feeds.json?results=1:
HTTP/1.1 400 Bad Request
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: close
Status: 400 Bad Request
... [Shortened] ...
You can learn all about HTTP status codes in a simple Google search but the summary is that it is a number between 100 and 599 that indicates:
Request successfully handled: when it is between 200 and 299.
Request could not be handled, it failed: when it is between 400 and 499.
The request failed because the server basically blew up: (> 500).
In order to retrieve this status code, you need to call the getResponseCode() method of the urlConnection instance.
Only when the status code is successful (between 200 and 299) the call to urlConnection.getInputStream() will succeed. In case of error you need to call urlConnection.getErrorStream().
So in order to fix your code, you need to do something like this:
#Override
protected String doInBackground(String... urls) {
URL url;
result = "";
HttpURLConnection urlConnection = null;
try {
url = new URL(urls[0]);
// Open the conection
urlConnection = (HttpURLConnection) url.openConnection();
// Retrieve status code
int statusCode = urlConnection.getResponseCode();
// Determine whether the request was handled successfully or not
boolean success = (statusCode >= 200) && (statusCode < 300);
InputStream in;
if(success) {
// Read the response when request was handled successfully
in = urlConnection.getInputStream();
} else {
// Read the error stream when the request failed
in = urlConnection.getErrorStream();
}
InputStreamReader reader = new InputStreamReader(in);
int data = reader.read();
while (data != -1) {
char current = (char) data;
result += current;
data = reader.read();
}
return result;
} catch (MalformedURLException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} finally {
// Close the connection
if(urlConnection != null) {
urlConnection.disconnect();
}
}
return null;
}
First of all, don't forget to close the connection once you are done (I included the disconnection in the finally {} block).
If you debug or log that code, you will see that the status code you are receiving is 400. That is the HTTP status code for Bad Request, indicating that your request is not correct and you need to fix it. If you analyze the content of the result variable, you will see that the value equals to -1.
Therefore in the onPostExecute callback you should make sure the value is different than -1 before attempting to deserialize it, since otherwise it will blow up again.
You can fix your onPostExecute callback like this:
#Override
protected void onPostExecute(final String result) {
super.onPostExecute(result);
if(result.equals(-1)) {
// Do something else, show an error to the user indicating the channel id is wrong
} else {
// Since there is no error, you can proceed with the deserialization of the response
search(result);
}
}
I hope it helps and that it was clear enough.
Cheers!
As you described you need to update your code like
public class MainActivity extends AppCompatActivity {
TextView a, b;
String result = "";
String field1,field2,field3;
private int count = 0;
int channelId=497970;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
a = (TextView) findViewById(R.id.a);
b = (TextView) findViewById(R.id.b);
startCounter(channelId);
}
public void startCounter(int channelId){
new CountDownTimer(100000, 10000) {
#Override
public void onTick(long l) {
DownloadTask task = new DownloadTask();
task.execute("https://api.thingspeak.com/channels/"+channelId+"/feeds.json?results=1");
}
#Override
public void onFinish() {
}
}.start();
}
public class DownloadTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
URL url;
result = "";
HttpURLConnection urlConnection = null;
try {
url = new URL(urls[0]);
urlConnection = (HttpURLConnection) url.openConnection();
InputStream in = urlConnection.getInputStream();
InputStreamReader reader = new InputStreamReader(in);
int data = reader.read();
while (data != -1) {
char current = (char) data;
result += current;
data = reader.read();
}
return result;
} catch (FileNotFoundException e){
startCounter(channelId+1);
}
catch (MalformedURLException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(final String result) {
super.onPostExecute(result);
search(result);
}
public void search(String result) {
try {
JSONObject jsonObject = new JSONObject(result);
JSONObject fieldinfo = jsonObject.getJSONObject("channel");
String ff1 = fieldinfo.optString("field1","No Value");
String ff2 = fieldinfo.optString("field2","No Value");
JSONArray weatherInfo = jsonObject.getJSONArray("feeds");
JSONObject legsobject = weatherInfo.getJSONObject(0);
field1 = legsobject.getString("field1");
field2 = legsobject.getString("field2");
a.setText(ff1);
c.setText(field1);
} catch (JSONException e1) {
e1.printStackTrace();
}
}
}
}
i want to download the html content of a website using asynctask, and use regex to manipulate the source code to get what i need and finally i want to display those result in a list view. this is my code it doesnt show error but when i run it on my emulator the app crashes
please guys i need assistant this is really important
public class DownloadTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
String result = "";
URL url;
HttpURLConnection urlConnection = null;
try {
url = new URL(urls[0]);
urlConnection = (HttpURLConnection)url.openConnection();
InputStream in = urlConnection.getInputStream();
InputStreamReader reader = new InputStreamReader(in);
int data = reader.read();
while (data != -1) {
char current = (char) data;
result += current;
data = reader.read();
}
return result;
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView myLV = (ListView)findViewById(R.id.teamLV);
ArrayList<String> clubName = new ArrayList<String>();
DownloadTask task = new DownloadTask();
String result = null;
try {
result = task.execute("https://www.premierleague.com/clubs").get();
//Log.i("Content of URL", result);
System.out.println(result);
Pattern p = Pattern.compile("class=\"clubName\">(.*?)<");
Matcher m = p.matcher(result);
while(m.find()){
System.out.println(m.group(1));
clubName.add(m.group(1));
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_activated_1, clubName);
myLV.setAdapter(arrayAdapter);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
try adding onPostExecute after doInBackground. then remove the part from execute.get(). The get method makes the UI blocking (which kinda makes the asynctask pointless).
also preset your adapter instead of creating a new one each time you do a loop. you might need a custom adapter (depending on what you need).
#Override
protected void onPostExecute(String result) {
//Log.i("Content of URL", result);
System.out.println(result);
Pattern p = Pattern.compile("class=\"clubName\">(.*?)<");
Matcher m = p.matcher(result);
while(m.find()){
System.out.println(m.group(1));
// clubName.add(m.group(1));
adapter.add (m.group(1));
}
}
I have seen one similar question to this, but i couldn't solve my problem like he did.
Here i'm getting data from url:
public class AsyncHttpTask extends AsyncTask<String, Void, Integer> {
#Override
protected void onPreExecute() {
setProgressBarIndeterminateVisibility(true);
}
#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();
// 200 represents HTTP Ok
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);
}
parseResult(response.toString());
result = 1; // Successful
} else {
result = 0; //"Failed to fetch data!";
}
} catch (Exception e) {
Log.d(TAG, e.getLocalizedMessage());
}
return result; //"Failed to fetch data!";
}
#Override
protected void onPostExecute(Integer result) {
// Download complete. Let us update UI
progressBar.setVisibility(View.GONE);
if (result == 1) {
giftListAdapter = new GiftListAdapter(GiftsActivity.this, gifts);
recyclerView.setAdapter(giftListAdapter);
} else {
SuperActivityToast.create(GiftsActivity.this, getString(R.string.no_internet_connection),
SuperToast.Duration.SHORT, Style.getStyle(Style.RED, SuperToast.Animations.FLYIN)).show();
}
}
}
private void parseResult(String result) {
try {
JSONObject response = new JSONObject(result);
JSONArray posts = response.optJSONArray("gifts");
gifts = new ArrayList<>();
for (int i = 0; i < posts.length(); i++) {
JSONObject post = posts.optJSONObject(i);
GiftItem item = new GiftItem();
item.setThumbnail(post.optString("image"));
item.setTitle(post.optString("title"));
item.setDescription(post.optString("description"));
item.setSource(getString(R.string.source) + " " + post.optString("source"));
item.setTotalRating(post.optInt("rating"));
item.setPrice(post.optDouble("price"));
gifts.add(item);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
And here i'm executing that url. I'm trying now to parse this second url and load all items after first data is loaded:
new AsyncHttpTask().execute(url_movies);
new AsyncHttpTask().execute(url_books); // This is the second url i'm trying to get
How could i do that?
I'm trying now to parse this second url and load all items after first
data is loaded:
Then instead of calling execute method just after first, call it inside onPostExecute method when first url all items are loaded successfully.
Also add second call in if-else block to avoid it execute again :
#Override
protected void onPostExecute(Integer result) {
// Download complete. Let us update UI
progressBar.setVisibility(View.GONE);
... your code here
if(params[0].equals(url_movies)){
new AsyncHttpTask().execute(url_books);
}
}
also add
If you want both urls than send them as an String array like this
String urls[] = new String[]{"url1" , "url2"};
new AsyncHttpTask().execute(urls);
and in doinbackground method exec them like
url1 = params[0];
url2 = params[1];
I have a problem. When I click a button I have to download a main page from a website (the source). Then I have to parse the source to get other links from it. After that I have to add as many rows to my list view as I have found links in the main page source. And finally I have to download each link (the source again) and parse them. During the downloading / parsing I have to update my list view (the progressbar for example) with the information I get from these sources.
But how do I do that?
I thought I could just create two classes which both extends the AsyncTask. A MainPageDownloader and a PageDownloader. The MainPageDownloader would be started on button click. It downloads the main page and in the onPostExecute method it will parse the source and add the rows to the list. And then it will start the PageDownloaders.
I tried that but the PageDownloaders will start when the list has not even been updated yet. So I cannot update the ProgressBar's in each row.
What am I doing wrong? Is there maybe a whole different and better way?
Here are the two classes :
public class MainPageDownloader extends AsyncTask<String, Integer, String>{
#Override
protected String doInBackground(String... params) {
InputStream in = null;
final int BUFSIZE = 1024;
String result = "";
try {
URL url = new URL( params[0] );
URLConnection con = url.openConnection();
con.connect();
in = con.getInputStream();
byte[] data = new byte[BUFSIZE];
while (in.read(data) != -1) {
result += new String(data);
}
} catch (MalformedURLException e) {
cancel(true);
} catch (IOException e) {
cancel(true);
}
return result;
}
#Override
protected void onPostExecute(String result) {
List<String> links = new PageParser(result).getPageLinks();
super.onPostExecute(result);
int rows = listView.getChildCount();
//Add items & update list
for( int i = 0; i < links.size(); i++ )
adapter.add( new Item() );
for( int i = 0; i < links.size(); i++ ){
new PageDownloader(rows+i).execute("http://www.mylink.com/" + links.get(i) );
}
}
}
public class PageDownloader extends AsyncTask<String, Integer, String>{
ProgressBar p;
int row;
public PageDownloader(int row) {
//p is null, I have to comment it out or the program will crash
//p = (ProgressBar) listView.getChildAt( row ).findViewById( R.id.progressBar1 );
this.row = row;
}
#Override
protected String doInBackground(String... params) {
InputStream in = null;
final int BUFSIZE = 1024;
String result = "";
try {
URL url = new URL( params[0] );
URLConnection con = url.openConnection();
con.connect();
in = con.getInputStream();
byte[] data = new byte[BUFSIZE];
while (in.read(data) != -1) {
result += new String(data);
}
} catch (MalformedURLException e) {
cancel(true);
} catch (IOException e) {
cancel(true);
}
return result;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
PageParser parser = new PageParser( result );
//Somehow the list is updated now and the titles will change
listItems.get(row).setTitle( parser.getTitle() );
adapter.notifyDataSetChanged();
//Not Possible because p is always null
//p.setVisibility( ListView.GONE );
}
}
I create one asynctask class to download on file from web.
This is my class :
private class DownloadFile1 extends AsyncTask<String, Integer, String> {
private boolean done = false;
#Override
protected String doInBackground(String... sUrl) {
done = false;
if (isOnline() == true && sdmounted() == true) {
try {
URL url = new URL(sUrl[0]); // get url
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream in = new BufferedInputStream(connection.getInputStream());
// url[1]= file name
OutputStream out = (downloaded == 0) ? new FileOutputStream("/sdcard/Points.ir/" + sUrl[1])
: new FileOutputStream("/sdcard/Points.ir/"
+ sUrl[1], true);
OutputStream output = new BufferedOutputStream(out, 1024);
byte[] data = new byte[1024];
int count = 0;
while (done != true && isOnline() == true && (count = in.read(data, 0, 1024)) >= 0) {
output.write(data, 0, count);
downloaded += count;
}
output.flush();
output.close();
in.close();
} catch (Exception e) {
}
} else {
networkerror = 1;
}
return null;
}
#Override
protected void onPreExecute()
{
super.onPreExecute();
}
#Override
protected void onProgressUpdate(Integer... progress)
{
super.onProgressUpdate(progress);
}
#Override
protected void onPostExecute(String result)
{
super.onPostExecute(result);
}
}
when I create a object of this class and Execute, every thing works fine . but when create 2 object and Execute for download 2 file at the same time it get FC ??
what do I ? (Sorry for my bad English Speaking)
AsyncTask's can only be executed 1 time. So once your AsyncTask is running, you cannot run it again. That's the reason why you have the String... param in the doInBackground method, that can be a list of Strings.
So, instead of creating two objects, you can use the following:
DownloadFile1 task = new DownloadFile1();
task.execute("url1", "url2", "url3"); // All these urls will be processed after each other
Than, in your AsyncTask doInBackground(), you can do something like:
#Override
protected String doInBackground(String... sUrl) {
done = false;
for(int i=0 ; i < sUrl.length ; i++){
if (isOnline() == true && sdmounted() == true) {
try {
String currentUrl = sUrl[i];
// continue with your code here, using currentUrl instead
// of using sUrl[0];
}
}
}
}