AsyncTask มีข้อบกพร่องทางแนวคิดจริงๆหรือฉันแค่ทำบางสิ่งหายไป?


264

ฉันได้ตรวจสอบปัญหานี้มาหลายเดือนแล้วและหาวิธีแก้ปัญหาที่แตกต่างกันไปซึ่งฉันไม่พอใจเพราะมันเป็นแฮ็คขนาดใหญ่ ฉันยังไม่สามารถเชื่อได้ว่าคลาสที่มีข้อบกพร่องด้านการออกแบบทำให้มันเข้ากับกรอบงานและไม่มีใครพูดถึงมันดังนั้นฉันคิดว่าฉันต้องพลาดบางสิ่งบางอย่าง

AsyncTaskปัญหาอยู่ที่ ตามเอกสารนั้น

"อนุญาตให้ดำเนินการพื้นหลังและเผยแพร่ผลลัพธ์บนเธรด UI โดยไม่ต้องจัดการกับเธรดและ / หรือตัวจัดการ"

ตัวอย่างแล้วยังแสดงให้เห็นว่าบางอย่างที่เป็นแบบอย่างวิธีการที่เรียกว่าshowDialog() onPostExecute()นี้ แต่ดูเหมือนว่าที่วางแผนอย่างสิ้นเชิงกับผมเพราะแสดงโต้ตอบเสมอต้องมีการอ้างอิงถึงที่ถูกต้องContextและ AsyncTask ไม่ต้องถืออ้างอิงที่แข็งแกร่งให้กับวัตถุบริบท

เหตุผลชัดเจน: เกิดอะไรขึ้นถ้ากิจกรรมถูกทำลายซึ่งก่อให้เกิดงาน? สิ่งนี้สามารถเกิดขึ้นได้ตลอดเวลาเช่นเนื่องจากคุณพลิกหน้าจอ หากงานนั้นจะมีการอ้างอิงถึงบริบทที่สร้างคุณไม่เพียง แต่จะถือวัตถุบริบทที่ไร้ประโยชน์ (หน้าต่างจะถูกทำลายและการโต้ตอบ UI ใด ๆจะล้มเหลวด้วยข้อยกเว้น!) คุณยังมีความเสี่ยงในการสร้าง หน่วยความจำรั่ว

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

วิธีแก้ปัญหาหนึ่งคือการไม่ส่งอินสแตนซ์บริบทไปยัง AsyncTask แต่เป็นHandlerอินสแตนซ์ ใช้งานได้: เนื่องจากตัวจัดการผูกบริบทและภารกิจไว้อย่างหลวม ๆ คุณสามารถแลกเปลี่ยนข้อความระหว่างกันโดยไม่เสี่ยงต่อการรั่วไหล (ใช่ไหม) แต่นั่นหมายความว่าสถานที่ตั้งของ AsyncTask คือคุณไม่จำเป็นต้องกังวลกับตัวจัดการผิด ดูเหมือนว่าการใช้ Handler ในทางที่ผิดเนื่องจากคุณกำลังส่งและรับข้อความในเธรดเดียวกัน (คุณสร้างมันบนเธรด UI และส่งผ่านมันใน onPostExecute () ซึ่งยังทำงานบนเธรด UI)

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

วิธีแก้ปัญหาของฉันในเรื่องนี้ (ตามที่นำไปใช้ในไลบรารี Droid-Fu ) คือการรักษาการแมปของWeakReferenceชื่อคอมโพเนนต์ไปยังอินสแตนซ์ปัจจุบันของพวกเขาบนวัตถุแอปพลิเคชันที่ไม่ซ้ำกัน เมื่อใดก็ตามที่ AsyncTask เริ่มต้นมันจะบันทึกบริบทการโทรในแผนที่นั้นและในทุกการติดต่อกลับมันจะดึงอินสแตนซ์บริบทปัจจุบันจากการทำแผนที่นั้น สิ่งนี้ทำให้มั่นใจได้ว่าคุณจะไม่อ้างอิงอินสแตนซ์บริบทเก่าและคุณสามารถเข้าถึงบริบทที่ถูกต้องในการเรียกกลับเพื่อให้คุณสามารถใช้ UI ที่มีความหมายได้ นอกจากนี้ยังไม่รั่วไหลเนื่องจากการอ้างอิงอ่อนแอและถูกล้างเมื่อไม่มีอินสแตนซ์ขององค์ประกอบที่กำหนดอีกต่อไป

ถึงกระนั้นมันก็เป็นวิธีการแก้ปัญหาที่ซับซ้อนและต้องการให้ชั้นย่อยของห้องสมุด Droid-Fu บางคลาสทำให้วิธีการนี้ค่อนข้างน่ารำคาญ

ตอนนี้ฉันแค่อยากรู้ว่า:ฉันเพิ่งทำอะไรบางอย่างหายไปอย่างมากมายหรือว่า AsyncTask มีข้อบกพร่องจริงๆหรือ ประสบการณ์ของคุณทำงานกับมันอย่างไร คุณแก้ปัญหาเหล่านี้ได้อย่างไร

ขอบคุณสำหรับข้อมูลของคุณ


1
หากคุณอยากรู้เราเพิ่งเพิ่มชั้นเรียนไปยังห้องสมุดหลักของการจุดระเบิดที่เรียกว่า IgnitedAsyncTask ซึ่งเพิ่มการสนับสนุนสำหรับการเข้าถึงบริบทแบบปลอดภัยในการโทรกลับทั้งหมดโดยใช้รูปแบบการเชื่อมต่อ / ตัดการเชื่อมต่อที่ระบุโดย Dianne ด้านล่าง นอกจากนี้ยังอนุญาตให้โยนข้อยกเว้นและจัดการกับพวกเขาในการโทรกลับแยกต่างหาก ดูgithub.com/kaeppler/ignition-core/blob/master/src/com/github/…
Matthias

ลองดูที่: gist.github.com/1393552
Matthias

1
คำถามนี้มีความเกี่ยวข้องเช่นกัน
Alex Lockwood

ฉันเพิ่มภารกิจ async ไปยังรายการอาร์เรย์และตรวจสอบให้แน่ใจว่าปิดพวกเขาทั้งหมด ณ จุดหนึ่ง
NightSkyCode

คำตอบ:


86

เกี่ยวกับบางสิ่งเช่นนี้:

class MyActivity extends Activity {
    Worker mWorker;

    static class Worker extends AsyncTask<URL, Integer, Long> {
        MyActivity mActivity;

        Worker(MyActivity activity) {
            mActivity = activity;
        }

        @Override
        protected Long doInBackground(URL... urls) {
            int count = urls.length;
            long totalSize = 0;
            for (int i = 0; i < count; i++) {
                totalSize += Downloader.downloadFile(urls[i]);
                publishProgress((int) ((i / (float) count) * 100));
            }
            return totalSize;
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
            if (mActivity != null) {
                mActivity.setProgressPercent(progress[0]);
            }
        }

        @Override
        protected void onPostExecute(Long result) {
            if (mActivity != null) {
                mActivity.showDialog("Downloaded " + result + " bytes");
            }
        }
    }

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

        mWorker = (Worker)getLastNonConfigurationInstance();
        if (mWorker != null) {
            mWorker.mActivity = this;
        }

        ...
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        return mWorker;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mWorker != null) {
            mWorker.mActivity = null;
        }
    }

    void startWork() {
        mWorker = new Worker(this);
        mWorker.execute(...);
    }
}

5
ใช่ mActivity จะเป็น! = null แต่ถ้าไม่มีการอ้างอิงไปยังอินสแตนซ์ของผู้ทำงานของคุณดังนั้นการอ้างอิงใด ๆ ที่อินสแตนซ์นั้นจะต้องถูกกำจัดด้วยเช่นกัน หากงานของคุณดำเนินไปอย่างไม่หยุดหย่อนแสดงว่าคุณมีหน่วยความจำรั่วอยู่แล้ว (งานของคุณ) ไม่ต้องพูดถึงว่าคุณกำลังใช้แบตเตอรี่โทรศัพท์หมด นอกจากนี้ตามที่กล่าวไว้ในที่อื่นคุณสามารถตั้งค่า mActivity เป็นโมฆะใน onDestroy
EboMike

13
กระบวนการ onDestroy () วิธีการตั้งค่า mActivity เป็นโมฆะ ไม่สำคัญว่าใครจะเป็นผู้อ้างอิงถึงกิจกรรมก่อนหน้านั้นเพราะมันยังคงทำงานอยู่ และหน้าต่างของกิจกรรมจะใช้ได้ตลอดเวลาจนกว่าจะเรียก onDestroy () โดยการตั้งค่าเป็นโมฆะงาน async จะรู้ว่ากิจกรรมนั้นไม่ถูกต้องอีกต่อไป (และเมื่อมีการเปลี่ยนแปลงการกำหนดค่า onDestroy () ของกิจกรรมก่อนหน้านี้จะถูกเรียกและ onCreate () ของตัวต่อไปจะทำงานโดยไม่มีข้อความใด ๆ บนลูปหลักที่ประมวลผลระหว่างพวกเขาดังนั้น AsyncTask จะไม่เห็นสถานะไม่สอดคล้องกัน)
hackbod

8
จริง แต่ก็ยังไม่สามารถแก้ปัญหาสุดท้ายที่ฉันพูดถึง: จินตนาการว่างานดาวน์โหลดบางสิ่งบางอย่างนอกอินเทอร์เน็ต การใช้วิธีการนี้หากคุณพลิกหน้าจอ 3 ครั้งในขณะที่งานกำลังทำงานอยู่มันจะเริ่มใหม่ทุกครั้งที่มีการหมุนหน้าจอและทุกงานยกเว้นงานสุดท้ายที่จะทิ้งผลลัพธ์เนื่องจากการอ้างอิงกิจกรรมเป็นโมฆะ
แมทเทียส

11
ในการเข้าถึงพื้นหลังคุณจะต้องทำการซิงโครไนซ์ที่เหมาะสมรอบ ๆ mActivity และจัดการกับการทำงานในช่วงเวลาที่มันเป็นโมฆะหรือมีเธรดพื้นหลังเพียงแค่ใช้ Context.getApplicationContext () ซึ่งเป็นอินสแตนซ์ส่วนกลางเดียวสำหรับแอป บริบทของแอปพลิเคชั่นถูก จำกัด ในสิ่งที่คุณสามารถทำได้ (เช่นไม่มี UI เช่นไดอะล็อก) และต้องการการดูแลบางอย่าง (ตัวรับสัญญาณที่ลงทะเบียนและการผูกบริการจะถูกทิ้งไว้ตลอดไปหากคุณไม่ได้ล้างข้อมูลเหล่านั้น) แต่โดยทั่วไป ไม่เชื่อมโยงกับบริบทขององค์ประกอบเฉพาะ
hackbod

4
นั่นเป็นประโยชน์อย่างเหลือเชื่อขอบคุณ Dianne! ฉันหวังว่าเอกสารจะเป็นสิ่งที่ดีในตอนแรก
Matthias

20

เหตุผลชัดเจน: เกิดอะไรขึ้นถ้ากิจกรรมถูกทำลายซึ่งก่อให้เกิดงาน?

ด้วยตนเองยกเลิกการเชื่อมโยงกิจกรรมจากในAsyncTask onDestroy()อีกครั้งด้วยตนเองเชื่อมโยงกิจกรรมใหม่ไปในAsyncTask onCreate()สิ่งนี้ต้องใช้คลาสภายในแบบคงที่หรือคลาส Java มาตรฐานรวมถึงโค้ด 10 บรรทัด


ระวังด้วยการอ้างอิงแบบคงที่ - ฉันเคยเห็นวัตถุที่ถูกเก็บรวบรวมขยะแม้ว่าจะมีการอ้างอิงแบบคงที่กับพวกเขา อาจเป็นผลข้างเคียงของคลาสโหลดเดอร์ของ Android หรือข้อผิดพลาด แต่การอ้างอิงแบบคงที่ไม่ใช่วิธีที่ปลอดภัยในการแลกเปลี่ยนสถานะระหว่างวงจรชีวิตของกิจกรรม อย่างไรก็ตามแอปวัตถุก็คือเหตุผลที่ฉันใช้มัน
Matthias

10
@ Matias: ฉันไม่ได้บอกว่าจะใช้การอ้างอิงแบบคงที่ ฉันบอกว่าจะใช้ชั้นในแบบคงที่ มีความแตกต่างอย่างมีนัยสำคัญแม้จะมีทั้ง "คงที่" ในชื่อของพวกเขา
CommonsWare


5
ฉันเห็น - พวกเขาสำคัญที่นี่คือ getLastNonConfigurationInstance () ไม่ใช่ชั้นในแบบคงที่ คลาสภายในแบบสแตติกจะไม่มีการอ้างอิงโดยนัยถึงคลาสภายนอกดังนั้นมันจึงมีความหมายเทียบเท่ากับคลาสสาธารณะแบบธรรมดา เพียงแค่คำเตือน: onRetainNonConfigurationInstance () ไม่รับประกันว่าจะถูกเรียกเมื่อกิจกรรมถูกขัดจังหวะ (การขัดจังหวะอาจเป็นสายโทรศัพท์เช่นกัน) ดังนั้นคุณจะต้องแยกงานของคุณใน onSaveInstanceState () เช่นกัน สารละลาย. แต่ก็ยังเป็นความคิดที่ดี
Matthias

7
อืม ... onRetainNonConfigurationInstance () จะถูกเรียกเสมอเมื่อกิจกรรมอยู่ในกระบวนการของการถูกทำลายและสร้างขึ้นใหม่ ไม่เหมาะสมที่จะโทรหาในเวลาอื่น หากการสลับไปใช้กิจกรรมอื่นเกิดขึ้นกิจกรรมปัจจุบันจะหยุดชั่วคราว / หยุดทำงาน แต่จะไม่ถูกทำลายดังนั้นภารกิจ async สามารถทำงานต่อไปได้และใช้อินสแตนซ์กิจกรรมเดียวกัน หากเสร็จสิ้นและพูดแสดงโต้ตอบกล่องโต้ตอบจะแสดงอย่างถูกต้องเป็นส่วนหนึ่งของกิจกรรมนั้นจึงไม่แสดงให้ผู้ใช้จนกว่าพวกเขาจะกลับไปที่กิจกรรม คุณไม่สามารถใส่ AsyncTask ใน Bundle
hackbod

15

ดูเหมือนว่าAsyncTaskเป็นบิตมากขึ้นกว่าเพียงแค่ข้อบกพร่องแนวคิด มันยังใช้ไม่ได้จากปัญหาความเข้ากันได้ เอกสาร Android อ่านแล้ว:

เมื่อเปิดตัวครั้งแรก AsyncTasks จะถูกดำเนินการตามลำดับบนเธรดพื้นหลังเดียว เริ่มต้นด้วย DONUT สิ่งนี้ถูกเปลี่ยนเป็นกลุ่มของเธรดที่อนุญาตให้งานหลายอย่างทำงานพร้อมกัน เริ่มต้น HONEYCOMB งานจะกลับมาดำเนินการในเธรดเดี่ยวเพื่อหลีกเลี่ยงข้อผิดพลาดทั่วไปของแอปพลิเคชันที่เกิดจากการดำเนินการแบบขนาน หากคุณต้องการการประมวลผลแบบขนานอย่างแท้จริงคุณสามารถใช้ executeOnExecutor(Executor, Params...) เวอร์ชันของวิธีนี้ด้วย THREAD_POOL_EXECUTOR ; อย่างไรก็ตามดูความเห็นที่นั่นสำหรับคำเตือนในการใช้งาน

ทั้งสองexecuteOnExecutor()และTHREAD_POOL_EXECUTORถูกเพิ่มใน API ระดับ 11 (Android 3.0.x, HONEYCOMB)

ซึ่งหมายความว่าหากคุณสร้างสองAsyncTaskไฟล์เพื่อดาวน์โหลดสองไฟล์การดาวน์โหลดครั้งที่สองจะไม่เริ่มจนกว่าไฟล์แรกจะเสร็จสิ้น หากคุณแชทผ่านเซิร์ฟเวอร์สองเครื่องและเซิร์ฟเวอร์แรกไม่ทำงานคุณจะไม่เชื่อมต่อกับเซิร์ฟเวอร์ที่สองก่อนการเชื่อมต่อกับเซิร์ฟเวอร์แรกจะหมดเวลา (ยกเว้นว่าคุณใช้คุณสมบัติ API11 ใหม่ แต่จะทำให้โค้ดของคุณไม่สามารถใช้ได้กับ 2.x)

และหากคุณต้องการกำหนดเป้าหมายทั้ง 2.x และ 3.0 ขึ้นไปสิ่งต่างๆจะยุ่งยากมาก

นอกจากนี้เอกสารพูดว่า:

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


12

อาจเป็นไปได้ว่าเราทุกคนรวมถึง Google นั้นใช้มุมมองMVCAsyncTaskในทางที่ผิด

กิจกรรมเป็นตัวควบคุมและควบคุมไม่ควรเริ่มดำเนินการที่อาจอายุยืนดู นั่นคือ AsyncTasks ควรใช้จากModelจากคลาสที่ไม่ได้ผูกไว้กับวงจรชีวิตของกิจกรรม - โปรดจำไว้ว่ากิจกรรมจะถูกทำลายเมื่อหมุน (สำหรับมุมมองคุณมักจะไม่ได้เรียนโปรแกรมที่มาจากเช่น android.widget.Button แต่คุณสามารถทำได้โดยปกติสิ่งเดียวที่คุณทำเกี่ยวกับมุมมองคือ xml)

กล่าวอีกนัยหนึ่งมันเป็นความผิดที่จะวาง AsyncTask ในวิธีการของอนุพันธ์ OTOH ถ้าเราไม่ต้องใช้ AsyncTasks ในกิจกรรม AsyncTask จะสูญเสียความน่าดึงดูดใจ: มันเคยถูกโฆษณาว่าเป็นการแก้ไขที่ง่ายและรวดเร็ว


5

ฉันไม่แน่ใจว่าเป็นความจริงที่คุณเสี่ยงต่อหน่วยความจำรั่วโดยอ้างอิงจากบริบทจาก AsyncTask

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


2
จริง - แต่ถ้างานบล็อกไม่สิ้นสุด งานมีขึ้นเพื่อดำเนินการบล็อกอาจเป็นงานที่ไม่เคยยุติ ที่นั่นคุณมีหน่วยความจำรั่ว
Matthias

1
ผู้ปฏิบัติงานที่ดำเนินการบางอย่างด้วยการวนซ้ำไม่รู้จบหรืออะไรก็ตามที่เพิ่งล็อคเช่นการดำเนินการ I / O
Matthias

2

มันจะมีประสิทธิภาพมากขึ้นในการเก็บ WeekReference ในกิจกรรมของคุณ:

public class WeakReferenceAsyncTaskTestActivity extends Activity {
    private static final int MAX_COUNT = 100;

    private ProgressBar progressBar;

    private AsyncTaskCounter mWorker;

    @SuppressWarnings("deprecation")
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_task_test);

        mWorker = (AsyncTaskCounter) getLastNonConfigurationInstance();
        if (mWorker != null) {
            mWorker.mActivity = new WeakReference<WeakReferenceAsyncTaskTestActivity>(this);
        }

        progressBar = (ProgressBar) findViewById(R.id.progressBar1);
        progressBar.setMax(MAX_COUNT);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_async_task_test, menu);
        return true;
    }

    public void onStartButtonClick(View v) {
        startWork();
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        return mWorker;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mWorker != null) {
            mWorker.mActivity = null;
        }
    }

    void startWork() {
        mWorker = new AsyncTaskCounter(this);
        mWorker.execute();
    }

    static class AsyncTaskCounter extends AsyncTask<Void, Integer, Void> {
        WeakReference<WeakReferenceAsyncTaskTestActivity> mActivity;

        AsyncTaskCounter(WeakReferenceAsyncTaskTestActivity activity) {
            mActivity = new WeakReference<WeakReferenceAsyncTaskTestActivity>(activity);
        }

        private static final int SLEEP_TIME = 200;

        @Override
        protected Void doInBackground(Void... params) {
            for (int i = 0; i < MAX_COUNT; i++) {
                try {
                    Thread.sleep(SLEEP_TIME);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d(getClass().getSimpleName(), "Progress value is " + i);
                Log.d(getClass().getSimpleName(), "getActivity is " + mActivity);
                Log.d(getClass().getSimpleName(), "this is " + this);

                publishProgress(i);
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            if (mActivity != null) {
                mActivity.get().progressBar.setProgress(values[0]);
            }
        }
    }

}

นี่คล้ายกับที่เราทำกับ Droid-Fu เป็นครั้งแรก เราจะเก็บแผนที่ของการอ้างอิงอ่อนไปยังวัตถุบริบทและจะทำการค้นหาในการเรียกกลับงานเพื่อรับการอ้างอิงล่าสุด (ถ้ามี) เพื่อเรียกใช้การเรียกกลับ อย่างไรก็ตามวิธีการของเราหมายความว่ามีเอนทิตีเดียวที่ดูแลการทำแผนที่นี้ในขณะที่วิธีการของคุณไม่ได้ดังนั้นนี่จึงดีกว่า
Matthias

1
คุณได้ดู RoboSpice แล้วหรือยัง? github.com/octo-online/robospice ฉันเชื่อว่าระบบนี้ดียิ่งขึ้น
Snicolas

โค้ดตัวอย่างในหน้าแรกดูเหมือนจะเป็นการอ้างอิงบริบท (คลาสภายในรักษาการอ้างอิงโดยนัยไปยังคลาสภายนอก) ไม่เชื่อ !!
Matthias

@ Matias คุณพูดถูกนั่นเป็นเหตุผลว่าทำไมฉันจึงเสนอคลาสภายในแบบคงที่ที่จะระงับ WeakReference บนกิจกรรม
Snicolas

1
@ Matias ฉันเชื่อว่าสิ่งนี้เริ่มเป็นหัวข้อ แต่ตัวตักไม่ให้การแคชออกจากกล่องอย่างที่เราทำมากกว่านั้นตัวตักมักจะมีความละเอียดมากกว่า lib ของเรา อันที่จริงพวกเขาจัดการเคอร์เซอร์ที่ดี แต่สำหรับเครือข่ายวิธีการที่แตกต่างกันตามแคชและบริการที่เหมาะสมดีกว่า ดูneilgoodman.net/2011/12/26/…ตอนที่ 1 & 2
Snicolas

1

ทำไมไม่เพียงแค่แทนที่onPause()วิธีในการเป็นเจ้าของกิจกรรมและยกเลิกAsyncTaskจากที่นั่น?


ขึ้นอยู่กับงานที่ทำ ถ้ามันแค่โหลด / อ่านข้อมูลบางอย่างมันก็จะโอเค แต่ถ้ามันเปลี่ยนสถานะของข้อมูลบางอย่างบนเซิร์ฟเวอร์ระยะไกลเราต้องการให้งานสามารถทำงานได้จนจบ
Vit Khudenko

@Ahheded และฉันจะใช้มันถ้าคุณถือเธรด UI ในonPauseสิ่งที่ไม่ดีพอ ๆ กับที่อื่น? คือคุณสามารถรับ ANR ได้หรือไม่?
Jeff Axelrod

อย่างแน่นอน เราไม่สามารถบล็อกเธรด UI (ไม่ว่าจะเป็นonPauseหรือเป็นอย่างอื่น) เพราะเรามีความเสี่ยงที่จะได้รับ ANR
Vit Khudenko

1

คุณพูดถูก - นั่นคือเหตุผลที่การเคลื่อนไหวห่างจากการใช้งาน async / loader ในกิจกรรมเพื่อดึงข้อมูลกำลังได้รับแรงผลักดัน หนึ่งในวิธีการใหม่คือการใช้เฟรมเวิร์กของวอลเล่ย์ที่ให้การโทรกลับเมื่อข้อมูลพร้อม - สอดคล้องกับรูปแบบ MVC มากขึ้น วอลเล่ย์ได้รับความนิยมใน Google I / O 2013 ไม่แน่ใจว่าทำไมคนอื่นถึงไม่รู้เรื่องนี้


ขอบคุณที่ ... ฉันจะดูมัน ... เหตุผลของฉันที่ไม่ชอบ AsyncTask เป็นเพราะมันทำให้ฉันติดอยู่กับชุดคำแนะนำบนโพสต์ Execute ... เว้นแต่ฉันแฮ็คมันเหมือนใช้อินเทอร์เฟซหรือเอาชนะมันทุกครั้ง ฉันต้องการมัน.
carinlynchin

0

โดยส่วนตัวแล้วฉันเพิ่งขยาย Thread และใช้อินเตอร์เฟสการโทรกลับเพื่ออัปเดต UI ฉันไม่สามารถทำให้ AsyncTask ทำงานได้อย่างถูกต้องหากไม่มีปัญหา FC ฉันยังใช้คิวที่ไม่บล็อกเพื่อจัดการกลุ่มการเรียกใช้


1
การบังคับปิดของคุณอาจเป็นเพราะปัญหาที่ฉันกล่าวถึง: คุณลองอ้างอิงบริบทที่เกินขอบเขต (เช่นหน้าต่างถูกทำลาย) ซึ่งจะส่งผลให้เกิดข้อยกเว้นกรอบงาน
Matthias

ไม่ ... อันที่จริงเป็นเพราะคิวเข้าข่ายที่มีอยู่ใน AsyncTask ฉันใช้ getApplicationContext () เสมอ ฉันไม่ได้มีปัญหากับ AsyncTask ถ้ามันเป็นการดำเนินการเพียงไม่กี่อย่าง ... แต่ฉันกำลังเขียนเครื่องเล่นสื่อที่อัปเดตรูปหน้าปกอัลบั้มในเบื้องหลัง ... ในการทดสอบของฉันฉันมี 120 อัลบั้มที่ไม่มีรูปแบบศิลปะ ... ดังนั้นขณะที่ แอปของฉันไม่ได้ปิดไปตลอดทาง asynctask กำลังโยนข้อผิดพลาด ... ดังนั้นฉันจึงสร้างคลาสซิงเกิลที่มีคิวที่จัดการกระบวนการและจนถึงตอนนี้มันใช้งานได้ดี
androidworkz

0

ฉันคิดว่าการยกเลิกการทำงาน แต่ไม่ได้

ที่นี่พวกเขา RTFMing เกี่ยวกับเรื่องนี้:

"" ถ้างานเริ่มขึ้นแล้วพารามิเตอร์ mayInterruptIfRunning จะกำหนดว่าเธรดที่เรียกใช้งานนี้ควรถูกขัดจังหวะในความพยายามที่จะหยุดงานหรือไม่ "

แต่นั่นไม่ได้แปลว่าเธรดนั้นสามารถอินเตอร์รัปต์ได้ นั่นคือสิ่งที่จาวาไม่ใช่สิ่งที่ AsyncTask "

http://groups.google.com/group/android-developers/browse_thread/thread/dcadb1bc7705f1bb/add136eb4949359d?show_docid=add136eb4949359d


0

คุณน่าจะคิดว่า AsyncTask เป็นสิ่งที่แนบคู่กับกิจกรรม, บริบท, ContextWrapper และอื่น ๆ ได้สะดวกยิ่งขึ้นเมื่อเข้าใจขอบเขตของมันอย่างสมบูรณ์

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

โดยไม่ต้องยกเลิก AsyncTask ของคุณในขณะที่อยู่ห่างจากบริบทของคุณคุณจะพบกับการรั่วไหลของหน่วยความจำและ NullPointerExceptions ถ้าคุณเพียงแค่ต้องการให้ข้อเสนอแนะเช่น Toast เป็นกล่องโต้ตอบแบบเรียบง่าย

AsyncTask ไม่ได้เลวร้ายอะไรนัก แต่มีเวทมนตร์มากมายที่สามารถนำไปสู่การผิดพลาดที่ไม่คาดฝัน


-1

สำหรับ "ประสบการณ์การทำงานกับมัน": เป็นไปได้ที่จะฆ่ากระบวนการพร้อมกับ AsyncTasks ทั้งหมด Android จะสร้างกิจกรรมสแต็กใหม่อีกครั้งเพื่อให้ผู้ใช้ไม่ต้องพูดถึงอะไร

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.