Приоритет на AsyncCallback
У меня есть другая проблема в моем приложении. Я использую следующий код в MainActivity для получения пользовательских данных из моего 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;
}
И 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;
}
Конечно, моя проблема заключается в том, что запрос Backendless замедляется... поэтому в моем Logcat я вижу сначала пустой "SPINNER", а затем "getAllContacts" с пользовательскими данными с сервера. В результате оператор возврата контактов из loadContactData() тоже пуст.
Как я могу добиться, чтобы getAllContacts() была завершена первой, чтобы получить пользовательские данные в loadContactData()?
2 ответа
Ваша проблема в том, что find
вызов асинхронный (что хорошо и означает handleResponse()
вызывается по новой ветке). Итак, когда ваш return contacts_name
это бег, contacts_name
все еще пусто, потому что ваш код в handleResponse()
еще не запущен, потому что новый поток все еще ожидает ответа базы данных.
Вызов к вашей базе данных медленный, потому что он может включать файловый или даже сетевой ввод-вывод. Вы никогда не должны делать это в главном потоке, который работает с вашими представлениями, иначе приложение больше не будет отвечать, потому что основной поток, обновляющий пользовательский интерфейс (и рисующий вращающуюся анимацию), занят ожиданием базы данных. Делая запрос к базе данных асинхронным, ваше приложение остается отзывчивым, и пользовательский интерфейс не застревает.
Для простоты вы можете сделать это так:
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();
}
}
Наилучшим вариантом здесь будет присоединение интерфейса слушателя к ContactAdapter
и сделать MainActivity
реализовать прикрепленный интерфейс. Итак, когда ContactAdapter
завершил выборку данных, он уведомит класс слушателя и затем вы можете вызвать loadContactData()
от основной деятельности по получению обратного вызова.
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);
}
И интерфейс может выглядеть так
public interface MyListener {
void done(List<String> contactList);
}
затем MainActivity
следует реализовать MyListener
Интерфейс и внутри готового метода вы можете настроить счетчик там.
Я верил loadContact()
может быть переработан в нечто вроде
private void loadContactData() {
ContactAdapter db = new ContactAdapter(getApplicationContext());
// Spinner Drop down elements
db.getAllContacts(this);
}
Обратите внимание, что ваш MainActivity
инвентарь MyListener
интерфейс. т.е.
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));
}
Это должно дать вам голову, я думаю, вы сможете найти свой путь отсюда. Также попробуйте рефакторинг кода.