2017-08-05 100 views
0

我的活動有問題。它很好的工作,我第一次開始,但與此重新啓動活動後,片段不起作用(onSaveInstanceState後無法執行此操作)

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

首先我打電話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再次
UPDATE我的活動

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); 
} 
} 
+0

顯示你的活動也執行替換片段 –

+0

@PhátPhát我已經展示了替換片段的功能已經 –

+0

我只是想檢查你的活動是否有任何內存泄漏。發生此異常的原因是,當活動被銷燬或其狀態已更改時,您會顯示碎片(應該在UI線程中運行)。 –

回答

0

這很簡單。

您應該使用commitAllowingStateLoss(),而不是提交()

transaction.commitAllowingStateLoss(); 
+0

當我使用commitAllowingStateLoss()而不是commit()來改變Fragment時,現在的錯誤是Activity已經被銷燬。我必須關閉應用程序。我不知道爲什麼它只在第一次被稱爲活動時運行。 –

1

有很多事情需要檢查你的代碼中。讓我們一個一個來看看。

  1. 內部類GetQuizTask

這可能要花費很多時間API請求任務。默認情況下,如果一切正常,AsyncTask將幫助您在background中處理它,然後將結果發佈在UI thread

在您的onPostExecute(Integer status)中,您隱式呼叫showWaitingFragment(),它與this.showWaitingFragment()相同。

這意味着您在inner class中持有父引用。這可能會導致內存泄漏。示例:如果您的請求需要10秒鐘完成,但在撥打onPostExecute()之前,如果您的活動被破壞或在10秒之前更改其狀態,會發生什麼情況? (有很多事情可以讓你改變活動,如你打電話finish(),旋轉屏幕,系統必須清理內存,用戶按Home按鈕等)。所以當這些事情發生時,活動(或)不能被垃圾回收收集)。這導致你到內存泄漏。

還有一件事。如果您的活動從前景更改爲背景(用戶按Home按鈕),則onPostExecute需要在UI線程中執行showFragment,這不能立即完成,那麼會發生什麼?

這些錯誤會給你IllegalStateException

解決方案:利用WeakPreference,這樣的事情:

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

    然後

    showFragment(yourResultFragment); 
    showFragment(yourTransactionFragment); 
    
  • 使用dismissProgressDialog之前,檢查if (progressDialog.isShowing())。因爲用戶可以在執行onPostExecute()之前將其解僱。

  • 請解耦您的代碼。不要把一切都推到你的內心階層。它會很容易導致你大量的錯誤。還有一件事,如果沒有特殊用例,請嘗試使用靜態內部類。按照這個answers

  • 我不能檢測所有的錯誤,因爲我不明白你的情況,但我希望這有助於!小心地逐個重寫你的代碼。

    +0

    感謝您的解釋,但爲什麼它在第一次工作,然後當我再次開始它崩潰 –

    +0

    放慢你的連接,然後殺死應用程序或旋轉,看看是否真的工作 –

    相關問題