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

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

برای رفع این مشکل، اندروید ۷.۰ (سطح API 24) محدودیت‌های زیر را اعمال می‌کند:

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

If your app uses any of these intents, you should remove dependencies on them as soon as possible so that you can properly target devices running Android 7.0 or higher. The Android framework provides several solutions to mitigate the need for these implicit broadcasts. For example, JobScheduler and the new WorkManager provide robust mechanisms to schedule network operations when specified conditions, such as a connection to an unmetered network, are met. You can now also use JobScheduler to react to changes to content providers. JobInfo objects encapsulate the parameters that JobScheduler uses to schedule your job. When the conditions of the job are met, the system executes this job on your app's JobService .

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

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

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

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

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

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

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

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

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

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

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

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

Apps targeting Android 7.0 (API level 24) do not receive CONNECTIVITY_ACTION broadcasts if they register to receive them in their manifest, and processes that depend on this broadcast will not start. This could pose a problem for apps that want to listen for network changes or perform bulk network activities when the device connects to an unmetered network. Several solutions to get around this restriction already exist in the Android framework, but choosing the right one depends on what you want your app to accomplish.

نکته: یک BroadcastReceiver که با Context.registerReceiver() ثبت شده است، در حین اجرای برنامه، همچنان این broadcastها را دریافت می‌کند.

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

هنگام استفاده از کلاس 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 مراجعه کنید.

A new alternative to JobScheduler is WorkManager, an API that allows you to schedule background tasks that need guaranteed completion, regardless of whether the app process is around or not. WorkManager chooses the appropriate way to run the work (either directly on a thread in your app process as well as using JobScheduler, FirebaseJobDispatcher, or AlarmManager) based on such factors as the device API level. Additionally, WorkManager does not require Play services and provides several advanced features, such as chaining tasks together or checking a task's status. To learn more, see WorkManager .

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

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

NetworkRequest objects define the parameters of the network callback in terms of NetworkCapabilities . You create NetworkRequest objects with the NetworkRequest.Builder class. registerNetworkCallback() then passes the NetworkRequest object to the system. When the network conditions are met, the app receives a callback to execute the onAvailable() method defined in its ConnectivityManager.NetworkCallback class.

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

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

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

اجرای کارها در صورت تغییر URI محتوا

برای اجرای کارها بر اساس تغییرات URI محتوا، اندروید ۷.۰ (سطح API 24) API JobInfo را با متدهای زیر توسعه می‌دهد:

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

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

کد نمونه زیر، وظیفه‌ای را زمان‌بندی می‌کند تا زمانی که سیستم تغییری در 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 ارسال می‌شود.

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

اندروید ۷.۰ (سطح API 24) همچنین JobParameters گسترش می‌دهد تا به برنامه شما اجازه دهد اطلاعات مفیدی در مورد اینکه کدام منابع محتوا و URIها کار را آغاز کرده‌اند، دریافت کند:

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

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

Optimizing your apps to run on low-memory devices, or in low-memory conditions, can improve performance and user experience. Removing dependencies on background services and manifest-registered implicit broadcast receivers can help your app run better on such devices. Although Android 7.0 (API level 24) takes steps to reduce some of these issues, it is recommended that you optimize your app to run without the use of these background processes entirely.

دستورات زیر از طریق 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