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(){
}
}
Related
I am trying to make a layout with recyclerview something like the video. I made a recyclerview which update list after certain interval but the problem is after data update it scroll to top position automatically. I want to make something like the video. https://youtu.be/omcS-6LeKoo
I have tried with link from SO
RecyclerView scrolls to top position when change the adapter data RecyclerView notifyDataSetChanged scrolls to top position but unable to solve. below is my attempt
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerViewId);
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
// Toast.makeText(getApplicationContext(),"Updating",Toast.LENGTH_LONG).show();
listShow();
handler.postDelayed(this,1000);
}
},1000);
}
void listShow(){
retrofitApiCall = RetrofitInstance.getRetrofitInstance().create(RetrofitApiCall.class);
Call<ModelClass_JSONParse> getDetails = retrofitApiCall;
anime = ExtendedAnime.getAll();
getDetails.enqueue(new Callback<ModelClass_JSONParse>() {
#Override
public void onResponse(Call<ModelClass_JSONParse> call,
Response<ModelClass_JSONParse> response) {
Log.v("Res",response.toString());
getWithValues = new HashMap<>();
if (response.isSuccessful()){
list.add(mModelClass_adapter);
}
adapter = new Adapter(getApplicationContext(),list);
StaggeredGridLayoutManager layoutManager = new
StaggeredGridLayoutManager(1,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
}
#Override
public void onFailure(Call<ModelClass_JSONParse> call, Throwable t) {
Log.v("Res",call.toString());
}
});
}
These lines of code are causing the problem for you. You're setting a new adapter reference and linear layout manager reference every time of your API calling.
adapter = new Adapter(getApplicationContext(),list);
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(1,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
To Do your desired task you need to do following steps -
Just set your LayoutManager and adapter for the first time.
Make a setDataList method in your adapter class. And set your updated list to adapter list.
And then every time of calling API set that list to setDataList and call adapter.notifyDataSetChanged() method of your adapter class.
The above steps will solve your problem. Just give it a try.
The problem is probably because of you are setting new adapter reference in network callback method onResponse(). Try setting adapter in onCreate and then update dataset in callback.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerViewId);
recyclerView.setAdapter(yourAdapter);
}
In network callback,
#Override
public void onResponse(Call<ModelClass_JSONParse> call,
Response<ModelClass_JSONParse> response) {
Log.v("Res",response.toString());
getWithValues = new HashMap<>();
if (response.isSuccessful()){
adapter.setDataSet(newDataList) //not change adapter reference,only update data set
}
}
Implement setDataSet() method in your adapter to update list like below.
class YourAdapter extends RecyclerView.Adapter<>{
priavate List<> list = new ArrayList();
public void setDataSet(newList:List<>){
list.clear();
list.addAll(newList);
notifyDataSetChanged();
}
}
Don't use adapter.notifyDataSetChanged(); method because I think your main view must be wrap content so either set a fixed height like 150dp.
Try different methods like notifyItemChanged(), notifyItemRangeChanged(), notifyItemInserted()
You are setting adapter again and again when the response is changing, so you should change list and set adapter in onCreate.
Arraylist<ModelClass_adapter> list = new Arraylist<ModelClass_adapter>;
Adapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerViewId);
//set adapter here
adapter = new Adapter(getApplicationContext(),list);
StaggeredGridLayoutManager layoutManager = new
StaggeredGridLayoutManager(1,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
// Toast.makeText(getApplicationContext(),"Updating",Toast.LENGTH_LONG).show();
listShow();
handler.postDelayed(this,1000);
}
},1000);
}
void listShow(){
retrofitApiCall = RetrofitInstance.getRetrofitInstance().create(RetrofitApiCall.class);
Call<ModelClass_JSONParse> getDetails = retrofitApiCall;
anime = ExtendedAnime.getAll();
getDetails.enqueue(new Callback<ModelClass_JSONParse>() {
#Override
public void onResponse(Call<ModelClass_JSONParse> call,
Response<ModelClass_JSONParse> response) {
Log.v("Res",response.toString());
getWithValues = new HashMap<>();
if (response.isSuccessful()){
list.clear();
list.add(mModelClass_adapter);
}
adapter.notifyDataSetChanged();
}
}
#Override
public void onFailure(Call<CurrencyModelClass_JSONParse> call, Throwable t) {
Log.v("Res",call.toString());
}
});
}
You are setting a new adapter every time and a new layout manager response comes.
which may cause this type of problem. you need to set adapter and layout manager in onCreate. just update adapter list in response of the api.
according to this answer you need linear layout manager only.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerViewId);
list= ArrayList<>();
adapter = new Adapter(getApplicationContext(),list);
LinearLayoutManager linearLayoutManager = new
LinearLayoutManager(context, OrientationHelper.VERTICAL, false);
recycleView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
// Toast.makeText(getApplicationContext(),"Updating",Toast.LENGTH_LONG).show();
listShow();
handler.postDelayed(this,1000);
}
},1000);
}
void listShow(){
retrofitApiCall = RetrofitInstance.getRetrofitInstance().create(RetrofitApiCall.class);
Call<ModelClass_JSONParse> getDetails = retrofitApiCall;
anime = ExtendedAnime.getAll();
getDetails.enqueue(new Callback<ModelClass_JSONParse>() {
#Override
public void onResponse(Call<ModelClass_JSONParse> call,
Response<ModelClass_JSONParse> response) {
Log.v("Res",response.toString());
getWithValues = new HashMap<>();
if (response.isSuccessful()){
adapter.getList().add(mModelClass_adapter);
}
adapter.notifyDataSetChanged();
}
}
#Override
public void onFailure(Call<CurrencyModelClass_JSONParse> call, Throwable t) {
Log.v("Res",call.toString());
}
});
}
you can do by following way
First get the count of your current datalist
int position = datalist.size();
after adding data into datalist
call DataAdapter.notifyDataSetChanged();
then move cursor to position in recyclerview
recyclerView.scrollToPosition(position);
Happy coding...!
Hi in the below code we had created a listview .In the Screen contains one add button .If we press the button data will send as json request and get the response from the server.
If the response is successfull then I am adding each row add and delete button for each row.
can any one help me where i did the mistake
public class AddBuildingFragement extends Fragment {
EditText et_building;
TextView Add,mTitle,delete;
ListView lv;
String name;
ArrayList<String> arrayList;
ArrayAdapter<String> adapter;
public AddBuildingFragement() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//((NavigationViewActivity) getActivity ( )).setActionBarTitle ("LIGHT CONTROL");
View rootView = inflater.inflate(R.layout.activity_building, container, false);
init(rootView);
return rootView;
}
private void clickListener() {
Add.setOnClickListener (new View.OnClickListener ( ) {
#Override
public void onClick(View v) {
name=et_building.getText ().toString ();
String level="1";
// final ProgressDialog progressDialog = new ProgressDialog(getActivity ());
// progressDialog.setIndeterminate(true);
// progressDialog.setMessage("Authenticating...");
// progressDialog.setCanceledOnTouchOutside(false);
// progressDialog.setCancelable(false);
// progressDialog.show();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API.URL_BASE)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
API service = retrofit.create(API.class);
try{
JSONObject parmobject=new JSONObject ();
parmobject.put("name",name);
parmobject.put("level",level);
Call <NewBuilding> userCall = service.getbuildinglist (parmobject.toString());
userCall.enqueue(new Callback <NewBuilding> () {
#Override
public void onResponse(Call<NewBuilding> call, Response <NewBuilding> response) {
// if (progressDialog != null)
// progressDialog.dismiss();
Integer response1= response.code();
Log.d ("response", String.valueOf (response1));
if (response !=null && response.isSuccessful()&&response.code()==200) {
String status=response.body ().getStatus ();
if(status.equalsIgnoreCase ("success")){
makeText(getActivity (), "Building successfully Added", Toast.LENGTH_SHORT).show();
arrayList.add (name);
adapter.notifyDataSetChanged ();
}
} else {
makeText(getActivity (), "Invalid EmailId and password", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<NewBuilding> call, Throwable t) {
}
});
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
private void init(View rootView) {
et_building=(EditText)rootView.findViewById (R.id.build_name);
Add=(TextView)rootView.findViewById (R.id.addbuild);
//delete=(TextView)rootView.findViewById (R.id.delete_item);
lv=(ListView)rootView.findViewById (R.id.list_building);
arrayList=new ArrayList<String> ();
adapter=new ArrayAdapter<String> (getActivity (),R.layout.building_listview_item,arrayList);
MyCustomAdapter adapter = new MyCustomAdapter(arrayList, this);
//handle listview and assign adapter
//ListView lView = (ListView)findViewById(R.id.my_listview);
//lView.setAdapter(adapter);
lv.setAdapter(adapter);
clickListener();
}
I guess the problem is coming from adapter.notifyDataSetChanged() as it is not working. Here :-
if(status.equalsIgnoreCase ("success")){
makeText(getActivity (), "Building successfully Added", Toast.LENGTH_SHORT).show();
arrayList.add (name);
adapter.notifyDataSetChanged ();
}
Possible Fix #1 : Clone the base method in your Adapter class as :
public void notifyDataSetChangedCustom(List<String> newdata) {
mData.clear();
mData.addAll(newdata);
this.notifyDataSetChanged();
}
And call adapter.notifyDataSetChangedCustom(newdata) instead of adapter.notifyDataSetChanged
Possible Fix #2 : If still, it doesn't work, stop using only notifyDataSetChanged. Instead, try re-initializing the adapter & then call notifyDataSetChanged.
AttendanceAdapter adapter = new AttendanceAdapter(this, attendanceItems);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
Possible Fix #3: You're getting a null response from Retrofit. I think there might be 3 causes,
Target Server is not responding
You forgot to add Internet Permission.
The Invisible Killer ClearTextTraffic introduced on Android Pie : Fix by adding android:usesCleartextTraffic="true" to your <application> tag in AndroidManifest.xml
Hope it will help you.
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).
I made simple Client server to android.
I have problem when I send an object from server to the client.
The object is received ok and when I check the log, it shows me the the object was sent successfully.
The problem occurs when I'm trying to get this object and put it in my ListView adapter.
The adapter works, I checked it with a random ArrayList I created.
My issue is when I'm trying to to put the values of AsyncTask in my adapter.
public class RestaurantListFragment extends Fragment {
private ArrayList<Resturant> res = new ArrayList<>();
private ListAdapter adapter;
private Firebase restRef = new Firebase("https://restmeup.firebaseio.com/restaurants");
private Client mClient;
// private connectTask t = (connectTask)new connectTask().execute();
public RestaurantListFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new connectTask().execute();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// new connectTask(getView()).execute();
final View rootView = inflater.inflate(R.layout.fragment_two, container, false);
ListView restaurantList = (ListView) rootView.findViewById(R.id.list);
adapter = new ListAdapter(getContext(), res, getActivity());
restaurantList.setAdapter(adapter);
// connectTask t = (connectTask)new connectTask().execute();
if (mClient != null) {
mClient.sendMessage("bar");
}
SqlQueriesConverter sql = new SqlQueriesConverter();
sql.getResurantsListQuery("bar");
// sql.getUserFavoritesResturants(accessToken.getUserId());
mClient.sendMessage(sql.getQuery());
// t.setArray(res);
mClient.sendMessage("/quit");
mClient.stopClient();
final EditText searchText = (EditText)rootView.findViewById(R.id.searchListView);
searchText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
System.out.println("Before---------");
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String text = searchText.getText().toString().toLowerCase(Locale.getDefault());
adapter.filter(text);
adapter.notifyDataSetChanged();
System.out.println("array: " + res.toString());
}
#Override
public void afterTextChanged(Editable s) {
System.out.println("After---------");
}
});
// Inflate the layout for this fragment
return rootView;
}
public class connectTask extends AsyncTask<ArrayList<?>,ArrayList<?>,Client> {
// private Client mClient;
private ArrayList<?> arrayList = new ArrayList<>();
#Override
protected Client doInBackground(ArrayList<?>... message) {
//we create a Client object and
mClient = new Client(new Client.OnMessageReceived() {
#Override
//here the messageReceived method is implemented
public void messageReceived(ArrayList<?> message) {
//this method calls the onProgressUpdate
// publishProgress(message);
onProgressUpdate(message);
}
});
mClient.run();
return null;
}
// #Override
protected void onProgressUpdate(ArrayList<?>... values) {
super.onProgressUpdate(values);
ArrayList<?> arr2;
if (values[0].get(0) instanceof Resturant){
Log.d("step 1", "1");
if (((ArrayList<?>)values[0]).get(0)instanceof Resturant) {
// arr2 = (ArrayList<Resturant>) values[0];
res = (ArrayList<Resturant>) values[0];
adapter.notifyDataSetChanged();
Log.d("array",res.toString());
}
}
if (values[0].get(0)instanceof Review){
arr2 = (ArrayList<Review>) values[0];
}
if (values[0].get(0)instanceof UserFavorites){
arr2 = (ArrayList<Review>) values[0];
Log.d("step 2", "2");
}
}
}
}
There are two things you need to change to use the AsyncTask as you intend. The first change you need is to return the ArrayList you get from your mClient in the doInBackground method. This is a bit troublesome because it looks like the Client is already running asynchronously since you pass a callback to get the result (this is the Client.OnMessageReceived anonymous class you have there). The second change would be to implement onPostExecute on your AsyncTask since that is where the results from doInBackground are sent. You'd add the result sent from doInBackground to your adapter in there.
In any case, since it looks like Client is already performing the work asynchronously, you shouldn't need to use an AsyncTask at all. Just implement the logic to add the results to your adapter in the Client.OnMessageReceived callback.
Just get the code you already have in onProgressUpdate and throw it inside messageReceived. Something like this:
mClient = new Client(new Client.OnMessageReceived() {
#Override
//here the messageReceived method is implemented
public void messageReceived(ArrayList<?> values) {
if (values[0].get(0) instanceof Resturant){
Log.d("step 1", "1");
if (((ArrayList<?>)values[0]).get(0)instanceof Resturant) {
res = (ArrayList<Resturant>) values[0];
adapter.notifyDataSetChanged();
Log.d("array",res.toString());
}
}
}
});
I am programming a messaging app and I want to add users in a group. However, when a list of users pops up and I select one from the list, it doesn't pass the string (the username) to the other activity. All I get is an empty list.
Here is my code:
First Activity = Sending data (usernames from list) through putExtra()
public class ListUsersActivity extends Activity {
private String currentUserId;
private ArrayAdapter<String> namesArrayAdapter;
private ArrayList<String> names;
private ListView usersListView;
private Button logoutButton;
private ProgressDialog progressDialog;
private BroadcastReceiver receiver = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_users);
Parse.initialize(this, "embpZ0spRUv5XwDgI23innll1sgHg0KZNiKzg6kl", "LPsU4UffPeqFXkQB1GfLCIJ4kvg20llPgbOnLise");
currentUserId = ParseUser.getCurrentUser().getObjectId();
names = new ArrayList<>();
ParseQuery<ParseUser> query = ParseUser.getQuery();
query.whereNotEqualTo("objectId", currentUserId);
query.findInBackground(new FindCallback<ParseUser>() {
public void done(List<ParseUser> userList, com.parse.ParseException e) {
if (e == null) {
for (int i=0; i<userList.size(); i++) {
names.add(userList.get(i).getUsername().toString());
}
usersListView = (ListView)findViewById(R.id.usersListView);
namesArrayAdapter =
new ArrayAdapter<String>(getApplicationContext(),
R.layout.user_list_item, names);
usersListView.setAdapter(namesArrayAdapter);
usersListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> a, View v, int i, long l) {
Intent goBackToAddPoolIntent = new Intent(ListUsersActivity.this, addNewPoolActivity.class);
addNewPoolActivity checker = new addNewPoolActivity();
checker.checkIfUserIsSelected(usersListView.getItemAtPosition(i).toString());
goBackToAddPoolIntent.putExtra("username", usersListView.getItemAtPosition(i).toString());
startActivity(goBackToAddPoolIntent);
}
});
} else {
Toast.makeText(getApplicationContext(),
"Error loading user list",
Toast.LENGTH_LONG).show();
}
}
});
}
Second Activity = Receiving data from putExtra()
public class addNewPoolActivity extends Activity {
private static ArrayList<String> addedUsers;
private ArrayAdapter <String> addedUserAdapter;
private boolean userIsSelected;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_new_pool);
Button addMembers = (Button) findViewById(R.id.bAddMembers);
addedUsers = new ArrayList<>();
//addedUsers.add("Group Members");
addMembers.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent showUsersToSelect = new Intent(addNewPoolActivity.this, ListUsersActivity.class);
startActivity(showUsersToSelect);
}
});
ListView addedUsersList = (ListView) findViewById(R.id.addedUsersListView);
addedUserAdapter = new ArrayAdapter<>(this, R.layout.user_list_item, addedUsers);
addedUsersList.setAdapter(addedUserAdapter);
if(userIsSelected){
Bundle extras = getIntent().getExtras();
addedUsers.add(extras.getString("username"));
}
}
public void checkIfUserIsSelected(String user){
if (user!=null){
userIsSelected = true;
}else{
userIsSelected = false;
}
}
Since the default value for a boolean is false, the code is never called because
if(userIsSelected){
will always evaluate to false since you have declared the varaible as
private boolean userIsSelected;
and the first snippet here is in onCreate() so it will only run the first time the Activity is created.
Maybe you are wanting to call checkIfUserIsSelected(someUser) before that code but without more context of what you hope to accomplish, it's hard to say.
Possibly, you want to use startActivityForResult() in some way?
In addition to #codeMagic 's answer (Since your boolean value is false, it won't call the statement that you are adding the new data). It's also because of you parse the Data "username" after you setAdapter of your ListView. So basically you are setting the data, and then trying to add the new data you parsed to the list. Either you need to do it before setting your data set to your adapter, or call addedUsersAdapter.notifyDataSetChanged() to refresh your listView's data set.
addedUserAdapter = new ArrayAdapter<>(this, R.layout.user_list_item, addedUsers);
addedUsersList.setAdapter(addedUserAdapter);
Bundle extras = getIntent().getExtras();
// Check if the username has been sent to this Activity.
if(extras != null && extras.containsKey("username")){
addedUsers.add(extras.getString("username"));
// Refresh Your Data Set
addedUserAdapter.notifyDataSetChanged();
}