当应用程序处于 onStop() 状态时如何启动对话框?

Posted

技术标签:

【中文标题】当应用程序处于 onStop() 状态时如何启动对话框?【英文标题】:How to launch a dialog when app is in onStop() state? 【发布时间】:2018-10-01 13:15:54 【问题描述】:

我正在创建一个使用线程的计时器应用程序。当我使用主页按钮退出应用程序时,计时器运行正常。通常,当时间到了时,会启动一个对话框,询问用户一些输入。如果应用程序处于其 onResume() 状态,这完全可以正常工作,但是当应用程序处于其 onStop() 状态时,对话框将不会启动并引发错误。

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

我怎样才能做到这一点,当时间到了并且应用程序不在前台时,对话框仍然会启动。我最初的想法是节省捆绑包中剩余的时间,但剩余时间会随着每个滴答声而变化。然后我考虑在包 mTimeRunning 中存储一个布尔值。然而,当时间到了,这个值也必须改变。所以现在我正在画一个空白。我该怎么做才能在应用不在前台时启动对话框?

TimerActivity.java

public class TimerActivity extends AppCompatActivity implements TimeDialogFragment.sendMinutes, 
TimeFinishDialogFragment.sendResponse, BreakFinishDialogFragment.userResponse 


// Variable to log activity state
private static final String TAG = "TimerActivity";
private static final boolean DEBUG = true;
// ^^ Variable used to log acitivty state


private Handler mHandler;
private Runnable mRunnable;

//private static final long START_TIME_MILLISECONDS = 600000;
// Below start time is for development purposes only
private static long mStartTime = 10000;
private long mTimeRemaining = mStartTime;

private boolean mTimeRunning;

private boolean mBreakTime = false;

private ProgressBar mTimeBar;
private TextView mTime;
private Button mStartPause;

private Button mSetTime;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_timer);

    if(DEBUG) Log.d(TAG, "+ onCreate() +");

    mHandler = new Handler();


    mTimeBar = findViewById(R.id.time_bar);
    mTime = findViewById(R.id.text_view_time);
    mStartPause = findViewById(R.id.button_start_pause);

    mSetTime = findViewById(R.id.button_set_time);
    updateCountDownText();
    mTimeBar.setMax((int) mTimeRemaining);


    mSetTime.setOnClickListener(new View.OnClickListener() 
        @Override
        public void onClick(View view) 
            //FragmentManager manager = getSupportFragmentManager();
            DialogFragment setTime = new TimeDialogFragment();
            setTime.show(getFragmentManager(),"SET_TIME_DIALOG");
        
    );


    mStartPause.setOnClickListener(new View.OnClickListener() 
        @Override
        public void onClick(View view) 
            if(mTimeRunning)
                //pauseTimer();
                mTimeRunning = false;
                mStartPause.setText("Start");
                mHandler.removeCallbacks(mRunnable);
            else
                //startTimer();
                timer();
            
        
    );


// Using a handler + anon runnable

private void timer()
    mRunnable = new Runnable() 
        @Override
        public void run() 
            mTimeRunning = true;
            mStartPause.setText("Pause");
            mTimeRemaining = mTimeRemaining - 1000;
            updateCountDownText();
            mTimeBar.incrementProgressBy(1000);
            if(mTimeRemaining > 0) 
                mHandler.postDelayed(this, 1000);
            else
                // if breaktime is false
                if(!mBreakTime) 

                    DialogFragment dialog = new TimeFinishDialogFragment();
                    dialog.show(getFragmentManager(),"TIME_FINISH_DIALOG");

                    mTimeRunning = false;
                else
                    // launch break time up dialog.
                    mBreakTime = false;

                    DialogFragment dialog = new BreakFinishDialogFragment();
                    dialog.show(getFragmentManager(), "BREAK_FINSIH_DIALOG");

                
            
        
    ;
    mHandler.postDelayed(mRunnable,1000);



public void updateCountDownText()
    int min = (int) (mTimeRemaining / 1000) / 60;
    int sec = (int) (mTimeRemaining / 1000) % 60;

    String formattedString = String.format(Locale.getDefault(), "%02d:%02d", min, sec);

    mTime.setText(formattedString);


public void setCountDownText(long time)
    int min = (int) (time / 1000) / 60;
    int sec = (int) (time / 1000) % 60;

    String formattedString = String.format(Locale.getDefault(), "%02d:%02d", min, sec);

    mTime.setText(formattedString);


@Override
public void userTime(int minutes) 
    TimerActivity.mStartTime = (minutes * 60) * 1000;
    mTimeRemaining = TimerActivity.mStartTime;
    mTimeBar.setMax((int) mTimeRemaining);

    setCountDownText(mTimeRemaining);


@Override
public void sendResponse(int val) 
    if(val == -1)
        mTimeRemaining = TimerActivity.mStartTime;
        mTimeBar.setMax((int) mTimeRemaining);
        updateCountDownText();
        mTimeBar.setProgress(0);
        mStartPause.setText("Start");
    else if(val == 1) 
        mBreakTime = true;
        mTimeRemaining = 15000;
        mTimeBar.setMax((int) mTimeRemaining);
        setCountDownText(mTimeRemaining);
        mTimeBar.setProgress(0);
        mStartPause.setVisibility(View.INVISIBLE);
        timer();
    else 
        mTimeRemaining = TimerActivity.mStartTime;
        mTimeBar.setMax((int) mTimeRemaining);
        updateCountDownText();
        mTimeBar.setProgress(0);
        timer();
    


@Override
public void userResponse(int val) 
    if(val < 0) 
        // user clicked cance
        mTimeRemaining = TimerActivity.mStartTime;
        mTimeBar.setMax((int) mTimeRemaining);
        updateCountDownText();
        mTimeBar.setProgress(0);
        mStartPause.setText("Start");
    else 
        mTimeRemaining = TimerActivity.mStartTime;
        mTimeBar.setMax((int) mTimeRemaining);
        updateCountDownText();
        mTimeBar.setProgress(0);
        timer();
    

    mStartPause.setVisibility(View.VISIBLE);

TimeFinishedDialog.java

public class TimeFinishDialogFragment extends DialogFragment implements View.OnClickListener

private Button mCancel;
private Button mSkip;
private Button mStartBreak;

private sendResponse mResponse;
interface sendResponse
    void sendResponse(int val);


@Override
public Dialog onCreateDialog(Bundle savedInstanceState) 
    //return super.onCreateDialog(savedInstanceState);
    View view = LayoutInflater.from(getActivity()).inflate(R.layout.time_finish_dialog_fragment, null, false);

    mCancel = view.findViewById(R.id.button_cancel);
    mSkip = view.findViewById(R.id.button_skip);
    mStartBreak = view.findViewById(R.id.button_start_break);

    mCancel.setOnClickListener(this);
    mSkip.setOnClickListener(this);
    mStartBreak.setOnClickListener(this);

    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setView(view)
           .setTitle("Start Break?");

    AlertDialog dialog = builder.create();
    dialog.setContentView(view);
    return dialog;


@Override
public void onClick(View view) 
    switch(view.getId())
        case R.id.button_cancel:
            mResponse.sendResponse(-1);
            getDialog().dismiss();
            break;
        case R.id.button_skip:
            mResponse.sendResponse(0);
            getDialog().dismiss();
            break;
        case R.id.button_start_break:
            mResponse.sendResponse(1);
            getDialog().dismiss();
            break;
        default:
            break;
    


@Override
public void onAttach(Context context) 
    super.onAttach(context);
    try
        mResponse = (sendResponse) getActivity();
    catch (ClassCastException e)
        System.out.println("Error: " + e);
    


【问题讨论】:

【参考方案1】:

当时间到并且应用不在前台时,对话框仍会启动

你不应该这样做,因为这会打断用户做他们当时可能正在做的其他工作,这可能会让人恼火。

当应用不在前台时,我该怎么做才能启动对话框?

您可以仅在用户正在积极使用您的应用程序时显示对话框,也可以在用户未使用应用程序时退回以显示通知

如果你非常想显示一个对话框,你可以试试dialog themed activity

【讨论】:

通知听起来不错,但我怎样才能让它在用户阅读通知然后打开应用程序后已经启动了一个对话框?

以上是关于当应用程序处于 onStop() 状态时如何启动对话框?的主要内容,如果未能解决你的问题,请参考以下文章

Activity的生命周期

当手机处于锁定状态时,我可以用一些手势启动应用程序吗? [关闭]

当应用程序处于非运行状态时,推送通知委托触发的解决方案是啥?

android生命周期

当应用程序处于后台或终止状态时,如何找到用户位置?

设备屏幕关闭时如何打开手电筒?