กระบวนการในเบื้องหลังอาจใช้หน่วยความจำและแบตเตอรี่มาก ตัวอย่างเช่น การออกอากาศโดยนัยอาจเริ่มกระบวนการเบื้องหลังหลายอย่างที่ลงทะเบียนเพื่อฟังการออกอากาศดังกล่าว แม้ว่ากระบวนการเหล่านั้นอาจไม่ได้ทำงานมากนักก็ตาม ซึ่งอาจส่งผลอย่างมากต่อทั้งประสิทธิภาพของอุปกรณ์และประสบการณ์ของผู้ใช้
Android 7.0 (API ระดับ 24) มีข้อจำกัดต่อไปนี้เพื่อลดปัญหานี้
- แอปที่กำหนดเป้าหมายเป็น Android 7.0 (API ระดับ 24) ขึ้นไปจะไม่ได้รับ
CONNECTIVITY_ACTION
การออกอากาศหากประกาศเครื่องรับการออกอากาศในไฟล์ 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 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
WorkManager เป็น API ที่ช่วยให้คุณกำหนดเวลางานในเบื้องหลังที่ต้องทำให้เสร็จสมบูรณ์ได้ ไม่ว่ากระบวนการของแอปจะทำงานอยู่หรือไม่ก็ตาม WorkManager จะเลือกวิธีที่เหมาะสมในการเรียกใช้งาน (ทั้งในเธรดในกระบวนการของแอปโดยตรง รวมถึงการใช้ JobScheduler, FirebaseJobDispatcher หรือ AlarmManager) โดยอิงตามปัจจัยต่างๆ เช่น ระดับ API ของอุปกรณ์ นอกจากนี้ WorkManager ยังไม่จำเป็นต้องใช้บริการ Google Play และมีฟีเจอร์ขั้นสูงหลายอย่าง เช่น การเชื่อมโยงงานเข้าด้วยกันหรือการตรวจสอบสถานะของงาน ดูข้อมูลเพิ่มเติมได้ที่ WorkManager
ตรวจสอบการเชื่อมต่อเครือข่ายขณะที่แอปทำงาน
แอปที่กำลังทำงานจะยังคงฟัง CONNECTIVITY_CHANGE
ได้โดยใช้ BroadcastReceiver
ที่ลงทะเบียนไว้ อย่างไรก็ตาม ConnectivityManager
API มีวิธีที่แข็งแกร่งกว่าในการขอ
การเรียกกลับเมื่อตรงตามเงื่อนไขเครือข่ายที่ระบุเท่านั้น
NetworkRequest
จะกำหนดพารามิเตอร์ของการเรียกกลับของเครือข่ายในแง่ของ NetworkCapabilities
คุณ
สร้างออบเจ็กต์ NetworkRequest
ด้วยคลาส NetworkRequest.Builder
registerNetworkCallback()
จากนั้นส่งออบเจ็กต์ NetworkRequest
ไปยังระบบ เมื่อ
ตรงตามเงื่อนไขของเครือข่าย แอปจะได้รับการเรียกกลับเพื่อเรียกใช้เมธอด onAvailable()
ที่กำหนดไว้ในคลาส ConnectivityManager.NetworkCallback
แอปจะยังคงได้รับ Callback จนกว่าแอปจะออกหรือเรียกใช้
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) ขยาย JobInfo
API ด้วยเมธอดต่อไปนี้เพื่อทริกเกอร์งานเมื่อมีการเปลี่ยนแปลง URI ของเนื้อหา
-
JobInfo.TriggerContentUri()
- แคปซูลพารามิเตอร์ที่จำเป็นในการทริกเกอร์งานเมื่อมีการเปลี่ยนแปลง URI ของเนื้อหา
-
JobInfo.Builder.addTriggerContentUri()
-
ส่งออบเจ็กต์
TriggerContentUri
ไปยังJobInfo
ContentObserver
จะตรวจสอบ URI ของเนื้อหาที่แคปซูล หากมีออบเจ็กต์TriggerContentUri
หลายรายการที่เชื่อมโยงกับงาน ระบบจะแสดง การเรียกกลับแม้ว่าจะรายงานการเปลี่ยนแปลงใน URI ของเนื้อหาเพียงรายการเดียวก็ตาม -
เพิ่มแฟล็ก
TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
เพื่อ ทริกเกอร์งานหากมีการเปลี่ยนแปลงในลูกหลานของ URI ที่ระบุ โดยแฟล็กนี้ สอดคล้องกับพารามิเตอร์notifyForDescendants
ที่ส่งไปยังregisterContentObserver()
หมายเหตุ: ใช้ TriggerContentUri()
ร่วมกับ setPeriodic()
หรือ setPersisted()
ไม่ได้ หากต้องการตรวจสอบการเปลี่ยนแปลงเนื้อหาอย่างต่อเนื่อง ให้กำหนดเวลาใหม่JobInfo
ก่อนที่JobService
ของแอปจะจัดการการเรียกกลับล่าสุดเสร็จสิ้น
โค้ดตัวอย่างต่อไปนี้จะตั้งเวลางานให้ทริกเกอร์เมื่อระบบรายงานการเปลี่ยนแปลง 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 ของเนื้อหาที่ระบุ แอปของคุณจะ
ได้รับการเรียกกลับและส่งออบเจ็กต์ JobParameters
ไปยัง
เมธอด onStartJob()
ใน MediaContentJob.class
ระบุว่าหน่วยงานด้านเนื้อหาใดที่ทริกเกอร์งาน
Android 7.0 (API ระดับ 24) ยังขยาย JobParameters
เพื่อ
อนุญาตให้แอปรับข้อมูลที่เป็นประโยชน์เกี่ยวกับสิ่งที่ Content Authority
และ 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; }
เพิ่มประสิทธิภาพแอปเพิ่มเติม
การเพิ่มประสิทธิภาพแอปให้ทำงานในอุปกรณ์ที่มีหน่วยความจำต่ำหรือในสภาวะที่มีหน่วยความจำต่ำ จะช่วยปรับปรุงประสิทธิภาพและประสบการณ์ของผู้ใช้ได้ การนำการอ้างอิง บริการที่ทำงานอยู่เบื้องหลังและ 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