Как получить доступ к диалогу из фонового процесса?

У меня есть ListView со специальным адаптером, и он показывает список Wi-Fi вокруг меня. Когда нажимается один элемент, я показываю диалог с подробностями. Диалог представляет собой пользовательский класс, производный от DialogFragment, и отображается с помощью этого кода после завершения сканирования Wi-Fi:

private class WifiScanReceiver extends BroadcastReceiver {
        public void onReceive(Context c, Intent intent) {
            List<ScanResult> wifiScanList = wifi.getScanResults();
            Log.i("WifiAnal", "rec");

            lv.setAdapter(new WifiListAdapter(getApplicationContext(), R.layout.itemlistrow, wifiScanList));
            lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    ScanResult listItem = (ScanResult)lv.getItemAtPosition(position);
                    WifiDetailsDialogFragment d = new WifiDetailsDialogFragment();

                    // Supply num input as an argument.
                    Bundle args = new Bundle();
                    args.putString("ssid", listItem.SSID);

                    d.setArguments(args);

                    d.show(getSupportFragmentManager(), "fir");

                    // TODO: PUT ASYNC CALL HERE AND PROCESS FETCHED DATA.
                    // TODO: SHOW ASYNC RESULT ON OPEN DIALOG

                }

            });

            mSwipeRefreshLayout.setRefreshing(false);
        }
    }

Мой класс WifiDetailsDialogFragment заботится о том, чтобы раздуть его макет xml и правильно отобразить его поверх приложения с помощью кнопки «ОК».

Теперь представьте, что я показываю диалог, и теперь я хочу запустить фоновую задачу (скажем, получить дополнительные сведения из БД на внешнем сервере), и после того, как я закончу, если диалог все еще открыт, поместите результат в его макет (вероятно, манипулировать значениями некоторых уже существующие и раздутые TextViews). Если к тому времени он будет закрыт, ничего не делайте.

Как я могу получить доступ к своим виджетам макета из процесса, работающего в фоновом режиме, инициированного onItemClick? Я пробовал d.getView(), но это возвращает null. Правильно ли как-то через FragmentManager? Я не хочу рисовать новый диалог, а редактировать уже существующие.

Спасибо!


person michnovka    schedule 21.10.2015    source источник
comment
вы не можете получить доступ к представлениям потока пользовательского интерфейса из фонового потока, если у вас есть значения, извлеченные из БД или из вызова сервера в методе runOnUIThread, а затем делайте то, что вы хотите сделать с вашим диалоговым фрагментом.   -  person dex    schedule 21.10.2015
comment
как мне получить ссылку на TextView внутри диалога из runOnUIThread? Макет раздувается изнутри onItemClick   -  person michnovka    schedule 21.10.2015
comment
я думаю, вам нужно передать этот завышенный фоновый поток через слабую ссылку.   -  person dex    schedule 21.10.2015


Ответы (2)


Ну, если вы просто хотите получить доступ к своему фрагменту, вы можете использовать

getSupportFragmentManager().findFragmentByTag("fir")

Этот тег должен быть определен в момент транзакции вашего фрагмента (в вашем случае show(), так как это DialogFragment.

Но я должен сообщить, что код, который вы определили выше, выглядит немного странно. Как вы ожидаете получить результат вашего асинхронного вызова прямо здесь? Если вы продолжите с таким кодом, вы получите android.os.NetworkOnMainThreadException

Чтобы избежать этого, вы должны создать AsyncTask и манипулировать своим пользовательским интерфейсом в методе onPostExecute, где вы вернетесь к основному циклу.

person luis.mazoni    schedule 21.10.2015
comment
Я делаю вызов, используя AsyncTask, именно здесь я его выполняю, у него будут свои собственные методы, включая метод onPostExecute, и именно здесь мне нужно будет обратиться к TextView. Так будет ли это решение работать в этих обстоятельствах? - person michnovka; 21.10.2015
comment
Также для getFragment требуется Bundle в качестве первого параметра. Что мне туда поставить? Зачем он нужен, он его создает с нуля? - person michnovka; 21.10.2015
comment
извините, используйте: findFragmentByTag - person luis.mazoni; 21.10.2015
comment
Я добавил этот код ((WifiDetailsDialogFragment)getSupportFragmentManager().findFragmentByTag("fir")).updateUI(); сразу после d.show(getSupportFragmentManager(), "fir");, и я получаю исключение nullPointerException, что означает, что я не получаю правильный фрагмент таким образом, вместо этого я получаю null... есть идеи, почему? P.S. updateUI() - это функция, определенная в моем пользовательском фрагменте, которая заботится о необходимых изменениях. - person michnovka; 21.10.2015

Я бы посоветовал вам использовать Otto для отправки событий из фонового потока в логику пользовательского интерфейса. Эти события могут содержать любую необходимую вам информацию. Шаги, которые вам нужно будет выполнить:

  • Вставьте свою шину как в фоновый поток, так и во фрагмент диалога (может варьироваться в зависимости от вашего решения диспетчера зависимостей)
  • register() твой фрагмент в автобус
  • Создайте метод, который получает событие, и аннотируйте его с помощью @Subscribe
  • post() событие из фонового потока (для этого вам нужно будет перейти к основному циклу, но это можно легко сделать, изменив шину otto, как показано ниже)

    public class MainThreadBus extends Bus {
     private final Handler handler = new Handler(Looper.getMainLooper());
    
     @Override public void post(final Object event) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            super.post(event);
        } else {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    MainThreadBus.super.post(event);
                }
            });
        }
    }
    

Этот код был предложен в этом ответе: Как отправить событие из службы в действие с помощью шины событий Otto?

Написав свое решение таким образом, вы на самом деле будете в большей безопасности, поскольку вы можете unregister() использовать свой фрагмент в методе onPause, поэтому вам не нужно будет проверять, прикреплен ли фрагмент к активности при манипулировании ее пользовательским интерфейсом.

person luis.mazoni    schedule 21.10.2015
comment
Спасибо, но кажется немного накладным для такой простой вещи. Я ищу что-то без сторонних компонентов - person michnovka; 21.10.2015