นักพัฒนา Android ทุกคนไม่ว่าในจุดใดจุดหนึ่งจำเป็นต้องจัดการกับเธรดในแอปพลิเคชันของตน
เมื่อเปิดแอปพลิเคชันใน Android แอปพลิเคชันจะสร้างเธรดการดำเนินการชุดแรกซึ่งเรียกว่าเธรด“ หลัก” เธรดหลักมีหน้าที่จัดส่งเหตุการณ์ไปยังวิดเจ็ตอินเทอร์เฟซผู้ใช้ที่เหมาะสมตลอดจนสื่อสารกับส่วนประกอบจากชุดเครื่องมือ Android UI
เพื่อให้แอปพลิเคชันของคุณตอบสนองต่อไปสิ่งสำคัญคือต้องหลีกเลี่ยงการใช้เธรดหลักเพื่อดำเนินการใด ๆ ที่อาจทำให้แอปพลิเคชันถูกบล็อก
การทำงานของเครือข่ายและการเรียกใช้ฐานข้อมูลตลอดจนการโหลดส่วนประกอบบางอย่างเป็นตัวอย่างทั่วไปของการดำเนินการที่ควรหลีกเลี่ยงในเธรดหลัก เมื่อมีการเรียกใช้ในเธรดหลักจะเรียกว่าพร้อมกันซึ่งหมายความว่า UI จะไม่ตอบสนองอย่างสมบูรณ์จนกว่าการดำเนินการจะเสร็จสิ้น ด้วยเหตุนี้จึงมักดำเนินการในเธรดที่แยกจากกันซึ่งจะหลีกเลี่ยงการบล็อก UI ในขณะที่ดำเนินการอยู่ (กล่าวคือดำเนินการแบบอะซิงโครนัสจาก UI)
Android มีหลายวิธีในการสร้างและจัดการเธรดและมีไลบรารีของบุคคลที่สามจำนวนมากที่ทำให้การจัดการเธรดน่าพอใจมากขึ้น อย่างไรก็ตามด้วยแนวทางที่แตกต่างกันมากมายการเลือกวิธีที่เหมาะสมอาจทำให้สับสนได้
วิธีทำโทเค็น ethereum
ในบทความนี้คุณจะได้เรียนรู้เกี่ยวกับสถานการณ์ทั่วไปใน การพัฒนา Android ที่เธรดกลายเป็นสิ่งสำคัญและวิธีแก้ปัญหาง่ายๆที่สามารถนำไปใช้กับสถานการณ์เหล่านั้นและอื่น ๆ
ใน Android คุณสามารถจัดหมวดหมู่ส่วนประกอบเธรดทั้งหมดออกเป็นสองประเภทพื้นฐาน:
AsyncTask
เป็นส่วนประกอบพื้นฐานของ Android สำหรับเธรด ใช้งานง่ายและใช้ได้ดีกับสถานการณ์พื้นฐาน
ตัวอย่างการใช้งาน:
public class ExampleActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); new MyTask().execute(url); } private class MyTask extends AsyncTask { @Override protected String doInBackground(String... params) { String url = params[0]; return doSomeWork(url); } @Override protected void onPostExecute(String result) { super.onPostExecute(result); // do something with result } } }
AsyncTask
อย่างไรก็ตามหากคุณต้องการให้งานรอการตัดบัญชีของคุณทำงานเกินอายุการใช้งานของกิจกรรม / ส่วนย่อย เป็นที่น่าสังเกตว่าแม้แต่สิ่งที่เรียบง่ายอย่างการหมุนหน้าจอก็สามารถทำให้กิจกรรมถูกทำลายได้
รถตักเป็นทางออกสำหรับปัญหาดังกล่าวข้างต้น รถตักสามารถหยุดได้โดยอัตโนมัติเมื่อกิจกรรมถูกทำลายและยังสามารถรีสตาร์ทเองได้หลังจากสร้างกิจกรรมใหม่
ส่วนใหญ่มีรถตักสองประเภท: AsyncTaskLoader
และ CursorLoader
. คุณจะได้เรียนรู้เพิ่มเติมเกี่ยวกับ CursorLoader
ต่อไปในบทความนี้
AsyncTaskLoader
คล้ายกับ AsyncTask
แต่ซับซ้อนกว่าเล็กน้อย
ตัวอย่างการใช้งาน:
public class ExampleActivity extends Activity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getLoaderManager().initLoader(1, null, new MyLoaderCallbacks()); } private class MyLoaderCallbacks implements LoaderManager.LoaderCallbacks { @Override public Loader onCreateLoader(int id, Bundle args) { return new MyLoader(ExampleActivity.this); } @Override public void onLoadFinished(Loader loader, Object data) { } @Override public void onLoaderReset(Loader loader) { } } private class MyLoader extends AsyncTaskLoader { public MyLoader(Context context) { super(context); } @Override public Object loadInBackground() { return someWorkToDo(); } } }
Service
เป็นส่วนประกอบที่มีประโยชน์สำหรับการดำเนินการที่ยาวนาน (หรืออาจยาวนาน) โดยไม่มี UI ใด ๆ
Service
ทำงานในเธรดหลักของกระบวนการโฮสต์ บริการไม่ได้สร้างเธรดของตัวเองและไม่ทำงานในกระบวนการแยกต่างหากเว้นแต่คุณจะระบุเป็นอย่างอื่น
ตัวอย่างการใช้งาน:
public class ExampleService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { doSomeLongProccesingWork(); stopSelf(); return START_NOT_STICKY; } @Nullable @Override public IBinder onBind(Intent intent) { return null; } }
ด้วย Service
มันคือ ของคุณ ความรับผิดชอบในการหยุดงานเมื่องานเสร็จสมบูรณ์โดยเรียก stopSelf()
หรือ stopService()
วิธี.
ชอบ Service
, IntentService
ทำงานบนเธรดแยกต่างหากและหยุดตัวเองโดยอัตโนมัติหลังจากทำงานเสร็จสิ้น
IntentService
โดยปกติจะใช้สำหรับงานสั้น ๆ ที่ไม่จำเป็นต้องเชื่อมต่อกับ UI ใด ๆ
ตัวอย่างการใช้งาน:
public class ExampleService extends IntentService { public ExampleService() { super('ExampleService'); } @Override protected void onHandleIntent(Intent intent) { doSomeShortWork(); } }
บางครั้งคุณอาจต้องการส่งคำขอ API ไปยังเซิร์ฟเวอร์โดยไม่ต้องกังวลกับการตอบสนอง ตัวอย่างเช่นคุณอาจส่งโทเค็นการลงทะเบียนแบบพุชไปยังส่วนหลังของแอปพลิเคชันของคุณ
เนื่องจากสิ่งนี้เกี่ยวข้องกับการร้องขอผ่านเครือข่ายคุณควรดำเนินการจากเธรดอื่นที่ไม่ใช่เธรดหลัก
คุณสามารถใช้ AsyncTask
หรือรถตักสำหรับการโทรและมันจะทำงาน
อย่างไรก็ตาม AsyncTask
และรถตักขึ้นอยู่กับวงจรชีวิตของกิจกรรม ซึ่งหมายความว่าคุณจะต้องรอให้การเรียกดำเนินการและพยายามป้องกันไม่ให้ผู้ใช้ออกจากกิจกรรมหรือหวังว่าจะดำเนินการก่อนที่กิจกรรมจะถูกทำลาย
Service
อาจเหมาะกว่าสำหรับกรณีการใช้งานนี้เนื่องจากไม่ได้เชื่อมต่อกับกิจกรรมใด ๆ ดังนั้นจึงจะสามารถดำเนินการโทรเครือข่ายต่อไปได้แม้ว่ากิจกรรมจะถูกทำลายไปแล้วก็ตาม นอกจากนี้เนื่องจากไม่จำเป็นต้องมีการตอบสนองจากเซิร์ฟเวอร์บริการจึงไม่ จำกัด ที่นี่เช่นกัน
อย่างไรก็ตามเนื่องจากบริการจะเริ่มทำงานบนเธรด UI คุณยังคงต้องจัดการเธรดด้วยตัวเอง นอกจากนี้คุณจะต้องตรวจสอบให้แน่ใจว่าบริการหยุดลงเมื่อการโทรผ่านเครือข่ายเสร็จสมบูรณ์
สิ่งนี้จะต้องใช้ความพยายามมากกว่าที่ควรจะเป็นสำหรับการกระทำง่ายๆเช่นนี้
อะไรคือความแตกต่างระหว่าง s corp และ c corp
ในความคิดของฉันนี่น่าจะเป็นตัวเลือกที่ดีที่สุด
ตั้งแต่ IntentService
ไม่ยึดติดกับกิจกรรมใด ๆ และทำงานบนเธรดที่ไม่ใช่ UI ซึ่งตอบสนองความต้องการของเราได้อย่างสมบูรณ์แบบที่นี่ ยิ่งไปกว่านั้น IntentService
หยุดตัวเองโดยอัตโนมัติดังนั้นจึงไม่จำเป็นต้องจัดการด้วยตนเองเช่นกัน
กรณีการใช้งานนี้อาจพบได้บ่อยกว่าเล็กน้อย ตัวอย่างเช่นคุณอาจต้องการเรียกใช้ API ในส่วนหลังและใช้การตอบกลับเพื่อเติมข้อมูลในฟิลด์บนหน้าจอ
แม้ว่า a Service
หรือ IntentService
เหมาะสำหรับกรณีการใช้งานก่อนหน้านี้การใช้ที่นี่คงไม่ใช่ความคิดที่ดี พยายามดึงข้อมูลจาก Service
หรือ IntentService
ในเธรด UI หลักจะทำให้สิ่งต่างๆซับซ้อนมาก
ตอนแรกอาย AsyncTask
หรือรถตักดูเหมือนจะเป็นทางออกที่ชัดเจนที่นี่ ใช้งานง่าย - เรียบง่ายและตรงไปตรงมา
อย่างไรก็ตามเมื่อใช้ AsyncTask
หรือรถตักคุณจะสังเกตเห็นว่ามีความจำเป็นต้องเขียนโค้ดสำเร็จรูป ยิ่งไปกว่านั้นการจัดการข้อผิดพลาดกลายเป็นงานที่น่าเบื่อสำหรับส่วนประกอบเหล่านี้ แม้จะมีการโทรผ่านเครือข่ายแบบธรรมดาคุณก็ต้องระวังข้อยกเว้นที่อาจเกิดขึ้นจับพวกเขาและดำเนินการตามนั้น สิ่งนี้บังคับให้เรารวมคำตอบของเราไว้ในคลาสแบบกำหนดเองที่มีข้อมูลพร้อมข้อมูลข้อผิดพลาดที่เป็นไปได้และแฟล็กระบุว่าการดำเนินการสำเร็จหรือไม่
นั่นเป็นงานที่ต้องทำมากมายสำหรับทุกการโทร โชคดีที่ตอนนี้มีวิธีแก้ปัญหาที่ดีและง่ายกว่ามาก: RxJava
คุณอาจเคยได้ยินเกี่ยวกับ RxJava ห้องสมุดที่พัฒนาโดย Netflix เกือบจะเป็นเวทมนตร์ใน Java
RxAndroid ช่วยให้คุณใช้ RxJava ใน Android และจัดการกับงานแบบอะซิงโครนัสได้อย่างง่ายดาย คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับ RxJava บน Android ที่นี่ .
RxJava มีสององค์ประกอบ: Observer
และ Subscriber
.
อัน ผู้สังเกตการณ์ เป็นส่วนประกอบที่มีการกระทำบางอย่าง ดำเนินการดังกล่าวและส่งกลับผลลัพธ์หากสำเร็จหรือเกิดข้อผิดพลาดหากล้มเหลว
ถึง สมาชิก ในทางกลับกันเป็นส่วนประกอบที่สามารถรับผลลัพธ์ (หรือข้อผิดพลาด) จากสิ่งที่สังเกตได้โดยสมัครรับข้อมูล
ด้วย RxJava คุณต้องสร้างสิ่งที่สังเกตได้ก่อน:
Observable.create((ObservableOnSubscribe) e -> { Data data = mRestApi.getData(); e.onNext(data); })
เมื่อสร้างสิ่งที่สังเกตได้แล้วคุณสามารถสมัครสมาชิกได้
ด้วยไลบรารี RxAndroid คุณสามารถควบคุมเธรดที่คุณต้องการดำเนินการในสิ่งที่สังเกตได้และเธรดที่คุณต้องการได้รับการตอบสนอง (เช่นผลลัพธ์หรือข้อผิดพลาด)
คุณเชื่อมโยงกับสิ่งที่สังเกตได้ด้วยฟังก์ชันทั้งสองนี้:
.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()
ตัวกำหนดตารางเวลาเป็นส่วนประกอบที่ดำเนินการดำเนินการในเธรดหนึ่ง ๆ AndroidSchedulers.mainThread()
เป็นตัวกำหนดตารางเวลาที่เกี่ยวข้องกับเธรดหลัก
ระบุว่าการเรียก API ของเราคือ mRestApi.getData()
และส่งกลับ Data
วัตถุการเรียกพื้นฐานอาจมีลักษณะดังนี้:
cfo คืออะไร?
Observable.create((ObservableOnSubscribe) e -> { try { Data data = mRestApi.getData(); e.onNext(data); } catch (Exception ex) { e.onError(ex); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(match -> Log.i(“rest api, 'success'), throwable -> Log.e(“rest api, 'error: %s' + throwable.getMessage()));
โดยไม่ต้องใช้ประโยชน์อื่น ๆ จากการใช้ RxJava คุณสามารถดูได้แล้วว่า RxJava ช่วยให้เราเขียนโค้ดที่เป็นผู้ใหญ่มากขึ้นได้อย่างไรโดยการแยกความซับซ้อนของเธรดออกไป
สำหรับการโทรผ่านเครือข่ายที่ต้องดำเนินการตามลำดับ (กล่าวคือโดยแต่ละการดำเนินการขึ้นอยู่กับการตอบสนอง / ผลลัพธ์ของการดำเนินการก่อนหน้านี้) คุณต้องระมัดระวังเป็นพิเศษในการสร้างรหัสสปาเก็ตตี้
ตัวอย่างเช่นคุณอาจต้องทำการเรียก API ด้วยโทเค็นที่คุณต้องดึงข้อมูลก่อนผ่านการเรียก API อื่น
ใช้ AsyncTask
หรือรถตักแทบจะนำไปสู่รหัสสปาเก็ตตี้ ฟังก์ชันโดยรวมจะทำให้ถูกต้องได้ยากและจะต้องใช้รหัสสำเร็จรูปจำนวนมากตลอดทั้งโครงการของคุณ
ใน RxJava นั้น flatMap
ตัวดำเนินการรับค่าที่ปล่อยออกมาจากแหล่งที่สามารถสังเกตได้และส่งกลับค่าที่สังเกตได้อื่น คุณสามารถสร้างสิ่งที่สังเกตได้จากนั้นสร้างสิ่งที่สังเกตได้อื่นโดยใช้ค่าที่ปล่อยออกมาจากค่าแรกซึ่งโดยทั่วไปจะเชื่อมโยงพวกมัน
ขั้นตอนที่ 1. สร้างสิ่งที่สังเกตได้ที่ดึงโทเค็น:
public Observable getTokenObservable() { return Observable.create(subscriber -> { try { String token = mRestApi.getToken(); subscriber.onNext(token); } catch (IOException e) { subscriber.onError(e); } }); }
ขั้นตอนที่ 2. สร้างสิ่งที่สังเกตได้ซึ่งรับข้อมูลโดยใช้โทเค็น:
public Observable getDataObservable(String token) { return Observable.create(subscriber -> { try { Data data = mRestApi.getData(token); subscriber.onNext(data); } catch (IOException e) { subscriber.onError(e); } }); }
ขั้นตอนที่ 3. เชื่อมโยงสิ่งที่สังเกตได้ทั้งสองเข้าด้วยกันและสมัครสมาชิก:
getTokenObservable() .flatMap(new Function() { @Override public Observable apply(String token) throws Exception { return getDataObservable(token); } }) .subscribe(data -> { doSomethingWithData(data) }, error -> handleError(e));
โปรดทราบว่าการใช้แนวทางนี้ไม่ จำกัด เฉพาะการโทรผ่านเครือข่าย สามารถทำงานร่วมกับชุดของการดำเนินการใด ๆ ที่ต้องรันตามลำดับ แต่แยกกันในเธรด
กรณีการใช้งานทั้งหมดข้างต้นค่อนข้างง่าย การสลับระหว่างเธรดจะเกิดขึ้นหลังจากแต่ละชุดทำงานเสร็จแล้วเท่านั้น สถานการณ์ขั้นสูงเพิ่มเติมตัวอย่างเช่นเมื่อเธรดตั้งแต่สองชุดขึ้นไปจำเป็นต้องสื่อสารกันอย่างแข็งขัน - แนวทางนี้สามารถรองรับได้เช่นกัน
พิจารณาสถานการณ์ที่คุณต้องการอัปโหลดไฟล์และอัปเดตอินเทอร์เฟซผู้ใช้เมื่อเสร็จสมบูรณ์
เนื่องจากการอัปโหลดไฟล์อาจใช้เวลานานจึงไม่จำเป็นต้องให้ผู้ใช้รอ คุณสามารถใช้บริการและอาจเป็น IntentService
สำหรับการใช้งานฟังก์ชันที่นี่
อย่างไรก็ตามในกรณีนี้ความท้าทายที่ใหญ่กว่าคือความสามารถในการเรียกใช้เมธอดบนเธรด UI หลังจากการอัปโหลดไฟล์ (ซึ่งดำเนินการในเธรดแยกต่างหาก) เสร็จสมบูรณ์
RxJava ไม่ว่าจะเป็นของตัวเองหรือภายใน IntentService
อาจไม่เหมาะ คุณจะต้องใช้กลไกตามการโทรกลับเมื่อสมัครรับ Observable
และ IntentService
ถูกสร้างขึ้นเพื่อทำการโทรแบบซิงโครนัสแบบธรรมดาไม่ใช่การโทรกลับ
ในทางกลับกันด้วย Service
คุณจะต้องหยุดบริการด้วยตนเองซึ่งต้องทำงานมากขึ้น
Android มีส่วนประกอบนี้ซึ่งสามารถรับฟังเหตุการณ์ทั่วโลก (เช่นเหตุการณ์แบตเตอรี่เหตุการณ์เครือข่าย ฯลฯ ) รวมทั้งเหตุการณ์ที่กำหนดเอง คุณสามารถใช้คอมโพเนนต์นี้เพื่อสร้างเหตุการณ์ที่กำหนดเองซึ่งจะถูกทริกเกอร์เมื่อการอัปโหลดเสร็จสมบูรณ์
ในการดำเนินการนี้คุณต้องสร้างคลาสแบบกำหนดเองที่ขยาย BroadcastReceiver
ลงทะเบียนในไฟล์ Manifest และใช้ Intent
และ IntentFilter
เพื่อสร้างเหตุการณ์ที่กำหนดเอง ในการทริกเกอร์เหตุการณ์คุณจะต้องมี sendBroadcast
วิธี.
ประจักษ์:
public class UploadReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getBoolean(“success”, false) { Activity activity = (Activity)context; activity.updateUI(); } }
ผู้รับ:
Intent intent = new Intent(); intent.setAction('com.example.upload'); sendBroadcast(intent);
ผู้ส่ง:
Handler
แนวทางนี้เป็นทางเลือกที่ทำได้ แต่อย่างที่คุณสังเกตเห็นว่ามันเกี่ยวข้องกับงานบางอย่างและการออกอากาศมากเกินไปอาจทำให้สิ่งต่างๆช้าลงได้
ก Runnable
เป็นส่วนประกอบที่สามารถต่อเข้ากับเธรดแล้วทำการดำเนินการบางอย่างกับเธรดนั้นผ่านข้อความธรรมดาหรือ Looper
งาน ทำงานร่วมกับคอมโพเนนต์อื่น Handler
ซึ่งรับผิดชอบการประมวลผลข้อความในเธรดเฉพาะ
เมื่อ a Looper
ถูกสร้างขึ้นมันจะได้รับ Looper.getMainLooper()
อ็อบเจ็กต์ในตัวสร้างซึ่งระบุว่าเธรดใดที่ตัวจัดการแนบอยู่ หากคุณต้องการใช้ตัวจัดการที่เชื่อมต่อกับเธรดหลักคุณต้องใช้ looper ที่เชื่อมโยงกับเธรดหลักโดยเรียก Runnable
ในกรณีนี้หากต้องการอัปเดต UI จากเธรดพื้นหลังคุณสามารถสร้างตัวจัดการที่แนบมากับเธรด UI จากนั้นโพสต์การดำเนินการเป็น Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override public void run() { // update the ui from here } });
:
EventBus
วิธีนี้ดีกว่าวิธีแรกมาก แต่มีวิธีที่ง่ายกว่านี้อีก ...
UIEvent
ซึ่งเป็นไลบรารียอดนิยมของ GreenRobot ช่วยให้ส่วนประกอบต่างๆสามารถสื่อสารระหว่างกันได้อย่างปลอดภัย เนื่องจากกรณีการใช้งานของเราเป็นกรณีที่เราต้องการอัปเดต UI เท่านั้นจึงเป็นทางเลือกที่ง่ายและปลอดภัยที่สุด
ขั้นตอนที่ 1. สร้างคลาสกิจกรรม เช่น @Subscribe(threadMode = ThreadMode.MAIN) public void onUIEvent(UIEvent event) {/* Do something */}; register and unregister eventbus : @Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); } @Override public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); }
.
ขั้นตอนที่ 2. สมัครเข้าร่วมกิจกรรม
EventBus.getDefault().post(new UIEvent());
ขั้นตอนที่ 3. โพสต์เหตุการณ์: ThreadMode
ด้วย UIEvent
ในคำอธิบายประกอบคุณกำลังระบุเธรดที่คุณต้องการสมัครรับข้อมูลสำหรับเหตุการณ์นี้ ในตัวอย่างของเราที่นี่เรากำลังเลือกเธรดหลักเนื่องจากเราต้องการให้ผู้รับของเหตุการณ์สามารถอัปเดต UI ได้
คุณสามารถจัดโครงสร้าง class UploadFileService extends IntentService { // … Boolean success = uploadFile(File file); EventBus.getDefault().post(new UIEvent(success)); // ... }
ของคุณได้ คลาสเพื่อให้มีข้อมูลเพิ่มเติมตามความจำเป็น
ในบริการ:
@Subscribe(threadMode = ThreadMode.MAIN) public void onUIEvent(UIEvent event) {//show message according to the action success};
ในกิจกรรม / ส่วน:
EventBus library
การใช้ EventBus
การสื่อสารระหว่างเธรดจะง่ายขึ้นมาก
สมมติว่าคุณกำลังสร้างเครื่องเล่นสื่อและต้องการให้สามารถเล่นเพลงต่อไปได้แม้ว่าหน้าจอแอปพลิเคชันจะปิดอยู่ก็ตาม ในสถานการณ์นี้คุณจะต้องการให้ UI สามารถสื่อสารกับเธรดสื่อ (เช่นเล่นหยุดชั่วคราวและการดำเนินการอื่น ๆ ) และยังต้องการให้เธรดสื่ออัปเดต UI ตามเหตุการณ์บางอย่าง (เช่นข้อผิดพลาดสถานะการบัฟเฟอร์ ฯลฯ )
ตัวอย่างโปรแกรมเล่นสื่อแบบเต็มอยู่นอกเหนือขอบเขตของบทความนี้ อย่างไรก็ตามคุณสามารถค้นหาบทเรียนที่ดีได้ ที่นี่ และ ที่นี่ .
cfo หมายถึงอะไรในธุรกิจ
คุณสามารถใช้ BoundService
ที่นี่. อย่างไรก็ตามโดยทั่วไปไม่ปลอดภัยที่จะโพสต์เหตุการณ์จากเธรด UI และรับในบริการ เนื่องจากคุณไม่มีทางรู้ได้ว่าบริการกำลังทำงานอยู่หรือไม่เมื่อคุณส่งข้อความไปแล้ว
ก Service
คือ Binder
ที่ผูกไว้กับกิจกรรม / ส่วน ซึ่งหมายความว่ากิจกรรม / ส่วนย่อยจะรู้อยู่เสมอว่าบริการกำลังทำงานอยู่หรือไม่และนอกจากนี้ยังสามารถเข้าถึงวิธีการสาธารณะของบริการได้อีกด้วย
ในการใช้งานคุณต้องสร้าง public class MediaService extends Service { private final IBinder mBinder = new MediaBinder(); public class MediaBinder extends Binder { MediaService getService() { // Return this instance of LocalService so clients can call public methods return MediaService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } }
ที่กำหนดเอง ภายในบริการและสร้างวิธีที่ส่งคืนบริการ
ServiceConnection
ในการเชื่อมโยงกิจกรรมกับบริการคุณต้องใช้ bindService
ซึ่งเป็นคลาสที่ตรวจสอบสถานะบริการและใช้เมธอด // in the activity MediaService mService; // flag indicates the bound status boolean mBound; @Override protected void onStart() { super.onStart(); // Bind to LocalService Intent intent = new Intent(this, MediaService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { MediaBinder binder = (MediaBinder) service; mService = binder.getService(); mBound = true; } @Override public void onServiceDisconnected(ComponentName arg0) { mBound = false; } };
เพื่อทำการผูก:
BroadcastReceiver
คุณสามารถดูตัวอย่างการใช้งานแบบเต็มได้ ที่นี่ .
ในการสื่อสารกับบริการเมื่อผู้ใช้แตะที่ปุ่มเล่นหรือหยุดชั่วคราวคุณสามารถเชื่อมโยงกับบริการแล้วเรียกใช้วิธีการสาธารณะที่เกี่ยวข้องกับบริการ
เมื่อมีกิจกรรมสื่อและคุณต้องการสื่อสารสิ่งนั้นกลับไปที่กิจกรรม / ส่วนย่อยคุณสามารถใช้หนึ่งในเทคนิคก่อนหน้านี้ (เช่น Handler
, EventBus
หรือ merge()
)
สมมติว่าคุณกำลังสร้างแอปการท่องเที่ยวและต้องการแสดงสถานที่ท่องเที่ยวบนแผนที่ที่ดึงมาจากหลายแหล่ง (ผู้ให้บริการข้อมูลต่างกัน) เนื่องจากแหล่งที่มาทั้งหมดอาจไม่น่าเชื่อถือคุณอาจต้องการเพิกเฉยต่อแหล่งที่มาที่ล้มเหลวและแสดงแผนที่ต่อไป
เว็บไซต์บูตสแตรปคืออะไร
ในการทำให้กระบวนการขนานกันการเรียก API แต่ละรายการต้องเกิดขึ้นในเธรดที่แตกต่างกัน
ใน RxJava คุณสามารถรวมสิ่งที่สังเกตได้หลายรายการเข้าด้วยกันโดยใช้ concat()
หรือ ExecutorService
ตัวดำเนินการ จากนั้นคุณสามารถสมัครรับข้อมูล 'ผสาน' ที่สังเกตได้และรอผลลัพธ์ทั้งหมด
อย่างไรก็ตามแนวทางนี้ไม่ได้ผลตามที่คาดไว้ หากการเรียก API หนึ่งครั้งล้มเหลวสิ่งที่สังเกตได้ที่ผสานจะรายงานความล้มเหลวโดยรวม
Future
ใน Java จะสร้างจำนวนเธรดที่คงที่ (กำหนดค่าได้) และรันงานบนเธรดพร้อมกัน บริการส่งคืน a invokeAll()
อ็อบเจ็กต์ที่ส่งกลับผลลัพธ์ทั้งหมดในที่สุดผ่านทาง ExecutorService
วิธี.
แต่ละงานที่คุณส่งไปที่ Callable
ควรมีอยู่ใน invokeAll()
อินเทอร์เฟซซึ่งเป็นอินเทอร์เฟซสำหรับการสร้างงานที่สามารถทำให้เกิดข้อยกเว้นได้
เมื่อคุณได้ผลลัพธ์จาก ExecutorService pool = Executors.newFixedThreadPool(3); List
คุณสามารถตรวจสอบทุกผลลัพธ์และดำเนินการตามนั้น
ตัวอย่างเช่นสมมติว่าคุณมีแหล่งท่องเที่ยวสามประเภทที่มาจากจุดสิ้นสุดที่ต่างกันสามจุดและคุณต้องการโทรขนานกันสามสาย:
Cursor
ด้วยวิธีนี้คุณกำลังเรียกใช้การดำเนินการทั้งหมดพร้อมกัน ดังนั้นคุณสามารถตรวจสอบข้อผิดพลาดในแต่ละการกระทำแยกกันและเพิกเฉยต่อความล้มเหลวแต่ละรายการได้ตามความเหมาะสม
วิธีนี้ง่ายกว่าการใช้ RxJava ง่ายกว่าสั้นกว่าและไม่ล้มเหลวทุกการกระทำเพราะข้อยกเว้นเดียว
เมื่อจัดการกับฐานข้อมูล SQLite ในเครื่องขอแนะนำให้ใช้ฐานข้อมูลจากเธรดพื้นหลังเนื่องจากการเรียกฐานข้อมูล (โดยเฉพาะกับฐานข้อมูลขนาดใหญ่หรือการสืบค้นที่ซับซ้อน) อาจใช้เวลานานส่งผลให้ UI ค้าง
เมื่อค้นหาข้อมูล SQLite คุณจะได้รับ Cursor cursor = getData(); String name = cursor.getString();
จากนั้นสามารถใช้เพื่อดึงข้อมูลจริง
public Observable getLocalDataObservable() { return Observable.create(subscriber -> { Cursor cursor = mDbHandler.getData(); subscriber.onNext(cursor); }); }
คุณสามารถใช้ RxJava และรับข้อมูลจากฐานข้อมูลได้เช่นเดียวกับที่เรารับข้อมูลจากส่วนหลังของเรา:
getLocalDataObservable()
คุณสามารถใช้สิ่งที่สังเกตได้ที่ส่งคืนโดย getLocalDataObservable() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(cursor -> String name = cursor.getString(0), throwable -> Log.e(“db, 'error: %s' + throwable.getMessage()));
ดังต่อไปนี้:
CursorLoader
แม้ว่านี่จะเป็นแนวทางที่ดี แต่ก็มีวิธีหนึ่งที่ดีกว่านั้นเนื่องจากมีส่วนประกอบที่สร้างขึ้นเพื่อสถานการณ์นี้โดยเฉพาะ
Android มี Loader
ซึ่งเป็นส่วนประกอบดั้งเดิมสำหรับการโหลดข้อมูล SQLite และจัดการเธรดที่เกี่ยวข้อง มันคือ Cursor
ที่ส่งคืน getString()
ซึ่งเราสามารถใช้เพื่อรับข้อมูลโดยเรียกวิธีง่ายๆเช่น getLong()
, public class SimpleCursorLoader extends FragmentActivity implements LoaderManager.LoaderCallbacks { public static final String TAG = SimpleCursorLoader.class.getSimpleName(); private static final int LOADER_ID = 0x01; private TextView textView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_cursor_loader); textView = (TextView) findViewById(R.id.text_view); getSupportLoaderManager().initLoader(LOADER_ID, null, this); } public Loader onCreateLoader(int i, Bundle bundle) { return new CursorLoader(this, Uri.parse('content://com.github.browep.cursorloader.data') , new String[]{'col1'}, null, null, null); } public void onLoadFinished(Loader cursorLoader, Cursor cursor) { if (cursor != null && cursor.moveToFirst()) { String text = textView.getText().toString(); while (cursor.moveToNext()) { text += '
เป็นต้น
' + cursor.getString(1); cursor.moveToNext(); } textView.setText(Html.fromHtml(text) ); } } public void onLoaderReset(Loader cursorLoader) { } }
CursorLoader
ContentProvider
ใช้ได้กับ
|_+_|ส่วนประกอบ. ส่วนประกอบนี้มีคุณลักษณะฐานข้อมูลแบบเรียลไทม์มากมายเหลือเฟือ (เช่นการแจ้งเตือนการเปลี่ยนแปลงทริกเกอร์ ฯลฯ ) ที่ช่วยให้นักพัฒนาสามารถใช้ประสบการณ์ผู้ใช้ที่ดีขึ้นได้ง่ายขึ้น
Android มีหลายวิธีในการจัดการและจัดการเธรด แต่ไม่มีเลยที่จะเป็นกระสุนเงิน
การเลือกวิธีการทำเกลียวที่ถูกต้องขึ้นอยู่กับกรณีการใช้งานของคุณสามารถสร้างความแตกต่างทั้งหมดในการทำให้โซลูชันโดยรวมง่ายต่อการใช้งานและทำความเข้าใจ ส่วนประกอบดั้งเดิมเข้ากันได้ดีสำหรับบางกรณี แต่ไม่ใช่สำหรับทุกกรณี เช่นเดียวกับโซลูชันของบุคคลที่สามแบบแฟนซี
ฉันหวังว่าบทความนี้จะเป็นประโยชน์เมื่อทำงานกับโครงการ Android ถัดไป แบ่งปันประสบการณ์การใช้งานเธรดของคุณใน Android หรือกรณีการใช้งานที่วิธีแก้ปัญหาข้างต้นทำงานได้ดีหรือไม่ได้ผลในความคิดเห็นด้านล่าง