I am making a guessing app in Android Studio 2.2.3 for which I need to load some data from a website. I am using an AsyncTask class, which accepts the URL of the website as a String, and returns the data as String.
The problem is that the loading is extremely slow and so the app shows a blank white screen on the phone for about 15-20 seconds until (I presume) the whole thing is downloaded.
What I want to do is show an indeterminate progress bar (I think that's what the rotating circle is called) until the app loads the data.
For this, I tried to pass in the AsyncTask, and then made the progressbar view visible in the onPreExecute() and invisible again in the onPostExecute().
But this didn't work out and it still showed the same white screen.
Here's my AsyncTask:
public class DownloadTask extends AsyncTask<String, Void, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
pb.setVisibility(ProgressBar.VISIBLE);
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
pb.setVisibility(ProgressBar.INVISIBLE);
}
#Override
protected String doInBackground(String... urls) {
String result = "";
URL url;
HttpURLConnection urlConnection;
try {
url = new URL(urls[0]);
urlConnection = (HttpURLConnection) url.openConnection();
InputStream in = urlConnection.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
int data = isr.read();
while (data != -1) {
char current = (char) data;
result += current;
data = isr.read();
}
return result;
} catch (Exception e) {
e.printStackTrace();
}
//if try fails
return null;
}
}
And this is when I call it in the onCreate()
DownloadTask downloadTask = new DownloadTask();
String result;
try {
String websiteUrl = "http://www.india-forums.com/celebrity/bollywood/";
result = downloadTask.execute(websiteUrl).get();
I read somewhere that the get() blocks the main UI thread, but if I remove it, I get an Incompatible types error, so couldn't still resolve it...
What am I missing/doing wrong?
Any help would be appreciated!
Related
I am beginner in Android and I need some help. So, I have a procedure with sub-procedures inside. How can I finish one before starting a new one. Here is a code to better understand:
public void onCellLocationChanged(CellLocation lokacija) {
super.onCellLocationChanged(lokacija);
location = (GsmCellLocation) Phone.getCellLocation();
textCellId.setText(String.valueOf(location.getCid() % 65536));
textCellLac.setText(String.valueOf(location.getLac()));
String JSON_URL_string=JSON_URL + "?cellid=" + String.valueOf(location.getCid()%65636);
getJSON(JSON_URL_string);
myJSONString = textCellNameSakriven.getText().toString();
ParseJSON(myJSONString);
}
Problem is that myJSONString is empty, cause textCEllNameSkriven is also empty. That textView textCellNameSkriven is made when getJSON(JSON_URL_string) is finished. If I run debugger and go step by step, app goes directly from getJSON(JSON_URL_string) row to the next one and the next etc
Edit: Maybe the problem is that onPostExecute is not finished before starting ParseJSON. Here is also a code for getJSON:
private void getJSON(String url) {
class GetJSON extends AsyncTask<String, Void, String> {
ProgressDialog loading;
#Override
protected void onPreExecute() {
super.onPreExecute();
loading = ProgressDialog.show(MainActivity.this, "Please Wait...", null, true, true);
}
#Override
protected String doInBackground(String... params) {
String uri = params[0];
BufferedReader bufferedReader = null;
try {
URL url = new URL(uri);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
StringBuilder sb = new StringBuilder();
bufferedReader = new BufferedReader(new InputStreamReader(con.getInputStream()));
String json;
while ((json = bufferedReader.readLine()) != null) {
sb.append(json + "\n");
}
return sb.toString().trim();
} catch (Exception e) {
return null;
}
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
loading.dismiss();
textCellNameSakriven.setText(s);
}
}
GetJSON gj = new GetJSON();
gj.execute(url);
}
Since you are running a async task it practically runs on a different thread, so your getJson method need not wait for post execute and can return after starting the async task, so you can never be sure in this way that parseJson gets executed after textView is populated. You are running into classic race condition issue.
Your issue could be easily solved, if you have a callback which is called after postExecute is done, and you can handle parseJson there
So, something simple like , create interface MyCallback
public interface MyCallback {
public OnReadJsonDone();
}
Let your activity implement this MyCallback
public MainActivity implements MyCallback
{
...........
#Override
public OnReadJsonDone(){
parseJson();
}
Now change signature of getJson to
getJSON(string json, final MyCallback callback) {
Now in onpostexecute
//call OnReadJsonDone
callback.OnReadJsonDone()
So, all you now need is while calling getJSon pass this as second param
getJSON(JSON_URL_string,this);
Haven't tested this but you get the idea
I am currently wondering how to use AsyncTask in multiple Activitys without copying it.
I used this Guide to do it in one Activity and that worked just fine. But to load and use this information in more than one Activity seems to me like to much work. I tried to put my LoadUrl function into another Class and just pass my Textfield I want to be edited. But my App crashes when i start it. (I am not sure if this is the right approach )
public class LoadFromUrl {
public void loadAccountInfo(String key) {
if( key != null ) {
new DownloadWebpageTask().execute();
}
}
private class DownloadWebpageTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
// params comes from the execute() call: params[0] is the url.
try {
return downloadUrl(urls[0]);
} catch (IOException e) {
return "Unable to retrieve web page. URL may be invalid.";
}
}
// onPostExecute displays the results of the AsyncTask.
#Override
protected void onPostExecute(String result) {
//textView.setText(result);
}
// Given a URL, establishes an HttpUrlConnection and retrieves
// the web page content as a InputStream, which it returns as
// a string.
private String downloadUrl(String myurl) throws IOException {
InputStream is = null;
// Only display the first 500 characters of the retrieved
// web page content.
int len = 500;
try {
URL url = new URL(myurl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Starts the query
conn.connect();
int response = conn.getResponseCode();
//Log.d(DEBUG_TAG, "The response is: " + response);
is = conn.getInputStream();
// Convert the InputStream into a string
String contentAsString = readIt(is, len);
return contentAsString;
// Makes sure that the InputStream is closed after the app is
// finished using it.
} finally {
if (is != null) {
is.close();
}
}
}
// Reads an InputStream and converts it to a String.
public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
Reader reader = null;
reader = new InputStreamReader(stream, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
}
}
}
and calling it in:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
selectedAccount.setKey("google.com");
loadUrl.loadAccountInfo(selectedAccount.getKey());
}
All I want to do is, load the information of an URL and use this to fill my activitys (like multiple TextViews). Every activity uses different urls and structures.
Create DownloadWebPageTask in a separate file, as a public class. Then override its constructor to pass anything you need (a textfield, key, etc).
Put DownloadWebPageTask in a separate class. Then in onPostExecute, run a callback to the activity or fragment that will update its UI. This is done by having an activity implement a callback which is an inner interface inside the the DownloadWebpageTask (doesn't have to be an inner interface!). As you can see, the inner interface I put with your code is WebpageCallbacks.
This is your asynctask in another class (the spacing isn't perfect sorry...):
public class DownloadWebpageTask extends AsyncTask<String, Void, String> {
/**
* Any activity or fragment that implements WebPageCallbacks
*/
private WebPageCallbacks callbacks;
//start by referencing your activity to call onURLLoaded() for onPostExecute()
public DownloadWebpageTask(WebPageCallbacks callbacks) {
this.callbacks = callbacks; //note: I think weak references are preferred though
}
//no changes here
#Override
protected String doInBackground(String... urls) {
// params comes from the execute() call: params[0] is the url.
try {
return downloadUrl(urls[0]);
} catch (IOException e) {
return "Unable to retrieve web page. URL may be invalid.";
}
}
// onPostExecute displays the results of the AsyncTask by callback's onURLLoaded()
#Override
protected void onPostExecute(String result) {
//each activity or fragment will has a method to change their UI
callbacks.onURLLoaded(result);
}
// Given a URL, establishes an HttpUrlConnection and retrieves
// the web page content as a InputStream, which it returns as
// a string. No changes here
private String downloadUrl(String myurl) throws IOException {
InputStream is = null;
// Only display the first 500 characters of the retrieved
// web page content.
int len = 500;
try {
URL url = new URL(myurl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Starts the query
conn.connect();
int response = conn.getResponseCode();
//Log.d(DEBUG_TAG, "The response is: " + response);
is = conn.getInputStream();
// Convert the InputStream into a string
String contentAsString = readIt(is, len);
return contentAsString;
// Makes sure that the InputStream is closed after the app is
// finished using it.
} finally {
if (is != null) {
is.close();
}
}
/**
* Any Activity or fragment that implements this will have
* onURLLoaded() method to update its own UI.
*/
public interface WebpageCallbacks {
void onURLLoaded(String result);
}
}
I would then add implements DownloadWebpageTask.WebpageCallBacks to all your fragments and activities that will use this asynctask.
Here is your activity:
public class ExampleActivity extends AppCompatActivity implements DownloadWebpageTask.WebpageCallBacks {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
selectedAccount.setKey("google.com");
//changed your oncreate at line below to run your accountInfo
runAccountInfo(selectedAccount.getKey());
}
.......................
//runs the asynctask to load url info from account info like your old loadURLInfo()
public void runAccountInfo(String key) {
if( key != null ) {
//get url with getURL(key)
new DownloadWebpageTask(this).execute(getURL(key));
}
}
//this will be run from onPostExecute from the asynctask
#Override
public void onURLLoaded(String result) {
textView.setText(result);
}
}
If you have the time though, I suggest to not use AsyncTask at all and look into other libraries like rxJava/rxAndroid. I hope this code is ok........
I'm working on the music streaming service project. I like to show thumbnail jacket images when I started the application in the first place.
I've fetched all the String Json results, and I've converted Jason results to hashmap by using Gson lib.
What I'm trying to do is combine the base url with map value and return? or pass the result, so that I can use the result with another thread in order to show the thumbnail images. please give me some answers..:(
MainActivity.java
private static final String baseURLforgetNewMusic = "https://s3-ap-northeast-1.amazonaws.com/goblinsbucket/Artists/";
....
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
new Connection().execute(getMusicInfo_URL + "getNewMusic.php");
// Connect to the server.
}
private class Connection extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
return getMusicInfo(urls[0]);
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
}
}
....
private String getMusicInfo(String url) {
InputStream inputStream = null;
String inputStr = "";
HashMap<String, Object> map = new HashMap<String, Object>();
try {
URL getMusicUrl = new URL(url);
conn = (HttpURLConnection) getMusicUrl.openConnection();
conn.setDoInput(true);
conn.connect();
Log.d(TAGCP, "MADE POST REQUEST TO THE GIVEN URL");
inputStream = conn.getInputStream();
if (inputStream != null) {
inputStr = Util.convertInputStreamToString(inputStream);
Log.i(TAGCS, inputStr);
Music list = new Gson().fromJson(inputStr, Music.class);
for (Music.MusicInfo info : list.musicInfo) {
System.out.println(baseURLforgetNewMusic + info.artists
+ "/" + info.file_name);
}
} else {
inputStr = "Did not work!";
Log.d(TAGRR, inputStr);
}
} catch (Exception e) {
Log.i("InputStream", e.getLocalizedMessage());
}
return inputStr;
}
Results: (I want to use these url to show thumbnail images on the main view.)
I/System.out(22054): https://s3-ap-northeast-1.amazonaws.com/goblinsbucket/Artists/BrunoMars/Grenade
I/System.out(22054): https://s3-ap-northeast-1.amazonaws.com/goblinsbucket/Artists/BrunoMars/Justthewayyouare
I/System.out(22054): https://s3-ap-northeast-1.amazonaws.com/goblinsbucket/Artists/Beenzino/Aquaman
I/System.out(22054): https://s3-ap-northeast-1.amazonaws.com/goblinsbucket/Artists/Gummy/Thinkaboutme
Try using volley to load your images. It will be faster and easier to use than Universal Image Loader. The library contains a class called NetworkImageView that will load all of your images for you.
Try showing images with this library:
https://github.com/nostra13/Android-Universal-Image-Loader
You can pass the list of URLs here and will show images in a gridview.
If you want to make something cool like CoverFlow for showing album covers use this:
https://github.com/Polidea/android-coverflow
I'm having a really tough nut to crack with a bug. Api being used is v11, honeycomb 3.0
I have a asynctask inside a fragment downloading from a XML api with basic authentication. It works perfectly even when i change the parameters from the fragment within with the edittexts etc. But when i try to mutate a autocompletetextview from outside the fragment, suddenly i get a "no element at line 1. column 0" exception. I tried the androidhttpclient, fiddled with systemprop(http.keepalive), and completly narrowed it down to this method.
public void setStations(String a, String b){
AutoCompleteTextView fromET = (AutoCompleteTextView ) getView().findViewById(R.id.from);
fromET.setText(a);
AutoCompleteTextView toET = (AutoCompleteTextView) getView().findViewById(R.id.to);
toET.setText(b);
}
When this method executes it botches up my downloadtask somewhere. If i manually edit these textview it works fine.
class LoadDataTask extends AsyncTask<String, Integer, ArrayList<Reisadvies>> {
private Exception ex;
private ProgressDialog pd;
protected void onPreExecute() {
//loadprogressdialog
}
protected ArrayList<Reisadvies> doInBackground(String... params) {
try{
ex = null;
return new APIreader().getRA(params[0], params[1], params[2],params[3],params[4],params[5], params[6]);
}catch (Exception e){
cancel(true);
pd.dismiss();
ex = e;
return null;
}
}
protected void onPostExecute(ArrayList<Reisadvies> ra){
//send list to activity
}
protected void onCancelled() {
super.onCancelled();
showError(ex);
}
}
};
public ArrayList<Reisadvies> getRA(String fromStation, String toStation, String viaStation, String dateTime, String departure, String hslAllowed, String yearCard) throws APIException{
try{
String uri = url(fromStation, toStation, viaStation, dateTime, departure, hslAllowed,yearCard);
URL url = new URL(uri);
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
if (!url.getHost().equals(uc.getURL().getHost())) {
throw new APIException("HotspotForwadingActive");
}
String basicAuth = "Basic " + "username:password"; //base64 encoded
uc.setRequestProperty ("Authorization", basicAuth);
uc.connect();
BufferedReader in = new BufferedReader(new InputStreamReader(uc.getInputStream(), "UTF-8"));
try{
return (ArrayList<Reisadvies>) new XMLParser().parseRP(in);
}finally{
uc.connect();
}
}catch (Exception e){
e.printStackTrace();
throw new APIException(e.getMessage());
}
}
I think there is a problem in doInBackground:
pd.dismiss();
You can do operations on UI element only in UI Thread. It means that you can do this in onPostExecute method, or, if you want, you can use runOnUiThread method:
runOnUiThread(new Runnable() {
public void run() {
pd.dismiss();
}
});
I hope this is helpful...
You are right about that too, but the problem was different. Just found out that it was to urlencoding. Should have figured that out right away but was throw off by the fact that it worked sometimes with a space in it :)
I am developing an android app in which i need to display images after downloading them from server and when the downloading is proceeding a progress dialog is being show. For that i an using an asynctask class.
I am using t he following source code for it.
private void startDownload() {
new DownloadFileAsync().execute(imageUrl);
image.setImageBitmap(bitmap);
}
#Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_DOWNLOAD_PROGRESS:
dialog = new ProgressDialog(this);
dialog.setTitle("Loading");
dialog.setMessage("Please wait...");
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setCancelable(false);
dialog.show();
return dialog;
default:
return null;
}
}
class DownloadFileAsync extends AsyncTask<String, String, String> {
int count;
URL myFileUrl;
#Override
protected void onPreExecute() {
super.onPreExecute();
showDialog(DIALOG_DOWNLOAD_PROGRESS);
}
#Override
protected String doInBackground(String... aurl) {
try {
myFileUrl = new URL(imageUrl);
HttpURLConnection conn = (HttpURLConnection) myFileUrl
.openConnection();
int lenghtOfFile = conn.getContentLength();
//conn.setDoInput(true);
conn.setConnectTimeout(10000);
conn.setReadTimeout(10000);
conn.connect();
InputStream is = conn.getInputStream();
bmImg = BitmapFactory.decodeStream(is);
bitmap = BitmapFactory.decodeStream((InputStream) new URL(imageUrl)
.getContent());
bitmap = Bitmap.createScaledBitmap(bitmap, 70, 70, true);
byte data[] = new byte[1024];
System.out.println("mmmmmmmmmmmm");
long total = 0;
System.out.println("nnnnnnnnnn");
while ((count = ((InputStream) new URL(imageUrl)
.getContent()).read(data)) != -1) {
total += count;
publishProgress(""+(int)((total*100)/lenghtOfFile));
for(int l=0;l<4;l++){
if(listObject.get(l).getImage()!="")
image.setImageBitmap(bitmap);
}}
}
catch(Exception e){
System.out.println(e);}
return null;
}
protected void onProgressUpdate(String... progress) {
dialog.setProgress(Integer.parseInt(progress[0]));
}
#Override
protected void onPostExecute(String unused) {
image.setImageBitmap(bitmap);
dismissDialog(DIALOG_DOWNLOAD_PROGRESS);
}
}
But it giving the following exception.:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
i am unable to figure out the problem. Can anyone help me over this.
Thanks
This is your problem:
for(int l=0;l<4;l++){
if(listObject.get(l).getImage()!="")
image.setImageBitmap(bitmap);
}
Short answer: just like the exception says, you're trying to manipulate a view in the wrong thread. Do it in the right thread (UI thread).
Long answer: In an AsyncTask the right places to do view manipulation are generally onPreExecute, onProgressUpdate and onPostExecute. doInBackground is not usually a good place to modify views. You could call back to the UI thread in one of many ways (for example you could use post). However, that block of code that you posted doesn't make a whole lot of sense to me in general, and you haven't shown enough context to explain what it is, what listObject is, etc.
You have some other problems too. You seem to be trying to read the data in twice in a row in two different ways... Also, I imagine your while condition is going to give you problems as you're recreating the URL object and as long as you get content back you're going to keep doing it. Assuming that URL doesn't have dynamic content, you'll have an infinite loop.