1

I have another problem in my application. I use the following code in the MainActivity to fetch the user data from my ContactAdapter:

private List<String> loadContactData() {
    ContactAdapter db = new ContactAdapter(getApplicationContext());
    // Spinner Drop down elements
    List<String> contacts = db.getAllContacts();        
    // Creating adapter for spinner
    ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,
            android.R.layout.simple_spinner_item, contacts);
    // Drop down layout style - list view with radio button   
 dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);        
    Log.d("SPINNER", String.valueOf(contacts));
    return contacts;
}

And the ContactAdapter:

public List<String> getAllContacts(){
    final List<String> contacts_id = new ArrayList<String>();
    final List<String> contacts_name = new ArrayList<String>();
    Backendless.Data.of(BackendlessUser.class).find(new AsyncCallback<BackendlessCollection<BackendlessUser>>() {
        public void handleResponse(BackendlessCollection<BackendlessUser> users) {
            Iterator<BackendlessUser> userIterator = users.getCurrentPage().iterator();
            while (userIterator.hasNext()) {
                BackendlessUser user = userIterator.next();
                String user_mail = user.getEmail();
                String user_id = user.getUserId();
                contacts_name.add(user_mail);
                contacts_id.add(user_id);
            }
            Log.d("getAllContacts: ", String.valueOf(contacts_name)); }
        public void handleFault(BackendlessFault backendlessFault) {
            System.out.println("Server reported an error - " + backendlessFault.getMessage());}});
    return contacts_name;
}

Certainly my problem is that the Backendless query is to slow... so in my Logcat I see first an empty "SPINNER" and second "getAllContacts" with the user data from the server. As a result of that the return contacts statement from loadContactData() is empty too.

How can I achieve that getAllContacts() is finished first to get the user data in loadContactData()?

Timitrov
  • 211
  • 1
  • 3
  • 14

2 Answers2

1

Your issue is that the find call is asynchronous (which is good and means handleResponse() is called on a new thread). So when your return contacts_name is run, contacts_name is still empty because your code in handleResponse() has not yet run because the new thread is still waiting for the response of the database.

The call to your database is slow because it may include file or even network IO. You should never do this on the main thread which is working with your views, otherwise the app will no longer respond because the main thread updating the UI (and painting the spinner animation) is busy waiting for the database. By making the database request asynchronous your app stays responsive and the UI will not get "stuck".

To keep things simple you can do it like this:

public class MyActivity extends AppCompatActivity {

   private List<String> mContactIds = new ArrayList<>();

    private Spinner mSpinner;

    private ProgressDialog mProgressDialog;

    @Overrride
    public void onCreate() {

        this.setContentView(...);
        this.mSpinner = ...;

        this.showLoadingDialog();

        Backendless.Data.of(BackendlessUser.class).find(new AsyncCallback<BackendlessCollection<BackendlessUser>>() {
            public void handleResponse(BackendlessCollection<BackendlessUser> users) {

                List<String> mails = new ArrayList<>();
                List<String> ids = new ArrayList<>();
                Iterator<BackendlessUser> userIterator = users.getCurrentPage().iterator();
                while (userIterator.hasNext()) {
                    BackendlessUser user = userIterator.next();
                    String mail = user.getEmail();
                    String id = user.getUserId();
                    mails.add(user_mail);
                    ids.add(user_id);
                }

                Log.d("getAllContacts: ", String.valueOf(contacts_name)); 
                applyData(mails, ids);

            } 

            public void handleFault(BackendlessFault backendlessFault) {
                Log.e("error: ", "Something went wrong");

            }
        });
    }

    private void applyData(List<String> mails, List<String> ids) {
       Log.d("getAllContacts: ", String.valueOf(contacts_name)); 
       this.hideLoadingDialog();
       this.mContactIds = ids;
       this.mSpinner.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mails);

    }

    private void showLoadingDialog() {
        this.mLoadingDialog = new ProgressDialog(this, null, "Loading contacts...", true);
        this.mLoadingDialog.dismiss();

    }

    private void hideLoadingDialog() {
        if(this.mLoadingDialog != null) {
            this.mLoadingDialog().dismiss();

        }
    }

    private void showErrorDialog() {
        this.hideLoadingDialog();
        new AlertDialog.Builder(this)
            .setMessage("Something went wrong, try again")
            .setPositiveButton("Ok", null)
            .show();

    }
}
crysxd
  • 3,177
  • 20
  • 32
  • I try to fill a MultiSelectionSpinner with the registred users on the server. Therefor I use the following code in my onCreate of the MainActivity: List contact_list = loadContactData(); String[] contact_stringArray = contact_list.toArray(new String[contact_list.size()]); spinner = (MultiSelectionSpinner) findViewById(R.id.mySpinner1); spinner.setItems(contact_stringArray); – Timitrov Aug 14 '16 at 12:55
  • I added some sample code, this should be the minimum effort solution. You should show a progress dialog to let the user now something is happening while waiting for the data. Also you should show an error dialog if something went wrong – crysxd Aug 14 '16 at 16:15
1

The best option here will be to attach a listener interface to the ContactAdapter and make the MainActivity implement the attached interface. So when the ContactAdapter has done fetching the data, it will notify the listener class and then you can call loadContactData() from the main activity onRecieve callback.

public void getAllContacts(MyListener listener){
final List<String> contacts_id = new ArrayList<String>();
final List<String> contacts_name = new ArrayList<String>();
Backendless.Data.of(BackendlessUser.class).find(new AsyncCallback<BackendlessCollection<BackendlessUser>>() {
    public void handleResponse(BackendlessCollection<BackendlessUser> users) {
        Iterator<BackendlessUser> userIterator = users.getCurrentPage().iterator();
        while (userIterator.hasNext()) {
            BackendlessUser user = userIterator.next();
            String user_mail = user.getEmail();
            String user_id = user.getUserId();
            contacts_name.add(user_mail);
            contacts_id.add(user_id);
        }
        Log.d("getAllContacts: ", String.valueOf(contacts_name)); }
    public void handleFault(BackendlessFault backendlessFault) {
        System.out.println("Server reported an error - " + backendlessFault.getMessage());}});
listener.done(contacts_name);

}

And the interface may look like this

public interface MyListener {
   void done(List<String> contactList);
}

Then MainActivity should implement the MyListener interface and inside the done method you can setup the spinner there.

I believed the loadContact() can be refactored to something like

private void loadContactData() {
ContactAdapter db = new ContactAdapter(getApplicationContext());
// Spinner Drop down elements
db.getAllContacts(this);        

}

Note that your MainActivity implements MyListener interface. i.e.

class MainActivity ... implements MyListener {
  @Override
  public void done(List<String> contacts) {
      // Creating adapter for spinner
     ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,
        android.R.layout.simple_spinner_item, contacts);
// Drop down layout style - list view with radio button   
     dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);        
     Log.d("SPINNER", String.valueOf(contacts));

  }

This should give you a head up I guess you should be able to find your way from here. Also try to refactor the code.

  • Thanks for the trouble! I understand which steps are necessary for my target but I don't know how to call loadContactData() from the MainActivity onReceive callback... At the moment I call List contact_list = loadContactData(); in the onCreate and then I can supply my Spinner with the data in onCreate too with: spinner = (MultiSelectionSpinner) findViewById(R.id.mySpinner1); spinner.setItems(contact_stringArray); I hope you can help me again! – Timitrov Aug 14 '16 at 16:27
  • Based on what I understand from your question, the main goal is to wait for the `Backendless` service to complete before you set the adapter and the spinner value. I presume the `ContactAdapter ` is in a separate class and that's why I suggested using callback. – Hassan Semiu Ayomon Aug 15 '16 at 08:34