Return arraylist populated inside Callback - java

I'm trying to use an arraylist of Tweets that has been populated inside a callback but I can't figure out how to return the built list. Whenever I try to use the built list it's empty.
I got some code from this post
public ArrayList<Tweet> tweetList() {
final ArrayList<Tweet> tweets = new ArrayList<>();
final UserTimeline userTimeline = new UserTimeline.Builder()
.screenName("xxxxxxxx")
.build();
userTimeline.next(null, new Callback<TimelineResult<Tweet>>() {
#Override
public void success(Result<TimelineResult<Tweet>> result) {
for(Tweet tweet : result.data.items){
tweets.add(tweet);
}
Log.d("Finished Tweet List", String.valueOf(tweets));
// when this is printed I can see the ArrayList and all tweets are there
}
#Override
public void failure(TwitterException exception) {
exception.printStackTrace();
}
});
Log.d("Tweet list returned", String.valueOf(tweets));
// the value of tweets is empty here for some reason
return tweets;
}

Instead of returning the empty ArrayList of Tweets, you can modify your method so another ArrayList of Tweets is updated when the response from the Callback succeeds.
So I suggest modifying your method as follows:
private ArrayList<Tweet> tweetList = new ArrayList<>();
public void tweetList() {
final ArrayList<Tweet> tweets = new ArrayList<>();
final UserTimeline userTimeline = new UserTimeline.Builder()
.screenName("xxxxxxxx")
.build();
userTimeline.next(null, new Callback<TimelineResult<Tweet>>() {
#Override
public void success(Result<TimelineResult<Tweet>> result) {
for(Tweet tweet : result.data.items){
tweets.add(tweet);
}
this.tweetList = tweets;
// execute the next sequence of instructions in your program here
// and make use of tweetList instead of tweets
}
#Override
public void failure(TwitterException exception) {
exception.printStackTrace();
}
});
}

Of course, the value of tweets is empty here !
The function userTimeline.next(null, new Callback<TimelineResult<Tweet>>() is not executed on the main thread (It is an asynchronous task).
So the return tweets is called before your ArrayList<Tweets> tweets has been filled.
You can only do operations on your list in the
#Override
public void success(Result<TimelineResult<Tweet>> result) {
//do whatever with your list here !
}
Hope I'll have been clear !

Related

how to url calling return from list function with retrofit

i tried to return list from the url that i get with retrofit. it works and i get the data but it wont return.
this is my code
public List<MovieResponse> loadCourses() {
ArrayList<MovieResponse> list = new ArrayList<>();
ApiServices apiService =
NetworkClient.getRetrofitClient().create(ApiServices.class);
Call<MovieResult> call = apiService.getMovies();
call.enqueue(new Callback<MovieResult>() {
#Override
public void onResponse(Call<MovieResult> call, Response<MovieResult> response) {
if (response.body() != null) {
ArrayList<MovieResponse> movies = new ArrayList<>();
movies = response.body().getResults();
Log.d("",""+movies);
list.addAll(movies);
Log.d("",""+list);
}
}
#Override
public void onFailure(Call<MovieResult> call, Throwable t) {
// Log error here since request failed
Log.e("error", t.toString());
}
});
return list;
}
when i print list inside onResponse it works and there are the data. but when i return it or trying to print list outside onResponse for example below ArrayList<MovieResponse> list = new ArrayList<>(); it not show the data.
please help what is actually wrong with it. i really appreciate it.
The simplest way is to define your movies list directly inside activity or fragment(in other words, a field member of the class).
It's not a good idea to return data from an asynchronous method.
Change the return type of the loadCourses method to void and instantiate the filed movies inside onResponse().
public class SomeActivity extends AppCompatActivity {
private ArrayList<MovieResponse> movies = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_some);
}
public void loadCourses() {
ApiServices apiService =
NetworkClient.getRetrofitClient().create(ApiServices.class);
Call<MovieResult> call = apiService.getMovies();
call.enqueue(new Callback<MovieResult>() {
#Override
public void onResponse(Call<MovieResult> call, Response<MovieResult> response) {
if (response.body() != null) {
movies = response.body().getResults();
...
}
}
#Override
public void onFailure(Call<MovieResult> call, Throwable t) {
...
}
});
}
}
It is because you are making asynchronous call which is being handled by a separate thread. So after call.enqueue(), the main thread directly jumps to return statement without waiting for API response, that's why you are getting empty list.
Assuming your API takes 1 sec to respond,
just for an experiment, you can try adding a sleep() for 3 sec right before your return statement, it should return all the movies.
If you must return from the method then go for retrofit synchronous call.
To make a sync call create a new thread in main thread and make call from there, it is not allowed to make network call from main thread because it blocks the thread.

How can i save on arraylist the response that i receive from my api?

i'm new on retrofit and i want to save the response from my api like an object on an array list.
i've search solutions but i dont know how callback method works and i can't understand very well.
public ArrayList<Match> recolectar_partido(){
final ArrayList<Match> datos=new ArrayList<Match>();
Call<List<MatchResponse>> call = RetrofitClient.getInstance().getApi().getmatch();
call.enqueue(new Callback<List<MatchResponse>>() {
#Override
public void onResponse(Call<List<MatchResponse>> call, Response<List<MatchResponse>> response) {
matchlist=response.body();
for (MatchResponse fix:matchlist) {
Integer idfix=fix.getId_fixture();
Integer idsta=fix.getId_stadium();
String fecha=fix.getFecha();
String hora=fix.getHora();
Match variable= new Match(idfix,idsta,fecha,hora);
datos.add(variable);
}
}
#Override
public void onFailure(Call<List<MatchResponse>> call, Throwable t) {
Toast.makeText(getApplicationContext(),"error de conexion",Toast.LENGTH_SHORT).show();
}
});
return datos;
}
i want the arraylist to be filled.
Do call.execute() instead of enqueue.
E.g.
final ArrayList<Match> datos=new ArrayList<Match>();
Call<List<MatchResponse>> call = RetrofitClient.getInstance().getApi().getmatch();
matchlist= call.execute().body();
for (MatchResponse fix:matchlist) {
Integer idfix=fix.getId_fixture();
Integer idsta=fix.getId_stadium();
String fecha=fix.getFecha();
String hora=fix.getHora();
Match variable= new Match(idfix,idsta,fecha,hora);
datos.add(variable);
}
return datos;

AsyncTask with result of ArrayList of objects is returning null in OnPostExecute

I'm trying to retrieve some data from API, but i'm always getting null in async task. Here is my asynctask:
private class DownloadTask extends AsyncTask<Bundle, Void, List<Topic>> {
#Override
protected void onPreExecute() {
HomeActivity.mProgressBar.setVisibility(View.VISIBLE);
HomeActivity.mProgressBar.setIndeterminate(true);
}
#Override
protected List<Topic> doInBackground(Bundle... params) {
return downloadPhotos(params[0]);
}
#Override
protected void onPostExecute(List<Topic> topics) {
HomeActivity.mProgressBar.setVisibility(View.INVISIBLE);
HomeActivity.mProgressBar.setIndeterminate(false);
Log.d("List Size: ", ""+topics); // 0
adapter = new TopicListAdapter(activity, topics);
RecyclerView.LayoutManager manager = new MyCustomLayoutManager(activity);
recyclerView.setLayoutManager(manager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(adapter);
}
}
Method for retrieving data should merge two arrays into one array because i'm retrieving data from two places:
private List<Topic> downloadPhotos(Bundle params) {
String profileId = activity.getPreferencesManager().getProfileId();
List<Topic> topicsFromMe, topicsFromFriends;
topicsFromFriends = setValuesFromFriends(params);
topicsFromMe = setValuesFromMe(profileId, params);
topicsFromFriends.addAll(topicsFromMe);
sortTopics(topicsFromFriends);
int k = topicsFromFriends.size();
Log.d("List Size: ", "" + topicsFromFriends); // here also 0 for size
if (k > 10)
topicsFromFriends.subList(10, k).clear();
return topicsFromFriends;
}
And here is one method where i'm setting values to array list. It is strange that RecyclerView in this case is populated with this array, but i'm not getting results i want. For instance i should sort this list and show only 10 records from it.
private List<Topic> setValuesFromFriends(final Bundle params) {
final List<Topic> topics = new ArrayList<>();
activity.getSimpleFacebook().getFriends(new OnFriendsListener() {
#Override
public void onComplete(List<Profile> friends) {
for (final Profile profile : friends) {
activity.getSimpleFacebook().get(profile.getId(), "photos/uploaded", params,
new OnActionListener<List<Photo>>() {
#Override
public void onComplete(List<Photo> photos) {
for (final Photo photo : photos) {
// Initialize instance of Topic
final User user = photo.getFrom();
final Topic topic = new Topic();
topic.setCaption(photo.getName());
topic.setImageId(photo.getId());
topic.setCreatedTime(photo.getCreatedTime());
topic.setPostImage(photo.getSource());
topic.setUserId(user.getId());
topic.setName(user.getName());
final Bundle likeParams = new Bundle();
likeParams.putString("fields", "total_count");
likeParams.putString("limit", "100000");
activity.getSimpleFacebook().get(photo.getId(), "likes",
likeParams, new OnActionListener<List<Like>>() {
#Override
public void onComplete(List<Like> likes) {
topic.setNumOfLikes(likes.size());
topics.add(topic);
}
#Override
public void onThinking() {
super.onThinking();
}
});
}
}
});
}
}
});
return topics;
}
You are using AsyncTask incorrectly.
AsyncTask is launching another Thread (thread1) where where it is executing the method, doenloadPhotos. This method is calling setValuesFromFriends which is creating another thread (thread2) with the method getFriends. As thread2 has been launched, the rest of the code in setValuesFromFriends will get executed.
So here is how it is working:
private List<Topic> setValuesFromFriends(final Bundle params) {
final List<Topic> topics = new ArrayList<>();
//launched process on new thread
return topics; //this is 0 as topics = new ArrayList<>();
}
So now topicsFromFriends = 0. Hence you are getting the output = 0.
in effect thread1 is getting executed before thread2 is complete. As the output of thread1 is 0, nothing is displayed in UI after onPostExecute
There is no need of using AsyncTask.
You should put all the required code inside the onComplete of the new OnFriendsListener(). This way the info will be shown correctly. You can launch the progressbar before setValuesFromFriends and then remove it in the onComplete.

How to populate Android ListView with information from Firebase query

This is my first post so if I didn't follow some protocol I was supposed to, apologies.
I am trying to populate a ListView with some information from my Firebase database. I think the problem I am having is that the query to the database is too slow (the thread is probably downloading pictures) and my activity loads its activity layout without waiting for the thread to finish executing. (If I step through the debugger and wait a bit, I will eventually see the information I am parsing: user names, user numbers, and user pictures) Everything I have queried suggests I should use AsyncTask to accomplish this. As opposed to using thread blocking or a semaphore b/c AsyncTask is thread safe.
To my understanding, Firebase queries are already executing asynchronously; therefore, the doInBackground method for AsyncTask I have "tried" to implement seems redundant. Also, I am a bit confused of AsyncTask's overloaded signature and the call to: new someTask.execute("some stuff in a string").
Any suggestions on how I can accomplish this? Any feedback is very much appreciated!
// Please ignore the minor indentation from pasting my code in
protected void onCreate(Bundle savedInstanceState) {
...
new getFirebaseInfoTask();
}
private class getFirebaseInfoTask extends AsyncTask {
#Override
protected Object doInBackground(Object... args) {
// Do stuff
userInfoList = GetUserInfoFromFirebase.getUserInfo();
// Unsure if I need to return here.
return userInfoList;
}
#Override
protected void onProgressUpdate(Object... args) {
// Update your UI here
populateUserInfoList();
}
}
private void populateUserInfoList() {
// Create list of items
Collections.addAll(userInfoList);
populateFriendsListView();
}
private void populateFriendsListView() {
// Build the adapter
ArrayAdapter<UserInfo> adapter = new MyListAdapter();
// Configure the list view
ListView listView = (ListView) findViewById(R.id.friends_listview);
listView.setAdapter(adapter);
registerClickCallBack();
}
... // More code
public class GetUserInfoFromFirebase {
public static ArrayList getUserInfo() {
final ArrayList<UserInfo> list = new ArrayList<UserInfo>();
Firebase firebase = new Firebase("https:......firebaseio.com");
firebase.child("users").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
HashMap<String, Object> users = (HashMap<String, Object>) snapshot.getValue();
for(Object user : users.values()) {
HashMap<String, Object> userMap = (HashMap<String, Object>) user;
String userNumber = (String) userMap.remove("number");
if(!list.contains(userNumber)) {
String name = (String) userMap.remove("username");
String pic = (String) userMap.remove("profile_picture");
UserInfo info = new UserInfo(userNumber, name, pic);
list.add(info);
}
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {}
});
return list;
}
So I figured it out. I got rid of the AsyncTask and moved the method call I wanted to execute in onProgressUpdate to outside of the for loop of my onDataChange such that the thread that actually gets access to the onDataChange method calls my populateFriendsView method.
private void populateUserInfoList() {
userInfoList = new ArrayList<UserInfo>();
firebase = new Firebase("https://....firebaseio.com");
firebase.child("users").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
HashMap<String, Object> users = (HashMap<String, Object>) snapshot.getValue();
for (Object user : users.values()) {
HashMap<String, Object> userMap = (HashMap<String, Object>) user;
String userNumber = (String) userMap.remove("number");
if (!userInfoList.contains(userNumber)) {
String name = (String) userMap.remove("username");
String pic = (String) userMap.remove("profile_picture");
UserInfo info = new UserInfo(userNumber, name, pic);
userInfoList.add(info);
}
}
// thread executing here can get info from database and make subsequent call
Collections.addAll(userInfoList);
populateFriendsListView();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
String message = "Server error. Refresh page";
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
});
}
Firebase itself provide Asynchronous methods to query data , They have created a sample app to show How to backing up the Listview with firebase information through Android Chat
check this
In that example the core functionality is placed in a generic base adapter called FirebaseListAdapter...
Snippet from my working code base
Firebase firebase = new Firebase(Constants.FREEBASE_DB_URL);
Firebase childRef = firebase.child("sessions");
childRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
System.out.println(snapshot.getValue());
Map<String, Session> td = (HashMap<String, Session>) snapshot.getValue();
List<Session> valuesToMatch = new ArrayList<Session>(td.values());
apiClientCallback.onSuccess(valuesToMatch);
}
#Override
public void onCancelled(FirebaseError error) {
Toast.makeText(context, "onCancelled" + error.getMessage(), Toast.LENGTH_SHORT).show();
}
});

Filtering latest tweets

I am trying to filter the latest tweets with the words sad, okay, joyful.
When a tweet which has either word inside it, I would like it to print that tweet. But I would also like there to be a delay in the tweets which are printed. So about a 10 second delay between each tweet. For example:
If a tweet comes through : #joker im so sad today
Then I want that to print to the screen, and to the following message
System.out.println("*************************a sad tweet");
Then if a tweet comes through after this: #programmer im joyful
Then 10 seconds after the last tweet I want that tweet to come through with the following message drawn to the screen.
System.out.println("*************************a joyful tweet");
And so on.
Below, I have made some code which allows you to filter the tweets, but im unsure of how to test and print a separate message for each tweet. I tried storing this in an Arraylist and retrieving each tweet with a message, but this doesn't work. Is there a way to do this?
Im using processing 2 and twitter4j 3
Any suggestions? Solutions
void GetTweetsByKeywords()
{
List<String>mood = new ArrayList <String>();
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setOAuthConsumerKey("XXXX");
cb.setOAuthConsumerSecret("XXX");
cb.setOAuthAccessToken("XXXX");
cb.setOAuthAccessTokenSecret("XXXX");
TwitterStream twitterStream = new TwitterStreamFactory(cb.build()).getInstance();
StatusListener statusListener = new StatusListener()
{
private int count = 0;
private long originalTweetId = 0;
#Override
public void onStatus(Status status)
{
System.out.println(status.getUser().getName() + " : " + status.getText());
} //en of the onStatus()
public void onDeletionNotice(StatusDeletionNotice statusDeletionNotice)
{
// should really remove deleted tweets here ...
}
public void onTrackLimitationNotice(int numberOfLimitedStatuses)
{
}
public void onScrubGeo(long userId, long upToStatusId) {
// should really remove deleted location information here ...
}
public void onStallWarning(StallWarning stallWarning) {
// should really do something about stalls here ...
}
#Override
public void onException(Exception ex)
{
ex.printStackTrace();
}
}; //end of the listener
String keywords[] = {"sad","okay","joyful"};
for(int i=0; i<keywords.length; i++)
{
FilterQuery fq = new FilterQuery();
fq.track(keywords);
twitterStream.addListener(statusListener);
twitterStream.filter(fq);
mood.add(//here i want to add the filtered tweets);
System.out.println("Heres a filter :" + mood.get(i));
if (mood.get(i).equals("sad"))
{
System.out.println("*********************************************a sad tweet");
}
else if (mood.get(i).equals("joyful"))
{
System.out.println("*********************************************a joyfull tweet");
}
else if(mood.get(i).equals("okay"))
{
System.out.println("*********************************************okay tweet");
}
}
}
The easiest is to use a separate thread and a queue. Do this:
final Queue<Status> queue = new LinkedBlockingQueue<Status>(10000);
new Thread(){
// In your status listener, post tweets to the queue
...
public void onStatus(Status status){
queue.offer(status);
}
...
// Create TwitterStream instance, add query
// and start listening
twitterStream.filter(fq);
}.start();
while(!Thread.currentThread().isInterrupted()){
Status nextTweet = queue.take();
System.out.println("Do stuff with tweet");
}

Categories

Resources