ArrayList<String> dont charge data - JAVA - java

I doing a ArrayList with a Class "Company", this list a companys what get for firestore.
The code for class is:
public ArrayList<String> companyList (View view) {
CompanyList.add(0, "Seleccione un proveedor");
FirebaseUser currentFirebaseUser = FirebaseAuth.getInstance().getCurrentUser() ;
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("users").document(currentFirebaseUser.getUid()).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
User = (HashMap<String, Object>) documentSnapshot.getData();
if(User.get("List") != "") {
List = (HashMap<String, Object>) User.get("List");
CompanyList.addAll(List.keySet());
Log.i("Current User: ", CompanyList.toString());
}
}
});
return CompanyList;
}`
This class work fine, i used on spinners but when i use for charge a ArrayList on fragment don't work. The code for fragment is:
Company company = new Company();
ArrayList<String> arrayList = new ArrayList<>(company.companyList(root));
Log.i("Current User: ", arrayList.toString());
When executed code the log show this:
2022-04-02 13:34:12.370 17434-17434/com.martin.preventapp I/Current User:: [Seleccione un proveedor] 2022-04-02 13:34:12.615 17434-17434/com.martin.preventapp I/Current User:: [Seleccione un proveedor, Nutrifresca, Ideas Gastronomicas, Pollo]
The first log show a arrayList from fragment and second log show the class arrayList.
I can't charge the arrayList at fragment as a class show, what's wrong??
I try this methods for charge a ArrayList:
ArrayList<String> arrayList = new ArrayList<>(company.companyList(root)
ArrayList<String> arrayList = new ArrayList<>();
arrayList.addAll(company.companyList(root));
ArrayList<String> arrayList;
arrayList = (ArrayList<String>) company.companyList();

I could suggest that you start by declaring a variable as an ArrayList such as :
ArrayList<String> resultArraylist = new ArrayList<>();
And then you add your first item :`
resultArrayList.add("Seleccionne un providor");
With ArrayList except if you always want to add in first position, the method add will put your item at the end of the list.

Louis Wasserman, Cheshiremoe, Thomas Koenig, thanks for reply.
Thomas, i tried with this method but no result.
Louis and Cheshiremoe, i try implemented a AsyncTask. On doInBackground put the code what get arraylist from firestore, in onPostExecute i try compare a arrayList. The code is this:
private class Task extends AsyncTask<String, String, ArrayList<String>> {
// Runs in UI before background thread is called
#Override
protected void onPreExecute() {
super.onPreExecute();
// Do something like display a progress bar
}
// This is run in a background thread
#Override
protected ArrayList<String> doInBackground(String... params) {
Company company = new Company();
// Call this to update your progress
//publishProgress(companyList);
return company.companyList(getView());
}
// This is called from background thread but runs in UI
protected void onProgressUpdate(ArrayList<String> arr1) {
}
// This runs in UI when background thread finishes
#Override
protected void onPostExecute(ArrayList<String> result) {
super.onPostExecute(result);
ArrayList<String> companyList = new ArrayList<String>(result);
TextView tvt = getView().findViewById(R.id.textView7);
if(companyList.size() > 1){
addFragmentListAdd();
tvt.setText(companyList.toString());
}
Log.i("Current User TASK: ", companyList.toString());
}
}
and the call is:
new Task().execute();
but this don't work as expected, the log is:
I/Current User TASK:: [Seleccione un proveedor]
Is the first time i implement AsyncTask, is this way correct??

Related

doInBackground does not run inside Activity onCreate

What I have :
I have a SearchActivity which receives an ArrayList (NameDesSearch) from a fragment(after a button click) and it updates a simple listview. When I click a list view item a new view is appearing by the corresponding object (orgDesObj) of the clicked list item. This functionality works well.
Currently receiving list (NameDesSearch) consists of names and descriptions. All are strings.
But, I wanted to show lists names only. Hence I tried creating a function (titlefunc()).
Here a new ArrayList ( NameDesTitles ) was crated to add relevent names only.
Issue:
But, seems like Do In background function is not working by the time I call titlefunc().
Attempts:
I put several Log to capture the point.
I'm using the same function (getLocDesOb()) in the list view on item clicked as well.
Surprisingly it works, even the doInBackground function also works.
But when the search activity creates and titlefunc() is called, search list (finalODescriptionArrayList) in doInBackground is empty().
Form the Logs I receive the content of finalODescriptionArrayList as [] and size as 0.
But, when I click list view item finalODescriptionArrayList updates.
I even tried by moving NameDesSearch = getIntent().getStringArrayListExtra("searched"); outside of the function as well.
Seems like my doInBackground method is calling only when the list item clicked but not activity on creates. Every other function works well. I'm not sure by the time when why my titlefunc() is called, why finalODescriptionArrayList does not update.
I would appreciate any suggestions on this. Thank you !
My Code: I have removed Logs for clearness.
public class SearchActivity extends AppCompatActivity {
ListView searchedListView;
String SearchedWord;
private ArrayAdapter<String> orgAdapter;
ArrayList<String> NameDesSearch = new ArrayList<String>();
ArrayList<String> NameDesTitles = new ArrayList<String>();
private OService OService;
ArrayList<ODescription> finalODescriptionArrayList = new ArrayList<ODescription>();
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
searchedListView = (ListView) findViewById(R.id.searched_list_view);
new GetCourse().execute();
titlefunc();
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, NameDesTitles);
searchedListView.setAdapter(arrayAdapter);
searchedListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String selectedItemText = parent.getItemAtPosition(position).toString();
ODescription orgDesObj = getLocDesOb(selectedItemText);
if (orgDesObj != null) {
Intent intent = new Intent(SearchActivity.this, View.class);
intent.putExtra("sOb", orgDesObj);
startActivity(intent);
}
}
});
}
#SuppressLint("StaticFieldLeak")
private class GetCourse extends AsyncTask<Void, Void, Void> {
#TargetApi(Build.VERSION_CODES.N)
#Override
protected Void doInBackground(Void... voids) {
try {
finalODescriptionArrayList = JsontoObject.jsonToObjectData(getResources().openRawResource(R.raw.newdb));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
public ODescription getLocDesOb(String selectedItemText) {
if (finalODescriptionArrayList == null) {
return null;
}
for (ODescription locDescObj : finalODescriptionArrayList) {
if (locDescObj.getName().equals(selectedItemText) || locDescObj.getDescription().equals(selectedItemText)) {
return locDescObj;
}
}
return null;
}
public void titlefunc() {
NameDesSearch = getIntent().getStringArrayListExtra("searched");
for (String searchNameDes : NameDesSearch) {
ODescription orgDesObj2 = getLocDesOb(searchNameDes);
if (orgDesObj2 != null) {
NameDesTitles.add(orgDesObj2.getName());
}
}
}
}
Attempts After Answer Below
AsyncTask update with onPostExecute. Then Since it take a little bit of time a progress bar added with onPreExecute. titlefunc() in oncreate method removed.
This method works now. But, sometimes the same issue exists. Arraylist to adapter is empty so that listview is empty. Seems like still taking lot of time to do the background task.
Updated AsyncTask
#SuppressLint("StaticFieldLeak")
private class GetCourse extends AsyncTask<Void, Void, Void> {
ProgressDialog progressDialog;
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = new ProgressDialog(SearchActivity.this);
progressDialog.setMessage("Searching");
progressDialog.setCancelable(false);
progressDialog.show();
}
#TargetApi(Build.VERSION_CODES.N)
#Override
protected Void doInBackground(Void... voids) {
try {
finalODescriptionArrayList = JsontoObject.jsonToObjectData(getResources().openRawResource(R.raw.newdb));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
titlefunc();
arrayAdapter.notifyDataSetChanged();
if (progressDialog.isShowing())
progressDialog.dismiss();
}
}
Modifed titlefunc() - to remove duplicates
Set<String > set = new HashSet<>( NameDesTitles);
NameDesTitles.clear();
NameDesTitles.addAll(set);
Your AsyncTask runs asynchronously, in the background. It will (most likely) not be finished when you call titleFunc() (which is what you are seeing).
You can fix this in many ways. One way would be to update the content of your adapter after the AsyncTask completes. You can do this in onPostExecute() of your AsyncTask which will be called when the background processing completes. In that method you can run your titleFunc() or something similar to filter the results you want to display. You then need to tell your Adapter to update the view by calling notifyDatasetChanged() on the Adapter.

LiveData with added new item via push notification not update the list

I tried to add new message item who arrived from push notification to list.
I tried to achieve this by live data. I used databinding in recyclerview and in main activity.
The func onChanged is not called when item is added to live data list in MsgViewModel class.
what I doing wrong?
public class MyFirebaseMessagingService extends FirebaseMessagingService {
......
private void showNotification(Map<String, String> data) {
id = data.get("id");
phone = data.get("phone");
locations = data.get("locations");
textMessage = data.get("textMessage");
MsgViewModel viewModel = new MsgViewModel(getApplication());
viewModel.addMessage(new Message(id, phone, locations, textMessage));
}
public class MsgViewModel extends AndroidViewModel {
private MutableLiveData<ArrayList<Message>> messageArrayList;
public MsgViewModel(#NonNull Application application) {
super(application);
messageArrayList = new MutableLiveData<>();
}
public void addMessage(Message message){
List<Message> messages = messageArrayList.getValue();
ArrayList<Message> cloneMessageList;
if(messages == null){
cloneMessageList = new ArrayList<>();
}else {
cloneMessageList = new ArrayList<>(messages.size());
for (int i = 0; i < messages.size(); i++){
cloneMessageList.add(new Message(messages.get(i)));
}
}
cloneMessageList.add(message);
messageArrayList.postValue(cloneMessageList);
}
public MutableLiveData<ArrayList<Message>> getMessageList(){
return messageArrayList;
}
}
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private MsgViewModel msgViewModel;
private MsgListAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.contentMainId.recyclerview.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
binding.contentMainId.recyclerview.setLayoutManager(layoutManager);
msgViewModel = new ViewModelProvider(this).get(MsgViewModel.class);
msgViewModel.getMessageList().observe(this, new Observer<ArrayList<Message>>() {
#Override
public void onChanged(ArrayList<Message> list) {
mAdapter = new MsgListAdapter(getApplication(), list);
binding.contentMainId.recyclerview.setAdapter(mAdapter);
// mAdapter.notifyDataSetChanged();
}
});
}
Any help why it is not update the adapter will be appreciated
==========Update=======
onChanged() method not called when have new item who added to the list
The problem here is how you instantiate your viewModels. If I understand correctly, you want them to be the same instance in both the activity and the messaging service.
One is MsgViewModel viewModel = new MsgViewModel(getApplication());
The other one is msgViewModel = new ViewModelProvider(this).get(MsgViewModel.class);
On the second one this stands for the current instance of the activity. And its context is different from the one you get from getApplication().
As far as I know, when you call 'postValue()' or something 'setValue()' method, you should give new object of something like oldMutableList.toList().
In other words, after a list is entered as a parameter in 'postvalue()'method of livedata, even if a new value is added to the list, livedata is not recognized. In order for the observer to recognize, the newly created list object must be entered as a parameter when calling postvalue again.
_liveData.postValue(list.toList()) // "list.toList()" <- this code generate new List object which has another hashcode.
or
_liveData.postValue(list.sortedBy(it.somethingField))
sorry about this kotlin code, not java.
// this original in your code
messageArrayList.postValue(cloneMessageList);
// change to these codes
messageArrayList.postValue(new ArrayList(cloneMessageList));
or
messageArrayList.postValue(cloneMessageList.toList());

How to display random items from arraylist<T> derived from a doInBackground?

So i was making this app which displays an arrayList of different poems
I first request the poems from API then the Asynctask returns the poems in the form of Arraylist i wand to show 20 random poems from the list.
This is the Asynctask code
private class TitleAsynctask extends AsyncTask<URL,Void,List<view>> {
private ProgressDialog progressDialog;
#Override
public List<view> doInBackground(URL... urls){
URL url = Query_utils.createurl(POEM_TITLE);
String json = "";
Log.d(LOG_TAG,"this worked");
{
try {
json = Query_utils.makehttprequest(url);
Log.d(LOG_TAG, "make Httprequest works");
} catch (IOException e) {
}
}
List<view> title_view = Query_utils.extracttitlefromjson(json);
return title_view;
}
#RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
#Override
protected void onPostExecute(List<view> data) {
madapter.clear();
if (data != null && !data.isEmpty()){
madapter.addAll(data);
}
}
}
and the onCreate code is
TitleAsynctask task = new TitleAsynctask();
URL url = Query_utils.createurl(POEM_TITLE);
task.execute(url);
ArrayList<view > arr = new ArrayList<view>();
final ListView poem_Title_list = (ListView) findViewById(R.id.list_item);
madapter = new title_adapter(this ,arr);
poem_Title_list.setAdapter(madapter);
I can think of two ways:
Just use a random number generator to generate a number between 0 and the number of entries-1. The disadvantage with this method is that you can get repeats.
Randomly sort the list after fetching. You can do this using Collections.shuffle(list) This way you won't get any repeats but you will sort the whole list which could be a waste if there are hundreds of entries and you only want to show 20.

Firebase (Android) - Loading filtered data into custom ArrayList

Can someone please advice me on this issue:
I am trying to load some filtered data from Firebase DB onCreate and populate a custom array with data.
After doing some debugging I can tell that the data is loaded from DB but my custom array is not getting populated.
Please have a look at my code below.
Even though my loadAllBooks() method is populating the array, it gets completed too late and the line:
//3) Create the adapter
BooksAdapter adapter = new BooksAdapter (this, booksList);
is executed before loadAllBooks() is completed which results in an empty list...
It's as if i need some sort of onComple for the addChildEventListener...
Please Help, if more information is needed let me know, thank you:
ArrayList<BookItem> booksList;
ListView listView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//1) empty books list
booksList = new ArrayList<BookItem>();
//2) load all books from firebase DB - into booksList array
loadAllBooks();
//3) Create the adapter
BooksAdapter adapter = new BooksAdapter (this, booksList);
//4) Attach the adapter to a ListView
listView = (ListView) findViewById(R.id.lvBooks);
listView.setAdapter(adapter);
}
public void loadAllBooks() {
Firebase ref = new Firebase(".......firebaseio.com/books");
Query queryRef = ref.orderByChild("bookType").equalTo("drama");
queryRef.addChildEventListener(childEventListener);
}
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded (DataSnapshot bookNode, String previousChild){
BookItem bookItemModel = bookNode.getValue(BookItem.class);
booksList.add(bookItemModel);
}
public void onChildRemoved(DataSnapshot snapshot) {}
public void onChildChanged(DataSnapshot snapshot, String previousChild){}
public void onChildMoved(DataSnapshot snapshot, String previousChild) {}
#Override
public void onCancelled(FirebaseError firebaseError) {}
};
Thank you!
When you call addChildEventListener() Firebase starts loading the data from the remote location asynchronously. This means that the code after it executes straight away and you pass an empty list to the adapter. Later, when the initial data has synchronized from Firebase, it is added to the list. But by that time you've already created the adapter.
You can most easily see the flow, by adding a few log statements:
System.out.println("Start loading/synchronizing books");
loadAllBooks();
System.out.println("Creating adapter");
BooksAdapter adapter = new BooksAdapter (this, booksList);
public void onChildAdded (DataSnapshot bookNode, String previousChild){
System.out.println("Adding book to list");
BookItem bookItemModel = bookNode.getValue(BookItem.class);
booksList.add(bookItemModel);
}
These will print in this order:
Start loading/synchronizing books
Creating adapter
Adding book to list
Adding book to list
Adding book to list
...
Likely this is not the order that you expected. Welcome to asynchronous loading 101, it makes the modern web tic and makes developers lose their mind when they first encounter it. :-)
Most likely all that is required is that you call adapter.notifyDataSetChanged() from onChildAdded(). This informs Android that the data in the adapter has changed and that it should repaint the associated view(s).
public void onChildAdded (DataSnapshot bookNode, String previousChild){
BookItem bookItemModel = bookNode.getValue(BookItem.class);
booksList.add(bookItemModel);
adapter.notifyDataSetChanged();
}
Note that I can't be sure if this will work, because you didn't include the code for BooksAdapter in your question.

onPostExecute not being called

I am having problems trying to get to onPostExecute to fire on this piece of code, I am new to java and especially multithreading so if what i'm doing is completely wrong please feel free to correct me...need to learn.
So the problem is with this:
public class DataTask extends AsyncTask<Void, Void, Integer> {
Context context;
DataTask(Context context) {
this.context = context.getApplicationContext();
}
// Global Int for counting how many Tasks have been completed
int asynCount = 0;
ArrayList<String> arr_dataVts=new ArrayList<String>();
ArrayList<String> arr_dataNtm=new ArrayList<String>();
ArrayList<String> arr_dataOdas=new ArrayList<String>();
ArrayList<String> arr_dataMetAll=new ArrayList<String>();
ArrayList<String> arr_dataMet3HrTask=new ArrayList<String>();
ArrayList<String> arr_dataTideTask=new ArrayList<String>();
#Override
protected Integer doInBackground(Void... params) {
//VtsAsyncTask
VtsTask task1 = new VtsTask();
task1.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
//NtmAsyncTask
NtmTask task2 = new NtmTask();
task2.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
//OdasAsyncTask
OdasTask task3 = new OdasTask();
task3.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
//MetAllTask
MetAllTask task4 = new MetAllTask();
task4.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
//Met3HrTask
Met3HrTask task5 = new Met3HrTask();
task5.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
//TideTask
TideTask task6 = new TideTask();
task6.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return 1;
}
All the tasks execute as should do
but here in the onPostExecute:
#Override
protected void onPostExecute(Integer result) {
if (asynCount == 6){
//start intents for main activity
System.out.println("asynCount has reached= " + asynCount + " so now starting MainActivity");
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putStringArrayListExtra("data1", arr_dataVts);
intent.putStringArrayListExtra("data2", arr_dataNtm);
intent.putStringArrayListExtra("data3", arr_dataOdas);
intent.putStringArrayListExtra("data4", arr_dataMetAll);
intent.putStringArrayListExtra("data5", arr_dataMet3HrTask);
intent.putStringArrayListExtra("data6", arr_dataTideTask);
// context.startActivity(intent);
}else{
//update dialogue
}
}
}
The onPostExecute never gets called? I don't know why.
I have tried using Integers, Void and Booleans to get it to return but doesn't work.
All advice is appreciated.
EDIT: I now realise that I should really be using Bundle here for all the extras.
Your DataTask class is unnecessary. The only thing that you are doing in the background is starting several other AsyncTask's you don't need to wrap all of those execute() commands inside of the own AsyncTask because they already get executed in a background thread just by the very nature of AsyncTask.
Each of these other Tasks that you have needs to have it's own onPostExecute() that handles the results from that specific task and does something with it.
EDIT:
Here is some pseudocode of how it might look if you combined all of your operations into a single AsyncTask:
public class DataTask extends AsyncTask<Void, Void, Integer> {
Context context;
DataTask(Context context) {
this.context = context; // <-- you already have a context, you don't need to call getApplicationContext();
}
#Override
protected Integer doInBackground(Void... params) {
doSomeWork();
doSomeOtherWork();
doYetSomeMoreWork();
//...
}
#Override
protected void onPostExecute(Integer result) {
doSomethingWithThe(result);
}
}
The doSomeWork() etc... methods should not be AsyncTasks, just normal methods so that they execute sequentially. That way, when all of them are complete onPostExecute() will be called.

Categories