Make volley http request during app installation - android

I want to make a volley http request only once and it should be during the time the app is installed.
I achieved this by making the http request in onCreate() method of SQLiteOpenHelper class which fetch data from remote MySQL ready for use. The problem I however runs into is that, after the app installation finishes, the app is presented with blank screen(fragment hosted on the main Activity). But when I close the app and opens for the second time, it is able to fetch data from the SQLite onto the screen.
Is there something special I have to do in the onCreate() method to ensure that the app runs only after the volley request finishes?
Here is my code.
SQLiteOpenHelper onCreate()
#Override
public void onCreate(final SQLiteDatabase db) {
db.execSQL(CREATE_NOTICE_TABLE);
db.execSQL(CREATE_ROSTER_TABLE);
/*Perform One time sync operations from remote MySQL*/
requestQueue = Volley.newRequestQueue(ContextGetter.getAppContext());
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, URL, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
if(response == null || response.length() == 0){
return;
}
if(response.has("notices")){
//Save to notices table
try {
JSONArray notices = response.getJSONArray("notices");
for (int i = 0; i < notices.length(); i++) {
JSONObject noticeObject = notices.getJSONObject(i);
String noticeID = noticeObject.getString(NOTICE_ID_KEY);
String noticeTitle = noticeObject.getString(NOTICE_TITLE_KEY);
String noticeBody = noticeObject.getString(NOTICE_BODY_KEY);
String dateCreated = noticeObject.getString(NOTICE_DATE_KEY);
NoticeItem noticeItem = new NoticeItem();
noticeItem.setId(Integer.parseInt(noticeID));
noticeItem.setTitle(noticeTitle);
noticeItem.setBody(noticeBody);
try {
noticeItem.setDate(formatDate(dateCreated));
} catch (ParseException e) {
e.printStackTrace();
}
//Save to SQLite
createNoticeBoard(noticeItem, db);
}
} catch (JSONException e) {
Log.d(TAG, "JSONException: " + e.getMessage());
}
}
//If roster available
if(response.has("rosters")){
//Save to roster table
try {
JSONArray rosters = response.getJSONArray("rosters");
for (int i = 0; i <rosters.length() ; i++) {
JSONObject rosterObject = rosters.getJSONObject(i);
String rosterID = rosterObject.getString(ROSTER_ID_KEY);
String rosterOwner = rosterObject.getString(ROSTER_OWNER_KEY);
String rosterDate = rosterObject.getString(ROSTER_DATE_KEY);
String rosterShift = rosterObject.getString(ROSTER_SHIFT_KEY);
//Check to verify that the user actually owns that roster later by using shared preference
RosterItem rosterItem = new RosterItem();
rosterItem.setSyncNumber(Integer.parseInt(rosterID));
rosterItem.setStaffNumber(rosterOwner);
rosterItem.setShift(rosterShift);
try {
rosterItem.setDate(formatDate(rosterDate));
} catch (ParseException e) {
e.printStackTrace();
}
createRoster(rosterItem, db);
}
}catch(JSONException e){
Log.d(TAG, "JSONException: "+ e.getMessage());
}
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "VolleyError "+error.getMessage());
}
});
//Add to requestQueue
requestQueue.add(request);
}
Fragment class
public class NoticeListFragment extends Fragment{
private static final String TAG = "NoticeListFragment";
private RecyclerView recyclerView;
private NoticeListAdapter mNoticeListAdapter;
public NoticeListFragment() {
//Requires empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "Notices onCreate() called");
}
#Override
public void onResume() {
super.onResume();
updateUI(); //In case data changes
Log.d(TAG, "onResume() called");
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
//Inflate layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_notice_list, container, false);
recyclerView = (RecyclerView) rootView.findViewById(R.id.rv_recycler_view);
recyclerView.setHasFixedSize(true);
LinearLayoutManager linearManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(linearManager);
updateUI();
return rootView;
}
/*View Holder*/
private class NoticeViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private NoticeItem mNoticeItem;
public CardView mCardView;
public TextView mTextViewTitle;
public TextView mTextViewDate;
public TextView mTextViewBody;
public NoticeViewHolder(View itemView) {
super(itemView);
mCardView = (CardView) itemView.findViewById(R.id.card_view);
mTextViewBody = (TextView) itemView.findViewById(R.id.tv_notice_summary);
mTextViewTitle = (TextView) itemView.findViewById(R.id.tv_notice_title);
mTextViewDate = (TextView) itemView.findViewById(R.id.tv_notice_date);
itemView.setOnClickListener(this);
}
//Bind properties to views
private void bindNotice(NoticeItem noticeItem){
mNoticeItem = noticeItem;
mTextViewTitle.setText(noticeItem.getTitle());
mTextViewDate.setText(noticeItem.getDate());
mTextViewBody.setText(noticeItem.getSummary());
}
#Override
public void onClick(View view) {
Intent intent = NoticePagerActivity.newIntent(getActivity(), mNoticeItem.getId());
startActivity(intent);
}
}
/*Adapter*/
private class NoticeListAdapter extends RecyclerView.Adapter<NoticeViewHolder>{
//private Context mContext;
private List<NoticeItem> listItems;
//Provide a suitable constructor (depends on the kind of dataset you have)
public NoticeListAdapter(List<NoticeItem> data) {
//this.mContext = context;
this.listItems = data;
}
#Override
public NoticeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//Create a new view
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.notice_lists_card, parent, false);
//Set the view size, margin, padding and layout parameters
NoticeViewHolder vh = new NoticeViewHolder(view);
return vh;
}
#Override
public void onBindViewHolder(NoticeViewHolder holder, int position){
final NoticeItem noticeItem = listItems.get(position);
//Bind data properties to views here...
holder.bindNotice(noticeItem);
}
#Override
public int getItemCount() {
return listItems.size();
}
public void setNotices(List<NoticeItem> notices){
listItems = notices;
}
}
//Bind adapter to recycler view
private void updateUI(){
NoticeLab noticeLab = NoticeLab.get(getActivity());
List<NoticeItem> notices = noticeLab.getNotices();
if(mNoticeListAdapter == null){
mNoticeListAdapter = new NoticeListAdapter(notices);
recyclerView.setAdapter(mNoticeListAdapter);
}else{
mNoticeListAdapter.setNotices(notices);
mNoticeListAdapter.notifyDataSetChanged();
}
}
}

I want to make a volley http request only once and it should be during the time the app is installed.
You do not get control when your app is installed.
Is there something special I have to do in the onCreate() method to ensure that the app runs only after the volley request finishes?
Volley is asynchronous. That is the complete and entire point behind using Volley. Immediately after you call requestQueue.add(request);, your onCreate() method continues executing, while Volley performs the network I/O on a background thread.
Some options are:
Get rid of all the Volley code, by packaging your starter data in the APK as an asset and using SQLiteAssetHelper to deploy the packaged database on first run of your app.
Do not use Volley. Instead, use something with a synchronous network I/O option (HttpURLConnection, OkHttp, etc.), and perform synchronous network I/O here. You should always be using your SQLiteOpenHelper subclass on a background thread, in case the database needs to be created or updated. So your onCreate() method of your SQLiteOpenHelper should always be called on a background thread, and you would not need yet another background thread for the network I/O. Then, you can be sure that by the time onCreate() ends that your starter data is there... except if you do not have Internet connectivity, or your server is down, etc.
Move all your initialization logic to something else, such as an IntentService. Have it create the database (using the IntentService's own background thread) and have it do the network I/O (again, using a synchronous API, since IntentService has its own background thread). Only start your UI once the IntentService is done with its work. You are in better position here to deal with connectivity errors via some sort of retry policy, while presenting some temporary UI to the user while that work is going on (e.g., ProgressBar).

Related

Accessing asynchronous activity state in Espresso

I want to know how many list items are used in a RecyclerView so I can iterate over the list items and click on them with Espresso. The problem is I'm using Volley asynchronously to get the JSON data I need for the underlying data set of my adapter. I've used an idling resource counter in my UI test to defer the execution of the test until the counter is zero. However, calling the getItemCount on my adapter still results in zero, despite the fact that my networking operation should resolve before the test executes.
UI test code
#Test
public void foo(){
// Register a idling resource counter for Volley to get JSON data asynchronously
Espresso.registerIdlingResources(mActivityTestRule.getActivity().idlingCounter);
RecyclerView v
= (RecyclerView)
mActivityTestRule.getActivity().findViewById(R.id.rv_recycler_view);
int count = v.getAdapter().getItemCount();
// Prints zero; why?
System.out.println("adapter count = "+String.valueOf(count));
}
Here is my onCreate method. The adapter list is initialized as empty, but is populated in onReponse when Volley finishes networking. However, Espresso doesn't seem to wait for onReponse to be called. Not sure why, as I believe I'm using the idling resource counter correctly.
RecyclerView mRecyclerView;
public Adapter mAdapter;
public CountingIdlingResource idlingCounter = new CountingIdlingResource("DATA_LOADER");
#Override
public void onCreate(Bundle savedInstaceState){
// ... some code
// Init an empty array list
ArrayList<MyObject> list = new ArrayList<>();
// Init the adapter with the list, and set that adapter to the view
mRecyclerAdapter = new Adapter(List, MainActivity.this);
mRecyclerView.setAdapter(mRecyclerAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
// Add JSON data to list; increment idlingCounter resource counter.
// decrement idlingCounter in onResponse
idlingCounter.increment();
fetchJson();
}
Edit
MainActivity class
public class MainActivity extends AppCompatActivity {
// Debugging
private static final String TAG = MainActivity.class.getSimpleName();
// Testing
public CountingIdlingResource idlingCounter = new CountingIdlingResource("DATA_LOADER");
// Networking
private RequestQueue requestQueue;
// Data
private ArrayList<Recipe> mRecipes = new ArrayList<>();
// UI
public Adapter mRecyclerAdapter;
#BindView(R.id.rv_recycler_view) RecyclerView mRecyclerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
getSupportActionBar().setTitle("Recipes");
// Perform networking if there is no saved instance state
if(null == savedInstanceState){
requestQueue = Volley.newRequestQueue(this);
// TODO calling idlingCounter.increment()
idlingCounter.increment();
fetch(requestQueue); // Initializes the recycler view adapter when done fetching
} else {
Log.v(TAG,"null != savedInstanceState");
mRecipes = savedInstanceState.getParcelableArrayList(Constants.KEY_RECIPES);
mRecyclerAdapter = new Adapter(mRecipes, this);
mRecyclerView.setAdapter(mRecyclerAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putParcelableArrayList(Constants.KEY_RECIPES,mRecipes);
super.onSaveInstanceState(outState);
}
// Fetch json and build recipe-object array-list
private void fetch(RequestQueue requestQueue) {
// Define the request
JsonArrayRequest request = new JsonArrayRequest(Constants.JsonURL,
new Response.Listener<JSONArray>() {
// Handles JSON response data
#Override
public void onResponse(JSONArray jsonArray) {
Log.v(TAG,"onResponse");
for (int i = 0; i < jsonArray.length(); i++) {
try {
JSONObject jsonObject = jsonArray.getJSONObject(i);
Recipe recipe = new Recipe(jsonObject);
//recipe.mSteps.remove(0);
mRecipes.add(recipe);
Log.v(TAG,jsonObject.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
// Initialize adapter when JSON data is ready
// TODO initializing the adapter withe the data set
mRecyclerAdapter = new Adapter(mRecipes, MainActivity.this);
mRecyclerView.setAdapter(mRecyclerAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
// Test if this activity was opened from the widget using an intent
Intent intent = getIntent();
if(intent.getExtras() != null){
// Start TwoPaneActivity passing it the intent extra
int position = intent.getIntExtra(WidgetProvider.INTENT_KEY_RECIPE_IDX,-1);
intent = new Intent(MainActivity.this,TwoPaneActivity.class);
Recipe recipe = mRecipes.get(position);
intent.putExtra(Constants.KEY_SINGLE_RECIPE,recipe);
startActivity(intent);
}
// TODO calling idlingCounter.decrement()
idlingCounter.decrement();
}
},
// Error listener object
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
Toast.makeText(MainActivity.this, "Unable to fetch data: "
+ volleyError.getMessage(), Toast.LENGTH_SHORT).show();
}
});
// Queue the request
requestQueue.add(request);
}
public void foo(){
}
}

AsyncTaskLoader not calling onFinishedLoad after orientation change

Some background information:
I am using a Activity>ParentFragment(Holds ViewPager)>Child fragments.
Child Fragments are added dynamically with add, remove buttons.
I am using MVP architecture
Actual Problem:
In child fragment, we have listview that populates using an asynctaskloader via a presenter.
Child Fragment:
//Initialize Views
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
root = inflater.inflate(R.layout.fragment_search_view_child, container, false);
.......
mSearchViewPresenter= new SearchViewPresenter(
getActivity(),
new GoogleSuggestLoader(getContext()),
getActivity().getLoaderManager(),
this, id
);
SearchList list=new SearchList();
//requestList from presenter
searchListAdapter =new SearchViewListAdapter(getActivity(), list, this);
listView.setAdapter(searchListAdapter);
......
return root;
}
#Override
public void onResume(){
super.onResume();
mSearchViewPresenter.start();
searchBar.addTextChangedListener(textWatcher);
}
In the presenter class we have:
public SearchViewPresenter(#NonNull Context context, #NonNull GoogleSuggestLoader googleloader,#NonNull LoaderManager loaderManager,
#NonNull SearchViewContract.View tasksView, #NonNull String id) {
// mLoader = checkNotNull(loader, "loader cannot be null!");
mLoaderManager = checkNotNull(loaderManager, "loader manager cannot be null");
// mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null");
mSearchView = checkNotNull(tasksView, "tasksView cannot be null!");
mSearchView.setPresenter(this);
searchList=new SearchList();
this.googleLoader=googleloader;
this.context=context;
this.id=loaderID;
// this.id=Integer.parseInt(id);
}
#Override
public void start() {
Log.d("start>initloader","log");
mLoaderManager.restartLoader(1, null, this);
}
//TODO implement these when you are ready to use loader to cache local browsing history
#Override
public android.content.Loader<List<String>> onCreateLoader(int i, Bundle bundle) {
int loaderid=googleLoader.getId();
Log.d("Loader: ", "created");
googleLoader=new GoogleSuggestLoader(context);
googleLoader.setUrl("");
googleLoader.setUrl(mSearchView.provideTextQuery());
return googleLoader;
}
#Override
public void onLoadFinished(android.content.Loader<List<String>> loader, List<String> data) {
Log.d("Loader: ", "loadFinished");
searchList.clear();
for (int i = 0; i < data.size(); ++i) {
searchList.addListItem(data.get(i), null, LIST_TYPE_SEARCH, android.R.drawable.btn_plus);
Log.d("data Entry: ",i+ " is: "+searchList.getText(i));
}
mSearchView.updateSearchList(searchList);
}
#Override
public void onLoaderReset(android.content.Loader<List<String>> loader) {
}
Also we have this code in the presenter that is triggered by a edittext box on the fragment view being edited.
#Override
public void notifyTextEntry() {
//DETERMINE HOW TO GIVE LIST HERE
// Dummy List
Log.d("notifyTextEntry","log");
if(googleLoader==null)googleLoader=new GoogleSuggestLoader(context);
googleLoader.setUrl(mSearchView.provideTextQuery());
// mLoaderManager.getLoader(id).abandon();
mLoaderManager.getLoader(1).forceLoad();
mLoaderManager.getLoader(1).onContentChanged();
Log.d("length ", searchList.length().toString());
// googleLoader.onContentChanged();
}
Lastly we have the loader here:
public class GoogleSuggestLoader extends AsyncTaskLoader<List<String>>{
/** Query URL */
private String mUrl;
private static final String BASE_URL="https://suggestqueries.google.com/complete/search?client=firefox&oe=utf-8&q=";
private List<String> suggestions =new ArrayList<>();
public GoogleSuggestLoader(Context context) {
super(context);
this.mUrl=BASE_URL;
}
public void setUrl(String mUrl){
this.mUrl=BASE_URL+mUrl;
};
#Override
protected void onStartLoading() {forceLoad(); }
#Override
public List<String> loadInBackground() {
if (mUrl == null) {
return null;
}
try {
suggestions = new ArrayList<>();
Log.d("notifyinsideLoader","log");
String result=GoogleSuggestParser.parseTemp(mUrl);
if(result!=null) {
JSONArray json = new JSONArray(result);
if (json != null) {
JSONArray inner=new JSONArray((json.getString(1)));
if(inner!=null){
for (int i = 0; i < inner.length(); ++i) {
//only show 3 results
if(i==3)break;
Log.d("notifyinsideLoader",inner.getString(i));
suggestions.add(inner.getString(i));
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return suggestions;
}
}
So the problem:
The code loads the data fine to the listview on the fragment. When orientation changes loader is not calling onLoadFinished. I have tested the loader and it is processing the data fine.
I have already tried forceload and onContentChanged in the presenter to no avail.
If you need anymore info or if I should just use something else like RxJava let me know. But I would really like to get this working.
Before you ask I have seen similar problems like: AsyncTaskLoader: onLoadFinished not called after orientation change however I am using the same id so this problem should not exist.
The answer was on this page AsyncTaskLoader doesn't call onLoadFinished
but details were not given as to how to move to this.
So let me explain here for anyone else with this problem in future.
Support library is meant for fragments. So the class that is in charge of callbacks has to be importing AND implementing the correct methods from the support library. Same as if you are using MVP your presenter must extend from support loadermanager.
i.e: import android.support.v4.app.LoaderManager; Then implement correct callbacks.
Like
#Override
public android.support.v4.content.Loader<List<String>> onCreateLoader(int i, Bundle bundle) {
...
return new loader
}
and
#Override
public void onLoadFinished(android.support.v4.content.Loader<List<String>> loader, List<String> data) {
//do something here to your UI with data
}
Secondly: The loader itself must be extending from support asynctaskloader.
i.e: import android.support.v4.content.AsyncTaskLoader;

How to update a ListView inside a message listener?

I'm building a chat application, so I'm using two ListViews: one that shows the online friends and one for the chat itself, that receives the messages and so on. I'm using the XMPP protocol and the Smack Library for Android.
The Smack Library give me Listeners which are activated every time a friend status changes(online/offline) and the other one when the user receives a message. Here's how I declare the adapter and call an AsyncTask when the user press a button:
peopleList = (ListView) findViewById(R.id.peopleList);
adapter = new MyAdapter(this, people);
peopleList.setAdapter(adapter);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new ConnectAndLoad(MainActivity.this).execute();
}
});
Inside the AsyncTask, I connect to the server inside the doInBackground method and inside the onPostExecute I create the listener which adds the user to the array list of the listview and call adapter.notifyDataSetChanged();
public class ConnectAndLoad extends AsyncTask<String, Integer, Boolean> {
private ProgressDialog dialog;
public ConnectAndLoad(Activity activity)
{
this.dialog = new ProgressDialog(activity);
this.dialog.setTitle("Loading..");
this.dialog.setMessage("Connecting to the server..");
dialog.show();
}
#Override
protected Boolean doInBackground(String... arg0) {
MyConnectionManager.getInstance().setConnectionConfiguration(getApplicationContext());
MyConnectionManager.getInstance().connect();
MyConnectionManager.getInstance().login();
return true;
}
protected void onPostExecute(Boolean boo)
{
MyConnectionManager.getInstance().bored();
Roster roster = Roster.getInstanceFor(MyConnectionManager.getInstance().getConnection());
try
{
if (!roster.isLoaded()) roster.reloadAndWait();
}
catch (Exception e)
{
Log.e(TAG, "reload");
}
roster.addRosterListener(new RosterListener() {
public void entriesDeleted(Collection<String> addresses) {
}
public void entriesUpdated(Collection<String> addresses) {
}
public void entriesAdded(Collection<String> addresses) {
}
#Override
public void presenceChanged(Presence presence) {
people.add(new People(presence.getFrom(), presence.getStatus()));
adapter.notifyDataSetChanged();
}
});
dialog.dismiss();
}
}
And below is my Custom Adapter:
public class PeopleAdapter extends ArrayAdapter<People> {
private ArrayList<People> events_list = new ArrayList<>();
Context context;
public PeopleAdapter(Context context, ArrayList<People> users) {
super(context, 0, users);
this.context = context;
this.events_list = users;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
People user = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.people_list, parent, false);
}
TextView tvName = (TextView) convertView.findViewById(R.id.name);
TextView tvStatus = (TextView) convertView.findViewById(R.id.status);
tvName.setText(user.name);
tvStatus.setText(user.status);
convertView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, "You Clicked " + events_list.get(position).name, Toast.LENGTH_SHORT).show();
Intent i = new Intent(context, ConversationActivity.class);
i.putExtra("user", events_list.get(position).name);
context.startActivity(i);
}
});
return convertView;
}
}
I mean what I want to do I think it's a simple thing, every single chat app does it, is basically update the list view automatically but I'm having two problems:
The listview ONLY updates after I click on it. So it basically works
but I have to click on the listview..
I receive this error every time the list view updates (the app keeps working though):
Exception in packet listener: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
I can give you a simple solution. Make local Activity variable in the ConnectAndLoad class
private Activity activity;
public ConnectAndLoad(Activity activity)
{
...
activity.activity= activity;
}
Instead on directly calling adapter.notifyDataSetChanged(); use
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
}
});
It seems like presenceChanged() called in another thread. But be careful and make sure you delete RosterListener when activity gets destroyed or it can lead to the memory leaks i.e activity is already destroyed but you keep getting notifications about presence change.

java.lang.IllegalStateException:Make sure the content of your adapter is not modified from a background thread, but only from the UI thread

java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(2131296513, class xyz.ScrollDetectableListView) with Adapter(class android.widget.HeaderViewListAdapter)]
I am getting above exception sometimes while scrolling through the dynamic listview and then clicking on item.I researched a lot but unable to find the exact reason that why i am getting this error sometimes and how it can be resolved?
private ScrollDetectableListView mFListView;
public FAdapter mFAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_feed_view, container, false);
View headerView = getActivity().getLayoutInflater().inflate(R.layout.view_feed_header, null);
try{
mFListView = (ScrollDetectableListView) rootView.findViewById(R.id.feed_list_view);
mFContainer = (SwipeRefreshLayout) rootView.findViewById(R.id.feed_container);
mFListView.addHeaderView(headerView);
mFListView.setEmptyView(rootView.findViewById(R.id.empty_view));
mFContainer.setColorSchemeResources(R.color.green, R.color.pink, R.color.fbcolor,
R.color.instagramcolor, R.color.googlecolor, R.color.flickrcolor);
mFView = getActivity().getLayoutInflater().inflate(R.layout.view_footer, null);
ImageView rotateImageView = (ImageView) mFooterView.findViewById(R.id.spinner);
Animation rotation = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate);
rotation.setFillAfter(false);
rotateImageView.startAnimation(rotation);
mFContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh()
{
initializeFListView();
}
});
initializeFListView();
mProgressDialog.setVisibility(View.VISIBLE);
mHActivity.setDataChangedListener(new DataChangedListener() {
#Override
public void onDataChanged() {
mFContainer.setRefreshing(true);
mProgressDialog.setVisibility(View.GONE);
initializeFListView();
}
});
}catch(Exception e){}
return rootView;
}
public void initializeFListView()
{
FApi.getTrending(getActivity(), xyz, new APIResponseListener() {
#Override
public void onResponse(Object response) {
setFeedAdapter((List<Video>) response);
}
#Override
public void onError(VolleyError error) {
if (error instanceof NoConnectionError) {
String errormsg = getResources().getString(R.string.no_internet_error_msg);
Toast.makeText(getActivity(), errormsg, Toast.LENGTH_LONG).show();
}
}
});
}
private void setFAdapter(List<Video> response)
{try {
List<Video> videos = response;
mFAdapter = new FAdapter(getActivity(), videos, mProfileClickListener, mCommentClickListener);
mFListView.setOnScrollListener(new EndlessScrollListenerFeedView(getActivity(), mFListView, mFView, mFAdapter, videos, mFType, ""));
mFListView.setAdapter(mFAdapter);
mProgressDialog.setVisibility(View.GONE);
if (mFContainer.isRefreshing()) {
mFContainer.setRefreshing(false);
}
if (mFAdapter.getCount() < mCount) {
mFView.setVisibility(View.GONE);
mFListView.removeFooterView(mFooterView);
}
}catch(Exception e){}
}
}
My suggestion try to set ur list adapter on UI Thread,,,
private void setFAdapter(List<Video> response)
{
try {
List<Video> videos = response;
mFAdapter = new FAdapter(getActivity(), videos, mProfileClickListener, mCommentClickListener);
mFListView.setOnScrollListener(new EndlessScrollListenerFeedView(getActivity(), mFListView, mFView, mFAdapter, videos, mFType, ""));
runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
mFListView.setAdapter(mFAdapter);
}
});
mProgressDialog.setVisibility(View.GONE);
if (mFContainer.isRefreshing()) {
mFContainer.setRefreshing(false);
}
if (mFAdapter.getCount() < mCount) {
mFView.setVisibility(View.GONE);
mFListView.removeFooterView(mFooterView);
}
}catch(Exception e){}
}
}
Keep one singleton class object in hand. So that you can synchronize two thread on it. Care to be taken to not to block the ui thread.
Reduce number of interfaces to only one method to start preparing data for your list and only one method to call your notifydatasetchanged/setAdapter on list.
Means there should be only one method like prepareData() which will be executed by a background thread. synchronise this method on your singleton object.
MyListAdaper adapter = null;
// Call this from a background thread
public void prepareData() {
synchronized (SingleTonProvider.getInstance()) {
List<AnyDataTypeYouWant> data = null;
// populate data here by your application logic.
adapter = new MyListAdaper(data);
}
}
And have only one method to refresh list.
// Also Call this from a background thread only
public void refreshList() {
synchronized (SingleTonProvider.getInstance()) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mFListView.setAdapter(adapter);
}
});
}
}
have no other code on any place to prepare data and set data on list.
Call the methods I mentioned from a background thread only.
I just gave general solution to your problem. You have to work on your specific case by yourself.

Endless adapter concept for view pager

Can any one please explain how to make endless adapter concept for view pager
I am currently using view pager to see my datas. On every 10th swipe of the view pager I need to hit the server and take dynamic response and need to update the viewpager. Obviously we need to use the endless adapter concept. But I was confused with the exact concept. Anyone please do the needful...
Thanks in advance...
I’ve implemented an endless ViewPager. I think it suits you needs. The request is simulated with a time delay ​​in the AsyncTask thread.
//ViewPagerActivity
public class ViewPagerActivity extends FragmentActivity {
private ViewPager vp_endless;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_endless_view_pager);
vp_endless = (ViewPager) findViewById(R.id.vp_endless);
vp_endless.setAdapter(new FragmentViewPagerAdapter(getSupportFragmentManager()));
}
}
//FragmentViewPagerAdapter
public class FragmentViewPagerAdapter extends FragmentStatePagerAdapter {
private List<CustomObject> _customObjects;
private volatile boolean isRequesting;
private static final int ITEMS_PER_REQUEST = 10;
public FragmentViewPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
_customObjects = HandlerCustomObject.INSTANCE._customObjects;
}
#Override
public Fragment getItem(int position) {
CustomFragment fragment = new CustomFragment();
fragment.setPositionInViewPager(position);
if (position == _customObjects.size() && !isRequesting)
new AsyncRequestItems().execute("www.test.com");
return fragment;
}
#Override
public int getCount() {
return Integer.MAX_VALUE;
}
public class AsyncRequestItems extends AsyncTask<String, Void, Void> {
#Override
protected Void doInBackground(String... urls) {
isRequesting = true;
//Fake request lag
try {Thread.sleep(2500);}
catch (InterruptedException e) {e.printStackTrace();}
for (int i = 0; i < ITEMS_PER_REQUEST; i++) {
_customObjects.add(new CustomObject());
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
isRequesting = false;
}
}
}
//CustomFragment
public class CustomFragment extends Fragment {
private CustomObject _customObject;
private TextView tv_position;
private ProgressBar pb_loading;
private View root;
private int _positionInViewPager;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
root = inflater.inflate(R.layout.frament_endless_view_pager, container, false);
pb_loading = (ProgressBar) root.findViewById(R.id.pb_loading);
tv_position = (TextView) root.findViewById(R.id.tv_position);
_customObject = retrieveDataSafety();
if(_customObject != null) bindData();
else createCountDownToListenerForUpdates();
return root;
}
public void createCountDownToListenerForUpdates() {
new CountDownTimer(10000, 250) {
public void onTick(long millisUntilFinished) {
_customObject = retrieveDataSafety();
if(_customObject != null) {
bindData();
cancel();
}
}
public void onFinish() {}
}.start();
}
private CustomObject retrieveDataSafety() {
List<CustomObject> customObjects = HandlerCustomObject.INSTANCE._customObjects;
if(customObjects.size() > _positionInViewPager)
return customObjects.get(_positionInViewPager);
else
return null;
}
private void bindData() {
pb_loading.setVisibility(View.GONE);
String feedback = "Position: " + _positionInViewPager;
feedback += System.getProperty("line.separator");
feedback += "Created At: " + _customObject._createdAt;
tv_position.setText(feedback);
}
public void setPositionInViewPager(int positionAtViewPager) {
_positionInViewPager = positionAtViewPager;
}
}
//CustomObject
public class CustomObject {
public String _createdAt;
public CustomObject() {
DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
_createdAt = dateFormat.format(new Date());
}
}
//HandlerCustomObject
public enum HandlerCustomObject {
INSTANCE;
public List<CustomObject> _customObjects = new ArrayList<CustomObject>();
}
Well, let's start from the beginning.
If you would like to have 'endless' number of pages you need to use some trick. E.g. you can't store endless number of pages in memory. Probably Android will destroy PageView everytime, when it isn't visible. To avoid destroying and recreating those views all the time you can consider recycling mechanism, which are used e.g. ListView. Here you can check and analyse idea how to implement recycling mechanism for pager adapter.
Moreover to make your UI fluid, try to make request and download new data before user gets to X0th page (10, 20, 30, 40...). You can start downloading data e.g when user is at X5th (5, 15, 25...) page. Store data from requests to model (it could be e.g. sqlite db), and user proper data based on page number.
It's just a brief of solution, but it's interesting problem to solve as well;)
Edit
I've started looking for inspiration and just found standalone view recycler implemented by Jake Wharton and called Salvage. Maybe it will be good start to create solution for your problem.

Categories

Resources