如何在片段中为计时器启动新线程

How to start new thread for chronometer in fragment

我正在制作一个计时器应用程序。它在我的旧设备上明显滞后,但在新设备上却没有那么多。所以经过研究,我明白我必须创建一个新线程并从中更新 UI 线程。但无论如何我都无法摆脱这种滞后。

这里是精简为重要组件的计时器片段。任何帮助,将不胜感激。谢谢

public class ChronometerFragment 扩展片段 { private static final String TAG = "ChronometerFragment";

private Button btnRestart,btnStartStop;
private TextView time;
private int currentTime;
private long startTime = 0L;
private Handler customHandler = new Handler();

long timeInMilliseconds = 0L;
long timeSwapBuff = 0L;
long updatedTime = 0L;
private int running = 0;

私人时间转换器时间转换器; CycleDataListener 回调;

DecimalFormat df = new DecimalFormat("#.#####");

public ChronometerFragment(){}

public interface CycleDataListener{
    void fragmentDataUpdate(Bundle  bundle);
}

@Override
public void onAttach(@NonNull Context context) {
    super.onAttach(context);
    callback = (CycleDataListener)getActivity();
}

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

}

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    final View view = inflater.inflate(R.layout.fragment_chronometer,container,false);
    initWidgets(view);
    view.setKeepScreenOn(true);
    df.setRoundingMode(RoundingMode.CEILING);
    timeConverter = new TimeConverter();

    btnRestart.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d(TAG, "onClick: button restart" );
            resetTime();
        }
    });



    btnStartStop.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d(TAG, "onClick: button startStop");
            btnStartStop.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);

            if(running == 0){
                running = 1;
                btnStartStop.setText(R.string.stop);
                startTime = SystemClock.uptimeMillis();
               customHandler.postDelayed(updateTimerThread, 0);
            }else{
                running = 0;
                btnStartStop.setText(R.string.start);
                timeSwapBuff += timeInMilliseconds;
                customHandler.removeCallbacks(updateTimerThread);
            }
        }
    });

    
    return view;

}

@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
}

private Runnable updateTimerThread = new Runnable() {
    @Override
    public void run() {
        timeInMilliseconds = SystemClock.uptimeMillis() - startTime;
        updatedTime = timeSwapBuff + timeInMilliseconds;

        timeConverter.setUpTime(updatedTime);
        time.setText(timeConverter.getTimeStringFormat());
        customHandler.postDelayed(this, 0);

    }
};



/**
 * *********************helper methods**********************
 */



private void resetTime(){
    running = 0;
    btnStartStop.setText(R.string.start);
    timeSwapBuff += timeInMilliseconds;
    startTime = 0L;
    timeInMilliseconds = 0L;
    timeSwapBuff = 0L;
    updatedTime = 0L;
    customHandler.removeCallbacks(updateTimerThread);
    time.setText("00:00:00:000");
    
}



private void initWidgets(View v){
    btnRestart = v.findViewById(R.id.restart_button);
    btnStartStop = v.findViewById(R.id.start_stop_button);
    time = v.findViewById(R.id.currentTime);
  
}

}

此行导致滞后。您几乎立即在主线程上发布了 runnable。

customHandler.postDelayed(this, 0);

改为

customHandler.postDelayed(this, 10);

最好的解决方案是只使用主处理程序来更新 UI。时间计算和转换应由与 HandlerThread 关联的后台处理程序处理,以避免 UI 阻塞。

private Handler mainHandler = new Handler(Looper.getMainLooper());

private HandlerThread handlerThread = new HandlerThread("MyHandlerThread");

private Handler backgroundHandler;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    handlerThread.start();
    backgroundHandler = new Handler(handlerThread.getLooper());
}

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    ...
    btnStartStop.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d(TAG, "onClick: button startStop");
            btnStartStop.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);

            if (running == 0){
                running = 1;
                btnStartStop.setText(R.string.stop);
                startTime = SystemClock.uptimeMillis();
                backgroundHandler.post(updateTimerThread);
            } else {
                running = 0;
                btnStartStop.setText(R.string.start);
                timeSwapBuff += timeInMilliseconds;
                backgroundHandler.removeCallbacks(updateTimerThread);
            }
        }
    });
}

private Runnable updateTimerThread = new Runnable() {
    @Override
    public void run() {
        timeInMilliseconds = SystemClock.uptimeMillis() - startTime;
        updatedTime = timeSwapBuff + timeInMilliseconds;

        timeConverter.setUpTime(updatedTime);
        final String timeWithFormat = timeConverter.getTimeStringFormat();
        mainHandler.post(() -> time.setText(timeWithFormat);)
        backgroundHandler.postDelayed(this, 10);

    }
};

@Override
public void onDestroy() {
    handlerThread.quitSafely();
    super.onDestroy()
}