Мое приложение падает, когда оно пытается отобразить сообщение об ошибке от AsyncTask, когда оно работает в фоновом режиме - PullRequest
0 голосов
/ 27 декабря 2018

Я показываю сообщение об ошибке из AsyncTask в мой фрагмент, но когда мое приложение находится в фоновом режиме, приложение падает.

код ниже:

import android.support.v4.app.FragmentManager;

public class AppUtil {

    public static void showErrorDialog(FragmentManager fm, int messageResourceId) {
        if (fm != null && !fm.isDestroyed()) {
            InfoDialogFragment.newInstance("", messageResourceId).show(fm, "ERROR");
        }
    }

    public static boolean isEmpty(String s) {
        return s == null || s.trim().length() == 0;
    }
}


import android.app.ProgressDialog;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class BlankFragment extends Fragment  implements ILoginable, 
View.OnClickListener {

     private ProgressDialog pd ;
    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View rootView =  inflater.inflate(R.layout.fragment_blank, 
     container, false);
        rootView.findViewById(R.id.pressBT).setOnClickListener(this);
        return rootView;
    }

    @Override
    public void starProgress(int messageId) {
        if (pd == null) {
            pd = new ProgressDialog(getContext(), R.string.loading);
            pd.show();
        }
    }

    @Override
    public void endProgress() {
        if(pd != null && isAdded()) {
            pd.dismiss();
            pd = null;
        }
    }

    @Override
    public void displayError(int messageId) {
        if (isAdded()) {
            AppUtil.showErrorDialog(getFragmentManager(), R.string.app_name);
        }
    }

    @Override
    public void loginResult(boolean success) {

    }

    @Override
    public void onClick(View v) {
        if (R.id.pressBT == v.getId()) {
            new LoginAsyncTask(this).execute("x");
        }
    }
}


public interface ILoginable {
    void starProgress(int messageId);
    void endProgress();
    void displayError(int messageId);
    void loginResult(boolean success);
}


import android.os.AsyncTask;

import java.lang.ref.WeakReference;

public class LoginAsyncTask extends AsyncTask<String, Void, String> {

    private WeakReference<ILoginable> reference;

    public LoginAsyncTask(ILoginable iLoginable) {
        reference = new WeakReference<>(iLoginable);
    }

    @Override
    protected void onPreExecute() {
        reference.get().starProgress(R.string.loading);
    }

    @Override
    protected String doInBackground(String... strings) {
        return "xx";
    }

    @Override
    protected void onPostExecute(String s) {
        if (reference.get() !=  null) {
            reference.get().endProgress();
            reference.get().displayError(R.string.hello_blank_fragment);
        }
        reference.clear();
    }
}

Как вы можетевидите, это простое поведение для базового asyncTask.Однако я не могу понять, почему метод isAdded возвращает значение true, когда приложение работает в фоновом режиме, а также почему происходит сбой приложения с сообщением:

*** Process: com.roscasend.android.testadd, PID: 2891
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
    at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:2053)
    at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:2079)
    at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:678)
    at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:632)
    at android.support.v4.app.DialogFragment.show(DialogFragment.java:143)
    at com.roscasend.android.testadd.AppUtil.showErrorDialog(AppUtil.java:17)
    at com.roscasend.android.testadd.BlankFragment.displayError(BlankFragment.java:51)
    at com.roscasend.android.testadd.LoginAsyncTask.onPostExecute(LoginAsyncTask.java:37)
    at com.roscasend.android.testadd.LoginAsyncTask.onPostExecute(LoginAsyncTask.java:15)***

С наилучшими пожеланиями, Aurelian

Ответы [ 4 ]

0 голосов
/ 05 января 2019

Попробуйте добавить это в свой фрагмент

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);

    if(isVisibleToUser && isResumed())
        onResume();
}

@Override
public void onResume() {
    super.onResume();

    if(!getUserVisibleHint()){
        return;
    }
    // Populate UI
}
0 голосов
/ 04 января 2019

Если вы попытаетесь показать FragmentDialog, используя commitAllowingStateLoss() вместо commit(), проблема будет решена.Чтобы имитировать родительский метод show и устанавливать внутренние поля, мы должны использовать отражение.Вы можете переопределить метод show в вашем классе InfoDialogFragment следующим образом:

import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;

import java.lang.reflect.Field;

public class InfoDialogFragment extends DialogFragment {

    @Override
    public void show(FragmentManager manager, String tag) {
        try {
            Field mDismissedField = DialogFragment.class.getField("mDismissed");
            mDismissedField.setAccessible(true);
            mDismissedField.setBoolean(this, false);

            Field mShownByMeField = DialogFragment.class.getField("mShownByMe");
            mShownByMeField.setAccessible(true);
            mShownByMeField.setBoolean(this, true);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        FragmentTransaction ft = manager.beginTransaction();
        ft.add(this, tag);
        ft.commitAllowingStateLoss();
    }

    // Other parts of code ...

}
0 голосов
/ 04 января 2019
IllegalStateException: Can not perform this action after onSaveInstanceState

Android FragmentManager использует пакет savedInstanceState для отслеживания того, какие фрагменты в данный момент отображаются пользователю.Если состояние сохраняется, и вы пытаетесь выполнить фрагментную транзакцию, система падает, потому что транзакция никогда не будет видна пользователю.

В более новых версиях библиотеки поддержки предлагается метод isStateSaved() для FragmentManager, который выможно использовать, чтобы проверить, действительно ли вы находитесь в безопасном окне, чтобы показать диалоговое окно.

@Override
public void displayError(int messageId) {
    FragmentManager fm = getFragmentManager();
    if (!fm.isStateSaved()) {
        AppUtil.showErrorDialog(fm, R.string.app_name);
    }
}

Однако это все равно оставляет проблему.Если AsyncTask завершает работу и пытается показать ошибку, когда состояние уже сохранено, пользователь никогда не увидит ошибку.

Вы можете получить запись приложения, в которой попытался показать ошибку, а затем также выполните проверку, когда ваше приложение возобновляет работу, чтобы увидеть, должно ли оно показывать ошибку сейчас.Нам нужно место для сохранения информации, которую мы пытались показать в диалоговом окне, и это не может быть просто boolean для действия / фрагмента (потому что это может привести к той же проблеме сохранения состояния).Я рекомендую SharedPreferences для хранения информации.

Добавьте этот код к вашему фрагменту:

private static final String SHOW_DIALOG = "SHOW_DIALOG";

private SharedPreferences getSharedPreferences() {
    return getActivity().getPreferences(Context.MODE_PRIVATE);
}

И затем используйте этот код, чтобы отобразить диалоговое окно:

@Override
public void displayError(int messageId) {
    FragmentManager fm = getFragmentManager();

    if (!fm.isStateSaved()) {
        AppUtil.showErrorDialog(getFragmentManager(), R.string.app_name);
    } else {
        getSharedPreferences()
                .edit()
                .putBoolean(SHOW_DIALOG, true)
                .apply();
    }
}

И этот код для отображения диалогового окна при возобновлении работы приложения:

@Override
public void onResume() {
    super.onResume();

    SharedPreferences prefs = getSharedPreferences();
    boolean triedToShowInBackground = prefs.getBoolean(SHOW_DIALOG, false);

    if (triedToShowInBackground) {
        AppUtil.showErrorDialog(getFragmentManager(), R.string.app_name);
        prefs.edit().remove(SHOW_DIALOG).apply();
    }
}
0 голосов
/ 27 декабря 2018

фрагмент всегда прикреплен, даже если ваше приложение находится в фоновом режиме, я думаю, что вы можете использовать isVisible (), вернуть true, если фрагмент в данный момент виден пользователю.так что если фрагмент виден, покажите диалог

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