Обозреватель не вызывается и пост-значение не работает из фонового потока, когда активность уничтожается, а затем воссоздается - PullRequest
0 голосов
/ 25 февраля 2019

В следующем простом примере кода есть асинхронная задача, которая несколько раз спит в течение 2 секунд, а затем запускает функцию обратного вызова onResultAvailable с "some text .." (которую я здесь использую для имитации потока веб-сокетов, получающего сообщения ...).

Метод onResultAvailable в классе MyViewModel должен обновлять переменную text (которая является MutableLiveData<String>), используя postValue (потому что он вызывается из фонового потока).

Таким образом, этот код должен обновлять переменную text каждые две секунды и отображать текст через наблюдателя в MyTestActivity;поэтому предполагается, что в первый раз будет работать следующее:

Some text 0
Some text 1
Some text 2
... etc

Однако, когда я нажимаю кнопку и затем снова открываю MyTestActivity, переменная text не изменяетсябольше на postvalue и наблюдатель больше не вызывается.Обратите внимание, что другие переменные, которые модифицируются с помощью setvalue, по-прежнему ведут себя как ожидалось.

Любые идеи, почему postvalue отлично работает в фоновом потоке в первый раз, но больше не работает, когда активностьуничтожили потом воссоздали?Как я могу обновить значение переменной text в этом случае?

MainActivity.java

public class MainActivity extends AppCompatActivity {
    Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = findViewById(R.id.myButton);

        button.setOnClickListener(v -> {
            startActivity(new Intent(MainActivity.this, MyTestActivity.class));
        });
    }
}

MyTestActivity.java

public class MyTestActivity extends AppCompatActivity {
    TextView myTextView, myTextViewTime;
    Button myButton;
    MyViewModel vm;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_test);
        myTextView = findViewById(R.id.myTextView);
        myTextViewTime = findViewById(R.id.myTextViewTime);
        myButton = findViewById(R.id.myButton);

        vm = ViewModelProviders.of(this).get(MyViewModel.class);

        vm.getText().observe(this, text -> {
            myTextView.setText(text);
            Log.d("DEBUG", "observed: " + text);
        });

        vm.getTimestamp().observe(this, strTs -> {
            myTextViewTime.setText(strTs);
            Log.d("DEBUG", "observed: " + strTs);
        });

        myButton.setOnClickListener(v -> {
            vm.setTimestamp("" + System.currentTimeMillis());
        });
    }
}

MyViewModel.java

public class MyViewModel extends ViewModel implements MyAsyncTaskDelegate {
    private MutableLiveData<String> text;
    private MutableLiveData<String> timestamp;
    private MyAsyncTask task;

    public MyViewModel() {
        text = new MutableLiveData<>();
        text.setValue("Hello World!");

        timestamp =  new MutableLiveData<>();
        timestamp.setValue(""+System.currentTimeMillis());

        task = new MyAsyncTask(this);
        task.execute();
    }

    public LiveData<String> getText() {
        return text;
    }

    public LiveData<String> getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(String ts) {
        timestamp.setValue(ts);
    }

    @Override
    public void onResultAvailable(String result) {
        text.postValue(result);
    }
}

MyAsyncTask.java

public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
    private MyAsyncTaskDelegate delegate;

    MyAsyncTask(MyAsyncTaskDelegate delegate) {
        this.delegate = delegate;
    }

    @Override
    protected Void doInBackground(Void... voids) {
        for(int i = 0; i < 100; i++) {
            try {
                Thread.sleep(2000);
                delegate.onResultAvailable("Some text " + i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

MyAsyncTaskDelegate.Ява

public interface MyAsyncTaskDelegate {
    void onResultAvailable(String result);
}

1 Ответ

0 голосов
/ 25 февраля 2019

Есть ли идеи, почему postvalue отлично работает в фоновом потоке в первый раз, но больше не работает, когда действие уничтожается, а затем воссоздается?

В двух словах: это происходит потому, чтоMyAsyncTask.doInBackground() будет зацикливаться бесконечно, и для большинства версий Android параллельное выполнение AsyncTask s отсутствует.

В MyTestActivity.onCreate() вы создаете новый экземпляр MyViewModel, который запускает свой собственный MyAsyncTask,Поскольку первый экземпляр MyAsyncTask никогда не заканчивается, второй действительно никогда не запускается.Первый экземпляр MyAsyncTask по-прежнему остается первым экземпляром MyViewModel (он же MyAsyncTaskDelegate).Но поскольку этот первый экземпляр MyViewModel не имеет никакого подключения к активному элементу пользовательского интерфейса, обновления не отображаются на экране.

Это можно проверить с помощью отладчика, установив точки останова в различных методах иСравнение «номеров экземпляров» (к каждому имени класса добавляется «@» и уникальный номер)

Обратите внимание, что проблемное поведение происходит только в том случае, если вы оставляете MyTestActivity нажатием НАЗАД.Если вы поворачиваете устройство, обновления продолжаются.

Это «работает как задумано».Нажав НАЗАД, вы указываете, что вам больше не нужен текущий экземпляр MyTestActivity: он не будет храниться в заднем стеке, он может и будет полностью уничтожен.

AsyncTask - это еще один случай: он является членом MyViewModel и с другой стороны содержит ссылку на MyViewModel.Можно сказать, что существует эталонный тупик: он будет остановлен, только если остановится весь окружающий процесс.

Как я могу обновить значение текстовой переменной в этом случае?

Переопределить onBackPressed() в MyTestActivity и остановить выполнение MyAsyncTask.Смотрите документацию по AsyncTask.cancel () , чтобы узнать, как это сделать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...