WebView başlatmayı optimize etme

Uygulamanız ilk kez WebView kullandığında sistem belirli başlangıç görevlerini gerçekleştirir. Bu başlangıç süreci ağır. Varsayılan olarak, uygulama android.webkit veya androidx.webkit paketlerindeki birçok API'yi ilk kez çağırdığında ya da WebView etiketi içeren bir düzeni genişlettiğinde kullanıcı arayüzü iş parçacığında örtülü olarak gerçekleşir.

Bu neden önemli?

Bu örtülü başlatma işlemi tamamen ana iş parçacığında gerçekleştiği için uygulamanızın kullanıcı girişini işlemesini engeller ve "Uygulama yanıt vermiyor" (ANR) hataları riskini önemli ölçüde artırır. Android'in tek iş parçacıklı yürütme modelini nasıl işlediği hakkında daha fazla bilgi için İşlemlere ve iş parçacıklarına genel bakış başlıklı makaleyi inceleyin.

Dolaylı başlatma tetikleyicileri

Örtülü başlatma aşağıdaki şekillerde tetiklenebilir:

  • Programatik olarak: WebSettings.getUserAgentString() gibi API'leri çağırma.
  • Düzenleri kullanma: <WebView> içeren bir XML kaynağında setContentView() veya layoutInflater.inflate()'ü çağırma.

Örtülü başlatma, uygulama başlatma süresi ve ilk görüntüleme süresi gibi iş metriklerinizi de olumsuz etkileyebilir. Örtülü başlatma uygulamanız için ideal değilse bunun yerine startUpWebView kullanın.

Bu sayfada, startUpWebView API kullanılarak WebView başlatma performansının nasıl optimize edileceği açıklanmaktadır.

WebView başlatma işlemini kontrol etme

Performansı artırmak ve ANR'leri en aza indirmek için startUpWebView API'yi kullanın. Bu API, Jetpack Webkit kitaplığında mevcuttur. Bu API, WebView'ın ne zaman başlatılacağı konusunda açık kontrol sağlar. Başlangıç iş yükünün önemli bir bölümünü arka plan iş parçacığına kaydırır ve kullanıcı arayüzü iş parçacığında yapılması gereken tüm işlerin tek bir büyük blok yerine parçalar halinde yapılmasını sağlar. Bu sayede, kullanıcı arayüzü iş parçacığınız diğer kritik uygulama görevlerini paralel olarak işleyebilir ve kullanıcı deneyiminin engellenme olasılığı azalır.

API, androidx.webkit.WebViewOutcomeReceiver geri çağırmasını kullanarak başarılı başlatmaları izlemenize olanak tanır.

Bu API'yi kullanmak için Jetpack Webkit kitaplığını build.gradle dosyanıza ekleyin. 1.16.0 veya daha yeni bir sürümü kullandığınızdan emin olun:

dependencies {
    implementation("androidx.webkit:webkit:1.16.0")
}

startUpWebView API'yi kullanma

Başlangıç akışınızı nasıl optimize edeceğiniz, uygulamanızın WebView'u ne zaman göstermesi gerektiğine bağlıdır.

WebView kritik yolda olmadığında

Uygulamanızın bir WebView'u hemen yüklemesi gerekmiyorsa başlatma maliyetini tamamen gizleyebilirsiniz. startUpWebView işlevini uygulamanızın yaşam döngüsünün başlarında çağırın ve başarı geri aramasının tetiklenmesini bekleyin.

İdeal olarak, diğer WebView API'lerini çağırmadan önce geri aramayı beklemeniz gerekir. startUpWebView işlevini tetiklerseniz ancak diğer WebView bileşenlerine dokunmadan önce tamamlanmasını beklemezseniz sistem, başlatma işleminin tamamlanmasını beklerken kullanıcı arayüzü iş parçacığını engeller. Uygulamanız, önceden tamamlanmış arka plan çalışmalarından bir miktar performans avantajı elde edebilir ancak maksimum avantajı elde edemez.

WebView kritik yolda olduğunda

Uygulamanızın temel kullanıcı yolculuğu için hemen bir WebView gerekiyorsa WebView başlatma işleminin tamamlanmasını bekleyemezsiniz. Bu senaryoda, uygulama yaşam döngüsünde (ör. Application.onCreate içinde) startUpWebView yöntemini olabildiğince erken çağırmaya devam etmeniz gerekir ancak geri çağırmanın tetiklenmesini beklemeyin. Bunun yerine, gerektiğinde doğrudan WebView API'lerini kullanın.

Eşzamansız başlatmadan en iyi şekilde yararlanmak için WebView'ı başlatmayı veya WebView API'lerini çağırmayı, kritik yol kullanıcı arayüzü iş parçacığı işlemlerinin (ör. düzen hiyerarşilerini genişletme, diğer SDK'ları başlatma veya ilk kareyi çizme) tamamlanmasına kadar erteleyin.

startUpWebView işlevini çağırıp hemen ardından ana iş parçacığında WebView API'lerini çağırırsanız kullanıcı arayüzü iş parçacığı, başlatma işleminin yetişmesini beklerken engellenir. Bu senaryoda performans avantajı yoktur.

WebView kullanımı kritik yola girebiliyorsa ancak WebView'u tamamen başlatmak istemiyorsanız arka plan iş parçacığında çalışabilen WebView başlatma görevlerini seçerek çalıştırabilir, böylece kullanıcı arayüzü iş parçacığını diğer uygulama için kritik görevler için boşaltabilirsiniz. Bunun için shouldRunUiThreadStartUpTasks(false) kullanabilirsiniz.

Uygulamanızın yaşam döngüsünün ilerleyen aşamalarında, kullanıcı arayüzü iş parçacığında kalan başlatma görevlerini tamamlamak için startUpWebView işlevini shouldRunUiThreadStartUpTasks(true) ile tekrar çağırabilirsiniz. Bu noktada geri aramayı bekleyip beklemeyeceğiniz, WebView kullanımının kritik yolda olup olmadığına bağlıdır.

Uygulama örneği

API, androidx.webkit.WebViewOutcomeReceiver geri çağırmasını kullanarak başarılı başlatmaları izlemenize veya teşhis hatalarını yönetmenize olanak tanır.

Uygulamanızın farklı bölümlerinden startUpWebView işlevini birden fazla kez çağırmak güvenlidir. Basit bir yeniden deneme döngüsü uygulamaktan kaçınmanızı öneririz.

Aşağıdaki kod örneğinde, eşzamansız başlatma için WebViewCompat.startUpWebView API'sinin nasıl kullanılacağı gösterilmektedir.

Kotlin

import android.content.Context
import android.util.Log
import androidx.webkit.WebViewCompat
import androidx.webkit.WebViewOutcomeReceiver
import androidx.webkit.WebViewStartUpConfig
import androidx.webkit.WebViewStartUpResult
import androidx.webkit.WebViewStartupException
import java.util.concurrent.Executors

fun initializeWebView(context: Context) {
    // 1. Create a startup configuration specifying the background thread
    // that WebView will use to run its initialization tasks.
    val startUpConfig = WebViewStartUpConfig.Builder(
        Executors.newSingleThreadExecutor()
    ).build()

    // 2. Trigger WebView startup asynchronously
    WebViewCompat.startUpWebView(
        context,
        startUpConfig,
        object : WebViewOutcomeReceiver<WebViewStartUpResult, WebViewStartupException> {

            override fun onResult(result: WebViewStartUpResult) {
                // Success: The WebView has finished its background initialization.
                // This callback is guaranteed to be invoked on the UI thread.
                setupWebView()
            }

            override fun onError(error: WebViewStartupException) {
                // Failure: The initialization encountered a startup exception.
                Log.e("WebViewStartup", "Failed to initialize WebView", error)
            }
        }
    )
}

Java

import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewOutcomeReceiver;
import androidx.webkit.WebViewStartUpConfig;
import androidx.webkit.WebViewStartUpResult;
import androidx.webkit.WebViewStartupException;
import java.util.concurrent.Executors;

public void initializeWebView(Context context) {
    // 1. Create the startup configuration specifying the background thread pool
    // to handle internal non-UI initialization processes.
    WebViewStartUpConfig startUpConfig = new WebViewStartUpConfig.Builder(
            Executors.newSingleThreadExecutor()
    ).build();

    // 2. Trigger WebView startup asynchronously
    WebViewCompat.startUpWebView(
            context,
            startUpConfig,
            new WebViewOutcomeReceiver<WebViewStartUpResult, WebViewStartupException>() {

                @Override
                public void onResult(@NonNull WebViewStartUpResult result) {
                    // Success: The WebView has finished its background initialization.
                    // This callback is invoked directly on the UI thread.
                    setupWebView();
                }

                @Override
                public void onError(@NonNull WebViewStartupException error) {
                    // Failure: Handled using the concrete WebViewStartupException
                    Log.e("WebViewStartup", "Failed to initialize WebView", error);
                }
            }
    );
}

Eşzamansız başlatma sorunlarında hata ayıklama

startUpWebView beklenen performans avantajlarını sağlamıyorsa bunun nedeni genellikle çağrınız yürütülmeden önce WebView'ın uygulamanızda başka bir yerde örtülü olarak başlatılmasıdır. Bu durum, aşağıdaki nedenlerden kaynaklanabilir:

  • Üçüncü taraf kitaplıkları veya SDK'ları, uygulama yaşam döngüsünün başlarında başlatılır.

  • Uygulama başlatılırken WebView API'lerini tetikleyen, APK'nıza yerleştirilmiş ContentProviders.

  • Düzen şişirmeleri veya beklenmedik şekilde erken gerçekleşen programatik çağrılar (ör. kullanıcı aracısı dizelerini getirme).

Bu beklenmedik başlatmaların nerede ve neden gerçekleştiğini teşhis etmenize yardımcı olmak için WebViewStartUpResult nesnesi yerleşik denetleme özellikleri sunar:

  • getUiThreadBlockingStartUpLocations(): WebView başlatma görevlerinin ana kullanıcı arayüzü iş parçacığını engellediği konumları temsil eden StartUpLocation nesnelerinin listesini döndürür.

  • getNonUiThreadBlockingStartUpLocations(): Başlangıç görevlerinin çalıştırılması arka plan iş parçacıklarını engellediğinde belirli görüşme sitelerini döndürür.

Her StartUpLocation, başlatmayı tetikleyen sınıfı ve yöntemi tam olarak bulmak için kaydedebileceğiniz veya inceleyebileceğiniz bir yığın izi içerir.

Uygulama örneği

Başlangıç yolunuzu denetlemek için onResult geri aramanızdaki bu konumları inceleyebilirsiniz:

override fun onResult(result: WebViewStartUpResult) {
    // Check if WebView startup was blocked on the UI thread prior to or during initialization
    val uiBlockingLocations = result.getUiThreadBlockingStartUpLocations()
    if (!uiBlockingLocations.isNullOrEmpty()) {
        for (location in uiBlockingLocations) {
            // Log the stack trace of the call site that triggered the UI-blocking startup
            Log.w("WebViewDebug", "WebView startup blocked the UI thread here:", location.getStack())
        }
    } else {
        Log.i("WebViewDebug", "Excellent! No UI-blocking WebView startup detected.")
    }

    // Check where background initialization tasks were executed
    val backgroundLocations = result.getNonUiThreadBlockingStartUpLocations()
    backgroundLocations?.forEach { location ->
        Log.d("WebViewDebug", "WebView background startup occurred at: ${location.getStack()}")
    }

    setupWebView()
}

Bu verileri denetim sırasında kullanma

Uygulamanızın WebView başlatma işlemini denetlerken teşhis verilerini analiz etmek ve performans darboğazlarını gidermek için aşağıdaki stratejileri kullanın:

  • Beklenmeyen yığın izlerini arayın: getUiThreadBlockingStartUpLocations() boş değilse yazdırılan yığın izlerine bakın. Üçüncü taraf SDK'larına ait sınıflar veya beklenmedik bileşenler görürseniz örtülü başlatma darboğazı bulmuşsunuzdur.

  • Çağrı sırasını doğrulayın: Günlük çıktınız, manuel startUpWebView çağrınızdan önce örtülü bir başlatma işleminin gerçekleştiğini gösteriyorsa startUpWebView başlatma işlemini uygulamanızda daha erken bir aşamaya taşımanız veya WebView'a bağlı görevlerini ertelemek için sorunlu SDK'yı yapılandırmanız gerekir.

Önceki geçici çözümlerden geçiş yapma

Geçmişte, kullanıcı aracısı dizesini getirme gibi, WebView başlatmayı arka plan iş parçacığında zorlamak için açıkça belirtilen geçici çözümler kullanmış olabilirsiniz.

Bu geçici çözümler desteklenmeyen uygulamalar olarak kabul edilir ve temel davranışları, gelecek sürümlerde değişebilir. Uygulamanız WebView başlatmayı tetiklemek veya yönetmek için açıkça belirtilmiş, belgelenmemiş geçici çözümler kullanıyorsa bunun yerine startUpWebView API'sini kullanmanızı öneririz. startUpWebView API, Jetpack Webkit kitaplığı tarafından desteklenen tüm Android ve WebView sürümlerinde çalışır.

Jetpack Webkit uygulamasını kullanmak, Android ekosisteminin tamamında tutarlı davranışlar sağlanmasına yardımcı olur. Bu API'nin temel avantajlarından biri dayanıklılığıdır: Yeni optimizasyonların kullanılamadığı eski cihazlarda API, manuel geçici çözümlerle performans eşitliğini korur. Bu sayede, eski cihazlarda performans cezası almadan yeni cihazlarda modern başlangıç avantajlarından yararlanabilirsiniz.

startUpWebView API ile ilgili sorun yaşarsanız veya geri bildiriminiz varsa herkese açık Issue Tracker'da hata kaydı oluşturun.