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

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

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

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

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

در این صفحه، با نحوه استفاده از روش‌های جایگزین، مثل JobScheduler، برای سازگار کردن برنامه‌تان با این محدودیت‌های جدید آشنا خواهیم شد.

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

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

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

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

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

  • قفل‌های بیداری مفرط: ۱ قفل بیداری جزئی به‌مدت یک ساعت درحالی‌که صفحه‌نمایش خاموش است نگه داشته شده است
  • سرویس‌های پس‌زمینه بیش‌ازحد: اگر برنامه سطوح میانای برنامه‌سازی کاربردی پایین‌تر از ۲۶ را هدف قرار دهد و سرویس‌های پس‌زمینه بیش‌ازحد داشته باشد

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

  • سرویس‌های پیش‌زمینه‌ای راه‌اندازی نمی‌شود
  • سرویس‌های پیش‌زمینه‌ای موجود از پیش‌زمینه برداشته می‌شوند
  • زنگ‌های ساعت به‌صدا درنمی‌آیند
  • شغل‌ها اجرا نمی‌شوند

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

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

محدودیت‌های دریافت همه‌فرستی‌های فعالیت شبکه

برنامه‌هایی که Android 7.0 (سطح میانای برنامه‌سازی کاربردی ۲۴) را هدف قرار می‌دهند، اگر برای دریافت CONNECTIVITY_ACTION در مانیفست خود ثبت‌نام کنند، آن را دریافت نمی‌کنند و فرایندهایی که به این پخش وابسته هستند شروع نخواهند شد. این امر می‌تواند برای برنامه‌هایی که می‌خواهند تغییرات شبکه را گوش دهند یا فعالیت‌های شبکه انبوه را هنگام اتصال دستگاه به شبکه بدون سنجش مصرف انجام دهند، مشکل ایجاد کند. درحال‌حاضر چندین راه‌حل برای دور زدن این محدودیت در چارچوب 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 را ببینید.

WorkManager جایگزین جدیدی برای JobScheduler است، میانای برنامه‌سازی کاربردی‌ای که به شما امکان می‌دهد تکالیف پس‌زمینه‌ای را که نیاز به تکمیل تضمینی دارند زمان‌بندی کنید، صرف‌نظر از اینکه فرایند برنامه درحال اجرا باشد یا نه. WorkManager روش مناسب برای اجرای کار را (مستقیماً در رشته‌ای در فرایند برنامه شما و همچنین بااستفاده از JobScheduler،‏ FirebaseJobDispatcher، یا AlarmManager) براساس عواملی مانند سطح میانای برنامه‌سازی کاربردی دستگاه انتخاب می‌کند. علاوه‌براین، WorkManager به خدمات Play نیاز ندارد و چندین ویژگی پیشرفته ارائه می‌دهد، مانند زنجیره‌ای کردن تکالیف با یکدیگر یا بررسی وضعیت یک تکلیف. برای کسب اطلاعات بیشتر، WorkManager را ببینید.

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

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

NetworkRequest شیء پارامترهای بازخوان شبکه را ازنظر NetworkCapabilities تعریف می‌کند. NetworkRequest شیء با کلاس NetworkRequest.Builder ایجاد می‌کنید. registerNetworkCallback() سپس شیء NetworkRequest را به سیستم ارسال می‌کند. وقتی شرایط شبکه برآورده شود، برنامه یک فراخوانی برگشتی برای اجرای روش onAvailable() تعریف‌شده در کلاس ConnectivityManager.NetworkCallback خود دریافت می‌کند.

برنامه تا زمانی که از آن خارج نشوید یا unregisterNetworkCallback() را فراخوانی نکند، به دریافت فراخوانی‌های برگشتی ادامه می‌دهد.

محدودیت‌های دریافت همه‌فرستی‌های تصویری و ویدیویی

در Android نسخه ۷.۰ (سطح میانای برنامه‌سازی کاربردی ۲۴)، برنامه‌ها نمی‌توانند همه‌فرستی‌های ACTION_NEW_PICTURE یا ACTION_NEW_VIDEO را ارسال یا دریافت کنند. این محدودیت به کاهش تأثیرات بر عملکرد و تجربه کاربری کمک می‌کند، به‌ویژه زمانی که چندین برنامه باید برای پردازش تصویر یا ویدیو جدید فعال شوند. Android 7.0 (سطح میانای برنامه‌سازی کاربردی ۲۴) JobInfo و JobParameters را گسترش می‌دهد تا راه‌حل جایگزینی ارائه دهد.

راه‌اندازی کردن کارها در تغییرات شناسه منبع یکسان محتوا

برای راه‌اندازی کارها در تغییرات شناسه منبع یکسان محتوا، Android 7.0 (سطح میانای برنامه‌سازی کاربردی ۲۴) میانای برنامه‌سازی کاربردی JobInfo را با روش‌های زیر گسترش می‌دهد:

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

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

کد نمونه زیر زمان‌بندی می‌کند که وقتی سیستم تغییری را در شناسه منبع یکسان محتوا (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());
}

وقتی سیستم تغییری را در شناسه(های) منبع یکسان محتوای مشخص‌شده گزارش می‌کند، برنامه‌تان یک فراخوانی برگشتی دریافت می‌کند و یک شیء JobParameters به روش onStartJob() در MediaContentJob.class ارسال می‌شود.

تعیین اینکه کدام مراجع محتوا باعث راه‌اندازی کار شده‌اند

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

Uri[] getTriggeredContentUris()
آرایه‌ای از URIهایی را که کار را راه‌اندازی کرده‌اند برمی‌گرداند. اگر هیچ شناسه منبع یکسانی باعث راه‌اندازی کار نشده باشد (برای مثال، کار به‌دلیل مهلت یا دلیل دیگری راه‌اندازی شده باشد) یا تعداد شناسه‌های منبع یکسان تغییریافته بیشتر از ۵۰ باشد، این مقدار 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‏ ۲۴) اقداماتی برای کاهش برخی‌از این مشکلات انجام می‌دهد، توصیه می‌شود برنامه‌تان را به‌گونه‌ای بهینه‌سازی کنید که کاملاً بدون استفاده از این فرایندهای پس‌زمینه‌ای اجرا شود.

فرمان‌های 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