ฉันจะรันโค้ดบนเธรดพื้นหลังบน Android ได้อย่างไร


131

ฉันต้องการให้โค้ดทำงานอยู่เบื้องหลังอย่างต่อเนื่อง ฉันไม่ต้องการทำในสถานบริการ มีวิธีอื่นที่เป็นไปได้หรือไม่?

ฉันพยายามโทรหาThreadชั้นเรียนในชั้นเรียนActivityแต่Activityยังคงอยู่ในพื้นหลังอยู่สักพักแล้วมันก็หยุดลง Threadชั้นยังหยุดการทำงาน

class testThread implements Runnable {
        @Override
        public void run() {
            File file = new File( Environment.getExternalStorageDirectory(), "/BPCLTracker/gpsdata.txt" );
            int i = 0;

            RandomAccessFile in = null;

            try {
                in = new RandomAccessFile( file, "rw" );
            } catch (FileNotFoundException e) {
// TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
// TODO Auto-generated catch block
                e.printStackTrace();
            }
//String line =null;
            while ( true ) {
                HttpEntity entity = null;
                try {
                    if ( isInternetOn() ) {
                        while ( ( line = in.readLine() ) != null ) {

                            HttpClient client = new DefaultHttpClient();
                            String url = "some url";
                            HttpPost request = new HttpPost( url );
                            StringEntity se = new StringEntity( line );
                            se.setContentEncoding( "UTF-8" );
                            se.setContentEncoding( new BasicHeader( HTTP.CONTENT_TYPE, "application/json" ) );
                            entity = se;
                            request.setEntity( entity );
                            HttpResponse response = client.execute( request );
                            entity = response.getEntity();
                            i++;
                        }
                        if ( ( line = in.readLine() ) == null && entity != null ) {
                            file.delete();
                            testThread t = new testThread();
                            Thread t1 = new Thread( t );
                            t1.start();
                        }


                    } else {
                        Thread.sleep( 60000 );
                    } // end of else

                } catch (NullPointerException e1) {
                    e1.printStackTrace();
                } catch (InterruptedException e2) {
                    e2.printStackTrace();
                } catch (IOException e1) {
// TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }// end of while
        }// end of run

    }

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

3
มีเหตุผลเฉพาะหรือไม่ที่จะไม่ใช้บริการ
DeltaCap019

ไม่แนะนำให้ใช้ การทำงานอย่างต่อเนื่องจะทำให้แบตเตอรี่ของอุปกรณ์หมด
HelmiB

ฉันส่งรหัสไปแล้ว
user2082987

ฉันลองใช้มันในบริการ แต่มันส่งบันทึกเดียวกันหลายครั้ง
user2082987

คำตอบ:


379

หากคุณต้องการ:

  1. รันโค้ดบนเธรดพื้นหลัง

  2. รันโค้ดที่ไม่ได้สัมผัส / อัปเดต UI

  3. รันโค้ด (สั้น) ซึ่งจะใช้เวลาไม่กี่วินาทีในการดำเนินการให้เสร็จสมบูรณ์

จากนั้นใช้รูปแบบที่สะอาดและมีประสิทธิภาพต่อไปนี้ซึ่งใช้ AsyncTask:

AsyncTask.execute(new Runnable() {
   @Override
   public void run() {
      //TODO your background code
   }
});

10
หมายเหตุ: ต้องใช้ API ระดับ 11
friederbluemle

9
ทำไมคุณไม่ใช้สิ่งต่อไปนี้ที่มีน้ำหนักเบากว่า: new Thread (new Runnable () {@Override public void run () {// background code}}). start ();
awardak

3
มีความเป็นไปได้ไหมที่จะทำให้หน่วยความจำรั่ว?
Hilfritz

5
สิ่งหนึ่งที่ควรทราบก็คือ AsyncTasks ถูก qeued หากคุณใช้สิ่งนี้สำหรับการเรียกใช้ API ของเซิร์ฟเวอร์จำนวนมากการเรียกใช้เหล่านี้จะไม่ทำงานแบบขนาน
Chase Roberts


45

จำไว้ว่า Running Background การทำงานอย่างต่อเนื่องเป็นสองงานที่แตกต่างกัน

สำหรับกระบวนการเบื้องหลังในระยะยาวเธรดจะไม่เหมาะสมกับ Android อย่างไรก็ตามนี่คือรหัสและยอมรับความเสี่ยงของคุณเอง ...

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

Timer (ทริกเกอร์เป็นระยะ), Alarm (Timebase trigger), Broadcast (Event base Trigger), การเรียกซ้ำจะปลุกฟังก์ชันของเรา

public static boolean isRecursionEnable = true;

void runInBackground() {
    if (!isRecursionEnable)
        // Handle not to start multiple parallel threads
        return;

    // isRecursionEnable = false; when u want to stop
    // on exception on thread make it true again  
    new Thread(new Runnable() {
        @Override
        public void run() {
            // DO your work here
            // get the data
            if (activity_is_not_in_background) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // update UI
                        runInBackground();
                    }
                });
            } else {
                runInBackground();
            }
        }
    }).start();
}

การใช้บริการ: หากคุณเปิดใช้บริการมันจะเริ่มทำงานมันจะดำเนินการและจะยุติเอง หลังจากดำเนินการงาน การยุติอาจเกิดจากข้อยกเว้นหรือผู้ใช้ฆ่าด้วยตนเองจากการตั้งค่า START_STICKY (Sticky Service) เป็นตัวเลือกที่ Android กำหนดให้บริการจะรีสตาร์ทเองหากบริการถูกยกเลิก

จำความแตกต่างของคำถามระหว่างการประมวลผลหลายขั้นตอนและมัลติเธรดได้หรือไม่? บริการเป็นกระบวนการพื้นหลัง (เช่นเดียวกับกิจกรรมที่ไม่มี UI) วิธีเดียวกับที่คุณเปิดเธรดในกิจกรรมเพื่อหลีกเลี่ยงการโหลดบนเธรดหลัก (เธรดกิจกรรม) เช่นเดียวกับที่คุณต้องเปิดเธรด (หรืองาน async) บนบริการ เพื่อหลีกเลี่ยงการโหลดบริการ

ในคำสั่งเดียวถ้าคุณต้องการให้งานเบื้องหลังดำเนินต่อไปคุณต้องเปิด StickyService และรันเธรดในบริการบนฐานเหตุการณ์


คุณจะแนะนำวิธีนี้ในการเรียกใช้ SignalStrengthListener ที่กำหนดเองในพื้นหลังเป็นเวลาโดยเฉลี่ย 20 นาทีในขณะที่รับค่าจากผู้ฟังหนึ่งครั้งต่อวินาทีเพื่ออัปเดตเธรด UI หรือไม่ นี่คือบริบทเพิ่มเติม: stackoverflow.com/questions/36167719/…
JParks

กระบวนการทำงานอีกครั้งอย่างไรหากคุณตั้งค่า 'isRecursionEnable = true' จากนั้นคุณเรียกใช้เมธอด 'run' สำหรับ 'runInBackground ()' จะไม่ป้อนคำสั่ง if ที่จุดเริ่มต้นของฟังก์ชันได้อย่างไร?
มิกกี้

23

ฉันต้องการให้โค้ดทำงานอยู่เบื้องหลังอย่างต่อเนื่อง ฉันไม่ต้องการทำในสถานบริการ มีวิธีอื่นที่เป็นไปได้หรือไม่?

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

หากคุณไม่รู้ว่าจะเริ่มต้นอย่างไรนี่คือบทแนะนำที่ดี:

หมายเหตุ:นอกจากนี้ยังมีความเป็นไปได้ในการใช้งานIntentServiceที่มีResultReceiverผลงานที่ดี


@ user2082987 คุณลองดูไหม
Simon Dorociak

พารามิเตอร์เหล่านี้ของ asyncTask ทำให้สับสน
user2082987

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

สวัสดี @LassiKinnunen ฉันเขียนคำตอบนั้นเมื่อ 5 ปีที่แล้ว ตอนนี้ทุกอย่างเปลี่ยนไปและตอนนี้ฉันไม่ได้ใช้ AsyncTask เพราะหน่วยความจำรั่วไม่ปลอดภัย
Simon Dorociak

ใช่ฉันตระหนักดีว่า แต่ผู้คนจะยังคงพบสิ่งนี้ผ่านทาง Google และฉันได้เห็นความคิดเห็นมากมายของผู้คนที่แนะนำให้ใช้กระดาษห่อหุ้มของ Google แม้ว่าจะมีจุดประสงค์เพียงเล็กน้อยหรือไม่มีเลยก็ตามขออภัย สำหรับคำถามเดิมหากมีใครกำลังมองหาคำตอบในปี 2018 ให้สร้างบริการและเปิดใช้งานและทำเครื่องหมายว่าอยู่เบื้องหน้าพร้อมกับการแจ้งเตือนหากคุณต้องการเพิ่มโอกาสที่จะไม่ถูกฆ่า กระบวนการทำงานที่ยาวนานจริงสามารถจัดการได้ด้วยตัวจัดการหรือเธรดแยกต่างหาก ไม่ทราบว่าพวกเขาพยายามทำให้หน่วยความจำรั่วปลอดภัยหรือไม่?
Lassi Kinnunen

16

3-Liner ง่ายๆ

วิธีง่ายๆในการทำสิ่งนี้ที่ฉันพบในความคิดเห็นของ@awardakในคำตอบของ Brandon Rude :

new Thread( new Runnable() { @Override public void run() { 
  // Run whatever background code you want here.
} } ).start();

ฉันไม่แน่ใจว่าวิธีนี้ดีกว่าการใช้หรืออย่างไรAsyncTask.executeแต่ดูเหมือนว่าจะได้ผลสำหรับเรา ความคิดเห็นใด ๆ เกี่ยวกับความแตกต่างจะได้รับการชื่นชม

ขอบคุณ@awardak !


เราใช้เมธอด handler.post () เพื่อโพสต์กลับบนเธรดหลักภายในวิธีการรันของเธรด
androidXP

2

อีกทางเลือกหนึ่งของ AsyncTask คือ robospice https://github.com/octo-online/robospice

คุณสมบัติบางอย่างของ robospice

1. ทดสอบคำขอเครือข่ายแบบอะซิงโครนัส (ใน AndroidService พื้นหลัง) (เช่นคำขอ REST โดยใช้ Spring Android) แจ้งแอปของคุณบนเธรด UI เมื่อผลลัพธ์พร้อม

2. พิมพ์ผิดอย่างแรง! คุณส่งคำขอโดยใช้ POJO และคุณจะได้รับ POJO เป็นผลลัพธ์ของคำขอ

3. ไม่บังคับใช้ข้อ จำกัด ทั้ง POJO ที่ใช้สำหรับคำขอหรือในคลาสกิจกรรมที่คุณใช้ในโครงการของคุณ

4. แคชผลลัพธ์ (ใน Json ที่มีทั้ง Jackson และ Gson หรือ Xml หรือไฟล์ข้อความแบบแบนหรือไฟล์ไบนารีแม้จะใช้ ORM Lite)

5. แจ้งกิจกรรมของคุณ (หรือบริบทอื่น ๆ ) เกี่ยวกับผลลัพธ์ของคำขอเครือข่ายในกรณีที่พวกเขายังมีชีวิตอยู่

6. หน่วยความจำไม่รั่วไหลเลยเช่น Android Loaders ซึ่งแตกต่างจาก Android AsyncTasks แจ้งเตือนกิจกรรมของคุณบน UI Thread

7. ใช้รูปแบบการจัดการข้อยกเว้นที่เรียบง่าย แต่มีประสิทธิภาพ

ตัวอย่างที่จะเริ่มต้นด้วย https://github.com/octo-online/RoboSpice-samples

ตัวอย่างของ robospice ที่https://play.google.com/store/apps/details?id=com.octo.android.robospice.motivations&feature=search_result


วิธีการจัดเก็บอาร์เรย์ที่ได้รับจากเครือข่ายไปยัง SQLite โดยทั่วไปฉันต้องการ: รับข้อมูลจากเครือข่ายและบันทึกลงใน SQLite ของฉัน - ทั้งในเธรดพื้นหลังจากนั้นรับ UI ของฉันแจ้งให้รีเฟรช ListView ฉันมักจะใช้ IntentService แต่ RoboSpice พิมพ์น้อยกว่า
Yar

@yar คุณสามารถใช้บริการความตั้งใจได้หากใช้งานเป็นเวลานาน นอกจากนี้คุณสามารถใช้ asynctask ได้ แต่เฉพาะในกรณีที่เป็นช่วงเวลาสั้น ๆ คุณสามารถสร้างเธรดและทำสิ่งต่างๆได้ที่นั่น ตอนนี้ฉันไม่ได้ใช้ Robospice มานานแล้ว มีห้องสมุดเครือข่ายอื่น ๆ เช่นวอลเลย์ ...
Raghunandan

โอเคขอบคุณ. ฉันคิดจะใช้ Robospice แต่ดูเหมือนว่ามันจะไม่ค่อยดี (ยืดหยุ่น) สำหรับความต้องการของฉัน ฉันใช้ IntentService เป็นเวลานานและประสบความสำเร็จ
Yar

2
class Background implements Runnable {
    private CountDownLatch latch = new CountDownLatch(1);
    private  Handler handler;

    Background() {
        Thread thread = new Thread(this);
        thread.start();
        try {
            latch.await();
        } catch (InterruptedException e) {
           /// e.printStackTrace();
        }
    }

    @Override
    public void run() {
        Looper.prepare();
        handler = new Handler();
        latch.countDown();
        Looper.loop();
    }

    public Handler getHandler() {
        return handler;
    }
}

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

0

หากคุณต้องการรันเธรดที่มีรหัสต่างกันนี่คือตัวอย่าง:

ฟัง:

public interface ESLThreadListener {

    public List onBackground();

    public void onPostExecute(List list);

}

คลาสของเธรด

public class ESLThread extends AsyncTask<Void, Void, List> {


    private ESLThreadListener mListener;

    public ESLThread() {

        if(mListener != null){

            mListener.onBackground();
        }
    }

    @Override
    protected List doInBackground(Void... params) {

        if(mListener != null){

            List list = mListener.onBackground();

            return list;
        }

        return null;
    }

    @Override
    protected void onPostExecute(List t) {
        if(mListener != null){

            if ( t != null) {
                mListener.onPostExecute(t);
            }
        }

    }


    public void setListener(ESLThreadListener mListener){

        this.mListener = mListener;
    }
}

เรียกใช้รหัสที่แตกต่างกัน:

  ESLThread thread = new ESLThread();
                        thread.setListener(new ESLThreadListener() {
                            @Override
                            public List onBackground() {
                                List<EntityShoppingListItems>  data = RoomDB.getDatabase(context).sliDAO().getSL(fId);

                                return data;

                            }

                            @Override
                            public void onPostExecute(List t) {

                                List<EntityShoppingListItems> data = (List<EntityShoppingListItems>)t;
                                adapter.setList(data);
                            }
                        });

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