Cannot Parse HTML Data Using Android / JSOUP - java

I'm having an issue where I'm attempting to use JSOUP to obtain data from an webpage (in this case - google.com) and when debugging the title data is returned and shown in the logcat - however my textview never seems to update with the freshly obtained data.
SOURCE:
package com.example.test;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView textView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.TextView01);
}
private class DownloadWebPageTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
String response = "";
for (String url : urls) {
DefaultHttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
try {
Document doc = Jsoup.connect("http://google.com")
.userAgent("Mozilla")
.get();
// get page title
String title = doc.title();
System.out.println("title : " + title);
// get all links
Elements links = doc.select("a[href]");
for (Element link : links) {
// get the value from href attribute
System.out.println("\nlink : " + link.attr("href"));
System.out.println("text : " + link.text());
}
} catch (IOException e) {
e.printStackTrace();
}
}
return response;
}
#Override
protected void onPostExecute(String title) {
textView.setText(title);
}
}
public void onClick(View view) {
DownloadWebPageTask task = new DownloadWebPageTask();
task.execute(new String[] { "http://www.google.com" });
}
}
EDIT: (in response to superuser's suggestion - implementing handler)
public class MainActivity extends Activity {
private TextView textView;
private Handler handler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.TextView01);
handler = new Handler();
}
private class DownloadWebPageTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
String response = "";
for (String url : urls) {
DefaultHttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
try {
Document doc = Jsoup.connect("http://google.com")
.userAgent("Mozilla")
.get();
// get page title
String title = doc.title();
System.out.println("title : " + title);
// get all links
Elements links = doc.select("a[href]");
for (Element link : links) {
// get the value from href attribute
System.out.println("\nlink : " + link.attr("href"));
System.out.println("text : " + link.text());
}
} catch (IOException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
#Override
public void run() {}});
}
return response;
}
#Override
protected void onPostExecute(String title) {
textView.setText(title);
View.invalidate();
}
}
public void onClick(View view) {
DownloadWebPageTask task = new DownloadWebPageTask();
task.execute(new String[] { "http://www.google.com" });
}
}
RESULTS (from edit shown above):
Cannot make a static reference to the non-static method invalidate() from the type View MainActivity.java
Cannot refer to a non-final variable title inside an inner class defined in a different method MainActivity.java

Sorry, was about to answer this question yesterday but fell asleep on my keyboard :P
But your result string: protected void onPostExecute(String result) doesn't get anything passed. The problem is easily solved.
Above your onCreate:
String title;
In your doInBackGround:
title = doc.title();
In your onPostExecute:
#Override
protected void onPostExecute(String result) {
textView.setText(title);
}

Try passing the textview as an contructor argument to your class DownloadWebPageTask.
DownloadWebPageTask task = new DownloadWebPageTask(textView);
In your DownloadWebPageTask class define a TextView variable to hold this object.
Update the same in onPostExecute() method.

Related

How can I use a variable outside the onPostExecute method?

I have a MySQL database on a webserver and I read the data from this database in my application, but after I read the variables I can't use the "volt" variable outside the onPostExecute. I try t use adapter, but i can't use the data in the adapter like a intiger variable, just i can add to listview. So far i Don't find a solution for my problam.
I hope you can help me.
package com.example.wifis;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;
public class MainActivity extends AppCompatActivity {
ListView listView;
ArrayAdapter<String> adapter;
// int tomb []={};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView=(ListView)findViewById(R.id.list_item);
adapter= new ArrayAdapter<>(this, android.R.layout.simple_list_item_1);
listView.setAdapter(adapter);
new Conection().execute();
}
class Conection extends AsyncTask<String, String, String>{
#Override
public String doInBackground(String... strings) {
String result="";
String host="http://localhost/store/cars.php";
try {
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet();
request.setURI(new URI(host));
HttpResponse response = client.execute(request);
BufferedReader reader= new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer stringBuffer= new StringBuffer("");
String line = "";
while ((line = reader.readLine()) !=null ){
stringBuffer.append(line);
break;
}
reader.close();
result = stringBuffer.toString();
}
catch (Exception e){
return new String("There exeption: "+ e.getMessage());
}
return result;
}
#Override
public void onPostExecute(String result){
// Toast.makeText(getApplicationContext(), result, Toast.LENGTH_SHORT).show();
JSONObject jsonResult = null;
try {
jsonResult = new JSONObject(result);
int success = jsonResult.getInt("success");
if(success==1){
JSONArray cars = jsonResult.getJSONArray("cars");
JSONObject car = cars.getJSONObject(0);
int id = car.getInt("id");
int volt = car.getInt("szam");
String line = id + "-" + volt;
adapter.add(line);
// tomb[0]=szam;
}else{
Toast.makeText(getApplicationContext(), "NOT OK ", Toast.LENGTH_SHORT).show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
As I have tried to explain in my post here
the values you're trying to access aren't synchronous, meaning that your code does not execute top down. The AsyncTask returns a value at some point. we don't know when that will be, but when it returns the value, you'll have access to it within onPostExecute. this means that you can make use of the values as they are received there and only there, as that is the only place where you'll actually receive those values.
to get this value returned to your main activity, you can do something like this :
create an interface
public interface MyCallback {
void myResult(YourResultType output); //here, i believe this will be string for your specific case
}
This interface allows us to move the value we receive to another class when it's received
Next,
Go to your AsyncTask class, and declare interface MyCallback as a variable :
public class MyAsyncTask extends AsyncTask<String, String, String> {
public MyCallback callback = null;
#Override
protected void onPostExecute(String result) {
callback.myResult(result);
}
}
#Override
protected void onPostExecute(String result) {
callback.myResult(result);
}
now for your main activity:
public class MainActivity implements MyCallback {
MyAsyncTask asyncTask = new MyAsyncTask();
#Override
public void onCreate(Bundle savedInstanceState) {
//set your listener to this class
asyncTask.callback = this;
//execute the async task
asyncTask.execute();
}
//this overrides the implemented method from asyncTask
#Override
void myResult(YourResultType output){
//Here you will receive the result returned from the async task
}
}
please also note that async tasks are deprecated
also note, my java is quite rusty, I am fortunate enough to only use kotlin these days, feel free to correct me on any mistakes :)

Android Studio Activity start delayed

I got a AsyncTask:
package e.marco.swimcommit;
import android.os.AsyncTask;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
public class News extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... strings) {
final StringBuilder builder = new StringBuilder();
final StringBuilder builder2 = new StringBuilder();
{
try {
Document doc = Jsoup.connect("http://www.schwimmclub-schwandorf.de/index.php/8-home/56-infos-neuigkeiten").get();
String title = doc.title();
Elements links = doc.select("h2");
Elements links2 = doc.select("h3");
builder.append(title).append("\n");
for (Element link : links) {
builder.append(link.text()).append("$");
}
for (Element link : links2) {
builder2.append(link.text()).append("$");
}
} catch (IOException e) {
e.printStackTrace();
}
}
String text = builder.toString() + "%" + builder2.toString();
return text;
}
}
and a onResume Methode in my MainActivity which set the returned text in a textview
#Override
protected void onResume()
{
super.onResume();
try {
eins.setText(new News().execute().get());
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
But if i start the App it shows me a white Screen until onResume Methode get the text and set it to the Textview. How is it possible to load the App show all other Elements like Buttons Background and so on without the delayed start? So that the Textview is blank until the onResume Methode get the Information and set it?
Edit: Without blocking the UI
AsyncTask is used to perform background operations and publish results on the UI thread. In your case you should put eins.setText into onPostExecute of AsyncTask.
Another problem is because the AsyncTask is a separate class so you need to define an interface to pass result back to MainActivity.
News
public class News extends AsyncTask<String, Void, String> {
private WeakReference<OnNewsListener> mOnNewsListener;
public void setOnNewsListener(OnNewsListener listener) {
mOnNewsListener = new WeakReference<>(listener);
}
#Override
protected String doInBackground(String... strings) {
final StringBuilder builder = new StringBuilder();
final StringBuilder builder2 = new StringBuilder();
{
try {
Document doc = Jsoup.connect("http://www.schwimmclub-schwandorf.de/index.php/8-home/56-infos-neuigkeiten").get();
String title = doc.title();
Elements links = doc.select("h2");
Elements links2 = doc.select("h3");
builder.append(title).append("\n");
for (Element link : links) {
builder.append(link.text()).append("$");
}
for (Element link : links2) {
builder2.append(link.text()).append("$");
}
} catch (IOException e) {
e.printStackTrace();
}
}
String text = builder.toString() + "%" + builder2.toString();
return text;
}
#Override
protected void onPostExecute(String text) {
if (mOnNewsListener != null) {
if (mOnNewsListener.get() != null) {
mOnNewsListener.get().onNews(text);
}
}
}
public interface OnNewsListener {
void onNews(String text);
}
}
MainActivity
public class MainActivity extends AppCompatActivity implements News.OnNewsListener{
TextView eins;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
eins = findViewById(R.id.eins);
}
#Override
protected void onResume() {
super.onResume();
News news = new News();
news.setOnNewsListener(this);
news.execute();
}
#Override
public void onNews(String text) {
eins.setText(text);
}
}
As suggested by pz64, set the text in onPostExecute() method and call the AsyncTask() without calling get() method. get() method on AsyncTask makes task synchronous and also affects your UI.
public class News extends AsyncTask<String, Void, String> {
#override
protected void onPreExecute(){
//initiate your loading views
}
#Override
protected String doInBackground(String... strings) {
final StringBuilder builder = new StringBuilder();
final StringBuilder builder2 = new StringBuilder();
{
try {
Document doc = Jsoup.connect("http://www.schwimmclub-schwandorf.de/index.php/8-home/56-infos-neuigkeiten").get();
String title = doc.title();
Elements links = doc.select("h2");
Elements links2 = doc.select("h3");
builder.append(title).append("\n");
for (Element link : links) {
builder.append(link.text()).append("$");
}
for (Element link : links2) {
builder2.append(link.text()).append("$");
}
} catch (IOException e) {
e.printStackTrace();
}
}
String text = builder.toString() + "%" + builder2.toString();
return text;
}
#override
protected void onPostExecute(String response){
//dispose loading views
if(response != null){
eins.setText(response);
}else{
//could not load
}
}
}
Call:
#Override
protected void onResume()
{
super.onResume();
new News().execute(); //do not call get method
}
You can call asyncTask in oncreate method.
And set the result in onProgressUpdate method.
#Override
protected void onProgressUpdate(String... text) {
eins.setText.setText(text);
}

How to Implement / Link Run Keeper API in Android using Eclipse?

I want to use run keeper API in my Code as I am developing Application which will track walking distance etc . This can be done by using Run Keeper API.
During registering my app, it ask me to enter post call back URL , I don't know from where to get The CALL BACK URL :(
Here is the code where I am stuck.
package com.example.testapp;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.CookieManager;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity implements OnClickListener {
private Button button;
private WebView webView;
private final static String CLIENT_ID = "b25ef732fdea4fc1a5d59036f05cfad0";
private final static String CLIENT_SECRET = "741a1216e5f14c38b5768840d6720d2c";
private final static String CALLBACK_URL = "";
#SuppressLint("SetJavaScriptEnabled")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Force to login on every launch.
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
button = (Button) findViewById(R.id.button);
webView = (WebView) findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
}
#Override
public void onClick(View v) {
button.setVisibility(View.GONE);
webView.setVisibility(View.VISIBLE);
getAuthorizationCode();
}
private void getAuthorizationCode() {
String authorizationUrl = "https://runkeeper.com/apps/authorize";
authorizationUrl = String.format(authorizationUrl, CLIENT_ID,CALLBACK_URL);
Toast.makeText(MainActivity.this, "Milestone 1", Toast.LENGTH_SHORT).show();
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Toast.makeText(MainActivity.this, url, Toast.LENGTH_SHORT).show();
if (url.startsWith(CALLBACK_URL)) {
final String authCode = Uri.parse(url).getQueryParameter("code");
webView.setVisibility(View.GONE);
getAccessToken(authCode);
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
});
webView.loadUrl(authorizationUrl);
}
private void getAccessToken(String authCode) {
Toast.makeText(MainActivity.this, "Milestone 3", Toast.LENGTH_SHORT).show();
String accessTokenUrl = "https://runkeeper.com/apps/token";
final String finalUrl = String.format(accessTokenUrl, authCode,CLIENT_ID, CLIENT_SECRET);
Thread networkThread = new Thread(new Runnable() {
#Override
public void run() {
try {
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(finalUrl);
HttpResponse response = client.execute(post);
String jsonString = EntityUtils.toString(response
.getEntity());
final JSONObject json = new JSONObject(jsonString);
String accessToken = json.getString("access_token");
getTotalDistance(accessToken);
} catch (Exception e) {
displayToast("Exception occured:(");
e.printStackTrace();
resetUi();
}
}
});
networkThread.start();
}
private void getTotalDistance(String accessToken) {
try {
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet("http://api.runkeeper.com/user/");
get.addHeader("Authorization", "Bearer " + accessToken);
get.addHeader("Accept", "*/*");
HttpResponse response = client.execute(get);
String jsonString = EntityUtils.toString(response.getEntity());
JSONArray jsonArray = new JSONArray(jsonString);
findTotalWalkingDistance(jsonArray);
} catch (Exception e) {
displayToast("Exception occured:(");
e.printStackTrace();
resetUi();
}
}
private void findTotalWalkingDistance(JSONArray arrayOfRecords) {
try {
// Each record has activity_type and array of statistics. Traverse
// to activity_type = Walking
for (int ii = 0; ii < arrayOfRecords.length(); ii++) {
JSONObject statObject = (JSONObject) arrayOfRecords.get(ii);
if ("Walking".equalsIgnoreCase(statObject
.getString("activity_type"))) {
// Each activity_type has array of stats, navigate to
// "Overall" statistic to find the total distance walked.
JSONArray walkingStats = statObject.getJSONArray("stats");
for (int jj = 0; jj < walkingStats.length(); jj++) {
JSONObject iWalkingStat = (JSONObject) walkingStats
.get(jj);
if ("Overall".equalsIgnoreCase(iWalkingStat
.getString("stat_type"))) {
long totalWalkingDistanceMeters = iWalkingStat
.getLong("value");
double totalWalkingDistanceMiles = totalWalkingDistanceMeters * 0.00062137;
displayTotalWalkingDistance(totalWalkingDistanceMiles);
return;
}
}
}
}
displayToast("Something went wrong!!!");
} catch (JSONException e) {
displayToast("Exception occured:(");
e.printStackTrace();
resetUi();
}
}
private void resetUi() {
runOnUiThread(new Runnable() {
#Override
public void run() {
button.setVisibility(View.VISIBLE);
webView.setVisibility(View.GONE);
}
});
}
private void displayTotalWalkingDistance(double totalWalkingDistanceMiles) {
final String milesWalkedMessage = (totalWalkingDistanceMiles < 1) ? "0 miles?, You get no respect, Start walking already!!!"
: String.format("Cool, You have walked %.2f miles so far.",
totalWalkingDistanceMiles);
displayToast(milesWalkedMessage);
resetUi();
}
private void displayToast(final String message) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), message,
Toast.LENGTH_LONG).show();
}
});
}
}
I found this Runkeeper CallBack Hope it works for you. All the best.
com.example.runkeeperapi://RunKeeperIsCallingBack"

Async class not passing the argument

My async class is throwing some errors. The line with AsyncLoadData says that I should create local variable url
public void getData() {
new AsyncLoadData(this,this).execute(url);
}
My AsyncLoadData class
package com.example.hay;
import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import android.content.Context;
import android.os.AsyncTask;
import android.widget.EditText;
public class AsyncLoadData extends AsyncTask<String, Void, String> {
private Context mContext;
private ILoadDataListener mListener;
public AsyncLoadData(Context context, ILoadDataListener listener) {
this.mContext = context;
this.mListener = listener;
}
#Override
protected String doInBackground(String... params) {
try {
EditText tf = (EditText) this.findViewById(R.id.editText1);
String url = params[0];
url = tf.getText().toString();
Document doc;
doc = Jsoup.connect(url).get();
String title = doc.text();
return title;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private EditText findViewById(int edittext1) {
return null;
}
#Override
protected void onPostExecute(String result) {
mListener.complete(result);
}
#Override
protected void onPreExecute() {
mListener.loading();
}
public interface ILoadDataListener {
void loading();
void complete(String result);
}
}
As you can see the AsyncLoadData should pass the url variable.
Have you declared url somewhere else in the code before calling this line : new AsyncLoadData(this,this).execute(url); ?
If not, you should add line String url = "the value of the url you are trying to call"; just before it, otherwise the variable url does not exist in the getData method...

VISIBLE cannot be resolved to a variable

I'm not sure exactly how this can be corrected but I'm getting an error stating: VISIBLE cannot be resolved to a variable. Any suggestions are greatly appreciated. Thus far I've looked over:
http://developer.android.com/reference/android/view/View.html#setVisibility(int)
but I do not understand exactly how this can be implemented in this case.
SOURCE:
public class MainActivity extends Activity {
private TextView textView;
private String response;
public interface Callback {
void onModifiedTextView(String value);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.TextView01);
textView.setVisibility(VISIBLE);
}
public void onModifiedTextView(final String title) {
runOnUiThread(new Runnable() {
public void run() {
textView.setText(title);
textView.invalidate(); // not even necessary
}
});
}
public class DownloadWebPageTask extends AsyncTask<String, Void, String> {
public DownloadWebPageTask(MainActivity mainActivity) {
this.callback = mainActivity;
}
private MainActivity callback;
private String title;
public DownloadWebPageTask() {
// TODO Auto-generated constructor stub
}
public DownloadWebPageTask(TextView textView) {
// TODO Auto-generated constructor stub
}
#Override
protected String doInBackground(String... urls) {
String response = title;
for (String url : urls) {
DefaultHttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
try {
Document doc = Jsoup.connect("http://google.com")
.userAgent("Mozilla")
.get();
// get page title
String title = doc.title();
System.out.println("title : " + title);
// get all links
Elements links = doc.select("a[href]");
for (Element link : links) {
// get the value from href attribute
System.out.println("\nlink : " + link.attr("href"));
System.out.println("text : " + link.text());
}
} catch (IOException e) {
e.printStackTrace();
}
}
// callback.onModifiedTextView(response);
return response;
}
#Override
protected void onPostExecute(final String title) {
callback.onModifiedTextView(title);
callback.onModifiedTextView(response);
}
}
public void onClick(View view) {
DownloadWebPageTask task = new DownloadWebPageTask(this);
task.execute(new String[] { "http://www.google.com" });
}
}
VISIBLE is an integer value in the View class. You should change this to View.VISIBLE instead of just VISIBLE, unless you are inside a custom view.
In addtion to #kcoppock answer. You can use
you can use this import:
import static android.view.View.VISIBLE;
or just
textView.setVisibility(View.VISIBLE);
VISIBLE is defined as static final int in the View class.

Categories