Updating sqlite database and using SimpleCursorAdapter causing UI to hang - android

EDIT: tl;dr How do you insert into db using content resolver and simpleCursorAdapter without a listfragment hanging?
I have an AsyncTask that I am using to download JSON data. In onPostExecute() of that task I call handleNewMessageReponse() to update my Sqlite database with the results.
The code for handleNewMessageResponse()
private void handleNewMessagesResponse() {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
//Step 1. Ensure we got something back.
if (mMessagesResponse.getMessages().length > 0){
//Step 2. Delete all Message DB rows.
getActivity().getContentResolver().delete(EntegraContentProvider.CONTENT_URI_MESSAGES, null, null);
//Step 3. Populate DB with new info.
//Hangs UI here.
for (int i = 0; i < mMessagesResponse.getMessages().length; i++) {
Messages messages = mMessagesResponse.getMessages()[i];
ContentValues values = new ContentValues();
values.put("id", messages.getId());
values.put("parent", messages.getParent());
values.put("type", messages.getType());
values.put("user", messages.getUser());
values.put("subject", messages.getSubject());
values.put("body", messages.getBody());
values.put("datetime", messages.getDatetime());
values.put("status", messages.getStatus());
values.put("touched", messages.getTouched());
//Perform the Insert.
getActivity().getContentResolver().insert(
EntegraContentProvider.CONTENT_URI_MESSAGES, values);
mWasDataLoaded = true;
}
}
mDataListener.onDataFetchComplete();
}
});
}
This is all contained in a ListFragment which implements LoaderManager.LoaderCallbacks
The onCreate of this fragment looks like this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] uiBindFrom = { "user", "subject" };
int[] uiBindTo = { R.id.messageUser , R.id.messageTitle };
getLoaderManager().initLoader(MESSAGES_LIST_LOADER, null, this);
adapter = new SimpleCursorAdapter(
getActivity(), R.layout.messages_list_item,
null, uiBindFrom, uiBindTo,
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
setListAdapter(adapter);
}
Everything works great with one exception the below section of the code takes up to 10 seconds to execute and hangs the UI/List during the 10 seconds.
this section is when UI hangs:
//Step 3. Populate DB with new info.
for (int i = 0; i < mMessagesResponse.getMessages().length; i++) {
Messages messages = mMessagesResponse.getMessages()[i];
ContentValues values = new ContentValues();
values.put("id", messages.getId());
values.put("parent", messages.getParent());
values.put("type", messages.getType());
values.put("user", messages.getUser());
values.put("subject", messages.getSubject());
values.put("body", messages.getBody());
values.put("datetime", messages.getDatetime());
values.put("status", messages.getStatus());
values.put("touched", messages.getTouched());
//Perform the Insert.
getActivity().getContentResolver().insert(
EntegraContentProvider.CONTENT_URI_MESSAGES, values);
Can someone plese point me in the right direction and tell me what the standard approach to this seemingly common task is? This should be pretty simple but am having a hard time finding examples that fit my use case.
Thanks.
Here is my final code. Much more fluid:
private class EventsFetcher extends AsyncTask<String, Void, EventsResponse> {
private static final String TAG = "EventsFetcher";
#Override
protected EventsResponse doInBackground(String... params) {
EventsResponse returnResponse = null;
try {
//Create an HTTP client
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(mEventsBaseUrl);
//Perform the request and check the status code
HttpResponse response = client.execute(post);
StatusLine statusLine = response.getStatusLine();
if(statusLine.getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
InputStream content = entity.getContent();
try {
//Read the server response and attempt to parse it as JSON
Reader reader = new InputStreamReader(content);
Gson gson = new Gson();
returnResponse= gson.fromJson(reader, EventsResponse.class);
content.close();
} catch (Exception ex) {
Log.e(TAG, "Failed to parse JSON due to: " + ex);
failedGettingEvents();
}
} else {
Log.e(TAG, "Server responded with status code: " + statusLine.getStatusCode());
failedGettingEvents();
}
} catch(Exception ex) {
Log.e(TAG, "Failed to send HTTP POST request due to: " + ex);
failedGettingEvents();
}
return returnResponse;
}
#Override
protected void onPostExecute(EventsResponse resultResponse) {
EventsResultsHandler resultsHandler = new EventsResultsHandler();
resultsHandler.execute(resultResponse);
}
}
private class EventsResultsHandler extends AsyncTask<EventsResponse, Void, String> {
private static final String TAG = "EventsResultsHandler";
#Override
protected String doInBackground(EventsResponse... params) {
try {
EventsResponse evResponse = params[0];
//Step 1. Ensure we got something back.
if (evResponse.getEvents().length > 0){
//Step 2. Populate Array with new info.
List<ContentValues> jsonResults = new ArrayList<ContentValues>();
for (int i = 0; i < evResponse.getEvents().length; i++) {
Events events = evResponse.getEvents()[i];
ContentValues values = new ContentValues();
values.put("start_date", events.getStart_date());
values.put("end_date", events.getEnd_date());
values.put("name", events.getName().replace("'","''"));
values.put("location", events.getLocation().replace("'","''"));
values.put("summary", events.getSummary().replace("'","''"));
values.put("lat", events.getLat());
values.put("lng", events.getLng());
values.put("is_active", events.getIs_active());
values.put("hide_calendar_button", events.getHide_calendar_button());
values.put("hide_map", events.getHide_map());
jsonResults.add(values);
}
//Step 3. Delete all data in table.
getActivity().getContentResolver().delete(EntegraContentProvider.CONTENT_URI_EVENTS, null, null);
//Step 4. Insert new data via via Bulk insert.
getActivity().getContentResolver().bulkInsert(EntegraContentProvider.CONTENT_URI_EVENTS, jsonResults.toArray(new ContentValues[jsonResults.size()]));
mWasDataLoaded = true;
}
} catch(Exception ex) {
Log.e(TAG, "Failed to update results due to: " + ex);
failedGettingEvents();
}
return null;
}
#Override
protected void onPostExecute(String result) {
mDataListener.onDataFetchComplete();
}
}

You don't want to run the DB update on the UI thread since that will hang the UI as you saw.
But you method of updating is likely to cause the problems you are describing because you are using a cursor adapter.
You are effectively clearing the database and repopulating. If the cursor adapter gets to run before you have rewritten all rows you will see the result you are talking about in your second comment above.
You have two choices, the first is to load to memory, and then quickly swap the list adapters internal list by swapping a pointer to the list data in the adapter and then telling the list adapter that things changed. You have to do this swap on the UI thread but since it is just updating a pointer to the data it is fast and keeps the UI responsive. The downside is you have to have two copies of the data in memory to do the swap.
The second and perhaps harder is to change the way that you sync with the server so that you don't have to clear everything and reload everything, but rather are deleting, adding or modifying single records. I recommend this route but it is probably more work for you.

Related

Is there a simpler way to check which download has been completed?

I'm currently creating an app that needs to download a couple of videos then save the local path of it on a SQLite database.
At first, I wanted to get the URL of the video I downloaded but I can't seem to find anything that discusses about it. I tried to get COLUMN_MEDIAPROVIDER_URI and COLUMN_URI from the intent passed on the BroadcastReceiver for DownloadManager.ACTION_DOWNLOAD_COMPLETE but they return null.
Then I found about EXTRA_DOWNLOAD_ID. But if I use that, I still need to use something like a new HashMap that got the EXTRA_DOWNLOAD_ID of my download and the id of the video on my SQLite database for checking which is which.
I'm fine with that but I want to know if there's an easier way to do the thing I want.
I did this using OkHttp, as follows:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(YOUR_URL)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// ERROR MESSAGE
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
response.body().byteStream(); // byteStream with your result.
}
}
});
Another thing, maybe would be better if you store the videos on memory and just the address in your SQLite.
Using the code below from the SO question here
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
// get the DownloadManager instance
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Query q = new DownloadManager.Query();
Cursor c = manager.query(q);
if(c.moveToFirst()) {
do {
String name = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
Log.i("DOWNLOAD LISTENER", "file name: " + name);
} while (c.moveToNext());
} else {
Log.i("DOWNLOAD LISTENER", "empty cursor :(");
}
c.close();
}
}
and saving the download id on my ArrayList I was able to make a simpler way to check which download is finished.
I modified it to look like this for my use case.
Cursor c = dlMgr.query(new DownloadManager.Query());
boolean found = false;
if(c.moveToFirst()) {
do {
String dlFilePath = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
int dlId = Integer.parseInt( c.getString(c.getColumnIndex(DownloadManager.COLUMN_ID)) );
for(int x = 0; x < vidArrLst.size(); x++){
VideoAd va = vidArrLst.get(x);
if(va.getDownloadId() == dlId){
dbHelper.updateLocalPath(va.getVideoId(), dlFilePath);
va.setLocalPath(dlFilePath);
found = true;
break;
}
}
} while (c.moveToNext() && !found);
} else {
Log.d(TAG, "empty cursor :(");
}
UPDATE:
Sometimes this method will show that 2 downloads finished with the same file name which results to a video item to not have a local path. What I did is check if the local path is empty, download id is greater than 0, and if the download id is still downloading before playing a video so I can redownload a video and fix the gap and play the local file the next time the video needs to be played.

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

data not passing when running doInBackground method within for loop

I have some pics to upload to the ftp server and I am using Asynctask for it.The images need to be sent to multiple host so I am using a for loop.The data to be passed is very well being fetched by the constructor but the doInBackground method is not running which was earlier running very well without the for loop and the additional data apart from the String filePathName that I am trying to pass in now in doInBackground.please help me
class uploadTask extends AsyncTask<String, Void, String> {
public uploadTask(String filePathName,String host_2,String user_2,String pass_2)
{
filePath=filePathName;
host_1=host_2;
user_1=user_2;
pass_1=pass_2;
Toast.makeText(getBaseContext(),"FTP DATA RECEIVING:"+"HOST:"+host_2+" USERNAME:"+user_2+" PASS:"+pass_2,Toast.LENGTH_LONG).show();
//hostName=host;
}
#Override
protected String doInBackground(String... params) {
try {
Toast.makeText(getBaseContext(),"Entered Do in Background Method to upload",Toast.LENGTH_SHORT).show();
ftp_host = "ftp.photoshelter.com";//This is not the correct way. Supposed to get from Backendless table
ftp_username = "brytest";//This is not the correct way. Supposed to get from Backendless table
ftp_password = "passtest";//This is not the correct way. Supposed to get from Backendless table
Toast.makeText(getBaseContext(),"HOST:"+ftp_host+" USERNAME:"+ftp_username+" PASS:"+ftp_password,Toast.LENGTH_LONG).show();
news_agency = "news agency";
easyFTP ftp = new easyFTP();
ftp.connect(ftp_host, ftp_username, ftp_password);
status = ftp.setWorkingDirectory("mem/images"); // if User say provided any Destination then Set it , otherwise
// Upload will be stored on Default /root level on server
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageTimeStamped = ftp_username + "_" + timeStamp + ".png";
FileInputStream is = new FileInputStream(imageFileLocation);
//addPhotoGrapherInfo();
ftp.uploadFile(is, imageTimeStamped);
System.out.println("Successfull ftp upload to " + ftp_host);
Toast.makeText(getBaseContext(), "Photo uploading by ftp to " + ftp_host, Toast.LENGTH_LONG).show();
//}
//reset booleans
//cameraPicTaken = false;
//galleryImageSelected = false;
//System.out.println("reset cameraPicTaken and galleryImageSelected");
// }
return new String("Upload Successful");
}catch (Exception e){
String t="Failure : " + e.getLocalizedMessage();
return t;
}
}
}
my onClickListener with for loop
if(cameraPicTaken || galleryImageSelected) {
Toast.makeText(SubmitActivity.this,"Image Location is:"+ imageFileLocation,Toast.LENGTH_LONG).show();
//addPhotoGrapherInfo();
for(int i=0;i<Common.selectedHostArray.size();i++) {
uploadFile(imageFileLocation,Common.selectedHostArray.get(i),Common.selectedUsernameArray.get(i),Common.selectedPasswordArray.get(i));
}
cameraPicTaken = false;
galleryImageSelected = false;
}
funnction called in onClick
public void uploadFile(String filePath,String host_1,String user_1,String pass_1)
{
if(cameraPicTaken == true) {
System.out.println("camera photo start upload");
//for(int i=0;i<Common.selectedHostArray.size();i++) {
//host_1=Common.selectedHostArray.get(i);
//user_1=Common.selectedUsernameArray.get(i);
//pass_1=Common.selectedPasswordArray.get(i);
//host_1="ftp.photoshelter.com";
//user_1="brytest";
//pass_1="passtest";
Toast.makeText(getBaseContext(),"FTP DATA PASSING:"+"HOST:"+host_1+" USERNAME:"+user_1+" PASS:"+pass_1,Toast.LENGTH_LONG).show();
new uploadTask(filePath,host_1,user_1,pass_1).execute();
// }
//cameraPicTaken = false;
//galleryImageSelected = false;
System.out.println("reset cameraPicTaken and galleryImageSelected");
//cameraPicTaken = false;
}
if(galleryImageSelected == true){
System.out.println("gallery image start upload");
Toast.makeText(getBaseContext(),"FTP DATA PASSING:"+"HOST:"+host_1+" USERNAME:"+user_1+" PASS:"+pass_1,Toast.LENGTH_LONG).show();
new uploadTask(filePath,host_1,user_1,pass_1).execute();
//new uploadTask(filePat)h.execute();
//galleryImageSelected = false;
}
Toast.makeText(getBaseContext(), "Photo uploading by ftp to photoshelter.com" /*+ news_agency*/, Toast.LENGTH_LONG).show();
}
You're trying to perform a UI command on a background thread (Toast). This is causing your background tasks to fail early. Since your background tasks catch their own errors, they fail silently.
#Override
protected String doInBackground(String... params) {
try {
// you can't Toast on a background thread, this should throw an exception
Toast.makeText(getBaseContext(),"Entered Do in Background Method to upload",Toast.LENGTH_SHORT).show();
...
}catch (Exception e){
// your Toast exception is getting caught silently here
String t="Failure : " + e.getLocalizedMessage();
return t;
}
}
By the way, the try/catch on everything is not a good practice. You end up with a ton of silent failures leaving you scratching your head and asking why things aren't working.

Updating a ContentProvider's underlying SQLite database from internet resource

I have a implemented a ContentProvider to serve up a list of geographical locations from an underlying SQLite database.
These locations are actually an output from a model and are available online in the form of a simple JSON string; I don't want to keep downloading them every time the app is started, so I want to store them locally in a database and update them at pre-defined intervals (once a day for instance).
My question is, where should I implement the downloading and parsing code? Should it be part of my ContentProvider implementation? Or part of the SQLiteOpenHelper implementation?
I started to implement it as a public function called updateSiteList in my ContentProvider, but I don't know how to actually call it (the ContentProvider is usually acessed indirectly via CursorLoader)!
I am bit stuck as to how to progress!
Can anyone point me in the right direction?
Thanks!
There are a couple of ways to achieve this.
The two most common ways I use are :-
1) a custom sync adapter.
2) An intent service.
With option 1 you get the benefit of the Android system handling network connection problems and is the recommended approach by the android Developers
With option 2 you get more control over when the data gets downloaded which may or may not be the best time for the user or the Android System.
Either way the solution is the same. At some point in time you will be making, in a background service, an HTTP get request to a url. when your request completes you will need to heck the status of the response and if appropriate you would then make a call to the content provider to wither insert or update your data accordingly. whichever approach you take this part will be the same.
Some further reading for you.
https://sites.google.com/site/andsamples/concept-of-syncadapter-androidcontentabstractthreadedsyncadapter
Be sure to watch that Google I/O video
Regardless of the approach you take, the code to download json and insert to your content provider could look something like this in an IntentService called from either your sync adapter or from somewhere within your app if not using a sync adapter.
public class ServiceInitialiseData extends IntentService {
static final String TAG = "ServiceSyncData";
//ACTION should include application package convention, just to show that this can
//be any string
public static final String SYNC_COMPLETED_ACTION="com.pjmobile.games.fantasyf1.SyncCompleted";
public ServiceInitialiseData() {
super("InitialiseDataService");
}
#Override
protected void onHandleIntent(Intent intent) {
String sJson;
try {
sJson = downloadFromServer("Some parsed url");
int i, x;
boolean res = false;
List <ContentValues> bulkValues = new ArrayList <ContentValues>();
JSONArray entries;
try {
entries = new JSONArray(sJson);
ContentValues cvEntity = null;
JSONObject entity;
x = entries.length();
for (i=0;i<x;i++){
entity = entries.getJSONObject(i).getJSONObject("some_json_key");
bulkValues.add(cvEntity);
}
}
int qCount = getContentResolver().bulkInsert(uri,
(ContentValues[])bulkValues.toArray(new
ContentValues[bulkValues.size()]));
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
private String downloadFromServer(String url) throws ClientProtocolException, IOException {
HttpResponse sJson = getJSONEntityFromURL(this, url);
return EntityUtils.toString(sJson.getEntity());
}
private static HttpResponse getJSONEntityFromURL(Context context, String url) throws ClientProtocolException, IOException {
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet(url);
httpget.addHeader("Accept", "application/json");
httpget.addHeader("Content-Type", "application/json; charset=UTF-8");
HttpResponse response;
response = httpclient.execute(httpget);
return response;
}
For the above to work you would have to ode up the bulk insert method of your content provider which could look something like this
#Override
public int bulkInsert(Uri uri, ContentValues[] values) {
final SQLiteDatabase db = mDB.getWritableDatabase();
final int match = sURIMatcher.match(uri);
int numInserted= 0;
// Util.log_debug_message("#### URI MATCH - " + match);
switch(match){
case TEAMS:
numInserted = insertTeams(db, values);
break;
default:
throw new UnsupportedOperationException("unsupported uri: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null, false);
return numInserted;
}
private int insertTeams(SQLiteDatabase db, ContentValues[] values) {
int numInserted = 0;
db.beginTransaction();
try {
//standard SQL insert statement, that can be reused
SQLiteStatement insert =
db.compileStatement(INSERT_OR_REPLACE_STRING + TeamModel.TEAM_TABLE_NAME
+ "(" + TeamModel.COL_SERVER_ID
+ "," + TeamModel.COL_BONUS_RACE_ID
+ "," + TeamModel.COL_POINTS
+ "," + TeamModel.COL_POSITION
+ "," + TeamModel.COL_TEAM_NAME + ")"
+" values " + "(?,?,?,?,?)");
for (ContentValues value : values){
//bind the 1-indexed ?'s to the values specified
insert.bindString(1, value.getAsString(TeamModel.COL_SERVER_ID));
insert.bindString(2, value.getAsString(TeamModel.COL_BONUS_RACE_ID));
insert.bindString(3, value.getAsString(TeamModel.COL_POINTS));
insert.bindString(4, value.getAsString(TeamModel.COL_POSITION));
insert.bindString(5, value.getAsString(TeamModel.COL_TEAM_NAME));
insert.execute();
}
db.setTransactionSuccessful();
numInserted = values.length;
} finally {
db.endTransaction();
db.close();
}
return numInserted;
}
This is not a copy and paste solution. Merely an example stripped out of one of my apps and you should look at each line of code and take great care to understand what is going on.

Running an FQL query while parsing a Graph API result gives arrayindexoutofbound exception

After making a call to the "me/home" Graph API, while parsing the JSON result, I am trying to make another query using FQL. The FQL query problem was solved in my earlier question.
The background of my implementation is: I am using a BaseAdapter and from the main activity, I am sending the data parsed from JSON using multiple ArrayLists. If I am not making the FQL query, everything is peachy. But when I introduce the FQL query, the query is always run after the adapter has been set to the ListView. This keeps causing the arrayindexoutofbound exception.
This is the code that I am using including the additional FQL query while parsing the JSON result. To keep the code short, I will include the relevant part as the rest works just fine. If more is needed, however, I will put that up too.
// GET THE POST'S LIKES COUNT
if (json_data.has("likes")) {
JSONObject feedLikes = json_data.optJSONObject("likes");
String countLikes = feedLikes.getString("count");
postLikesCountArrayList.add(countLikes);
// TEST STARTS
Runnable run = new Runnable() {
#Override
public void run() {
graph_or_fql = "fql";
String query = "SELECT likes.user_likes FROM stream WHERE post_id = \'"
+ finalThreadID + "\'";
Bundle params = new Bundle();
params.putString("method", "fql.query");
params.putString("query", query);
Utility.mAsyncRunner.request(null, params, new LikesListener());
}
};
TestNewsFeeds.this.runOnUiThread(run);
// TEST ENDS
} else {
String countLikes = "0";
postLikesCountArrayList.add(countLikes);
}
And this is the code for the LikesListener class. It is a private class declared in the same activity:
private class LikesListener extends BaseRequestListener {
#Override
public void onComplete(final String response, final Object state) {
// Log.e("response", response);
try {
JSONArray JALikes = new JSONArray(response);
// Log.v("JALikes", JALikes.toString());
for (int j = 0; j < JALikes.length(); j++) {
JSONObject JOTemp = JALikes.getJSONObject(j);
// Log.e("JOTemp", JOTemp.toString());
if (JOTemp.has("likes")) {
JSONObject optJson = JOTemp.optJSONObject("likes");
// Log.v("optJson", optJson.toString());
if (optJson.has("user_likes")) {
String getUserLikeStatus = optJson.getString("user_likes");
Log.e("getUserLikeStatus", getUserLikeStatus);
arrayLikeStatus.add(getUserLikeStatus);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
I have figured out using debugging that the cause of the crash is the setAdapter being called before the second query completes. I see the log's being added to logcat after the crash has occured.
Any help on a solution for this is appreciated
UPDATE: Figured out the solution almost when I was about to give up.
SOLUTION
So instead of calling the BaseRequestListener as used in the question, this modification had to be made.
try {
graph_or_fql = "fql";
String query = "SELECT likes.user_likes FROM stream WHERE post_id = \'"
+ finalThreadID + "\'";
// Log.d("finalThreadID", finalThreadID);
Bundle params = new Bundle();
params.putString("method", "fql.query");
params.putString("query", query);
// Utility.mAsyncRunner.request(null, params, new LikesListener());
String fqlResponse = Utility.mFacebook.request(params);
// Log.e("fqlResponse", fqlResponse);
JSONArray JALikes = new JSONArray(fqlResponse);
// Log.v("JALikes", JALikes.toString());
for (int j = 0; j < JALikes.length(); j++) {
JSONObject JOTemp = JALikes.getJSONObject(j);
// Log.e("JOTemp", JOTemp.toString());
if (JOTemp.has("likes")) {
JSONObject optJson = JOTemp.optJSONObject("likes");
// Log.v("optJson", optJson.toString());
if (optJson.has("user_likes")) {
String getUserLikeStatus = optJson.getString("user_likes");
// Log.e("getUserLikeStatus", getUserLikeStatus);
arrayLikeStatus.add(getUserLikeStatus);
// Log.d("arrayLikeStatus", arrayLikeStatus.toString());
}
}
}
} catch (Exception e) {
// TODO: handle exception
}
Hope this helps someone save time if they are stuck like I was.

Categories

Resources