重新启动活动后片段无法正常工作(onSaveInstanceState后无法执行此操作)

时间:2017-08-05 06:55:22

标签: android android-fragments

我的活动有问题。它在我第一次开始时效果很好但在此之后就崩溃了

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1842)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1860)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:650)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:609)

我尝试将此添加到我的活动但不起作用

@Override
protected void onSaveInstanceState(Bundle outState) {
    //No call for super(). Bug on API Level > 11.
}

当我使用commitAllowingStateLoss()代替commit()更改片段时,错误现在是活动已被销毁。我必须关闭该应用程序。我不知道为什么它只在第一次调用活动时运行。

这是我改变片段的功能

private void showWaitingFragment() {
    FragmentManager fragmentManager = this.getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    WaitingFragment fragment = new WaitingFragment();
    fragmentTransaction.replace(R.id.quiz_content_frame, fragment);

    fragmentTransaction.commit();
}

private void showAnswerFragment() {
    FragmentManager fragmentManager = this.getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    AnswerFragment fragment = new AnswerFragment();
    fragmentTransaction.replace(R.id.quiz_content_frame, fragment);

    fragmentTransaction.commit();
}

首先我在socket {i}上调用showWattingFragment发出事件后调用showAnswerFragment

套接字的代码

socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {

            @Override
            public void call(Object... args) {
                Log.d("socket_log", "connected");
            }

        }).on("quizQuestionReady", new Emitter.Listener() {

            @Override
            public void call(Object... args) {
                showWaitingFragment();
            }

        }).on("quizQuestionLoaded", new Emitter.Listener() {

            @Override
            public void call(Object... args) {
                showAnswerFragment();
            }

        }).on("quizEnded", new Emitter.Listener() {

            @Override
            public void call(Object... args) {
                showResultFragment();
            }

        }).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {

            @Override
            public void call(Object... args) {}

        });
        socket.connect();

正如我之前所说的,第一次我开始活动,在我再次呼叫onBackPress()finish()然后startActivity之后无法正常工作/> 更新我的活动

public class StudentQuizActivity extends AppCompatActivity {

@BindView(R.id.quiz_content_frame)
FrameLayout _frame_content;

public Socket socket;
String quiz_code;
public SharedPreferences prefs;
ProgressDialog progressDialog;
int user_id;
String token;
public int currentQuestionIndex = -1;
public int currentQuestionIndexForShowing = 0;
public int countCorrect = 0;

public boolean no_answer = true;

public ArrayList<String> answers;
public ArrayList<String> correct_answers;
public ArrayList<Boolean> corrects;

public JSONObject quizConfig;
public JSONArray quizQuestions;

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

    prefs = new SecurePreferences(this);

    quiz_code = prefs.getString(AppVariable.QUIZ_CODE, null);
    user_id = prefs.getInt(AppVariable.USER_ID, 0);
    token = prefs.getString(AppVariable.USER_TOKEN, null);

    ButterKnife.bind(this);

    this.setTitle("QUIZ");

    // prepare spinner
    progressDialog = new ProgressDialog(this, R.style.AppTheme_Dark_Dialog);
    progressDialog.setIndeterminate(true);
    progressDialog.setCanceledOnTouchOutside(false);

    prefs.edit().putString(AppVariable.QUIZ_MESSAGE, "Waiting for the quiz to start").apply();

    answers = new ArrayList<>();
    correct_answers = new ArrayList<>();
    corrects = new ArrayList<>();

    setSocket();

    new GetQuizTask().execute();
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    //No call for super(). Bug on API Level > 11.
}

//UI

private void showWaitingFragment() {
    FragmentManager fragmentManager = this.getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    WaitingFragment fragment = new WaitingFragment();
    fragmentTransaction.replace(R.id.quiz_content_frame, fragment);

    fragmentTransaction.commit();
}

private void showAnswerFragment() {
    FragmentManager fragmentManager = this.getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();


    AnswerFragment fragment = new AnswerFragment();
    fragmentTransaction.replace(R.id.quiz_content_frame, fragment);

    fragmentTransaction.commit();
}

private void showResultFragment() {
    FragmentManager fragmentManager = this.getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    ResultFragment fragment = new ResultFragment();
    fragmentTransaction.replace(R.id.quiz_content_frame, fragment);

    fragmentTransaction.commit();
}

public void showDetailFragment() {
    FragmentManager fragmentManager = this.getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    DetailFragment fragment = new DetailFragment();
    fragmentTransaction.replace(R.id.quiz_content_frame, fragment);

    fragmentTransaction.commit();
}

@Override
public void onBackPressed() {
    super.onBackPressed();
}

private class GetQuizTask extends AsyncTask<String, Void, Integer> {

    private Exception exception;
    private String strJsonResponse;

    @Override
    protected void onPreExecute() {
        progressDialog.setMessage("Loading...");
        progressDialog.show();
    }

    @Override
    protected Integer doInBackground(String... params) {
        int flag = 0;
        try {
            URL url = new URL(Network.API_GET_QUIZ);

            //prepare json data
            JSONObject jsonUserData = new JSONObject();
            jsonUserData.put("token", token);
            jsonUserData.put("quiz_code", quiz_code);

            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            try {

                connection.setReadTimeout(10000);
                connection.setConnectTimeout(15000);
                connection.setRequestMethod("POST");
                connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
                connection.setRequestProperty("Accept", "application/json");
                connection.setDoInput(true);
                connection.setDoOutput(true);

                //write
                OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
                writer.write(jsonUserData.toString());
                writer.flush();

                //check http response code
                int status = connection.getResponseCode();
                switch (status){
                    case HttpURLConnection.HTTP_OK:
                        //read response
                        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));

                        StringBuilder sb = new StringBuilder();
                        String line;

                        while ((line = bufferedReader.readLine()) != null) {
                            sb.append(line).append("\n");
                        }

                        bufferedReader.close();
                        strJsonResponse = sb.toString();

                        flag = HttpURLConnection.HTTP_OK;
                    default:
                        exception = new Exception(connection.getResponseMessage());
                }
            }
            finally{
                connection.disconnect();
            }
        }
        catch(Exception e) {
            exception = e;
        }
        return flag;
    }

    @Override
    protected void onPostExecute(Integer status) {
        if (status != HttpURLConnection.HTTP_OK){
            displayToast(exception.getMessage());
        }
        else {
            try{
                JSONObject jsonObject = new JSONObject(strJsonResponse);
                String result = jsonObject.getString("result");

                if (result.equals("failure")){
                    String message = jsonObject.getString("message");
                    progressDialog.dismiss();
                    displayToast(message);
                    return;
                }

                quizConfig = jsonObject.getJSONObject("quiz");
                quizQuestions = quizConfig.getJSONArray("questions");

                prefs.edit().putInt(AppVariable.QUIZ_TOTAL, quizQuestions.length())
                        .putString(AppVariable.QUIZ_TITLE, quizConfig.getString("title")).apply();

                progressDialog.dismiss();
                showWaitingFragment();
                return;

            } catch (JSONException e) {
                e.printStackTrace();
                displayToast(e.getMessage());
            }
        }
        progressDialog.dismiss();
    }
}

//Socket
private void setSocket(){
    if (Network.isOnline(this)){
        try {
            socket = IO.socket(Network.HOST);
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {

            @Override
            public void call(Object... args) {
                Log.d("socket_log", "connected");
            }

        }).on("quizQuestionReady", new Emitter.Listener() {

            @Override
            public void call(Object... args) {
                currentQuestionIndexForShowing += 1;
                prefs.edit().putString(AppVariable.QUIZ_MESSAGE, "Ready for the next question")
                        .putInt(AppVariable.QUIZ_INDEX, currentQuestionIndexForShowing).apply();
                showWaitingFragment();
            }

        }).on("quizQuestionLoaded", new Emitter.Listener() {

            @Override
            public void call(Object... args) {
                currentQuestionIndex += 1;
                showAnswerFragment();
            }

        }).on("quizQuestionEnded", new Emitter.Listener() {

            @Override
            public void call(Object... args) {
            }

        }).on("quizEnded", new Emitter.Listener() {

            @Override
            public void call(Object... args) {
                prefs.edit()
                        .putInt(AppVariable.QUIZ_INDEX, 0).apply();
                showResultFragment();
            }

        }).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {

            @Override
            public void call(Object... args) {}

        });
        socket.connect();
    }
}

public void emitAnswer(String option){
    JSONObject payload = new JSONObject();
    try {
        payload.put("quiz_code", quiz_code);
        payload.put("question_index", currentQuestionIndex);
        payload.put("option", option);
        payload.put("student_id", user_id);
    } catch (JSONException e) {
        e.printStackTrace();
    }

    no_answer = false;

    try {
        JSONObject quizDetail = quizQuestions.getJSONObject(currentQuestionIndex);
        String correct_option = quizDetail.getString("correct_option");

        ArrayList<String> options = new ArrayList<>();

        options.add(quizDetail.getString("option_a"));
        options.add(quizDetail.getString("option_b"));
        options.add(quizDetail.getString("option_c"));
        options.add(quizDetail.getString("option_d"));

        String[] keys = {"a", "b", "c", "d"};

        for (int i = 0; i < 4; i++){
            if (correct_option.equals(options.get(i))){
                correct_option = keys[i];
                break;
            }
        }

        if (option.equals(correct_option)){
            corrects.add(true);
            countCorrect += 1;
        }
        else {
            corrects.add(false);
        }

        correct_answers.add(correct_option);
    } catch (JSONException e) {
        e.printStackTrace();
    }

    answers.add(option);

    socket.emit("answeredQuiz", payload);
}
}

2 个答案:

答案 0 :(得分:1)

在代码中需要检查很多东西。让我们逐一看看。

  1. 内心课filesCSV <- function(id = 1:332){ library(stringr) # Append 0 ids in front fileNames <- paste(str_pad(id, 3, pad = "0"),".csv", sep = "") # Initially the data is NULL the_data <- NULL for(i in seq_along(id) { # Read the data in dat object dat <- read.csv(fileNames[i], header = TRUE) if(is.null(the_data) # For the first pass when dat is NULL { the_data <- dat }else{ # For all other passes theData <- rbind(theData, dat) } } return(the_data) }
  2. 这是一项API请求任务,可能需要很长时间。默认情况下,如果一切正常,GetQuizTask将帮助您在AsyncTask中处理,然后将结果发布到background

    UI thread中,您隐式调用onPostExecute(Integer status),与showWaitingFragment()相同。

    这意味着您在this.showWaitingFragment()内持有父参考。这可能会导致内存泄漏。示例:如果您的请求需要10秒才能完成,但在调用inner class之前,如果您的活动被销毁或在10秒之前更改其状态会发生什么? (有很多事情可以让你改变活动,就像你打电话onPostExecute(),旋转屏幕,系统必须清理内存,用户按finish()按钮等...)。因此,当这些事情发生时,活动(或Home中的this)无法通过垃圾收集收集。这会导致内存泄漏。

    还有一件事。如果您的活动从前台更改为后台(用户按this. showWaitingFragment()按钮),则Home需要在UI线程中执行onPostExecute,现在无法完成,会发生什么然后?

    这些错误会为您提供showFragment

    解决方案:使用WeakPreference,如下所示:

    IllegalStateException
    1. 避免使用样板代码
    2. 显示片段有很多功能,可以像这样简化

      private class GetQuizTask extends AsyncTask<String, Void, Integer> {
          WeakReference<StudentQuizActivity> studentQuizActivity;
      
          @Override
          protected void onPreExecute() {
              // do your work before the task execute..
          }
      
          @Override
          protected Integer doInBackground(String... params) {
              // do your work in background..
          }
      
          @Override
          protected void onPostExecute(Integer status) {
              try{
                  if (studentQuizActivity.get() != null) {
                      studentQuizActivity.get().showInfo();
                  }
              } catch (Exception e) {
              }
          }
      }
      

      然后

      private void showFragment(Fragment fragment) {
          FragmentManager fragmentManager = this.getSupportFragmentManager();
          FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
          fragmentTransaction.replace(R.id.quiz_content_frame, fragment);
          fragmentTransaction.commit();
      }
      
      1. showFragment(yourResultFragment); showFragment(yourTransactionFragment); 使用dismiss之前,请检查ProgressDialog。因为用户可以在if (progressDialog.isShowing())执行之前将其解雇。

      2. 请解密您的代码。不要把内心的一切都推到你的内心阶层。它很容易引发大量错误。还有一件事,如果没有特殊用例,请尝试使用静态内部类。请遵循此answers

      3. 我无法检测到你的所有错误,因为我无法了解你的情况,但我希望这有帮助!仔细地重写您的代码,一个接一个。

答案 1 :(得分:0)

这很简单。

您应该使用commitAllowingStateLoss()而不是commit()

transaction.commitAllowingStateLoss();