I was being a bit naive, thinking I can read webpages the same way as you do in Java, but apparently you need to use threads or AsyncTask. Which I have no prior experience in and are proving to be hassle.
I've read http://www.vogella.com/tutorials/AndroidBackgroundProcessing/article.html (3. point) which I semi understand but when I try to implement into my problem it all falls apart.
Here's my problem:
I need to read from an URL, it's a RSS feed.
Parse it all together
Insert it into ListView (arrayadapter)
This is my AsyncTask class, that I tried to recreate. I just want it to return a buffered reader, or the whole page but it's always null.
private class DownloadWebPageTask extends AsyncTask<String, Void, BufferedReader> {
#Override
protected BufferedReader doInBackground(String... urls) {
BufferedReader bs;
URL url = null;
try {
url = new URL(urls[0]);
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
bs = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
return bs;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(BufferedReader result) {
br = result;
}
}
public RSS() throws IOException, ExecutionException, InterruptedException {
DownloadWebPageTask dl = new DownloadWebPageTask();
dl.execute(new String[] {"http://www.promet.si/dc/PROMET.ROADEVENTS.PP.RSS.SL"});
}
I'm sorry if this is a stupid question, but I don't fully understand posts explaining this and I have to solve this problem. I just need the page's content in any way shape or form. (Can be a String, BufferedReader)
#Override
protected String doInBackground(String... urls) throws IOException {
URLConnection connection = new URL(urls[0]).openConnection();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()),"UTF-8");
StringBuilder stringBuilder = new StringBuilder();
String buffer;
while ((buffer = bufferedReader.readLine()) != null) {
stringBuilder.append(buffer);
}
return stringBuilder.toString();
}
Use the buffered reader to read line by line in the background thread, and make it return string.
Related
Its taking too long to compile the code (around 5mins +, only for this app).
Also when it's finally done, complete HTML is not displayed in the logcat! Only partial.
Can you guys please point out what's wrong with the code?
Is it because of "InputStream" reading character by character (as the HTML is huge)?
public class MainActivity extends AppCompatActivity {
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 "Failed";
}
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DownloadTask task = new DownloadTask();
String result = null;
try {
result = task.execute("http://www.amazon.com").get();
} catch (Exception e) {
e.printStackTrace();
}
Log.i("Result",result);
}
Yes. The more system calls you make like that, the worse your performance. You should be reading in multiple kilobytes at a time, not characters. If you need to loop over it one at a time, do that afterwards.
Also, use a StringBuilder!!!! + on a string is HIGHLY inefficient. For every character you make a new String object. StringBuilder avoids that.
I want to get an HTML from a web page, remove some tags from the code and display it using a TextView... But those HTMLs are too big to be temporaly stored into a String...
When I try this way:
String html = "myBigHTML";
myTextView.setText(fromHtml(html));
compiler says error: constant string too long
If I put the html into a .txt and try this way:
InputStream is = getAssets().open("html.txt");
tvTeste.setText(fromHtml(convertStreamToString(is)));
public static String convertStreamToString(InputStream is) throws Exception {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
reader.close();
return sb.toString();
}
It works but the app gets soooo slow, almost freezes... And also, if I store it in a .txt I couldn't work with the tags...
.:: EDIT ::.
My onCreate() method as asked...
private TextView tvTeste;
private InputStream is;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_frequencia);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
tvTeste = (TextView)findViewById(R.id.tvTeste);
try {
is = getAssets().open("html.txt");
} catch (IOException e) {
e.printStackTrace();
}
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String strLine;
List<String> stringList = new ArrayList<>();
try {
while ((strLine = br.readLine()) != null) {
stringList.add(strLine);
}
} catch (Exception e) {
e.printStackTrace();
}
tvTeste.setText(fromHtml(TextUtils.join("",stringList)));
}
Let's try this: each line of HTML text is a String. Each String is inside a List of String.
So, some pseudocode:
List<String> stringList = new ArrayList<>();
while (htmlHandler.next()) {
stringList.add(fromHtml(htmlHandler.readLine()));
}
myTextView.setText(joinStringArray(stringList));
Where joinStringArray uses a StringBuilder to produce a single big String object.
Basically you shouldn't read the entire web page, but you should read it sequentially.
Another point to mark. You should avoid any time consuming process that blocks the activity. try the same using, for example an AsyncTask.
Please check https://developer.android.com/reference/android/os/AsyncTask.html
Basically, i have an app that uses JSON. I get the JSON in new HentData.execute(); and pass it to a string variable. But when I try to acutally do something with it my program crashes.
HentData extends AsyncTask, I know it gives me a JSON string that works
INSIDE oncreate()
new HentData().execute();
jsonToArray();
arrayToText();
This crashes
But when I run them like this it works, do I have to close the HentData class somehow?
protected void onPostExecute(String resultat){
json_string = resultat;
jsonToArray();
arrayToText();
}
This is my doInBackground()
protected String doInBackground(Void... voids){
try {
URL url = new URL(json_url);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
InputStream IS = httpURLConnection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(IS));
StringBuilder sb = new StringBuilder();
while((json_string = bufferedReader.readLine())!=null){
sb.append(json_string+"\n");
}
bufferedReader.close();
IS.close();
httpURLConnection.disconnect();
return sb.toString().trim();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
But when I run them like this it works, do I have to close the HentData class somehow?
protected void onPostExecute(String resultat){
json_string = resultat;
jsonToArray();
arrayToText();
}
You don't have to close anything. This works because "async" in AsyncTask makes the code run in the background.
In other words,
new HentData().execute(); // Not waiting for result
jsonToArray(); // Continue on, even though there is no result yet --> error
arrayToText();
If you want a more flexible way to get results, see How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class?
If you hate writing AsyncTasks (for HTTP methods), see Comparison of Android networking libraries: OkHTTP, Retrofit, and Volley
I'm creating an app where I need a function to get plain text from a website. I am able to get the text and print it out on my PC just fine, but when I try running it on an Android device, the app won't start.
I believe it has something to do with throwing an IOException. I've been reading that I am not supposed to do that because I don't define the interface. Is there a way to get around this? Android Studio won't compile my code if I don't throw the exception.
The function:
public String getText(String site) throws IOException {
// Make a URL to the web page
URL url = new URL(site);
// Get the input stream through URL Connection
URLConnection con = url.openConnection();
InputStream is =con.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// read each line and return the final text
String res = "";
String line = null;
while ((line = br.readLine()) != null) {
//System.out.println(line);
res += line;
}
return res;
}
And this is how Android Studio makes me run it in the onCreate method:
String text = null;
try {
text = getText("http://myWebsite.com");
} catch (IOException e) {
e.printStackTrace();
}
Toast.makeText(getApplicationContext(), text, Toast.LENGTH_LONG).show();
First, read your logcat - you should see your exception there with full stacktrace. Second, there is nothing wrong with catching IOException, but you must do something with it once cached - like inform user of problem in functionality - like no more space, etc.
And this is how Android Studio makes me run it in the onCreate method:
this is a problem, because your are getting data from your site on UI thread, you must do it from worker thread, ie. AsyncTask.
You can not do it in the main thread
try this
class MyTask extends AsyncTask<Void, Void, String>{
private String site;
MyTask(String site) {
this.site = site;
}
#Override
protected String doInBackground(Void... params) {
try {
URL url = new URL(site);
URLConnection con = url.openConnection();
InputStream is =con.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// read each line and return the final text
String res = "";
String line = null;
while ((line = br.readLine()) != null) {
//System.out.println(line);
res += line;
}
return res;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if(s != null){
Toast.makeText(getApplicationContext(), s, Toast.LENGTH_LONG).show();
}
}
}
where to get a string is used as
new MyTask("http://myWebsite.com").execute()
I had a method like this in an Android app reading a raw file:
public String inputStreamToString(InputStream isTwo) throws IOException {
StringBuffer sBuffer = new StringBuffer();
DataInputStream dataIO = new DataInputStream(isTwo);
String strLineTwo = null;
while ((strLineTwo = dataIO.readLine()) != null) {
sBuffer.append(strLineTwo + "\n");
}
dataIO.close();
isTwo.close();
return sBuffer.toString();
}
However, the DataInputStream object appears to be deprecated now. I researched it and heard it is better to wrap the readline() with a BufferedInputStream. Can someone help me finish his (fill in the missing line)? I am not sure how to declare the br var. This is what I have so far:
public String inputStreamToString(InputStream isTwo) throws IOException {
String strLineTwo = null;
BufferedReader br = null;
StringBuffer sBuffer = new StringBuffer();
InputStreamReader dataIO = new InputStreamReader(isTwo);
while ((strLineTwo = br.readLine()) != null) {
sBuffer.append(strLineTwo + "\n");
}
dataIO.close();
isTwo.close();
return sBuffer.toString();
Here is the preceding code I have not touched yet that calls this method:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tech);
InputStream iFileTwo = getResources().openRawResource(R.raw.testing);
try {
TextView helpText = (TextView) findViewById(R.id.tvStream);
String strFileTwo = inputStreamToString(iFileTwo);
helpText.setText(strFileTwo);
} catch (Exception e) {
Log.e(DEBUG_TAG_THREE, "InputStreamToString failure", e);
}
}
Also, I want to make sure it works from Android 2.3 to 4.2 (current). Thanks for any help.
This is how I would write it. This has much less overhead and preserves the newlines as they were originally.
public String inputStreamToString(InputStream in) throws IOException {
StringBuilder out = new StringBuilder();
char[] chars = new char[1024];
Reader reader = new InputStreamReader(in /*, CHARSET_TO_USE */);
try {
for (int len; (len = reader.read(chars)) > 0; )
out.append(chars, 0, len);
} finally {
try {
in.close();
} catch (IOException ignored) {
}
}
return out.toString();
}
Just a suggestion, if you are migrating, then why not use the IOUtils from libraries like apache commons etc which actually take care of managing your streams and also save you with lot of errorneous conditions