Calling Async class from Async class in Android not working? - android

So my goal is to load a random wikipedia page, get the title from it, and then use the wikipedia api to get the correct title to return for display (titles with special characters need "translated" to be able to display them correctly.) My problem comes when I use my JSONRequest class (Async) to try to execute the api url and create a JSON object. When it tries to execute, it freezes and doesn't go any further (but does not crash.) It isn't a problem with the URL, it is valid and works on desktop and mobile. This method was also used in another non-async class so I know it works there. My guess is that it is an async issue. This may be a stupid question with a simple answer, but any help is greatly appreciated!
public class getRandom extends AsyncTask<String, Void, String> {
private static final String REQUEST_METHOD = "GET";
private static final int READ_TIMEOUT = 15000;
private static final int CONNECTION_TIMEOUT = 15000;
String result, rawTitle;
// Gets random wiki page and returns text to be loaded into search bar
#Override
protected String doInBackground(String... params){
String randomUrl = "https://en.wikipedia.org/wiki/Special:Random";
String titleApiUrl = "https://en.wikipedia.org/w/api.php?action=query&format=json&titles=";
// Get random title
try {
URL url = new URL(randomUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
//Set method and timeouts
con.setRequestMethod(REQUEST_METHOD);
con.setReadTimeout(READ_TIMEOUT);
con.setConnectTimeout(CONNECTION_TIMEOUT);
// Get status
int status = con.getResponseCode();
// Check for move or redirect and update url
if (status == HttpURLConnection.HTTP_MOVED_TEMP
|| status == HttpURLConnection.HTTP_MOVED_PERM) {
String location = con.getHeaderField("Location");
URL newUrl = new URL(location);
con = (HttpURLConnection) newUrl.openConnection();
}
// Get name of page
rawTitle = con.toString();
int temp = rawTitle.indexOf("wiki/") + 5;
rawTitle = rawTitle.substring(temp);
con.disconnect();
}
catch(IOException e){
e.printStackTrace();
rawTitle = "Sailboats"; // Very random, totally not hard coded result.
}
// Ensure correct title format (use wiki api)
try{
// Get json from api
JSONRequest wikiRequest = new JSONRequest();
String wikiApiJsonString = wikiRequest.execute(titleApiUrl + rawTitle).get();
// Create json object with returned string
JSONObject jsonObj = new JSONObject(wikiApiJsonString);
// Get correct title from json
result = jsonObj.getString("title");
}
catch(ExecutionException | InterruptedException | JSONException e){
e.printStackTrace();
result = "Sailboats"; // Very random, totally not hard coded result.
}
return result;
}
protected void onPostExecute(String result){
super.onPostExecute(result);
}
}

By default, async tasks all run on the same thread. So it won't actually start a second task until yours finishes. Your options are:
1)Call executeOnExecutor instead of execute and tell it to use a new thread.
2)Architect your code such that you can make the request on this thread directly. You're already on a thread, no reason to launch a new one.
3)Write your code such as you don't need to call .get().

Related

Getting the calling fragment within an asynchronous task

Aim
In a fragment, I have a search bar which looks for online news about what the user typed. I would want to display these news (title + description + date of publication + ... etc.) in the GUI, as vertical blocks.
Implementation
Explanations
In the fragment, within the search event handling, I instanciated an asynchronous task and execute it with the good URL REST API I use to do the search.
In the asynchronous task, I make use of this REST API (thanks to the URL and some required parameters as an authorization key, etc.). When my asynchronous task gets answered, it must update the fragment's GUI (i.e.: it must vertically stack GUI blocks containing the titles, descriptions, etc. of the got news).
Sources
You will find sources in the last part of this question.
My question
In the asynchronous task (more precisely: in its function that is executed after having got the answer), I don't know how to get the calling fragment. How to do this?
Sources
Fragment part
private void getAndDisplayNewsForThisKeywords(CharSequence keywords) {
keywords = Normalizer.normalize(keywords, Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", "");
new NetworkUseWorldNews().execute("https://api.currentsapi.services/v1/search?keyword=" + keywords + "&language=en&country=US");
}
Asynchronous task part
public class NetworkUseWorldNews extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String[] urls) {
StringBuilder string_builder = new StringBuilder();
try {
URL url = new URL(urls[0]);
HttpsURLConnection https_url_connection = (HttpsURLConnection) url.openConnection();
https_url
_connection.setRequestMethod("GET");
https_url_connection.setDoOutput(false);
https_url_connection.setUseCaches(false);
https_url_connection.addRequestProperty("Authorization", "XXX");
InputStream input_stream = https_url_connection.getInputStream();
BufferedReader buffered_reader = new BufferedReader(new InputStreamReader(input_stream));
String line;
while((line = buffered_reader.readLine()) != null) {
string_builder.append(line);
}
buffered_reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return string_builder.toString();
}
#Override
protected void onPostExecute(String result) {
try {
JSONObject news_response_http_call = new JSONObject(result);
switch(news_response_http_call.getString("status")) {
case "ok":
JSONArray news = news_response_http_call.getJSONArray("news");
for(int i = 0; i < news.length(); i++) {
JSONObject a_news = news.getJSONObject(i);
String title = a_news.getString("title");
String description = a_news.getString("description");
String date_of_publication = a_news.getString("published");
String url = a_news.getString("url");
String image = a_news.getString("image");
System.out.println(title + ": " + date_of_publication + "\n" + image + "\n" + url + "\n" + description);
WorldNewsFragment world_news_fragment = ...;
}
break;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
If I am right, you want to update View of your caller Fragment. if FragmentA called service then FragmentA should be update.
However the approach you are asking is wrong. Instead of getting caller Fragment in your AsyncTask response. You should do it with Callback.
So now you will need to pass callback in AsyncTask. So instead of posting full code, here are already answers with this problem.
Finally your calling syntax will look like.
NetworkUseWorldNews task = new NetworkUseWorldNews(new OnResponseListener() {
#Override
public void onResponse(String result) {
// Either get raw response, or get response model
}
});
task.execute();
Actually I am still very unclear about your question. Let me know in comments if you have more queries.
Must checkout
Retrofit or Volley for calling Rest APIs
Gson for parsing JSON response automatically to models

How do I extract data from multiple related web pages in Android using Jsoup?

Well, I have been working in a app to display news headings and contents from the site http://www.myagdikali.com
I am able to extract the data from 'myagdikali.com/category/news/national-news/' but there are only 10 posts in this page and there are links to other pages as 1,2,3... like myagdikali.com/category/news/national-news/page/2.
All I need to know is, how do I extract news from every possible pages under /national_news ? Is it even possible using Jsoup ?
Till now my code to extract data from a single page is:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_all, container, false);
int i = getArguments().getInt(NEWS);
String topics = getResources().getStringArray(R.array.topics)[i];
switch (i) {
case 0:
url = "http://myagdikali.com/category/news/national-news";
new NewsExtractor().execute();
break;
.....
[EDIT]
private class NewsExtractor extends AsyncTask<Void, Void, Void> {
String title;
#Override
protected Void doInBackground(Void... params) {
while (status == OK) {
currentURL = url + String.valueOf(page);
try {
response = Jsoup.connect(currentURL).execute();
status = response.statusCode();
if (status == OK) {
Document doc = response.parse();
Elements urlLists = doc.select("a[rel=bookmark]");
for (org.jsoup.nodes.Element urlList : urlLists) {
String src = urlList.text();
myLinks.add(src);
}
title = doc.title();
}
} catch (IOException e) {
e.printStackTrace();
}
page++;
}
return null;
}
EDIT:
While trying to extract data from single page without loop, I can extract the data. But after using while loop, I get the error stating No adapter attached.
Actually I am loading the extracted data in the RecyclerView and onPostExecute is like this:
#Override
protected void onPostExecute(Void aVoid) {
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
myRecyclerViewAdapter = new MyRecyclerViewAdapter(getActivity(),myLinks);
recyclerView.setAdapter(myRecyclerViewAdapter);
}
Since you know the URL of the pages you need - http://myagdikali.com/category/news/national-news/page/X (where X is the page number between 2 and 446), you can loop through the URLs. You'll also need to use the Jsoup's response, to make sure that the page exists (the number 446 can be changed - I believe that it increases).
The code should be something like this:
final String URL = "http://myagdikali.com/category/news/national-news/page/";
final int OK = 200;
String currentURL;
int page = 2;
int status = OK;
Connection.Response response = null;
Document doc = null;
while (status == OK) {
currentURL = URL + String.valueOf(page); //add the page number to the url
response = Jsoup.connect(currentURL)
.userAgent("Mozilla/5.0")
.execute(); //you may add here userAgent/timeout etc.
status = response.statusCode();
if (status == OK) {
doc = response.parse();
//extract the info. you need
}
page++;
}
This is of course not fully working code - you'll have to add try-catch sentences, but the compiler will help you.
Hope this helps you.
EDIT:
1. I've editted the code - I've had to send a userAgent string in order to get response from the server.
2. The code runs on my machine, it prints lots of ????, because I don't have the proper fonts installed.
3. The error you're getting is from the Android part - something to do with your views. You haven't posted that piece of code...
4. Try to add the userAgent, it might solve it.
5. Please add the error and the code you're running to the original question by editting it, it's much more readable.

Second HttpURLConnection is failing

I have a ListView that onLongClick it calls a method that is supposed to go out to a website, pull a jsonArray from it and then return information that is pulled from the array. However, when it calls the HttpURLConnection.connect() method it fails and goes to the catch block. When I use the getMessage() message on the exception it only returns Null. This is the second time in this program that I've connected to a URL in this same way and it works the first time perfectly. What could be causing this issue?
Here is the code for when the method is called:
list.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
public boolean onItemLongClick(AdapterView<?> a, View v, int pos, long id) {
String trainNum = list.getItemAtPosition(pos).toString();
String info = "hello";
try {
info = getCurrentTrainInfo(trainNum);
}catch(Exception e){
info = e.getMessage();
if(info == null)
info = "info is null";
tv.setText(info);
}
Toast.makeText(getApplicationContext(), info, Toast.LENGTH_LONG).show();
return true;
}
}
);
And here is the method getCurrentTrainInfo that is called in the try block above:
public String getCurrentTrainInfo(String num) throws IOException{
String sURL = "http://www3.septa.org/hackathon/RRSchedules/" + num;
URL url = new URL(sURL);
HttpURLConnection request2 = (HttpURLConnection) url.openConnection();
request2.connect();
JsonParser jp = new JsonParser();
JsonElement root = jp.parse(new InputStreamReader((InputStream) request.getContent()));
JsonArray rootArr = root.getAsJsonArray();
int i = 0;
String acTime = "";
String station = rootArr.get(i).getAsJsonObject().get("station").getAsString();
String schTime = rootArr.get(i).getAsJsonObject().get("sched_tm").getAsString();
String esTime = rootArr.get(i).getAsJsonObject().get("est_tm").getAsString();
tv.setText(station);
String info = "Current Station: " + station + "\nScheduled leave time: " + schTime + "\nEstimated leave time: " + esTime;*/
return info;
}
Is there anything I can do to fix this problem?
I see your request is being made ​​in the UI thread, you mentioned that in another moment used this same way and it worked, I believe this may have happened when you ran your application on a device/emulator with a version of Android prior to 3.0.
Within an Android application you should avoid performing long
running operations on the user interface thread. This includes file
and network access. StrictMode allows to setup policies in your
application to avoid doing incorrect things. As of Android 3.0
(Honeycomb) StrictMode is configured to crash with a
NetworkOnMainThreadException exception, if network is accessed in
the user interface thread.
You can create a AsyncTasks class and move the call request to it.

How to debug this Android Studio app

I have tried to access a web resource using my second android app, in Android Studio. Im on API 15 as min, target and build. Here is my class. Its not MVC at all, i just threw in all the stuff from the Developing your first Android App tutorial online into the MainActivity Class:
public class MainActivity extends Activity {
private static final String DEBUG_TAG = "HttpExample";
private static final String MYURL = "http://www.server.com/app/service.php";
private TextView textView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.hello_world);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
//Check connectivity
ConnectivityManager connMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
//fetch data
new DownloadWebpageTask().execute(MYURL);
textView.setText("Fetching data");
} else {
//show error
textView.setText("No network connection available.");
}
}
//Method to connect to the internet
// Uses AsyncTask to create a task away from the main UI thread. This task takes a
// URL string and uses it to create an HttpUrlConnection. Once the connection
// has been established, the AsyncTask downloads the contents of the webpage as
// an InputStream. Finally, the InputStream is converted into a string, which is
// displayed in the UI by the AsyncTask's onPostExecute method.
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);
}
}
//Method to convert url to url object
// 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();
}
}
}
//Convert input stream to string
// 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);
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
return rootView;
}
}
}
and here is the huge log file:
Well I won't post it, but I have 2 questions:
How can I reduce all the output in log cat. I already switched from verbose to debug but I still get lots of stuff. And in this particular case, because the app crashes, I don't have time to do what another SO post said which is select the running process and hit the 2 green arrow button that filters out only output for the running process. Would I need to get the app to NOT crash before I could filter out the log such that i can SEE what is making it crash? Otherwise its difficult to sort through all the logs. What is the best practice here.
I do get one of my logs; "01-30 10:40:58.665 2047-2072/com.santiapps.downloadwebdata D/HttpExample﹕ The response is: 200" which is in the code but then a few lines down I get the crash:
01-30 10:40:58.710 2047-2047/com.santiapps.downloadwebdata E/AndroidRuntime﹕ FATAL EXCEPTION: main java.lang.NullPointerException
at com.santiapps.downloadwebdata.MainActivity$DownloadWebpageTask.onPostExecute(MainActivity.java:77)
at com.santiapps.downloadwebdata.MainActivity$DownloadWebpageTask.onPostExecute(MainActivity.java:63)
at android.os.AsyncTask.finish(AsyncTask.java:631)
at android.os.AsyncTask.access$600(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5071)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:808)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:575)
at dalvik.system.NativeStart.main(Native Method) 01-30 10:40:58.717 837-2676/? W/ActivityManager﹕ Force finishing activity com.santiapps.downloadwebdata/.MainActivity
Its a null pointer exception I see. How do I follow it? Sorry, Im new to android. Thanks
You can follow the logcat by clicking on the links, for example MainActivity.java:77.
You can filter the log by creating a new filter, (see image)
There you can filter by Package Name, so only the log of your app will be visible
Your TextView is null ...
You typically initialize it like this :
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.id_of_your_textview);
As for reducing the amount of log, you should filter the log on your app's package name. In Android Studio, this should be located on the right of Log Level in the Android DDMS panel (it probably reads No Filter for now, you will need to create one by clicking Edit Filter Configuration).

Read random status from a page using facebook api

I am creating an android app for my facebook page. The app is supposed to display random statuses(not just the recent ones) from the facebook page. Is there anyway I could do this?
I haven't done anything of that kind ever, but I think you can gran some logic from this and get it to work.
Step 1:
Make a call to the Facebook API, get all Status Updates and in a for loop, add them to an ArrayList<String>. For example, Facebook returns its data in JSON format. I am assuming, you already know how to fetch data. You need to parse the "message" tag from the JSON data returned by your Facebook API call.
For example:
ArrayList<String> arrStatusMessage;
for (int i = 0; i < JAFeeds.length(); i++) {
JSONObject JOFeeds = JAFeeds.getJSONObject(i);
if (JOFeeds.has("message")) {
String strStatusMessage = JOFeeds.getString("message");
arrStatusMessage.add(strStatusMessage );
}
}
Step 2:
Once you have your entire set of Facebook Status Messages, you will now need to use a java.util.Random instance.
For example: (Please note: I have not tested this code and it might result in errors. You may have to play around with it a bit to get it to work. :-( )
private static final Random randomGenerator = new Random();
int intRandom = randomGenerator.nextInt(arrStatusMessage.size());
String strRandomStatus = arrStatusMessage.get(intRandom);
Step 3:
Use the strRandomStatus to set it on a TextView.
For example:
TextView txtRanStatus = (TextView) findViewById(R.id.txtRanStatus);
txtRanStatus.setText(strRandomStatus);
You haven't posted any code, so it is difficult to provide something that fits in your scheme of things. But I think this should get you started. You will, possibly, need to adapt a few things and fit them in your own code.
Hope this helps.
EDIT: As per a comment by th OP, adding some bits of code to fetch Facebook Status Messages:
in your onCreate() method:
Start a new AsyncTask:
new getFacebookFeeds().execute();
I use this method in my app to make the Facebook Call to get all feeds from the Graph API.
private class getFacebookFeeds extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
String URL = "https://graph.facebook.com/me/home&access_token=ACCESS_TOKEN?limit=10";
try {
HttpClient hc = new DefaultHttpClient();
HttpGet get = new HttpGet(URL);
HttpResponse rp = hc.execute(get);
if (rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
String result = EntityUtils.toString(rp.getEntity());
// GET THE INTIAL RESULTS JSON ROOT
JSONObject JORoot = new JSONObject(result);
// GET THE "DATA" TAG FOR FEEDS ROOT
JSONArray JAFeeds = JORoot.getJSONArray("data");
for (int i = 0; i < JAFeeds.length(); i++) {
JSONObject JOFeeds = JAFeeds.getJSONObject(i);
if (JOFeeds.has("message")) {
String strStatusMessage = JOFeeds.getString("message");
arrStatusMessage.add(strStatusMessage );
}
}
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
You can do the remaining code, where you select a random Status Update, in the onPostExecute() of the AsyncTask shown above:
#Override
protected void onPostExecute(Void result) {
int intRandom = randomGenerator.nextInt(arrStatusMessage.size());
String strRandomStatus = arrStatusMessage.get(intRandom);
txtRanStatus.setText(strRandomStatus);
}
Declare the TextView as a Global Variable and cast it on your onCreate() before calling the AsyncTask. I think this should work just fine. Let me know how it goes. :-)

Categories

Resources