กระบวนการที่ทำงานอยู่เบื้องหลังอาจใช้หน่วยความจำและแบตเตอรี่มาก ตัวอย่างเช่น การกระจายข้อมูลแบบไม่เจาะจงปลายทาง อาจเริ่มกระบวนการเบื้องหลังหลายอย่างที่ได้ลงทะเบียน ให้รับฟัง แม้ว่ากระบวนการเหล่านั้น อาจไม่ได้ผลมากนักก็ตาม ซึ่งอาจส่งผลอย่างมากต่อทั้งประสิทธิภาพของอุปกรณ์และประสบการณ์ของผู้ใช้
Android 7.0 (API ระดับ 24) ดำเนินการต่อไปนี้เพื่อลดปัญหานี้ ข้อจำกัด:
- แอปที่กำหนดเป้าหมายเป็น Android 7.0 (API ระดับ 24) ขึ้นไปจะไม่ได้รับ
CONNECTIVITY_ACTION
ประกาศหากผู้ลงโฆษณา ระบุ Broadcast Receiver ในไฟล์ Manifest แอปจะยังคง ได้รับการออกอากาศCONNECTIVITY_ACTION
รายการหากลงทะเบียนBroadcastReceiver
ของพวกเขากับContext.registerReceiver()
และบริบทนั้นก็ยังคงใช้ได้ - แอปจะส่งหรือรับการออกอากาศ
ACTION_NEW_PICTURE
หรือACTION_NEW_VIDEO
ไม่ได้ การเพิ่มประสิทธิภาพนี้ส่งผลต่อแอปทั้งหมด ไม่ใช่เฉพาะแอปที่กำหนดเป้าหมายเป็น Android 7.0 (API ระดับ 24)
หากแอปใช้ Intent เหล่านี้ คุณควรนําการพึ่งพา Intent ดังกล่าวออกโดยเร็วที่สุดเพื่อให้กำหนดเป้าหมายอุปกรณ์ที่ใช้ Android 7.0 ขึ้นไปได้อย่างถูกต้อง เฟรมเวิร์ก Android มีโซลูชันมากมายที่ช่วยลด
สำหรับการออกอากาศโดยนัยเหล่านี้ เช่น JobScheduler
และ
WorkManager ใหม่มีกลไกที่มีประสิทธิภาพในการจัดตารางเวลาเครือข่าย
การดำเนินการเมื่อเงื่อนไขที่ระบุ เช่น การเชื่อมต่อกับเครือข่ายที่ไม่มีการวัดปริมาณอินเทอร์เน็ต
ในเครือข่ายเดียวกัน นอกจากนี้ คุณยังใช้ JobScheduler
เพื่อตอบสนองต่อการเปลี่ยนแปลงของผู้ให้บริการเนื้อหาได้ด้วย JobInfo
ออบเจ็กต์จะรวมพารามิเตอร์ที่ JobScheduler
ใช้เพื่อตั้งเวลางาน เมื่อมีคุณสมบัติตรงตามเงื่อนไขของงาน
เรียกใช้งานนี้ใน JobService
ของแอป
ในหน้านี้ เราจะได้เรียนรู้วิธีใช้วิธีอื่น เช่น
JobScheduler
เพื่อปรับแอปของคุณให้เข้ากับ
ข้อจำกัด
ข้อจำกัดที่เริ่มต้นโดยผู้ใช้
ในหน้าการใช้งานแบตเตอรี่ภายในระบบ ผู้ใช้จึงสามารถ เลือกจากตัวเลือกต่อไปนี้
- ไม่จำกัด: อนุญาตการทำงานในเบื้องหลังทั้งหมด ซึ่งอาจใช้พลังงานแบตเตอรี่มากขึ้น
- เพิ่มประสิทธิภาพ (ค่าเริ่มต้น): เพิ่มประสิทธิภาพการทำงานของแอปในการทำงานเบื้องหลัง ขึ้นอยู่กับวิธีที่ผู้ใช้โต้ตอบกับแอป
- จำกัด: ป้องกันไม่ให้แอปทำงานในเบื้องหลังโดยสมบูรณ์ แอปอาจไม่ทำงานตามที่คาดไว้
หากแอปแสดงลักษณะการทำงานที่ไม่ถูกต้องตามที่อธิบายไว้ใน Android Dev Tools ระบบอาจแจ้งให้ผู้ใช้จำกัดการเข้าถึงทรัพยากรของระบบของแอปนั้น
หากระบบสังเกตเห็นว่าแอปใช้ทรัพยากรมากเกินไป ก็จะแจ้งเตือน ให้แก่ผู้ใช้ และให้ตัวเลือกแก่ผู้ใช้ในการจำกัดการดำเนินการของแอป ลักษณะการทำงานที่ทริกเกอร์การแจ้งเตือนได้มีดังนี้
- Wake Lock มากเกินไป: Wake Lock บางส่วน 1 ครั้งนาน 1 ชั่วโมงเมื่อหน้าจอปิดอยู่
- บริการที่ทำงานอยู่เบื้องหลังมากเกินไป: หากแอปกำหนดเป้าหมายระดับ API ต่ำกว่า 26 และมี บริการที่ทำงานอยู่เบื้องหลังมากเกินไป
ผู้ผลิตอุปกรณ์เป็นผู้กำหนดข้อจำกัดที่แน่นอน ตัวอย่างเช่น ในบิลด์ AOSP ที่ใช้ Android 9 (API ระดับ 28) ขึ้นไป แอปที่ทำงานอยู่เบื้องหลังซึ่งอยู่ในสถานะ "ถูกจำกัด" จะมีข้อจำกัดต่อไปนี้
- เปิดบริการที่ทำงานอยู่เบื้องหน้าไม่ได้
- นำบริการที่ทำงานอยู่เบื้องหน้าที่มีอยู่ออกจากเบื้องหน้าแล้ว
- การปลุกไม่ทำงาน
- ระบบไม่ดําเนินการงาน
นอกจากนี้ หากแอปกำหนดเป้าหมายเป็น Android 13 (API ระดับ 33) ขึ้นไปและอยู่ในสถานะ "ถูกจำกัด" ระบบจะไม่แสดงการออกอากาศ BOOT_COMPLETED
หรือการออกอากาศ LOCKED_BOOT_COMPLETED
จนกว่าแอปจะเริ่มต้นขึ้นด้วยเหตุผลอื่นๆ
ข้อจำกัดที่เฉพาะเจาะจงแสดงอยู่ในหัวข้อข้อจำกัดของการจัดการพลังงาน
ข้อจำกัดในการรับการออกอากาศกิจกรรมของเครือข่าย
แอปที่กำหนดเป้าหมายเป็น Android 7.0 (API ระดับ 24) จะไม่ได้รับการบรอดแคสต์ CONNECTIVITY_ACTION
หากแอปดังกล่าว
ลงทะเบียนเพื่อรับไฟล์ในไฟล์ Manifest และกระบวนการที่ต้องใช้
จะไม่เริ่มออกอากาศ ซึ่งอาจก่อให้เกิดปัญหาสำหรับแอปที่ต้องการคอยฟังการเปลี่ยนแปลงของเครือข่ายหรือทำกิจกรรมจำนวนมากบนเครือข่ายเมื่ออุปกรณ์เชื่อมต่อกับเครือข่ายแบบไม่จำกัดปริมาณ เฟรมเวิร์ก Android มีวิธีแก้ปัญหาการจํากัดนี้หลายวิธีแล้ว แต่การเลือกวิธีที่เหมาะสมนั้นขึ้นอยู่กับสิ่งที่คุณต้องการให้แอปทํา
หมายเหตุ: BroadcastReceiver
ที่ลงทะเบียนกับ Context.registerReceiver()
จะยังคงรับการออกอากาศเหล่านี้ต่อไปขณะที่แอปทำงานอยู่
ตั้งเวลางานของเครือข่ายบนการเชื่อมต่อที่ไม่มีการตรวจวัด
เมื่อใช้คลาส JobInfo.Builder
เพื่อสร้างออบเจ็กต์ JobInfo
ให้ใช้เมธอด setRequiredNetworkType()
และส่ง JobInfo.NETWORK_TYPE_UNMETERED
เป็นพารามิเตอร์งาน ตัวอย่างโค้ดต่อไปนี้จะกำหนดเวลาให้บริการทำงานเมื่ออุปกรณ์เชื่อมต่อกับเครือข่ายแบบไม่จำกัดปริมาณการใช้งานและกำลังชาร์จ
const val MY_BACKGROUND_JOB = 0 ... fun scheduleJob(context: Context) { val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val job = JobInfo.Builder( MY_BACKGROUND_JOB, ComponentName(context, MyJobService::class.java) ) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) .setRequiresCharging(true) .build() jobScheduler.schedule(job) }
public static final int MY_BACKGROUND_JOB = 0; ... public static void scheduleJob(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo job = new JobInfo.Builder( MY_BACKGROUND_JOB, new ComponentName(context, MyJobService.class)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) .setRequiresCharging(true) .build(); js.schedule(job); }
เมื่อเป็นไปตามเงื่อนไขสำหรับงานแล้ว แอปของคุณจะได้รับการติดต่อกลับให้ทำงาน
เมธอด onStartJob()
ใน
ระบุ JobService.class
ดูตัวอย่างเพิ่มเติมของการติดตั้งใช้งาน JobScheduler
ได้ที่แอปตัวอย่างของ JobScheduler
ทางเลือกใหม่ของ JobScheduler คือ WorkManager ซึ่งเป็น API ที่ช่วยให้คุณกำหนดเวลา งานเบื้องหลังที่จำเป็น รับประกันการดำเนินการเสร็จสมบูรณ์ ไม่ว่ากระบวนการของแอปจะเป็นไปหรือไม่ก็ตาม เครื่องมือจัดการงาน เลือกวิธีที่เหมาะสมในการทำงาน (ไม่ว่าจะเป็นเทรดโดยตรงในกระบวนการของแอป รวมทั้งการใช้ JobScheduler, FirebaseJobDispatcher หรือ LoadManager) โดยอิงตามปัจจัยต่างๆ เช่น ระดับ API ของอุปกรณ์ นอกจากนี้ WorkManager ยังไม่ต้องใช้บริการ Play และมีฟีเจอร์ขั้นสูงหลายอย่าง เช่น การเชื่อมโยงงานเข้าด้วยกันหรือการตรวจสอบสถานะของงาน เพื่อเรียนรู้ เพิ่มเติม โปรดดู WorkManager
ตรวจสอบการเชื่อมต่อเครือข่ายขณะที่แอปทำงานอยู่
แอปที่กำลังทำงานอยู่ยังคงฟัง CONNECTIVITY_CHANGE
ได้ด้วย
ลงทะเบียนเมื่อ BroadcastReceiver
อย่างไรก็ตาม ConnectivityManager
API มีวิธีส่งคำขอที่มีประสิทธิภาพมากกว่า
Callback เมื่อเป็นไปตามเงื่อนไขของเครือข่ายที่ระบุเท่านั้น
ออบเจ็กต์ NetworkRequest
จะกำหนดพารามิเตอร์ของ Callback ของเครือข่ายในแง่ของ NetworkCapabilities
คุณสร้างออบเจ็กต์ NetworkRequest
ด้วยคลาส NetworkRequest.Builder
registerNetworkCallback()
จากนั้นส่งออบเจ็กต์ NetworkRequest
ไปยังระบบ วันและเวลา
เป็นไปตามเงื่อนไขเครือข่าย แอปจะได้รับการเรียกกลับเพื่อดำเนินการ
มีการกำหนด onAvailable()
ในคลาส ConnectivityManager.NetworkCallback
แอปจะยังคงได้รับการติดต่อกลับจนกว่าแอปจะออกหรือเรียกใช้
unregisterNetworkCallback()
ข้อจำกัดในการรับการออกอากาศรูปภาพและวิดีโอ
ใน Android 7.0 (API ระดับ 24) แอปจะไม่สามารถส่งหรือรับการออกอากาศ ACTION_NEW_PICTURE
หรือ ACTION_NEW_VIDEO
ได้ การจำกัดนี้มีประโยชน์
ลดผลกระทบด้านประสิทธิภาพและประสบการณ์ของผู้ใช้เมื่อแอปหลายแอปต้อง
ทำงานเพื่อประมวลผลรูปภาพหรือวิดีโอใหม่ Android 7.0 (API ระดับ 24) ใช้ JobInfo
และ JobParameters
เป็นโซลูชันทางเลือก
ทริกเกอร์งานเมื่อมีการเปลี่ยนแปลง URI เนื้อหา
หากต้องการทริกเกอร์งานเมื่อมีการเปลี่ยนแปลง URI ของเนื้อหา Android 7.0 (API ระดับ 24) จะขยายJobInfo
API ด้วยเมธอดต่อไปนี้
-
JobInfo.TriggerContentUri()
- สรุปพารามิเตอร์ที่จำเป็นต่อการทริกเกอร์งานเมื่อมีการเปลี่ยนแปลง URI เนื้อหา
-
JobInfo.Builder.addTriggerContentUri()
-
ส่งผ่านออบเจ็กต์
TriggerContentUri
ไปยังJobInfo
ContentObserver
จะตรวจสอบ URI ของเนื้อหาที่รวมอยู่ หากมีออบเจ็กต์TriggerContentUri
หลายรายการที่เชื่อมโยงกับงาน ระบบจะระบุ Callback แม้ว่าจะรายงานการเปลี่ยนแปลงใน URI เนื้อหาเพียงรายการเดียวก็ตาม -
เพิ่มแฟล็ก
TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
ไปยัง ทริกเกอร์งานหากองค์ประกอบสืบทอดของ URI ที่ระบุมีการเปลี่ยนแปลง แฟล็กนี้ สอดคล้องกับพารามิเตอร์notifyForDescendants
ที่ส่งไปยังregisterContentObserver()
หมายเหตุ: TriggerContentUri()
ไม่สามารถใช้ใน
รวมกับ setPeriodic()
หรือ setPersisted()
ในการติดตามการเปลี่ยนแปลงของเนื้อหาอย่างต่อเนื่อง
JobInfo
ก่อนที่ JobService
ของแอปจะจัดการกับ Callback ล่าสุดให้เสร็จสิ้น
โค้ดตัวอย่างต่อไปนี้กำหนดเวลางานที่จะทริกเกอร์เมื่อระบบรายงาน
การเปลี่ยนแปลง URI เนื้อหา MEDIA_URI
:
const val MY_BACKGROUND_JOB = 0 ... fun scheduleJob(context: Context) { val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val job = JobInfo.Builder( MY_BACKGROUND_JOB, ComponentName(context, MediaContentJob::class.java) ) .addTriggerContentUri( JobInfo.TriggerContentUri( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS ) ) .build() jobScheduler.schedule(job) }
public static final int MY_BACKGROUND_JOB = 0; ... public static void scheduleJob(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo.Builder builder = new JobInfo.Builder( MY_BACKGROUND_JOB, new ComponentName(context, MediaContentJob.class)); builder.addTriggerContentUri( new JobInfo.TriggerContentUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS)); js.schedule(builder.build()); }
เมื่อระบบรายงานการเปลี่ยนแปลงใน URI เนื้อหาที่ระบุ แอปของคุณ
ได้รับการ Callback และออบเจ็กต์ JobParameters
มีค่า
ส่งไปยัง onStartJob()
ใน MediaContentJob.class
ระบุหน่วยงานด้านเนื้อหาที่ทริกเกอร์งาน
Android 7.0 (API ระดับ 24) ยังขยาย JobParameters
เป็น
ทำให้แอปของคุณได้รับข้อมูลที่เป็นประโยชน์เกี่ยวกับหน่วยงานด้านเนื้อหา
และ URI ที่ทริกเกอร์งาน:
-
Uri[] getTriggeredContentUris()
-
แสดงผลอาร์เรย์ของ URI ที่ทริกเกอร์งาน ค่านี้จะเท่ากับ
null
หากไม่มี URI ที่ทริกเกอร์งาน (เช่น งานเริ่มทํางานเนื่องจากถึงกำหนดเวลาหรือเหตุผลอื่นๆ) หรือมี URI ที่เปลี่ยนแปลงมากกว่า 50 รายการ -
String[] getTriggeredContentAuthorities()
-
แสดงผลอาร์เรย์สตริงของหน่วยงานด้านเนื้อหาที่ทริกเกอร์งาน
หากอาร์เรย์ที่แสดงผลไม่ใช่
null
ให้ใช้getTriggeredContentUris()
เพื่อดึงรายละเอียดของ URI ที่มีการเปลี่ยนแปลง
โค้ดตัวอย่างต่อไปนี้จะลบล้างเมธอด JobService.onStartJob()
และ
บันทึกหน่วยงานเนื้อหาและ URI ที่ทริกเกอร์งาน:
override fun onStartJob(params: JobParameters): Boolean { StringBuilder().apply { append("Media content has changed:\n") params.triggeredContentAuthorities?.also { authorities -> append("Authorities: ${authorities.joinToString(", ")}\n") append(params.triggeredContentUris?.joinToString("\n")) } ?: append("(No content)") Log.i(TAG, toString()) } return true }
@Override public boolean onStartJob(JobParameters params) { StringBuilder sb = new StringBuilder(); sb.append("Media content has changed:\n"); if (params.getTriggeredContentAuthorities() != null) { sb.append("Authorities: "); boolean first = true; for (String auth : params.getTriggeredContentAuthorities()) { if (first) { first = false; } else { sb.append(", "); } sb.append(auth); } if (params.getTriggeredContentUris() != null) { for (Uri uri : params.getTriggeredContentUris()) { sb.append("\n"); sb.append(uri); } } } else { sb.append("(No content)"); } Log.i(TAG, sb.toString()); return true; }
เพิ่มประสิทธิภาพแอปให้ดียิ่งขึ้น
การเพิ่มประสิทธิภาพให้แอปทำงานบนอุปกรณ์ที่มีหน่วยความจำต่ำหรือในหน่วยความจำต่ำ จะช่วยปรับปรุงประสิทธิภาพและประสบการณ์ของผู้ใช้ได้ การนําการพึ่งพาบริการที่ทำงานอยู่เบื้องหลังและ Broadcast Receiver ที่ไม่ระบุซึ่งลงทะเบียนในไฟล์ Manifest ออกจะช่วยให้แอปทำงานได้ดีขึ้นบนอุปกรณ์ดังกล่าว แม้ว่า Android 7.0 (API ระดับ 24) จะมีขั้นตอนในการลดปัญหาเหล่านี้บางส่วน แต่เราขอแนะนำให้คุณเพิ่มประสิทธิภาพแอปให้ทำงานได้โดยไม่ต้องใช้กระบวนการทำงานเบื้องหลังเหล่านี้เลย
Android Debug Bridge (ADB) ต่อไปนี้ คำสั่งจะช่วยให้คุณทดสอบลักษณะการทำงานของแอปเมื่อปิดใช้กระบวนการเบื้องหลังได้ ดังนี้
- เพื่อจำลองสภาวะที่มีการออกอากาศโดยนัยและบริการที่ทำงานอยู่เบื้องหลัง ไม่พร้อมใช้งาน ให้ป้อนคำสั่งต่อไปนี้
-
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore
- หากต้องการเปิดใช้การออกอากาศโดยนัยและบริการเบื้องหลังอีกครั้ง ให้ป้อน คำสั่งต่อไปนี้
-
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow
- คุณสามารถจําลองสถานการณ์ที่ผู้ใช้นําแอปของคุณไปไว้ในสถานะ "ถูกจํากัด" สําหรับการใช้งานแบตเตอรี่ในเบื้องหลัง การตั้งค่านี้จะป้องกันไม่ให้แอปทำงานอยู่เบื้องหลัง ซึ่งทำได้โดยการเรียกใช้คำสั่งต่อไปนี้ในหน้าต่างเทอร์มินัล
-
$ adb shell cmd appops set <PACKAGE_NAME> RUN_ANY_IN_BACKGROUND deny