Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/189.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 我的应用程序在后台运行时试图显示AsyncTask的错误消息时崩溃_Android_Android Asynctask - Fatal编程技术网

Android 我的应用程序在后台运行时试图显示AsyncTask的错误消息时崩溃

Android 我的应用程序在后台运行时试图显示AsyncTask的错误消息时崩溃,android,android-asynctask,Android,Android Asynctask,我正在显示一条从AsyncTask到我的片段的错误消息,但当我的应用程序在后台时,应用程序正在崩溃 代码如下: import android.support.v4.app.FragmentManager; public class AppUtil { public static void showErrorDialog(FragmentManager fm, int messageResourceId) { if (fm != null && !fm.

我正在显示一条从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();
    }
}
致以最良好的祝愿,
Aurelian

即使你的应用程序在后台,片段也会被附加,我认为你可以使用isVisible(),如果片段当前对用户可见,则返回true。因此,如果片段在show dialog中可见,那么即使你的应用程序在后台,片段也始终会被附加,我认为你可以使用isVisible(),如果片段当前对用户可见,则返回true。因此,如果片段可见,则显示对话框

如果尝试使用
committellowingstateloss()
而不是
commit()
显示
FragmentDialog
,则问题会得到解决。要模拟父
show
方法并设置内部字段,必须使用反射。您可以在
InfoDialogFragment
类中重写
show
方法,如下所示:

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 ...

}

如果尝试使用
commitAllowingStateLoss()
而不是
commit()
来显示
FragmentDialog
,问题就会得到解决。要模拟父
show
方法并设置内部字段,必须使用反射。您可以在
InfoDialogFragment
类中重写
show
方法,如下所示:

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 ...

}
Android FragmentManager使用
savedInstanceState
捆绑包跟踪当前向用户显示的片段。如果状态被保存,并且您尝试执行一个片段事务,那么系统将崩溃,因为用户永远看不到该事务

较新版本的支持库在FragmentManager上提供了一个
isStateSaved()
方法,您可以使用该方法检查您是否在显示对话框的安全窗口中

@Override
public void displayError(int messageId) {
    FragmentManager fm = getFragmentManager();
    if (!fm.isStateSaved()) {
        AppUtil.showErrorDialog(fm, R.string.app_name);
    }
}
然而,这仍然给您留下了一个问题。如果AsyncTask完成并试图在状态已保存时显示错误,则用户将永远不会看到错误

你可以让应用程序记录它试图显示错误,然后在你的应用程序恢复时执行检查,看它现在是否应该显示错误。我们需要一个地方来保存我们试图显示对话框的信息,它不能只是活动/片段上的
布尔值(因为这会受到相同的状态保存问题)。我建议
SharedReferences
存储信息

将此代码添加到片段中:

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();
    }
}
Android FragmentManager使用
savedInstanceState
捆绑包跟踪当前向用户显示的片段。如果状态被保存,并且您尝试执行一个片段事务,那么系统将崩溃,因为用户永远看不到该事务

较新版本的支持库在FragmentManager上提供了一个
isStateSaved()
方法,您可以使用该方法检查您是否在显示对话框的安全窗口中

@Override
public void displayError(int messageId) {
    FragmentManager fm = getFragmentManager();
    if (!fm.isStateSaved()) {
        AppUtil.showErrorDialog(fm, R.string.app_name);
    }
}
然而,这仍然给您留下了一个问题。如果AsyncTask完成并试图在状态已保存时显示错误,则用户将永远不会看到错误

你可以让应用程序记录它试图显示错误,然后在你的应用程序恢复时执行检查,看它现在是否应该显示错误。我们需要一个地方来保存我们试图显示对话框的信息,它不能只是活动/片段上的
布尔值(因为这会受到相同的状态保存问题)。我建议
SharedReferences
存储信息

将此代码添加到片段中:

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();
    }
}

试着在你的片段中添加这个

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

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

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

    if(!getUserVisibleHint()){
        return;
    }
    // Populate UI
}

试着在你的片段中添加这个

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

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

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

    if(!getUserVisibleHint()){
        return;
    }
    // Populate UI
}

isAdded()返回true,因为当应用程序处于后台时,您的片段未断开连接。您应该重写onPause()和onResume(),以检查片段是否在后台。你的例外情况是,当你的应用程序在后台时,你试图打开一个对话框。我知道,但我正在寻找一个比覆盖onPause和onresume更好的解决方案。我可以使用isResumed()isAdded()返回true,因为当你的应用程序在后台时,你的片段没有断开。您应该重写onPause()和onResume(),以检查片段是否在后台。你的例外情况是,当你的应用程序在后台时,你试图打开一个对话框。我知道,但我正在寻找一个比覆盖onPause和onresume更好的解决方案。我可以使用isResumed()isVisible返回true,即使片段和活动在后台。因此它仍然崩溃。即使片段和活动在后台,isVisible返回true。所以它仍然是崩溃。