بهینه سازی پس زمینه

فرآیندهای پس‌زمینه می‌توانند حافظه و باتری زیادی داشته باشند. برای مثال، یک پخش ضمنی ممکن است بسیاری از فرآیندهای پس‌زمینه‌ای را که برای گوش دادن به آن ثبت‌نام کرده‌اند، شروع کند، حتی اگر این فرآیندها کار زیادی انجام ندهند. این می تواند تأثیر قابل توجهی بر عملکرد دستگاه و تجربه کاربر داشته باشد.

برای کاهش این مشکل، Android 7.0 (سطح API 24) محدودیت های زیر را اعمال می کند:

  • برنامه‌هایی که Android 7.0 (سطح API 24) و بالاتر را هدف قرار می‌دهند، اگر گیرنده پخش خود را در مانیفست اعلام کنند، پخش‌های CONNECTIVITY_ACTION دریافت نمی‌کنند. اگر برنامه‌ها BroadcastReceiver خود را با Context.registerReceiver() ثبت کنند، همچنان پخش‌های CONNECTIVITY_ACTION را دریافت خواهند کرد و این زمینه همچنان معتبر است.
  • برنامه‌ها نمی‌توانند پخش‌های ACTION_NEW_PICTURE یا ACTION_NEW_VIDEO را ارسال یا دریافت کنند. این بهینه‌سازی بر همه برنامه‌ها تأثیر می‌گذارد، نه تنها برنامه‌هایی که Android 7.0 (سطح API 24) را هدف قرار می‌دهند.

اگر برنامه شما از هر یک از این مقاصد استفاده می‌کند، باید وابستگی‌ها را در اسرع وقت حذف کنید تا بتوانید دستگاه‌های دارای Android نسخه ۷.۰ یا بالاتر را به درستی هدف‌گیری کنید. چارچوب Android چندین راه حل برای کاهش نیاز به این پخش های ضمنی ارائه می دهد. به عنوان مثال، JobScheduler و WorkManager جدید مکانیزم‌های قوی برای برنامه‌ریزی عملیات شبکه در زمانی که شرایط مشخص شده، مانند اتصال به یک شبکه بدون اندازه‌گیری برآورده می‌شوند، ارائه می‌کنند. اکنون می توانید از JobScheduler برای واکنش به تغییرات ارائه دهندگان محتوا نیز استفاده کنید. اشیاء JobInfo پارامترهایی را که JobScheduler برای زمان‌بندی کار شما استفاده می‌کند، محصور می‌کند. وقتی شرایط کار برآورده شد، سیستم این کار را در JobService برنامه شما اجرا می کند.

در این صفحه، نحوه استفاده از روش‌های جایگزین، مانند JobScheduler را برای تطبیق برنامه خود با این محدودیت‌های جدید، یاد خواهیم گرفت.

محدودیت های ایجاد شده توسط کاربر

در صفحه استفاده از باتری در تنظیمات سیستم ، کاربر می‌تواند یکی از گزینه‌های زیر را انتخاب کند:

  • نامحدود: به همه کارهای پس‌زمینه اجازه دهید، که ممکن است باتری بیشتری مصرف کند.
  • بهینه‌سازی شده (پیش‌فرض): بهینه‌سازی توانایی برنامه برای انجام کارهای پس‌زمینه، بر اساس نحوه تعامل کاربر با برنامه.
  • محدود: به طور کامل از اجرای برنامه در پس‌زمینه جلوگیری می‌کند. ممکن است برنامه ها آنطور که انتظار می رود کار نکنند.

اگر برنامه‌ای برخی از رفتارهای بد توضیح داده شده در Android vitals را نشان دهد، ممکن است سیستم از کاربر بخواهد دسترسی آن برنامه را به منابع سیستم محدود کند.

اگر سیستم متوجه شود که برنامه ای منابع بیش از حد مصرف می کند، به کاربر اطلاع می دهد و به کاربر این امکان را می دهد که اقدامات برنامه را محدود کند. رفتارهایی که می توانند باعث ایجاد اخطار شوند عبارتند از:

  • Wake lock های بیش از حد: 1 قفل بیدار شدن جزئی به مدت یک ساعت در حالت خاموش نگه داشته می شود
  • خدمات پس‌زمینه بیش از حد: اگر برنامه سطوح API کمتر از 26 را هدف قرار می‌دهد و خدمات پس‌زمینه بیش از حد دارد

محدودیت های دقیق اعمال شده توسط سازنده دستگاه تعیین می شود. به عنوان مثال، در ساخت‌های AOSP که Android 9 (سطح API 28) یا بالاتر را اجرا می‌کنند، برنامه‌هایی که در پس‌زمینه اجرا می‌شوند و در حالت "محدود" هستند دارای محدودیت‌های زیر هستند:

  • نمی توان سرویس های پیش زمینه را راه اندازی کرد
  • سرویس های پیش زمینه موجود از پیش زمینه حذف می شوند
  • آلارم ها فعال نمی شوند
  • کارها اجرا نمی شوند

همچنین، اگر برنامه‌ای اندروید 13 (سطح API 33) یا بالاتر را هدف قرار دهد و در حالت «محدود» باشد، سیستم پخش BOOT_COMPLETED یا پخش LOCKED_BOOT_COMPLETED را تا زمانی که برنامه به دلایل دیگر راه‌اندازی نشود، ارائه نمی‌کند.

محدودیت‌های خاص در محدودیت‌های مدیریت انرژی فهرست شده‌اند.

محدودیت در دریافت پخش فعالیت های شبکه

برنامه‌هایی که Android 7.0 (سطح API 24) را هدف قرار می‌دهند، اگر برای دریافت آن‌ها در مانیفست خود ثبت نام کنند، پخش‌های CONNECTIVITY_ACTION دریافت نمی‌کنند و فرآیندهای وابسته به این پخش شروع نمی‌شوند. این می‌تواند برای برنامه‌هایی که می‌خواهند به تغییرات شبکه گوش دهند یا فعالیت‌های شبکه انبوه را هنگام اتصال دستگاه به یک شبکه بدون اندازه‌گیری انجام دهند، مشکل ایجاد کند. چندین راه حل برای دور زدن این محدودیت از قبل در فریم ورک اندروید وجود دارد، اما انتخاب راه حل مناسب به آنچه می خواهید برنامه شما انجام دهد بستگی دارد.

توجه: یک BroadcastReceiver ثبت شده در Context.registerReceiver() همچنان به دریافت این پخش ها در حین اجرای برنامه ادامه می دهد.

کارهای شبکه را روی اتصالات بدون اندازه‌گیری زمان‌بندی کنید

هنگامی که از کلاس JobInfo.Builder برای ساخت شی JobInfo خود استفاده می کنید، متد setRequiredNetworkType() را اعمال کنید و JobInfo.NETWORK_TYPE_UNMETERED را به عنوان پارامتر job ارسال کنید. نمونه کد زیر یک سرویس را برای اجرای زمانی که دستگاه به یک شبکه بدون اندازه‌گیری متصل می‌شود و در حال شارژ شدن است، برنامه‌ریزی می‌کند:

کاتلین

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 که به شما امکان می‌دهد کارهای پس‌زمینه‌ای را که نیاز به تکمیل تضمینی دارند، برنامه‌ریزی کنید، صرف نظر از اینکه فرآیند برنامه در حال انجام است یا خیر. WorkManager بر اساس عواملی مانند سطح API دستگاه، روش مناسب را برای اجرای کار انتخاب می‌کند (یا مستقیماً روی یک رشته در فرآیند برنامه شما و همچنین با استفاده از JobScheduler، FirebaseJobDispatcher یا AlarmManager). علاوه بر این، WorkManager به خدمات Play نیازی ندارد و چندین ویژگی پیشرفته را ارائه می‌کند، مانند زنجیره‌ای کردن وظایف با هم یا بررسی وضعیت یک کار. برای کسب اطلاعات بیشتر، به WorkManager مراجعه کنید.

اتصال شبکه را در حین اجرای برنامه نظارت کنید

برنامه‌هایی که در حال اجرا هستند همچنان می‌توانند با یک BroadcastReceiver ثبت‌شده به CONNECTIVITY_CHANGE گوش دهند. با این حال، ConnectivityManager API روش قوی تری برای درخواست پاسخ به تماس تنها زمانی که شرایط شبکه مشخص شده برآورده می شود، ارائه می دهد.

اشیاء NetworkRequest پارامترهای پاسخ تماس شبکه را بر اساس 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) API JobInfo را با روش‌های زیر گسترش می‌دهد:

JobInfo.TriggerContentUri()
پارامترهای مورد نیاز برای فعال کردن یک کار در تغییرات URI محتوا را در بر می گیرد.
JobInfo.Builder.addTriggerContentUri()
یک شی TriggerContentUri را به JobInfo ارسال می کند. یک ContentObserver URI محتوای محصور شده را نظارت می کند. اگر چندین شی TriggerContentUri مرتبط با یک کار وجود داشته باشد، سیستم حتی اگر تغییری را در تنها یکی از URI های محتوا گزارش دهد، یک تماس برگشتی ارائه می کند.
پرچم TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS را اضافه کنید تا در صورت تغییر هر یک از فرزندان URI داده شده، کار را فعال کنید. این پرچم مربوط به پارامتر notifyForDescendants است که به registerContentObserver() ارسال شده است.

توجه: TriggerContentUri() نمی توان در ترکیب با setPeriodic() یا setPersisted() استفاده کرد. برای نظارت مستمر برای تغییرات محتوا، قبل از اینکه JobService برنامه رسیدگی به آخرین تماس را به پایان برساند، یک JobInfo جدید برنامه ریزی کنید.

کد نمونه زیر زمانی که سیستم تغییری در 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(های) محتوای مشخص شده گزارش می‌دهد، برنامه شما یک پاسخ تماس دریافت می‌کند و یک شی JobParameters به ​​روش onStartJob() در MediaContentJob.class ارسال می‌شود.

تعیین کنید که کدام یک از مقامات محتوا یک شغل را راه اندازی کرده اند

Android 7.0 (سطح API 24) همچنین JobParameters را گسترش می‌دهد تا به برنامه شما اجازه دهد اطلاعات مفیدی در مورد اینکه مقامات محتوا و URIهایی که کار را فعال کرده‌اند را دریافت کند:

Uri[] getTriggeredContentUris()
آرایه ای از URI ها را که کار را راه اندازی کرده اند برمی گرداند. اگر هیچ URI کار را راه‌اندازی نکرده باشد (مثلاً کار به دلیل ضرب‌الاجل یا دلایل دیگری راه‌اندازی شده است)، یا تعداد URI‌های تغییر یافته بیشتر از 50 باشد، null می‌شود.
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;
}

برنامه خود را بیشتر بهینه کنید

بهینه سازی برنامه های خود برای اجرا در دستگاه های با حافظه کم یا در شرایط کم حافظه می تواند عملکرد و تجربه کاربر را بهبود بخشد. حذف وابستگی‌ها به خدمات پس‌زمینه و گیرنده‌های پخش ضمنی ثبت‌شده در مانیفست می‌تواند به اجرای بهتر برنامه شما در چنین دستگاه‌هایی کمک کند. اگرچه 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