Menggunakan utas dalam tampilan khusus

public class ThreadView extends View {

    Paint paint = new Paint();
    int count;
    Handler uiThread = new Handler();

    public ThreadView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint.setColor(Color.BLACK);
        paint.setTextSize(80);

        uiThread.post(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    count++;
                    invalidate();
                }
            }
        });
    }

    @Override
    public void onDraw(Canvas canvas) {
        canvas.drawText(count + "", 100, 100, paint);
    }
}

Saya melihat solusi ini untuk thread dalam tampilan khusus, tetapi tidak berhasil. Meskipun loop saya terus memanggil invalidate(), namun tidak terus memanggil onDraw() Saya juga melihat solusi di mana mereka mengimplementasikan Runnable dan mengganti run() apakah ini cara yang lebih baik untuk melakukannya?


person Derek Dawson    schedule 10.06.2015    source sumber
comment
Mungkin karena metode onDraw tidak dipanggil di dalam thread Anda. Anda hanya menambah variabel count. Mungkin Anda bisa mencoba memanggil metode drawText dari dalam Runnable Anda.   -  person Thyen Hong Guedes Chang    schedule 11.06.2015
comment
@ThyenHongGuedesChang: Dia memanggil invalidate() yang pada dasarnya menandai seluruh area tampilan klien sebagai perlu digambar ulang. Menggambar terjadi secara tidak sinkron dengan ini. Masalah sebenarnya adalah loop tak terbatas yang berjalan di UI Thread. Ini membuat UI kelaparan untuk melakukan apa pun.   -  person Michael Krause    schedule 11.06.2015


Jawaban (2)


Semua Tampilan sudah menyiapkan penangannya, sehingga tidak efisien untuk membuat penangan lainnya. Sebagai gantinya, Anda harus menggunakan metode post(Runnable action) kelas View atau postDelayed(Runnable action, long delayMillis).

Sejauh apa yang ingin Anda capai, saya berasumsi bahwa kode yang Anda berikan hanya sebagai contoh. Jika Anda benar-benar hanya mencoba memperbarui beberapa teks, menurut saya gunakan TextView dan panggil setText di atasnya. TextView menangani pembatalan dirinya sendiri ketika mengetahui teksnya telah berubah.

Tapi kembali ke pertanyaan Anda. Banyak hal bergantung pada kecepatan Anda ingin memperbarui rendering variabel count Anda. Saat ini Anda akan berjalan dalam putaran tanpa akhir di UI Thread. Hal ini pastinya akan menimbulkan masalah karena Anda akan mengirim spam ke handler/antrian kejadian dengan panggilan yang tidak valid sekaligus mencegah hal lain berjalan di UI Thread.

Sebagai gantinya, saya mengusulkan untuk memperkenalkan penundaan waktu yang dapat dikonfigurasi, dan cara untuk berhenti memperbarui tampilan.

Misalnya, Anda dapat mengatur sesuatu untuk membatalkan tampilan Anda setiap 10 detik sekali dan hanya memperbarui penghitung saat tampilan Anda dilampirkan ke jendela seperti itu (perhatikan bahwa Runnable sendiri yang bertanggung jawab apakah ia harus dijalankan kembali berdasarkan nilai bendera updateView):

public class ThreadView extends View {

    private class UpdateViewRunnable implements Runnable {
        public void run() {
            count++;
            invalidate();

            if (updateView) {
                postDelayed(this, DELAY_TIME_MILLIS);
            }
        }
    }

    private static final long DELAY_TIME_MILLIS = 100L;
    private boolean updateView = false;
    private UpdateViewRunnable updateViewRunnable = new UpdateViewRunnable();
    private Paint paint = new Paint();
    private int count;

    public ThreadView(Context context) {
        super(context);
        init(context, null);
    }

    public ThreadView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        paint.setColor(Color.BLACK);
        paint.setTextSize(80);
    }

    @Override
    public void onDraw(Canvas canvas) {
        canvas.drawText(count + "", 100, 100, paint);
    }

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        updateView = true;
        postDelayed(updateViewRunnable, DELAY_TIME_MILLIS);
    }

    @Override
    public void onDetachedFromWindow() {
        updateView = false;
        super.onDetachedFromWindow();
    }
}
person Michael Krause    schedule 10.06.2015

Pertama-tama, dalam cuplikan yang Anda berikan, handler tidak diperlukan sama sekali, Anda cukup menggunakan metode View.post karena konstruktor sudah dipanggil di thread UI.

Kedua, alasan invalidate tidak menyebabkan ui diperbarui adalah karena

1, metode View.invalide menempatkan tugas di antrian thread UI dan kembali. Biasanya ketika thread UI mendapat bagian waktu CPU untuk dieksekusi, thread tersebut akan menjalankan tugas-tugas ini secara berurutan dan kemudian tampilan akan digambar ulang.

2, namun, Runnable yang Anda posting melakukan loop tak terbatas, di thread UI, dan tugas menggambar ulang yang dibuat oleh panggilan yang tidak valid sedang mengantri, tetapi tidak pernah ditangani, kemungkinan besar aplikasi akan terhenti.

person ssynhtn    schedule 30.04.2017