การใช้เธรดในมุมมองแบบกำหนดเอง

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

ฉันเห็นวิธีแก้ปัญหานี้สำหรับเธรดในมุมมองที่กำหนดเอง แต่มันใช้งานไม่ได้ แม้ว่าลูปของฉันจะยังคงโทร invalidate() ต่อไป แต่มันก็ไม่โทร onDraw() ต่อไป ฉันยังเห็นวิธีแก้ปัญหาที่พวกเขาใช้ Runnable และแทนที่ run() นี่เป็นวิธีที่ดีกว่าหรือไม่


person Derek Dawson    schedule 10.06.2015    source แหล่งที่มา
comment
อาจเป็นเพราะไม่มีการเรียกเมธอด onDraw ภายในเธรดของคุณ คุณกำลังเพิ่มตัวแปร count เท่านั้น บางทีคุณอาจลองเรียกเมธอด drawText จากภายใน Runnable ของคุณ   -  person Thyen Hong Guedes Chang    schedule 11.06.2015
comment
@ThyenHongGuedesChang: เขากำลังเรียก invalidate() ซึ่งโดยพื้นฐานแล้วทำเครื่องหมายพื้นที่ไคลเอนต์ทั้งหมดของมุมมองว่าจำเป็นต้องวาดใหม่ การวาดภาพเกิดขึ้นแบบอะซิงโครนัสกับสิ่งนี้ ปัญหาที่แท้จริงคือการวนซ้ำไม่สิ้นสุดที่ทำงานบน UI Thread สิ่งนี้ทำให้ UI ไม่สามารถทำอะไรได้เลย   -  person Michael Krause    schedule 11.06.2015


คำตอบ (2)


ข้อมูลพร็อพเพอร์ตี้ทั้งหมดมีตัวจัดการที่ตั้งค่าไว้แล้ว ดังนั้นการสร้างอันใหม่จึงไม่มีประสิทธิภาพ คุณควรใช้เมธอด post(Runnable action) หรือ postDelayed(Runnable action, long DelayMillis) ของคลาส View แทน

เท่าที่คุณกำลังพยายามทำให้สำเร็จ ฉันกำลังสันนิษฐานว่าโค้ดที่คุณให้มานั้นเป็นเพียงเพื่อใช้เป็นตัวอย่างเท่านั้น หากคุณแค่พยายามอัปเดตข้อความจริงๆ ฉันจะบอกว่าลองใช้ TextView แล้วเรียก setText ลงไป TextView ดูแลการทำให้ตัวเองใช้ไม่ได้เมื่อรู้ว่าข้อความมีการเปลี่ยนแปลง

แต่กลับไปที่คำถามของคุณ ส่วนใหญ่ขึ้นอยู่กับอัตราที่คุณต้องการอัปเดตการเรนเดอร์ตัวแปรการนับของคุณ ขณะนี้คุณจะทำงานวนซ้ำไม่สิ้นสุดบน UI Thread สิ่งนี้จะทำให้เกิดปัญหาอย่างแน่นอน เนื่องจากคุณจะสแปมตัวจัดการ/คิวเหตุการณ์ด้วยการเรียกที่ไม่ถูกต้อง ในขณะเดียวกันก็ป้องกันไม่ให้สิ่งอื่นใดทำงานบน UI Thread

ฉันขอเสนอให้แนะนำการหน่วงเวลาที่สามารถกำหนดค่าได้ และวิธีหยุดการอัปเดตมุมมองแทน

ตัวอย่างเช่น คุณสามารถตั้งค่าต่างๆ เพื่อทำให้มุมมองของคุณใช้ไม่ได้ทุกๆ 10 วินาที และให้อัปเดตตัวนับเท่านั้นในขณะที่มุมมองของคุณแนบไปกับหน้าต่างเช่นนั้น (โปรดทราบว่า Runnable มีหน้าที่รับผิดชอบเองว่าควรจะทำงานอีกครั้งหรือไม่โดยอิงจาก ค่าของแฟล็ก 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

ก่อนอื่น ในตัวอย่างข้อมูลที่คุณระบุไม่จำเป็นต้องใช้ตัวจัดการเลย คุณสามารถใช้เมธอด View.post ได้เนื่องจากตัวสร้างถูกเรียกในเธรด UI แล้ว

ประการที่สอง สาเหตุที่ทำให้ใช้ไม่ได้ไม่ได้ทำให้ UI อัปเดตเป็นเพราะ

ในเวอร์ชัน 1.1 วิธีการ View.invalide จะวางงานไว้ในคิวของเธรด UI และส่งคืน โดยปกติเมื่อเธรด UI ได้รับส่วนแบ่งของเวลา CPU ในการดำเนินการ เธรดจะดำเนินการงานเหล่านี้ตามลำดับ จากนั้นมุมมองจะถูกวาดใหม่

อย่างไรก็ตาม Runnable ที่คุณโพสต์กำลังทำการวนซ้ำไม่สิ้นสุดในเธรด UI และงานวาดใหม่ที่สร้างขึ้นโดยการเรียกที่ไม่ถูกต้องกำลังเข้าคิว แต่ไม่เคยได้รับการจัดการ แอปมีแนวโน้มที่จะหยุดทำงาน

person ssynhtn    schedule 30.04.2017