Creating a JSoup class in android using AsyncTask - java

I know that I have to use ASyncTasks in order to make JSoup work for android, but all examples online illustrate that just by using random jsoup methods in the MainActivity.
I want to create a HTMLParser class which will contains a function for each element I want to parse but I can't seem to make it work.
My HTMLParser:
public class HTMLParser extends AsyncTask<Void, Void, Void> {
private Document doc;
#Override
protected Void doInBackground(Void... voids) {
try {
doc = Jsoup.connect("https://www.bodybuilding.com/exercises").get();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public ArrayList<String> findMuscleGroups(){
ArrayList<String> muscleGroups = new ArrayList<>();
Elements section = doc.select("a");
if (section != null) {
for (Element exercise : section) {
if (exercise.hasText() && !muscleGroups.contains(exercise.text()) &&
exercise.attr("href").contains("exercises/muscle")) {
muscleGroups.add(exercise.text());
}
}
}
return muscleGroups;
}
}
In my MainActivity I want to be able to create a HTMLParser object and be able to use something like ArrayList = htmlParser.findMuscleGroups()
My MainActivity:
HTMLParser parser = new HTMLParser();
new HTMLParser().execute();
for (String muscleGroup : parser.findMuscleGroups()){
textView.setText(muscleGroup + "\n");
}
Which won't work. I'm well aware that it isn't supposed to work and there is something I'm missing but I hope you guys can point me in the right direction.

Solution:
Probably not the best, but it works so there's that
I've added this toHTMLParser class
public Document initializeDoc(){
try {
Document doc = Jsoup.connect("https://www.bodybuilding.com/exercises").get();
return doc;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
In MainActivity I've created
private volatile Document doc = null;
private volatile HTMLParser htmlParser;
variables, and added the following in the onCreate method
htmlParser = new HTMLParser();
Thread t = new Thread(new Runnable() {
#Override
public void run() {
doc = htmlParser.initializeDoc();
}
});
try {
t.start();
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
And needless to say, calling htmlParser.findMuscleGroups(doc) works as expected

Related

Retrofit Task (Execute) Called from another function doesn't wait until Task finish

From my Login Activity (First Activity Opened) I always do a check if the token is still active on my server which is done through Async Task that does API call to server.
here's the code from LoginActivity :
private void checkIfAuthenticated(){
SharedPreferences reader_auth = getSharedPreferences(getString(R.string.auth_preferences), MODE_PRIVATE);
String auth_key = reader_auth.getString(getString(R.string.auth_access_key),null);
String mobile_token = reader_auth.getString(getString(R.string.auth_mobile_token),null);
if (auth_key != null) {
//THIS PART RUNS THE TOKEN CHECK TO SERVER
authGlobal = new AuthenticationGlobal(this);
// I WANT THIS FUNCTION TO FINISH FIRST BEFORE IT GOES TO THE NEXT PART OF THE CODE
authGlobal.runAuthenticationCheck(auth_key,mobile_token);
String Auth_Key = reader_auth.getString(getString(R.string.auth_access_key),null);
Log.d("Auth Key Check 0",Auth_Key);
if (Auth_Key != null) {
Log.d("Auth Key Check 1",Auth_Key);
MoveToDashboardActivity();
}
}
}
The runAuthenticationCheck(String,String) Code is located on another class (Because it was meant to be a global function which can be called from any function on any activity)
runAuthenticationCheck is located in AuthenticationGlobal Class, here's the code :
public void runAuthenticationCheck (String mobile_token, String Access_token) {
checkAuthTask = new checkAuthenticationTask(mobile_token, Access_token);
checkAuthTask.execute((Void) null);
}
public class checkAuthenticationTask extends AsyncTask<Void, Void, Boolean> {
private GetDataService service;
private String mobile_token;
private String access_token;
checkAuthenticationTask( String Access_token,String Mobile_token) {
/*Create handle for the RetrofitInstance interface*/
mobile_token = Mobile_token;
access_token = Access_token;
service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService.class);
}
#Override
protected Boolean doInBackground(Void... params) {
// TODO: attempt authentication against a network service.
try {
Call<CheckAuthenticationResponse> call = service.checkAuthentication(access_token,mobile_token);
Response<CheckAuthenticationResponse> CheckAuthenticationResponse = call.execute();
if (CheckAuthenticationResponse.code() == 200){
} else{
//clear shared preferences
clearAuthentication();
Log.e("AuthKey Global","Expired0");
}
} catch (IOException ea) {
clearAuthentication();
Log.e("AuthKey Global","Expired1");
Log.e("AuthenticationResponseError Global","Network Went Wrong");
ea.printStackTrace();
}
return true;
}
#Override
protected void onPostExecute(final Boolean success) {
//mAuthTask = null;
//showProgress(false);
if (success) {
Log.e("AuthKey Global","Done");
} else {
// mPasswordView.setError(getString(R.string.error_incorrect_password));
clearAuthentication();
Log.e("AuthKey Global","Expired2");
}
}
#Override
protected void onCancelled() {
//mAuthTask = null;
//showProgress(false);
}
There are 2 Class / Activity : "LoginActivity" and "AuthenticationGlobal".
There are 3 Function :
checkIfAuthenticated => located in LoginActivity, Which in turn actually call another function from another class (Function number 2 : "runAuthenticationCheck")
runAuthenticationCheck => located in AuthenticationGlobal. which in calls a AsyncTask via .execute(...) command.
checkAuthenticationTask => located in AuthenticationGlobal. Which actually does the API Call to server.
From "LoginActivity" I run a function "checkIfAuthenticated" => which calls function "runAuthenticationCheck" located at "AuthenticationGlobal" => which runs a Task "checkAuthenticationTask" which does API Call to server and does stuff.
The problem is, when I called the first Function, the code doesn't wait until the function "checkIfAuthenticated" / "checkAuthenticationTask" is done. Is there a way for me to make the app wait until the task / function finish first??
Thank you
UPDATE :
I ONLY NEED TO ADD .get() at the end of .execute() and wrap it inside try catch.
public void runAuthenticationCheck (String mobile_token, String Access_token) {
checkAuthTask = new checkAuthenticationTask(mobile_token, Access_token);
try {
checkAuthTask.execute((Void) null).get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Well. I just need to add a .get() on the execute() and wrap it inside a try catch.
A dumb mistake.
here's the updated code :
public void runAuthenticationCheck (String mobile_token, String Access_token) {
checkAuthTask = new checkAuthenticationTask(mobile_token, Access_token);
try {
checkAuthTask.execute((Void) null).get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

Using Jsoup to extract data for Android studio

I am trying to extract data to get public ip address of each of my users so I can compare if any are currently the same. the website has no html just text that says: {"ip":"current ip"}
I try to extact this and use a toast just to test I have the info right but the toast is always blank. Here is my code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_homepage);
new doit().execute();
}
public class doit extends AsyncTask<Void, Void, Void>{
String ipAddressGet;
#Override
protected Void doInBackground(Void... params) {
try {
Document doc = Jsoup.connect("http://ipv4bot.whatismyipaddress.com/").get();
ipAddressGet = doc.text();
}catch(Exception e){
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Toast.makeText(Homepage.this, ipAddressGet, Toast.LENGTH_LONG).show();
}
}
sorry I have never used this website before to post hopefully everything is clear.
all the descrptions for jsoup involve HTML use but this has none so i dont know how to apply the descrptions there
Just use this
ipAddressGet = doc.body().ownText();
instead of
ipAddressGet = doc.text();
Change your code:
try {
Document doc = Jsoup.connect("http://ipv4bot.whatismyipaddress.com/").get();
ipAddressGet = doc.text();
} catch(Exception e) {
e.printStackTrace();
}
To this:
try {
Document doc = Jsoup.connect("http://ipv4bot.whatismyipaddress.com/").get();
Element body = doc.body();
ipAddressGet = body.text();
} catch(Exception e) {
e.printStackTrace();
}
But to get and handle responses of requests to the web, use one of known libs, like Retrofit or Volley.
You do not need JSoup for this. Java can deal with it just fine:
private static String readStringFromURL(String requestURL) throws IOException
{
try (Scanner scanner = new Scanner(new URL(requestURL).openStream(),
StandardCharsets.UTF_8.toString()))
{
scanner.useDelimiter("\\A");
return scanner.hasNext() ? scanner.next() : "";
}
}
If your text is also JSON-formatted, most JSON parsing libraries also have built-in capabilities for reading from a URL.

Jsoup.connect().get() takes only part of html file on Android

So, I try to parse Wikipedia, and my code works well at computer.
All, what I changed - .connect().get is in AsyncTask, but I get only part of html file (no "body", only half of second "script" in "title") and I can't understand why.
This is my code example for Android.
protected String doInBackground(String... params) {
try {
Document doc = Jsoup.connect(params[0]).get();
return doc.toString();
} catch (IOException e) {
//...
e.printStackTrace();
}
return null;
}
And this is simple.
String url = "https://en.wikipedia.org/wiki/Protectorate";
Document doc = null;
try {
doc = Jsoup.connect(url).get();
} catch (IOException e) {
//...
e.printStackTrace();
}
I checked, params[0] is https://en.wikipedia.org/wiki/Protectorate, here's no mistake.
If you need some extra information, I will give it, of course.
Logcat fools us here, since it shortens the message (I assume you checked your string with logcat? See related question)
If you split your result string into chunks, you will see that the whole page was loaded. Try adding something like this logAll function to your AsyncTask class to see the full output:
private class DownloadTask extends AsyncTask<String, Integer, String> {
Document doc = null;
protected String doInBackground(String... params) {
try {
doc = Jsoup.connect(params[0]).get();
return doc.toString();
} catch (Exception e) {
e.printStackTrace();
}
return doc.toString();
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
logAll("async",doc.toString());
}
void logAll(String TAG, String longString) {
int splitSize = 300;
if (longString.length() > splitSize) {
int index = 0;
while (index < longString.length()-splitSize) {
Log.e(TAG, longString.substring(index, index + splitSize));
index += splitSize;
}
Log.e(TAG, longString.substring(index, longString.length()));
} else {
Log.e(TAG, longString.toString());
}
}
}

Jackson Parsing with java

I really hate to do this, but I have two questions: can Jackson 2.7.3 parse the following url and can do I have to parse every part of the JSON?
Here is the code I am working with so far:
public class Song {
private String tracks;
private String album;
private String images;
public void setTracks(String tracks){
this.tracks=tracks;
}
public void setAlbum(String album){
this.album= album;
}
public void setImages (String images){
this.images= images;
}
}
And
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
ObjectMapper mapper = new ObjectMapper();
Document doc = Jsoup.connect("http://api.spotify.com/v1/search?q=track:" + finalSong + "%20artist:" + finalArtist+"%20" + "&type=track").ignoreContentType(true).get();
String title = String.valueOf(doc.body().text());
Song obj = mapper.readValue(String.valueOf(title), Song.class);
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
thread.start();
return null;
}
All I need is the "preview_url" and one of the "images" url towards the top
the JSON is located at https://api.spotify.com/v1/search?q=track:Ready%20To%20Fall%20artist:rise%20against%20&type=track.
Do you necessary need to map your Json response into a class?
If not you can get your desired values as following e.g. for preview_url
You can use readTree to map the json result into a tree of nodes.
There after you can use findPath to search for the property you looking for.
In the case of image it contains an array. Thus if you want to select a specific item from that list you get use get to select the specific item you want.
example
JsonNode readTree = mapper.readTree(body);
for (JsonNode node : readTree.findPath("items")) {
System.out.println(node.findPath("images").get(2));
System.out.println(node.findPath("preview_url"));
}

Android getting a result from an asynctask from within and asynctask

I have two AsyncTasks running and the async task that is waiting for the result is just not getting the correct result.
I have a network class that runs like so:
public ArrayList<User> searchForFriends(String activeHash, TelephoneNumber telephone)
{
Object[] obj = {activeHash, telephone};
try
{
return new SearchForFriendsTelephone().execute(obj).get(Constants.TIMEOUT_TIME, Constants.TIMEOUT_UNIT);
}
catch (InterruptedException e)
{
return null;
}
catch (ExecutionException e)
{
return null;
}
catch (TimeoutException e)
{
return null;
}
}
private class SearchForFriendsTelephone extends AsyncTask<Object, Void, ArrayList<User>>
{
#Override
protected ArrayList<User> doInBackground(Object... searchTelephone)
{
if (config.getNetworkVersion() == config.NETWORK_PROTOCOL_VERSION_1)
{
TelephoneNumber tel = (TelephoneNumber) searchTelephone[1];
List<NameValuePair> params = new ArrayList<NameValuePair>(3);
params.add(new BasicNameValuePair(NetworkConfig.POST_ACTIVE_HASH, (String) searchTelephone[0]));
params.add(new BasicNameValuePair(NetworkConfig.POST_MOBILE_NUMBER_COUNTRY_CODE, tel.getCountryCode()));
params.add(new BasicNameValuePair(NetworkConfig.POST_MOBILE_NUMBER_RAW, tel.getNumberRaw()));
ServerCommunication csc = new ServerCommunication();
JSONObject jsonFoundFriends = csc.postToServer(config.getBaseUrl() + URL_FRIEND_SEARCH_MOBILE, params);
if (jsonFoundFriends == null || csc.networkError())
{
FriendNetworkCommunication.this.networkError = csc.getNetworkError();
return null;
}
return _processSearchFriends(jsonFoundFriends);
}
FriendNetworkCommunication.this.networkError = new NetworkError(NetworkLanguage.UNABLE_TO_PROCESS);
return null;
}
Anyway this works fine with no issues and pulls back the user/s. I know this as I tried the following code in the main ui thread and it populates a view just fine. When I call this code from another AsyncTask. I get a timeout error.
Code to all the searchForFriends code:
private class CompareNumbers extends AsyncTask<ArrayList<NameAndNumber>, Integer, Void>
{
#Override
protected Void doInBackground(ArrayList<NameAndNumber>... params)
{
for (NameAndNumber nameNumber : params[0])
{
try
{
FriendNetworkCommunication fnc = new FriendNetworkCommunication();
ArrayList<User> users = fnc.searchForFriends(CurrentUser.getInstance().getUserActiveHash(), new TelephoneNumber(String.valueOf(nameNumber.getNumber().getNationalNumber()), String.valueOf(nameNumber.getNumber().getCountryCode())));
if (users != null && users.size() == 1)
{
User u = users.get(0);
String[] s = nameNumber.getName().split(" ");
u.setFirstName(s[0]);
u.setLastName(s[1]);
((ArrayAdapter<User>) ((ListView) getView().findViewById(R.id.friend_add_fragment_search_cont_list)).getAdapter()).add(u);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
((ArrayAdapter<User>) ((ListView)getView().findViewById(R.id.friend_add_fragment_search_cont_list)).getAdapter()).notifyDataSetChanged();
return null;
}
}
Can I not run an asynctask that waits on another?
NOTE: This is all running in a fragment if this makes any difference?
NOTE2: The first Asynctask runs a network call and has to be run asynchronously and so I wanted it to be like this so if I wanted I could run it anywhere synchronously
try giving the .execute() of the second async task in the onpostexecute() of the first async task.
I have found the answer to my question and this is not possible.
A full answer can be found here:
Creating another AsyncTask inside of doInBackground

Categories