I'm trying to use "http://github.com/commonsguy/cwac-endless" to implement pagination for my list views.
I'm getting the first page results correctly, when I scrolled down to the bottom of the 1st page, i'm seeing the spinning wheel but im not getting any results.
I noticed from the LogCat that, getPendingView(), cacheInBackground(), appendCachedData() and MyCustomAdapter's getView() are getting called infinately.
Can anybody plz help.
Thanks,
nehatha.
Here is my code snippet:
Activity{
onCreate() {
myList = new ArrayList<Item>();
setListAdapter(new DemoAdapter(myList));
nextLink = "service_url"; //for first page results (say 1-25)
}
final Handler handler = new Handler() {
public void handleMessage(final Message msg) {
//
updateList(jsonResponse);
}
};
updateList(String jsonString) {
//parse json
//add to `myList`
//update `nextLink`, if there is next page available
}
class DemoAdapter extends EndlessAdapter {
private RotateAnimation rotate=null;
DemoAdapter(List<Item> list) {
super(new MyCustomAdapter(LatestUpdatesList.this, R.layout.latest_update_item, list));
//rotate code
}
#Override
protected boolean cacheInBackground() {
if(nextLink != null && nextLink.length() > 0){
HttpHandler httpHandler = new HttpHandler(nextLink, "GET", handler);
Thread latestUpdatesThread = new Thread(httpHandler);
latestUpdatesThread.start();
return true;
}
return false;
}
#Override
protected void appendCachedData() {
MyCustomAdapter adapter = (MyCustomAdapter)getWrappedAdapter();
adapter.setList(myList);
}
} //DemoAdapter
} //Activitiy
I have fixed the issue by not calling my web service in a new thread (inside cacheInBackground() method). Instead i'm directly calling my HttpHandler's get() method directly. But I'm not sure this is the best fix.
Thanks,
nehatha
Related
I want to develop an Android application which asks a server for some data, and displays these data in a ListView.
Currently, I am using a single Activity (without fragments), and the layout is very simple: it consists of an ImageView, an EditText and a ListView. When the ImageView is clicked it gets the content of the EditText and sends it to the server as a new item and automatically updates the Listview (am calling the method of retreiving the objects after the add operation).
I created an AsyncTask class with a progress dialog inside the Activity which the job in background is getting the objects from the server and then assigning them to a List (member of the enclosing class).
With that practice, am facing a lot of problems: the list gets displayed correctly but very slowly! and when I press the ImageView the AsyncTask is then called to do its job after adding the new item but the problem is that its dialog never dismisses.
My question is what is the best practice with this situation in Android? what is the best design pattern? should I use fragments? How should I manage my Threads?
UDATE:
here is the AsyncTask:
class RemoteDataTask extends AsyncTask<Void, Void, Void> {
private UserDetailsActivity context;
RemoteDataTask(UserDetailsActivity context) {
this.context = context;
}
#Override
protected void onPreExecute() {
super.onPreExecute();;
mProgressDialog = ProgressDialog.show(context, "Looking for posts", "Loading...", true, false);
}
#Override
protected Void doInBackground(Void... params) {
UserDetailsActivity.this.posts.clear();
posts = new PostManager(context).userPosts(ParseUser.getCurrentUser());
return null;
}
#Override
protected void onPostExecute(Void result) {
postList = (ListView) findViewById(R.id.post_list);
adapter = new PostsListAdapter(context, UserDetailsActivity.this.posts);
postList.setAdapter(adapter);
mProgressDialog.dismiss();
}
}
And the method wich retreives the posts:
public void refreshPostList() {
try {
BusInfo.getInstance().register(UserDetailsActivity.this); // register the Bus to recieve results.
} catch (Exception e) {
Log.d("My application says : ;) ", "Erro registering " + e);
}
pd = ProgressDialog.show(this, "Please Wait", "Loading");
new ExprienceEdit(this, "hello").execute();
}
And the Button with its method
public void newPost(View v) {
ParseObject post = new ParseObject("Post");
post.put("content", editText.getText().toString());
post.saveInBackground();
refreshPostList();
}
<ImageView
android:id="#+id/new_post"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:onClick="newPost"
android:padding="10dp"
android:src="#drawable/ic_action_post" />
Regarding the progress dialog not being dismissed:
Where is mProgressDialog dialog declared? I suggest you move it into the RemoteDataTask. (I'm guessing you are at some point overriding the current instance and therefore the dismiss isn't working)
Regarding the slow refresh of the list, post your Adapter code. You should do correct recycling of views and you shouldn't recreate the Adapter everytime but set the data and call notifyDataSetChanged so the listView will recycle the views with the new data. Look into this answer regarding correct recycling of views: https://stackoverflow.com/a/6923513/348378
Edit 1
I also suggest this to prevent having multiple refreshTasks:
public void refreshPostList() {
if(dataTask == null) {
dataTask = new RemoteDataTask(this).execute();
}
}
#Override
protected void onPostExecute(Void result) {
// you stuff
dataTask = null;
}
You can also consider cancelling the current task and starting a new one depending on required behavior.
you should pass ProgressDialog to your AsyncTask class constructor and in any class that want to use AsyncTask class(in your case RemoteDataTask) you should instantiate progress dialog and pass as second argument to your RemoteDataTask to control the visibility from specific custom class.
maybe this help.
The best way to deal with asynctasks is by using otto :
Otto actually is a singltone bus : please refer to this website http://square.github.io/otto/
Any piece of code would be great to help you more with the problem you are facing.
Any questions I am ready to answer.
BusInfo.getInstance.register(ActivityName.this) // register the Bus to recieve results.
pd = ProgressDialog.show(ActivityName.this, "Please Wait", "Loading");
new ExperienceEdit(getApplicationContext(), "hello").execute(); //async task to be executed let us say on button click
Now the experience edit is:
public class ExperienceEdit extends AsyncTask<Void, Void, String> {
Context c;
String id;
public ExperienceEdit(Context c, String id\) {
this.c = c;
this.id = id;
}
#Override
protected String doInBackground(Void... voids) {
//right the call to back here
}
#Override
public void onPostExecute(String result) {
try {
BusInfo.getInstance().post(new ExperienceEditResult(result));
} catch (Exception e) {
e.printStackTrace();
}
}
}
The result after posting is subscribed at the activity like this :
#Subscribe
public void onAsyncTaskResult(EditExperienceResult result) {
if (pd != null)
pd.dismiss();
object = result.getResult();
if (object != null) {
if (object.equals("success")) {
Toast.makeText(getApplicationContext(), "Success", Toast.LENGTH_SHORT).show();
onBackPressed();
} else Toast.makeText(getApplicationContext(), "Failure", Toast.LENGTH_SHORT).show();
} else
Toast.makeText(getApplicationContext(), "Please try again later", Toast.LENGTH_SHORT).show();
}
The ExperienceEditResult here happens to be a string (you can have it whatever you want) :
public class ExperienceEditResult {
private String result;
public ExperienceEditResult(String result) {
this.result = result;
}
public String getResult() {
return result;
}
}
The BusInfo class is :
public class BusInfo {
private static final Bus BUS = new Bus();
public static Bus getInstance() {
return BUS;
}
}
Do not forget to unregister the bus onDestroy of the activity: BusInfo.getInstance().unregister(ActivityName.this);
If you aslso want to prevent the progress dialogue from always showing because sometimes it is showing twice due to a double click on button add this : if(pd!=null&&pd.isShowing()){
Log.v("pd is showing","showing");
} else {pd= ProgressDialgue.show...}
I am using PullToRefresh ListView from chrisbanes which I found here.
I implemented it successfully, thanks to its documentations. :)
However, I am stuck at this one point now. I am using volley to get the data from the server. It works perfectly till I added a check to see if theres no more data then simply Toast the user.
I did like below,
#Override
public void onRefresh(
PullToRefreshBase<ListView> refreshView) {
if (hasMoreData()){
//Call service when pulled to refresh
orderService();
} else{
// Call onRefreshComplete when the list has been refreshed.
toastShort("No more data to load");
orderListAdapter.notifyDataSetChanged();
mPullRefreshListView.onRefreshComplete();
}
}
The toast comes up, but I also continue seeing the Loading... message below my ListView. I thought onRefreshComplete(); should take care of it but it didn't.
How do I do this? Please help.
After banging my head for almost 3hours I was able to solve this. It was quite simple tough.
What I did was created a Handler and a Runnable which calls mPullRefreshListView.onRefreshComplete(); and checked after some time that if mPullRefreshListView was still refreshing then call the method again which closes it on the next call. :)
Code goes like this..
#Override
public void onRefresh(PullToRefreshBase<ListView> refreshView) {
if (hasMoreData()) {
// Call service when pulled to refresh
toastShort("Last");
orderService();
} else {
// Call onRefreshComplete when the list has been
// refreshed.
toastShort("No more data to load");
upDatePull(); //this method does the trick
}
}
private void upDatePull() {
// lvOrders.setAdapter(null);
handler = new Handler();
handler.postDelayed(runnable, 1000);
}
Runnable runnable = new Runnable() {
#Override
public void run() {
mPullRefreshListView.onRefreshComplete();
if (mPullRefreshListView.isRefreshing()) {
Logger.d("xxx", "trying to hide refresh");
handler.postDelayed(this, 1000);
}
}
};
Credits to this link.
you should use onRefreshComplete(); in a separate thread like:
#Override
public void onRefresh(PullToRefreshBase<ListView> refreshView) {
if (hasMoreData()){
//Call service when pulled to refresh
orderService();
} else{
toastShort("No more data to load");
orderListAdapter.notifyDataSetChanged();
}
new GetDataTask(refreshView).execute();
}
public class GetDataTask extends AsyncTask<Void, Void, Void> {
PullToRefreshBase<?> mRefreshedView;
public GetDataTask(PullToRefreshBase<?> refreshedView) {
mRefreshedView = refreshedView;
}
#Override
protected Void doInBackground(Void... params) {
// Do whatever You want here
return null;
}
#Override
protected void onPostExecute(Void result) {
mRefreshedView.onRefreshComplete();
super.onPostExecute(result);
}
}
I have a main activity with three fragments. In the first fragment is a listview. I populate it in the fragment's onCreateView method like this:
private ArrayList<MobileNETDistinctChatInfo> m_parts = new ArrayList<MobileNETDistinctChatInfo>();
public MobileNETDistinctChatInfoAdapter m_adapter;
public ListView list;
public String logged_user;
onCreateView(){
LinearLayout view = (LinearLayout) inflater.inflate(R.layout.tab1, container, false);
list = (ListView)view.findViewById(R.id.chats_list)
m_parts = db.MESSAGES_getAllDistinctChatInfo(logged_user);
// adapter extends the ArrayAdapter
m_adapter = new MobileNETDistinctChatInfoAdapter(getActivity(), R.layout.chatlist_list_item, m_parts);
list.setAdapter(m_adapter);
return view;
}
I would like to refresh the listview in the onResume() method in fragment1, but I can't get it to work. I tried these two methods (If I use the first method, nothing happens. If I use the second, the app crashes, returning a NullPointerException):
# 1
public void onResume() {
super.onResume();
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
m_adapter.notifyDataSetChanged();
}
}
}
# 2
public void onResume() {
super.onResume();
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
m_parts.clear();
m_parts = db.MESSAGES_getAllDistinctChatInfo(logged_user);
ListView list = (ListView) getActivity().findViewById(R.id.chats_list);
MobileNETDistinctChatInfoAdapter caa = (MobileNETDistinctChatInfoAdapter) list.getAdapter();
caa.clear();
for(MobileNETDistinctChatInfo el : m_parts){
caa.add(el);
}
list.setAdapter(caa);
}
}
}
I've printed the size of m_parts and m_adapter in OnResume() and it seems the adapter isn't being refreshed, but m_parts is. Does anybody know why, or how I can solve this?
You are running a m_adapter.notifyDataSetChanged() in a separate thread which leads the execution of refreshing the list before even the list is modified or updated actually.
if you debeg your code then you can see that it is working fine because the thread got enough time to get executed.
I have a requirement where i need to parse the content of a URL in JSON format. I am able to do that successfully. But i need to save the contents of the URL in a array list and pass them back to the calling functions. Below is the code snippet of what i am trying to achieve.
#Override
protected ArrayList<String> onPostExecute(String result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
dialog.dismiss();
return ar; // ar is the arraylist i have created and updated it with the content of the url.
}
But running this gives an error. Can anyone please suggest how i can make this possible. However, when i make the return type of onPostExecute as void and toast the contents, its displaying properly. When i call this after the execute, its returning null even though i have updated the contents in doinbackground(). Hence i am unable to get the return values on arraylist format.
// Calling function
Myadapter.execute();
ArrayList<string> str = new ArrayList<string>();
str = print();
// Here str is getting null
// Called function
public ArrayList<String> print() {
ArrayList<String> names = new ArrayList<String>();
for(int i=0;i<al.size();i++)
{
names.add(al.get(i).getConstituencyName());
}
return names;
}
Use a handler
In your activity
mHandler = new Handler() {
#Override public void handleMessage(Message msg) {
ArrayList s=(ArrayList)msg.obj;
tv.setText("Result = "+s.get(0));
}
};
In your onPostexecute
Message msg=new Message();
msg.obj=ar;
mHandler.sendMessage(msg);
The proper way would be to let your activity implement an interface, and when you instantiate the AsyncTask pass the current activity as a parameter to the constructor. Then in onPostExecute() invoke the callback method defined in the Activity and pass the json result as an argument.
Something like this:
interface OnTaskFinished {
void onTaskFinished(String result);
}
public class MainActivity extends Activity implements OnTaskFinished {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ....
new MyAsyncTask(this).execute();
}
#Override
public void onTaskFinished(String result) {
// Process the json result here how you need.
}
}
And this is how the scheleton of your AsyncTask should look like:
private class MyAsyncTask extends AsyncTask<Void, Void, String> {
private final OnTaskFinished listener;
public MyAsyncTask(OnTaskFinished listener) {
this.listener = listener;
}
// ...
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
listener.onTaskFinished(result);
}
}
I am calling a webservice through asynctask, to call the webservice i am calling one method named makeRequest() in doInBackground(), I am getting the response in another methods success(), In success method i am updating the listview
But i am getting error like
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views."
Here Im adding my code.Im calling synctask from activity
new MyTask(this,urlAsString,sp).execute();
here is the Asynctask class
class MyTask extends AsyncTask<Void, Void, Void> {
ProgressDialog progress;
String url;
SharedPreferences sp;
HomepageH2desk c;
public MyTask(HomepageH2desk context,String url,SharedPreferences sp) {
this.c = context;
this.url = url;
this.sp = sp;
progress = new ProgressDialog(this.c);
progress.setMessage("Loading...");
}
public void onPreExecute() {
progress.show();
}
public Void doInBackground(Void... unused) {
c.getTickets(url,sp);
return null;
//progress.setMessage("Loading...");
}
public void onPostExecute(Void unused) {
progress.dismiss();
}
}
Here im getting webservice response
public void success(Object result) {
list = (ArrayList<Map<String, String>>) result;
this.adapter.setList(list);
this.adapter.notifyDataSetChanged();
}
listview is not getting updated and showing error
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Help me to solve this problem...
You should update your List like this.
Activity_name.this.runOnUiThread(new Runnable() {
#Override
public void run() {
list = (ArrayList<Map<String, String>>) result;
this.adapter.setList(list);
this.adapter.notifyDataSetChanged();
}
});
Since this the error that comes up when you do some MainThread task in another thread.....
try This:
runOnUiThread(new Runnable(){
public void run(){
this.adapter.setList(list);
this.adapter.notifyDataSetChanged();
}
});
This code might have some errors But in simple Words.. add the notifyDataSetChanged call into runOnUiThread() method. You will be Done..
OR this can also be DOne ( the perfect way )
add the following in your activity class
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
this.adapter.setList(list);
adapter.setnotifyDataSetChanged();
}
};
Call this handler when and where you want to call the notifydatasetchanged like this
handler.sendEmptyMessage(0);
Thanks
sHaH
call the success method on onPostExecute method