Bagikan melalui


Memori gratis saat aplikasi Anda berpindah ke latar belakang

Artikel ini menunjukkan kepada Anda cara mengurangi jumlah memori yang digunakan aplikasi Anda saat berpindah ke status latar belakang sehingga tidak akan ditangguhkan dan mungkin dihentikan.

Peristiwa latar belakang baru

Windows 10, versi 1607, memperkenalkan dua peristiwa siklus hidup aplikasi baru, EnteredBackground dan LeavingBackground. Peristiwa ini memberi tahu aplikasi Anda saat memasuki dan meninggalkan latar belakang.

Saat aplikasi Anda berpindah ke latar belakang, batasan memori yang diberlakukan oleh sistem dapat berubah. Gunakan peristiwa ini untuk memeriksa konsumsi memori Anda saat ini dan sumber daya gratis agar tetap berada di bawah batas sehingga aplikasi Anda tidak akan ditangguhkan dan mungkin dihentikan saat berada di latar belakang.

Peristiwa untuk mengontrol penggunaan memori aplikasi Anda

MemoryManager.AppMemoryUsageLimitChanging dinaikkan tepat sebelum batas total memori yang dapat digunakan aplikasi diubah. Misalnya, ketika aplikasi berpindah ke latar belakang dan pada Xbox, batas memori berubah dari 1024MB menjadi 128MB.
Ini adalah peristiwa terpenting yang harus ditangani agar platform tidak menangguhkan atau mengakhiri aplikasi.

MemoryManager.AppMemoryUsageIncreased dinaikkan ketika konsumsi memori aplikasi telah meningkat ke nilai yang lebih tinggi dalam enumerasi AppMemoryUsageLevel . Misalnya, dari Rendah ke Sedang. Menangani peristiwa ini bersifat opsional tetapi disarankan karena aplikasi masih bertanggung jawab untuk tetap di bawah batas.

MemoryManager.AppMemoryUsageDecreased dinaikkan ketika konsumsi memori aplikasi telah menurun ke nilai yang lebih rendah dalam enumerasi AppMemoryUsageLevel . Misalnya, dari Tinggi ke Rendah. Menangani peristiwa ini bersifat opsional tetapi menunjukkan aplikasi mungkin dapat mengalokasikan memori tambahan jika diperlukan.

Menangani transisi antara latar depan dan latar belakang

Saat aplikasi Anda berpindah dari latar depan ke latar belakang, peristiwa EnteredBackground akan dinaikkan. Saat aplikasi Anda kembali ke latar depan, peristiwa LeavingBackground dinaikkan. Anda dapat mendaftarkan handler untuk peristiwa ini saat aplikasi Anda dibuat. Dalam templat proyek default, ini dilakukan di konstruktor kelas Aplikasi di App.xaml.cs.

Karena berjalan di latar belakang akan mengurangi sumber daya memori yang diizinkan untuk dipertahankan aplikasi Anda, Anda juga harus mendaftar untuk peristiwa AppMemoryUsageIncreased dan AppMemoryUsageLimitChanging yang dapat Anda gunakan untuk memeriksa penggunaan memori aplikasi Anda saat ini dan batas saat ini. Handler untuk peristiwa ini ditampilkan dalam contoh berikut. Untuk informasi selengkapnya tentang siklus hidup aplikasi untuk aplikasi UWP, lihat Siklus hidup aplikasi.

public App()
{
    this.InitializeComponent();

    this.Suspending += OnSuspending;

    // Subscribe to key lifecyle events to know when the app
    // transitions to and from foreground and background.
    // Leaving the background is an important transition
    // because the app may need to restore UI.
    this.EnteredBackground += AppEnteredBackground;
    this.LeavingBackground += AppLeavingBackground;

    // During the transition from foreground to background the
    // memory limit allowed for the application changes. The application
    // has a short time to respond by bringing its memory usage
    // under the new limit.
    Windows.System.MemoryManager.AppMemoryUsageLimitChanging += MemoryManager_AppMemoryUsageLimitChanging;

    // After an application is backgrounded it is expected to stay
    // under a memory target to maintain priority to keep running.
    // Subscribe to the event that informs the app of this change.
    Windows.System.MemoryManager.AppMemoryUsageIncreased += MemoryManager_AppMemoryUsageIncreased;
}

Saat peristiwa EnteredBackground dinaikkan, atur variabel pelacakan untuk menunjukkan bahwa Anda saat ini berjalan di latar belakang. Ini akan berguna ketika Anda menulis kode untuk mengurangi penggunaan memori.

/// <summary>
/// The application entered the background.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AppEnteredBackground(object sender, EnteredBackgroundEventArgs e)
{
    _isInBackgroundMode = true;

    // An application may wish to release views and view data
    // here since the UI is no longer visible.
    //
    // As a performance optimization, here we note instead that
    // the app has entered background mode with _isInBackgroundMode and
    // defer unloading views until AppMemoryUsageLimitChanging or
    // AppMemoryUsageIncreased is raised with an indication that
    // the application is under memory pressure.
}

Saat aplikasi Anda beralih ke latar belakang, sistem mengurangi batas memori aplikasi untuk memastikan bahwa aplikasi latar depan saat ini memiliki sumber daya yang memadai untuk memberikan pengalaman pengguna yang responsif

Penanganan aktivitas AppMemoryUsageLimitChanging memberi tahu aplikasi Anda bahwa memori yang dialokasikan telah berkurang dan memberikan batas baru dalam argumen peristiwa yang diteruskan ke handler. Bandingkan properti MemoryManager.AppMemoryUsage, yang menyediakan penggunaan aplikasi Anda saat ini, dengan properti NewLimit dari argumen peristiwa, yang menentukan batas baru. Jika penggunaan memori Anda melebihi batas, Anda perlu mengurangi penggunaan memori Anda.

Dalam contoh ini, ini dilakukan dalam metode pembantu ReduceMemoryUsage, yang didefinisikan nanti dalam artikel ini.

/// <summary>
/// Raised when the memory limit for the app is changing, such as when the app
/// enters the background.
/// </summary>
/// <remarks>
/// If the app is using more than the new limit, it must reduce memory within 2 seconds
/// on some platforms in order to avoid being suspended or terminated.
///
/// While some platforms will allow the application
/// to continue running over the limit, reducing usage in the time
/// allotted will enable the best experience across the broadest range of devices.
/// </remarks>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MemoryManager_AppMemoryUsageLimitChanging(object sender, AppMemoryUsageLimitChangingEventArgs e)
{
    // If app memory usage is over the limit, reduce usage within 2 seconds
    // so that the system does not suspend the app
    if (MemoryManager.AppMemoryUsage >= e.NewLimit)
    {
        ReduceMemoryUsage(e.NewLimit);
    }
}

Catatan

Beberapa konfigurasi perangkat akan memungkinkan aplikasi untuk terus berjalan melalui batas memori baru sampai sistem mengalami tekanan sumber daya, dan beberapa tidak akan. Pada Xbox, khususnya, aplikasi akan ditangguhkan atau dihentikan jika tidak mengurangi memori hingga di bawah batas baru dalam waktu 2 detik. Ini berarti Anda dapat memberikan pengalaman terbaik di berbagai perangkat terluas dengan menggunakan peristiwa ini untuk mengurangi penggunaan sumber daya di bawah batas dalam waktu 2 detik dari peristiwa yang dinaikkan.

Ada kemungkinan bahwa meskipun penggunaan memori aplikasi Anda saat ini berada di bawah batas memori untuk aplikasi latar belakang ketika pertama kali bertransisi ke latar belakang, itu dapat meningkatkan konsumsi memorinya dari waktu ke waktu dan mulai mendekati batas. Handler appMemoryUsageIncreased memberikan kesempatan untuk memeriksa penggunaan Anda saat ini ketika meningkat dan, jika perlu, memori gratis.

Periksa untuk melihat apakah AppMemoryUsageLevel adalah High atau OverLimit, dan jika demikian, kurangi penggunaan memori Anda. Dalam contoh ini, ini ditangani oleh metode pembantu, ReduceMemoryUsage. Anda juga dapat berlangganan peristiwa AppMemoryUsageDecreased , periksa untuk melihat apakah aplikasi Anda berada di bawah batas, dan jika demikian, Anda tahu bahwa Anda dapat mengalokasikan sumber daya tambahan.

/// <summary>
/// Handle system notifications that the app has increased its
/// memory usage level compared to its current target.
/// </summary>
/// <remarks>
/// The app may have increased its usage or the app may have moved
/// to the background and the system lowered the target for the app
/// In either case, if the application wants to maintain its priority
/// to avoid being suspended before other apps, it may need to reduce
/// its memory usage.
///
/// This is not a replacement for handling AppMemoryUsageLimitChanging
/// which is critical to ensure the app immediately gets below the new
/// limit. However, once the app is allowed to continue running and
/// policy is applied, some apps may wish to continue monitoring
/// usage to ensure they remain below the limit.
/// </remarks>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MemoryManager_AppMemoryUsageIncreased(object sender, object e)
{
    // Obtain the current usage level
    var level = MemoryManager.AppMemoryUsageLevel;

    // Check the usage level to determine whether reducing memory is necessary.
    // Memory usage may have been fine when initially entering the background but
    // the app may have increased its memory usage since then and will need to trim back.
    if (level == AppMemoryUsageLevel.OverLimit || level == AppMemoryUsageLevel.High)
    {
        ReduceMemoryUsage(MemoryManager.AppMemoryUsageLimit);
    }
}

ReduceMemoryUsage adalah metode pembantu yang dapat Anda terapkan untuk merilis memori saat aplikasi Anda melebihi batas penggunaan saat berjalan di latar belakang. Cara Anda merilis memori tergantung pada spesifikasi aplikasi Anda, tetapi salah satu cara yang disarankan untuk mengosongkan memori adalah dengan membuang UI Anda dan sumber daya lain yang terkait dengan tampilan aplikasi Anda. Untuk melakukannya, pastikan Anda berjalan dalam status latar belakang lalu atur properti Konten jendela aplikasi Anda ke null dan batalkan pendaftaran penanganan aktivitas UI Anda dan hapus referensi lain yang mungkin Anda miliki ke halaman. Gagal membatalkan pendaftaran penanganan aktivitas UI Anda dan menghapus referensi lain yang mungkin Anda miliki ke halaman akan mencegah sumber daya halaman dirilis. Kemudian hubungi GC. Kumpulkan untuk mengklaim kembali memori yang dibesarkan segera. Biasanya Anda tidak memaksa pengumpulan sampah karena sistem akan mengurusnya untuk Anda. Dalam kasus khusus ini, kami mengurangi jumlah memori yang dibebankan ke aplikasi ini karena masuk ke latar belakang untuk mengurangi kemungkinan bahwa sistem akan menentukan bahwa itu harus menghentikan aplikasi untuk mengklaim kembali memori.

/// <summary>
/// Reduces application memory usage.
/// </summary>
/// <remarks>
/// When the app enters the background, receives a memory limit changing
/// event, or receives a memory usage increased event, it can
/// can optionally unload cached data or even its view content in
/// order to reduce memory usage and the chance of being suspended.
///
/// This must be called from multiple event handlers because an application may already
/// be in a high memory usage state when entering the background, or it
/// may be in a low memory usage state with no need to unload resources yet
/// and only enter a higher state later.
/// </remarks>
public void ReduceMemoryUsage(ulong limit)
{
    // If the app has caches or other memory it can free, it should do so now.
    // << App can release memory here >>

    // Additionally, if the application is currently
    // in background mode and still has a view with content
    // then the view can be released to save memory and
    // can be recreated again later when leaving the background.
    if (isInBackgroundMode && Window.Current.Content != null)
    {
        // Some apps may wish to use this helper to explicitly disconnect
        // child references.
        // VisualTreeHelper.DisconnectChildrenRecursive(Window.Current.Content);

        // Clear the view content. Note that views should rely on
        // events like Page.Unloaded to further release resources.
        // Release event handlers in views since references can
        // prevent objects from being collected.
        Window.Current.Content = null;
    }

    // Run the GC to collect released resources.
    GC.Collect();
}

Ketika konten jendela dikumpulkan, setiap Bingkai memulai proses pemutusan. Jika ada Halaman di pohon objek visual di bawah konten jendela, ini akan mulai menembakkan peristiwa Yang Tidak Dimuat. Halaman tidak dapat dihapus sepenuhnya dari memori kecuali semua referensi ke halaman dihapus. Dalam panggilan balik yang dibongkar, lakukan hal berikut untuk memastikan bahwa memori dibesarkan dengan cepat:

  • Hapus dan atur struktur data besar apa pun di Halaman Anda ke null.
  • Batalkan pendaftaran semua penanganan aktivitas yang memiliki metode panggilan balik dalam Halaman. Pastikan untuk mendaftarkan panggilan balik tersebut selama penanganan aktivitas Yang dimuat untuk Halaman. Peristiwa Dimuat dinaikkan ketika UI telah direkonstitusi ulang dan Halaman telah ditambahkan ke pohon objek visual.
  • Hubungi GC.Collect di akhir panggilan balik Yang Tidak Dimuat untuk mengumpulkan dengan cepat salah satu struktur data besar yang baru saja Anda atur ke null. Sekali lagi, biasanya Anda tidak memaksa pengumpulan sampah karena sistem akan mengurusnya untuk Anda. Dalam kasus khusus ini, kami mengurangi jumlah memori yang dibebankan ke aplikasi ini karena masuk ke latar belakang untuk mengurangi kemungkinan bahwa sistem akan menentukan bahwa itu harus menghentikan aplikasi untuk mengklaim kembali memori.
private void MainPage_Unloaded(object sender, RoutedEventArgs e)
{
   // << free large data sructures and set them to null, here >>

   // Disconnect event handlers for this page so that the garbage
   // collector can free memory associated with the page
   Window.Current.Activated -= Current_Activated;
   GC.Collect();
}

Di penanganan aktivitas LeavingBackground, atur variabel pelacakan (isInBackgroundMode) untuk menunjukkan bahwa aplikasi Anda tidak lagi berjalan di latar belakang. Selanjutnya, periksa untuk melihat apakah Konten jendela saat ini adalah null-- yang akan terjadi jika Anda membuang tampilan aplikasi untuk menghapus memori saat Anda berjalan di latar belakang. Jika konten jendela adalah null, bangun kembali tampilan aplikasi Anda. Dalam contoh ini, konten jendela dibuat dalam metode pembantu CreateRootFrame.

/// <summary>
/// The application is leaving the background.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AppLeavingBackground(object sender, LeavingBackgroundEventArgs e)
{
    // Mark the transition out of the background state
    _isInBackgroundMode = false;

    // Restore view content if it was previously unloaded
    if (Window.Current.Content == null)
    {
        CreateRootFrame(ApplicationExecutionState.Running, string.Empty);
    }
}

Metode pembantu CreateRootFrame membuat ulang konten tampilan untuk aplikasi Anda. Kode dalam metode ini hampir identik dengan kode handler OnLaunched yang disediakan dalam templat proyek default. Salah satu perbedaannya adalah bahwa handler Peluncuran menentukan status eksekusi sebelumnya dari properti PreviousExecutionState dari LaunchActivatedEventArgs dan metode CreateRootFrame hanya mendapatkan status eksekusi sebelumnya yang diteruskan sebagai argumen. Untuk meminimalkan kode duplikat, Anda dapat merefaktor kode handler aktivitas Peluncuran default untuk memanggil CreateRootFrame.

void CreateRootFrame(ApplicationExecutionState previousExecutionState, string arguments)
{
    Frame rootFrame = Window.Current.Content as Frame;

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active
    if (rootFrame == null)
    {
        // Create a Frame to act as the navigation context and navigate to the first page
        rootFrame = new Frame();

        // Set the default language
        rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0];

        rootFrame.NavigationFailed += OnNavigationFailed;

        if (previousExecutionState == ApplicationExecutionState.Terminated)
        {
            //TODO: Load state from previously suspended application
        }

        // Place the frame in the current Window
        Window.Current.Content = rootFrame;
    }

    if (rootFrame.Content == null)
    {
        // When the navigation stack isn't restored navigate to the first page,
        // configuring the new page by passing required information as a navigation
        // parameter
        rootFrame.Navigate(typeof(MainPage), arguments);
    }
}

Panduan

Berpindah dari latar depan ke latar belakang

Ketika aplikasi berpindah dari latar depan ke latar belakang, sistem bekerja atas nama aplikasi untuk membebaskan sumber daya yang tidak diperlukan di latar belakang. Misalnya, kerangka kerja UI membersihkan tekstur cache dan subsistem video membebaskan memori yang dialokasikan atas nama aplikasi. Namun, aplikasi masih perlu memantau penggunaan memorinya dengan hati-hati untuk menghindari ditangguhkan atau dihentikan oleh sistem.

Ketika aplikasi berpindah dari latar depan ke latar belakang, aplikasi akan terlebih dahulu mendapatkan peristiwa EnteredBackground dan kemudian peristiwa AppMemoryUsageLimitChanging .

  • Gunakan peristiwa EnteredBackground untuk mengosongkan sumber daya UI yang tidak Anda butuhkan saat aplikasi berjalan di latar belakang. Misalnya, Anda dapat membebaskan gambar sampul untuk sebuah lagu.
  • Gunakan peristiwa AppMemoryUsageLimitChanging untuk memastikan bahwa aplikasi Anda menggunakan lebih sedikit memori daripada batas latar belakang baru. Pastikan Anda membebaskan sumber daya jika tidak. Jika tidak, aplikasi Anda dapat ditangguhkan atau dihentikan sesuai dengan kebijakan khusus perangkat.
  • Lakukan pemanggilan pengumpul sampah secara manual jika aplikasi Anda melebihi batas memori baru saat peristiwa AppMemoryUsageLimitChanging dinaikkan.
  • Gunakan peristiwa AppMemoryUsageIncreased untuk terus memantau penggunaan memori aplikasi Anda saat berjalan di latar belakang jika Anda mengharapkannya berubah. Jika AppMemoryUsageLevel adalah High atau OverLimit, pastikan Anda membebaskan sumber daya.
  • Pertimbangkan untuk membebaskan sumber daya UI di penanganan aktivitas AppMemoryUsageLimitChanging alih-alih di handler EnteredBackground sebagai pengoptimalan performa. Gunakan nilai boolean yang diatur di penanganan aktivitas EnteredBackground/LeavingBackground untuk melacak apakah aplikasi berada di latar belakang atau latar depan. Kemudian di penanganan aktivitas AppMemoryUsageLimitChanging , jika AppMemoryUsage melebihi batas dan aplikasi berada di latar belakang (berdasarkan nilai Boolean) Anda dapat membebaskan sumber daya UI.
  • Jangan melakukan operasi jangka panjang dalam peristiwa EnteredBackground karena Anda dapat menyebabkan transisi antar aplikasi tampak lambat kepada pengguna.

Berpindah dari latar belakang ke latar depan

Saat aplikasi berpindah dari latar belakang ke latar depan, aplikasi akan terlebih dahulu mendapatkan peristiwa AppMemoryUsageLimitChanging lalu peristiwa LeavingBackground .

  • Gunakan peristiwa LeavingBackground untuk membuat ulang sumber daya UI yang dibuang aplikasi Anda saat berpindah ke latar belakang.
  • Sampel pemutaran media latar belakang - menunjukkan cara mengosongkan memori saat aplikasi Anda pindah ke status latar belakang.
  • Alat Diagnostik - gunakan alat diagnostik untuk mengamati peristiwa pengumpulan sampah dan memvalidasi bahwa aplikasi Anda merilis memori seperti yang Anda harapkan.