กระบวนการในพื้นหลังอาจต้องใช้หน่วยความจำและแบตเตอรี่มาก ตัวอย่างเช่น การกระจายข้อมูลแบบไม่เจาะจงปลายทาง อาจเริ่มกระบวนการเบื้องหลังหลายอย่างที่ได้ลงทะเบียน ให้รับฟัง แม้ว่ากระบวนการเหล่านั้น อาจไม่ได้ผลมากนักก็ตาม ซึ่งอาจ ส่งผลดีต่อทั้งประสิทธิภาพของอุปกรณ์และประสบการณ์ของผู้ใช้อย่างมาก
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 ดังกล่าว คุณควรนำทรัพยากร Dependency ออก
โดยเร็วที่สุดเพื่อให้คุณสามารถกำหนดเป้าหมายอุปกรณ์ที่ใช้ Android 7.0 ได้อย่างถูกต้อง
หรือสูงกว่า เฟรมเวิร์ก Android มีโซลูชันมากมายที่ช่วยลด
สำหรับการออกอากาศโดยนัยเหล่านี้ เช่น JobScheduler
และ
WorkManager ใหม่มีกลไกที่มีประสิทธิภาพในการจัดตารางเวลาเครือข่าย
การดำเนินการเมื่อเป็นไปตามเงื่อนไขที่ระบุ เช่น การเชื่อมต่อกับเครือข่ายที่ไม่มีการวัดปริมาณอินเทอร์เน็ต
ในเครือข่ายเดียวกัน ตอนนี้คุณใช้ JobScheduler
ได้ด้วย
เพื่อตอบสนองต่อการเปลี่ยนแปลงต่อผู้ให้บริการเนื้อหา JobInfo
ห่อหุ้มพารามิเตอร์ที่JobScheduler
ใช้ในการกำหนดเวลางาน เมื่อมีคุณสมบัติตรงตามเงื่อนไขของงาน
เรียกใช้งานนี้ใน JobService
ของแอป
ในหน้านี้ เราจะได้เรียนรู้วิธีใช้วิธีอื่น เช่น
JobScheduler
เพื่อปรับแอปของคุณให้เข้ากับ
ข้อจำกัด
ข้อจำกัดที่เริ่มต้นโดยผู้ใช้
ในหน้าการใช้งานแบตเตอรี่ภายในระบบ ผู้ใช้จึงสามารถ เลือกจากตัวเลือกต่อไปนี้
- ไม่จำกัด: อนุญาตการทำงานในเบื้องหลังทั้งหมด ซึ่งอาจสิ้นเปลืองแบตเตอรี่มากขึ้น
- เพิ่มประสิทธิภาพ (ค่าเริ่มต้น): เพิ่มประสิทธิภาพการทำงานของแอปในการทำงานเบื้องหลัง ขึ้นอยู่กับวิธีที่ผู้ใช้โต้ตอบกับแอป
- จำกัด: ป้องกันไม่ให้แอปทำงานในเบื้องหลังโดยสมบูรณ์ แอป อาจไม่ทำงานตามที่คาดไว้
หากแอปแสดงพฤติกรรมที่ไม่ดีบางอย่างที่อธิบายไว้ใน Android Vitals ระบบอาจแจ้งให้ผู้ใช้จำกัด สิทธิ์ของแอปนั้นในการเข้าถึงทรัพยากรระบบ
หากระบบสังเกตเห็นว่าแอปใช้ทรัพยากรมากเกินไป ก็จะแจ้งเตือน ให้แก่ผู้ใช้ และให้ตัวเลือกแก่ผู้ใช้ในการจำกัดการดำเนินการของแอป ลักษณะการทำงานที่ทริกเกอร์การแจ้งเตือนได้มีดังนี้
- 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
เป็นพารามิเตอร์งาน ตัวอย่างโค้ดต่อไปนี้
ตั้งเวลาให้บริการทำงานเมื่ออุปกรณ์เชื่อมต่อกับบริการที่ไม่มีการตรวจสอบการใช้งาน
เครือข่ายและกำลังชาร์จ:
Kotlin
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) }
Java
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 หรือ AlarmManager) โดยอิงตามปัจจัยต่างๆ เช่น ระดับ API ของอุปกรณ์ นอกจากนี้ WorkManager ไม่จำเป็นต้องใช้บริการ Google 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 เนื้อหา
Android 7.0 (API ระดับ 24) ได้ขยายเพื่อเรียกใช้งานเมื่อมีการเปลี่ยนแปลง URI เนื้อหา
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
:
Kotlin
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) }
Java
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 ที่ทริกเกอร์งาน:
Kotlin
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 }
Java
@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; }
เพิ่มประสิทธิภาพแอปให้ดียิ่งขึ้น
การเพิ่มประสิทธิภาพให้แอปทำงานบนอุปกรณ์ที่มีหน่วยความจำต่ำหรือในหน่วยความจำต่ำ จะช่วยปรับปรุงประสิทธิภาพและประสบการณ์ของผู้ใช้ได้ กำลังนำออก ทรัพยากร Dependency ในบริการในเบื้องหลังและโดยนัยที่ลงทะเบียนไฟล์ Manifest Broadcast Receiver จะช่วยให้แอปของคุณทำงานได้ดีขึ้นบนอุปกรณ์ดังกล่าว แม้ว่า 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