การเพิ่มประสิทธิภาพในเบื้องหลัง

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

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 หรือ Alarm Manager) โดยอิงตามปัจจัยต่างๆ เช่น ระดับ 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