0

人が運動をするのにかかる時間を測定する活動をしていますが、まだ解決できていないバグがあります...

TrainingFragment には、ユーザーがクリックできるエクササイズのリストが表示され、ExerciseActivity が起動され、変数「remainingsSets」が 0 に設定されるまで実行されます。

どのエクササイズでも初めてクリックすると、すべてが正常に機能し、ExerciseActivity が正しく機能し、TrainingFragment に戻ります。しかし、別のエクササイズをクリックしようとすると、ExerciseActivity が閉じられてしまいます。

私のデバッグでは、変数「remainingSets」に正しい値remainingSets = getIntent().getIntExtra("remaining_sets", 3)が含まれていることがわかりました (この状態:if (remainingSets > 0){...}

ここに私のTrainingFragmentがあります:

public class TrainingFragment extends Fragment {

private final static int START_EXERCISE = 1;

private Training training;
private String lastItemClicked;
private String[] values;

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    Bundle bundle = getArguments();
    if (bundle != null) {
        training = bundle.getParcelable("training");
    }
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    return (ScrollView) inflater.inflate(R.layout.template_exercises, container, false);
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    LinearLayout exercisesContainer = (LinearLayout) getView().findViewById(R.id.exercises);
    LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    List<Exercise> exercises = training.getExercises();
    values = new String[exercises.size()];

    if (savedInstanceState != null) {
        values = savedInstanceState.getStringArray("values");
    }

    for (int i = 0; i < exercises.size(); i++) {
        final View exerciseView = inflater.inflate(R.layout.template_exercise, null);

        exerciseView.setTag(String.valueOf(i));

        TextView remainingSets = (TextView) exerciseView.findViewById(R.id.remaining_sets);

        if (savedInstanceState != null) {
            remainingSets.setText(values[i]);
        } else {
            String sets = exercises.get(i).getSets();
            remainingSets.setText(sets);
            values[i] = sets;
        }

        exerciseView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getActivity(), ExerciseActivity.class);
                intent.putExtra("remaining_sets",
                        Integer.valueOf(((TextView) v.findViewById(R.id.remaining_sets)).getText().toString()));

                lastItemClicked = v.getTag().toString();

                startActivityForResult(intent, START_EXERCISE);
            }
        });

        exercisesContainer.addView(exerciseView);
    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putStringArray("values", values);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    View view = ((LinearLayout) getView().findViewById(R.id.exercises)).findViewWithTag(lastItemClicked);

    if (requestCode == START_EXERCISE) {
        if (resultCode == Activity.RESULT_OK) { // the exercise had been
                                                // finished.
            ((TextView) view.findViewById(R.id.remaining_sets)).setText("0");

            view.setClickable(false);

            values[Integer.valueOf(lastItemClicked)] = "0";

        } else if (resultCode == Activity.RESULT_CANCELED) {

            String remainingSets = data.getStringExtra("remaining_sets");
            ((TextView) view.findViewById(R.id.remaining_sets)).setText(remainingSets);

            values[Integer.valueOf(lastItemClicked)] = remainingSets;
        }
    }
}
}

私の運動活動:

public class ExerciseActivity extends Activity {

private Chronometer chronometer;
private TextView timer;
private Button startButton;
private Button endButton;
private int remainingSets;

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

    ExerciseEvents.addExerciseListener(new PopupExerciseListener());

    chronometer = (Chronometer) findViewById(R.id.exercise_doing_timer);
    timer = (TextView) findViewById(R.id.timer);

    startButton = (Button) findViewById(R.id.start_exercise);
    startButton.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            ExerciseEvents.onExerciseBegin();
        }
    });

    endButton = (Button) findViewById(R.id.end_exercise);
    endButton.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            ExerciseEvents.onExerciseRest();
        }
    });
}

@Override
public void onBackPressed() {
    Intent intent = new Intent();
    intent.putExtra("remaining_sets", String.valueOf(remainingSets));
    setResult(RESULT_CANCELED, intent);

    super.onBackPressed();
}

public class PopupExerciseListener implements ExerciseListener {

    public PopupExerciseListener() {
        remainingSets = getIntent().getIntExtra("remaining_sets", 3);
    }

    @Override
    public void onExerciseBegin() {
        if (remainingSets > 0) {
            chronometer.setVisibility(View.VISIBLE);
            timer.setVisibility(View.GONE);

            chronometer.setBase(SystemClock.elapsedRealtime());
            chronometer.start();

            startButton.setVisibility(View.GONE);
            endButton.setVisibility(View.VISIBLE);
        } else {
            ExerciseEvents.onExerciseFinish();
        }
    }

    @Override
    public void onExerciseFinish() {
        setResult(RESULT_OK);

        finish();
    }

    @Override
    public void onExerciseRest() {
        chronometer.setVisibility(View.GONE);
        endButton.setVisibility(View.GONE);
        timer.setVisibility(View.VISIBLE);

        long restTime = getIntent().getLongExtra("time_to_rest", 60) * 1000;
        new CountDownTimer(restTime, 1000) {

            @Override
            public void onTick(long millisUntilFinished) {
                timer.setText(String.valueOf(millisUntilFinished / 1000));
            }

            @Override
            public void onFinish() {
                ExerciseEvents.onExerciseBegin();
            }
        }.start();

        remainingSets--;
    }
}
}

そして私のExerciseEvents:

public class ExerciseEvents {

private static LinkedList<ExerciseListener> mExerciseListeners = new LinkedList<ExerciseListener>();

public static void addExerciseListener(ExerciseListener listener) {
    mExerciseListeners.add(listener);
}

public static void removeExerciseListener(String listener) {
    mExerciseListeners.remove(listener);
}

public static void onExerciseBegin() {
    for (ExerciseListener l : mExerciseListeners) {
        l.onExerciseBegin();
    }
}

public static void onExerciseRest() {
    for (ExerciseListener l : mExerciseListeners) {
        l.onExerciseRest();
    }
}

public static void onExerciseFinish() {
    for (ExerciseListener l : mExerciseListeners) {
        l.onExerciseFinish();
    }
}

public static interface ExerciseListener {

    public void onExerciseBegin();

    public void onExerciseRest();

    public void onExerciseFinish();
}
}

誰か助けてくれませんか?

4

1 に答える 1

1

コードを更新した後、コードに大きなメモリ リークがあることがわかりました。

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

    ExerciseEvents.addExerciseListener(new PopupExerciseListener());
    ....
}

この呼び出しは、静的/グローバル リストにExerciseEvents.addExerciseListener(new PopupExerciseListener())新しいものを追加します: . クラスは内部クラスであるため、それを囲む への参照を暗黙的に保持します。これは、コードが永遠に各インスタンスを保持していることを意味します。良くない。PopupExerciseListenerExcerciseEvents.mExerciseListenersPopupExerciseListenerExcerciseActivityExcerciseActivity

これは、あなたが見ている奇妙な行動を説明するかもしれません. メソッドの 1 つonExcersizeXXX()が呼び出されると、リンクされたリスト内のすべての ExcerciseListeners、前の画面および現在の画面のものが呼び出されます。

ExcerciseActivity.java でこれを試してください:

....
ExerciseListener mExerciseListener;
....

@Override
protected void onCreate(Bundle savedInstanceState) {
    ....
    ....
    mExerciseListener = new PopupExerciseListener()
    ExerciseEvents.addExerciseListener(mExerciseListener);
    ....
    ....
}

@Override
protected void onDestroy() {
    ExerciseEvents.removeExerciseListener(mExerciseListener);
    super.onDestroy();
}

....

ではonDestroy、リスナーの登録を解除して、メモリ リークを防ぎ、存在しなくなったアクティビティにアタッチされた PopupExerciseListeners への奇妙な複数のコールバックを防ぎます。

于 2013-02-28T20:13:19.500 に答える