WebView'da tahmine dayalı içerik yükleme

Gezinme gecikmesi, kullanıcı deneyimi açısından kritik bir metriktir. WebView, geliştiricilerin bu gecikmeyi azaltmasına yardımcı olmak için tahmine dayalı içerik API'leri sağlar. Bu API'ler, kullanıcının açıkça gezinmesinden önce uygulamanızın içeriği getirmesine veya oluşturmasına olanak tanır.

WebView, üç temel tahmine dayalı içerik türünü destekler: önceden bağlanma, önceden getirme ve önceden oluşturma.

Tahmine dayalı içerik yükleme stratejisi uygulayarak aşağıdakileri elde edebilirsiniz:

  • Web içeriği yükleme gecikmesinde önemli ölçüde azalma: Ağ başlatma süresini uygulama yaşam döngüsünde daha erkene taşıyın.
  • Daha yüksek gezinme başarı oranları: Ağı ve önbelleği önceden ısıtarak gezinmelerin geçici ağ sorunları nedeniyle başarısız olma olasılığı azalır.
  • Daha iyi algılanan yanıt verme hızı: Özellikle önceden işleme, uygulamanın önemli ölçüde daha hızlı hissedilmesini sağlayan anlık geçişlere olanak tanır.

Tahmine dayalı içerik stratejisi seçme

Bu stratejiler arasındaki temel fark kapsamlarıdır: Preconnect API, kaynak tabanlıdır. Bu nedenle yalnızca hedef alan gerektirir. Önceden getirme ve önceden işleme API'leri URL tabanlıdır. Bu nedenle, tam web sayfası yolu gerekir.

Ön bağlantı, kaynak düzeyinde çalıştığı için uygulama yaşam döngüsünde çok daha erken başlatılabilir. Hatta kullanıcının gideceği belirli içeriği veya sayfayı bilmeden bile başlatabilirsiniz.

Aşağıdaki tabloda, kullanım alanınız için doğru olanı seçmenize yardımcı olmak amacıyla bu üç strateji karşılaştırılmaktadır:

Özellik Önceden bağlama Önceden getirme Prerender
Birincil hedef Bağlantıyı ısıtma Yalnızca HTML'yi önbelleğe alma (JavaScript veya CSS olmadan) Sayfanın tamamını önceden oluşturma
Kapsam Profil düzeyi (WebView'larda paylaşılır) Profil düzeyi (WebView'larda paylaşılır) WebView düzeyi (belirli bir WebView'a bağlı)
Jetpack WebKit API'si androidx.webkit.Profile androidx.webkit.Profile androidx.webkit.WebViewCompat
Temel API yöntemleri preconnect(...) prefetchUrlAsync(...) prerenderUrlAsync(...)
Yapılandırma Yok PrefetchCache.setMaxPrefetches()
PrefetchCache.setPrefetchTtlSeconds()
setMaxPrerenders()
Kaynak kullanımı Düşük (ağ) Orta (ağ, bellek) Yüksek (CPU, bellek, ağ)
Kullanılacağı zaman Hedef kaynak biliniyor ancak belirli URL henüz belirlenmediğinde. Tam URL bilindiğinde ve gezinme olasılığı yüksek olduğunda, önbelleğe alma işlemi WebView'lar arasında paylaşılır. Tam URL bilindiğinde ve belirli bir WebView'da gezinme kesin olduğunda.
Avantajlar Kaynakta bulunan tüm URL'ler için daha hızlı bağlantı kurulumu Eşleşen URL'ler için daha hızlı ağ yükü Etkinleştirildikten sonra anında gezinme

Kaynaklara önceden bağlanma

Ön bağlantı, belirtilen bir kaynak için DNS aramaları ve TCP/TLS el sıkışmaları önceden yapılarak gelecekteki yüklemeleri hızlandırır.

Tam hedef URL gerektiren önceden getirme ve önceden oluşturma işlemlerinin aksine, ön bağlantı kesinlikle kaynak tabanlıdır. Bu sayede, önceden bağlama çağrısını önceden getirme ve önceden oluşturma işlemlerinden çok daha önce yapabilirsiniz.

Bu düşük kaynaklı, profil düzeyindeki strateji, kaynak daha önce ziyaret edilmemişse bu profili paylaşan tüm WebView'ler için ilk gecikmeyi azaltır. Bağlantı yaklaşık 30 saniye açık kalır. Bu sayede, el sıkışma ek yükünü ortadan kaldırarak sonraki tüm kaynaklar arası HTTP istekleri, gezinmeler veya alt kaynaklar avantaj sağlar.

Uygulama

Ön bağlantı başlatmak için Profile örneğinde preconnect(String url) hizmetini arayın. Bu API, kullanıcı arayüzü iş parçacığında çağrılmalı ve WebViewFeature.PRECONNECT'nın desteklenmesini gerektirir.

API, kaynak üzerinde çalışır ancak kolaylık sağlamak için tam bir URL (ör. https://www.example.com/index.html) sağlanabilir. Bu, otomatik olarak kaynağa yapılan bir çağrı (ör. https://www.example.com) olarak değerlendirilir. Bu API birden çok kez çağrılarak birden fazla kaynağa bağlanılabilir.

Kotlin

// Must be called on the @UiThread
if (WebViewFeature.isFeatureSupported(WebViewFeature.PRECONNECT)) {
    profile.preconnect("https://www.example.com/index.html")
    // This initiates a connection to the origin https://www.example.com
}

Java

// Must be called on the @UiThread
if (WebViewFeature.isFeatureSupported(WebViewFeature.PRECONNECT)) {
    profile.preconnect("https://www.example.com/index.html");
    // This initiates a connection to the origin https://www.example.com
}

Yaygın yapılandırma: PrefetchParameters ve PrerenderParameters

Hem önceden getirme hem de önceden oluşturma, isteği özelleştirmek için PrefetchParameters veya PrerenderParameters kullanır. Bu sınıflar, No-Vary-Search yapılandırmaları gibi URL eşleştirme için ek başlıklar ve ipuçları sağlamanıza olanak tanır.

Kotlin

// Isolated configuration specifically for Cache-Level Prefetching
val prefetchParams = PrefetchParameters.Builder()
    .addAdditionalHeader("X-Custom-Client", "Android-App-V2")
    .setExpectedNoVarySearchHeader(
        NoVarySearchHeader.varyExcept(true, listOf("session_id", "click_ref"))
    )
    .build()

Java

PrefetchParameters prefetchParams = new PrefetchParameters.Builder()
    .addAdditionalHeader("X-Custom-Header", "value")
    /**
     * Hint to ignore specific query parameters during cache matching.
     * This allows the cache to match even if the tracking_id differs.
     */
    .setExpectedNoVarySearchHeader(
        NoVarySearchHeader.varyExcept(true, Arrays.asList("tracking_id"))
    )
    /**
     * Determines if Client Hints are sent.
     * NOTE: This is ignored for Prerendering API requests, which default to
     * the WebView's WebSettings.getJavaScriptEnabled() value.
     */
    .setJavaScriptEnabled(true)
    .build();

İçeriği önceden getirme

Önceden getirme, bir URL'nin ana HTML kaynağını indirip profilin ağ önbelleğinde depolar. WebView'da Profile, çerezler, HTTP önbelleği ve hizmet çalışanları dahil olmak üzere tarayıcı verileri için kapsayıcı görevi görür. Önceden getirme, profil düzeyinde bir işlem olduğundan bu profille ilişkili tüm WebView, önbelleğe alınmış yanıttan yararlanabilir.

Uygulama

Önceden getirme başlatmak için bir Profile örneğinde prefetchUrlAsync() işlevini çağırın. Bu işlem yalnızca HTTPS şemasını destekler.

Kotlin

profile.prefetchUrlAsync(
    url,
    prefetchParams,
    cancellationSignal,
    executor,
    object : WebViewOutcomeReceiver<PrefetchResult, PrefetchException> {
        override fun onResult(result: PrefetchResult) {
            if (result.wasDuplicate()) {
                // URL and No-Vary-Search permutations already exist in the cache layer
            } else {
                // The HTML payload has been successfully secured in the HTTP cache
            }
        }

        override fun onError(error: PrefetchException) {
            when (error) {
                is PrefetchNetworkException -> {
                    // Isolates network layer or server-side HTTP anomalies
                    val code = error.httpStatusCode
                    // Facilitates rapid diagnosis of 4xx or 5xx server responses
                }
                else -> {
                    // Catches generalized execution failures and system constraints
                }
            }
        }
    }
)

Java

profile.prefetchUrlAsync(
    url,
    prefetchParams,
    cancellationSignal,
    executor,
    new WebViewOutcomeReceiver<PrefetchResult, PrefetchException>() {
        @Override
        public void onResult(PrefetchResult result) {
            if (result.wasDuplicate()) {
                // URL and No-Vary-Search permutations already exist in the cache layer
            } else {
                // The HTML payload has been successfully secured in the HTTP cache
            }
        }

        @Override
        public void onError(PrefetchException error) {
            if (error instanceof PrefetchNetworkException) {
                // Isolates network layer or server-side HTTP anomalies
                int code = ((PrefetchNetworkException) error).httpStatusCode;
                // Facilitates rapid diagnosis of 4xx or 5xx server responses
            } else {
                // Catches generalized execution failures and system constraints
            }
        }
    }
);

Pas arası yaşam döngüsü

WebView önceden getirme isteği, shouldInterceptRequest() geri çağırma işleminin ne zaman ve nasıl tetikleneceğini değiştirir. Bu durum, önceden getirilmiş içeriğinizin başarılı bir şekilde kullanılıp kullanılmayacağını doğrudan etkilediğinden iki adımlı yaşam döngüsünü anlamak çok önemlidir:

Spekülatif ve gezinme aşamalarındaki iki adımlı WebView önceden getirme yakalama yaşam döngüsünü gösteren diyagram.
Şekil 1. WebView önceden getirme istekleri ve gezinmeleri için iki adımlı yakalama yaşam döngüsü.

1. Spekülatif aşama (önceden getirme isteği)

prefetchUrlAsync() çağrıldığında WebView, ana HTML kaynağını arka planda indirir. shouldInterceptRequest(), bu arka plan isteği için tamamen atlanır. Genellikle kesicinizde işlenen özel mantıklar, yetkilendirme jetonları veya üstbilgi eklemeleri, önceden getirilmiş HTML kaynağına uygulanmaz.

2. Gezinme aşaması (kullanıcı etkinleştirme)

Uygulama açıkça URL'ye gittiğinde (örneğin, loadUrl() kullanarak) veya kullanıcı eşleşen bir bağlantıyı tıkladığında WebView, önceden getirilmiş önbelleği kullanıp kullanamayacağını belirler:

  • Ana HTML değerlendirmesi: WebView, bu noktada ana HTML için shouldInterceptRequest() tetikleyecektir. Sayfayı önceden getirme önbelleğinden başarıyla sunmak için engelleyicinizin null değerini döndürmesi gerekir. Özel bir WebResourceResponse döndürürseniz WebView, kesicinizin isteğini karşılar ve önceden getirme önbelleğini tamamen atlar.

  • Alt kaynak değerlendirmesi: Önceden getirilmiş HTML'nin kullanıma uygun olduğu onaylandıktan sonra, shouldInterceptRequest() sayfanın oluşturulmasını tamamlamak için gereken tüm sonraki alt kaynaklar (ör. resimler, komut dosyaları ve CSS) için normal şekilde tetiklenir.

Temel davranışlar

Aşağıdaki operasyonel özellikler ve uygunluk kontrolleri, WebView'ın önceden getirme isteklerini nasıl başlattığını ve yönettiğini belirler:

  • İş parçacığı güvenliği: İstekler herhangi bir iş parçacığından başlatılabilir.
  • Uygunluk: WebView, getirme işlemi başlatmadan önce aşağıdaki kontrolleri yaparak isteğin güvenli ve bağlama uygun olduğundan emin olur:
    • Mevcut çerezler: WebView, kullanıcı gizliliğini korumak ve CSRF benzeri yan etkileri önlemek için istek, sunucuda durum değişikliğini tetikleyebilecek belirli kimliği doğrulanmış çerezler gerektiriyorsa önceden getirme işlemini atlayabilir.
    • Hizmet çalışanı varlığı: Bir hizmet çalışanı URL'nin kapsamını zaten kontrol ediyorsa WebView, standart bir ağ önceden getirme işlemi başlatmak yerine hizmet çalışanının getirme işleyicisini kullanabilir.
    • Proxy kullanılabilirliği: WebView, karmaşık ağ yapılandırmalarında spekülatif isteklerin başarısız olmasını önlemek için mevcut ağ yolunun (yapılandırılmış tüm proxy'ler dahil) kararlı olduğunu doğrular.
  • Önceden getirme işlemi (geçerli parametrelerle bile) başlatılamazsa bunun nedeni genellikle WebView'ın, arka plan isteğinin kullanıcının mevcut oturumuna veya güvenlik durumuna müdahale edebileceğini belirlemesidir.
  • İptal: Devam eden bir isteği sonlandırmak ve önbelleğe alınmasını önlemek için CancellationSignal simgesini kullanın.

Sayfaları önceden oluşturma

Önceden işleme, komut dosyası yürütme ve alt kaynak getirme dahil olmak üzere bir sayfayı arka planda tamamen oluşturmak için gizli "web içeriği" oluşturur. Önceden oluşturma, önceden getirme ile aynı temel altyapıyı kullanır. Bir uygulama önceden oluşturma işlemi başlatırsa WebView, önceden oluşturma navigasyonuna hizmet vermek için önce yanıtın önceden getirme işlemini gerçekleştirir ve gereksiz ağ etkinliğini önler.

Uygulama

Önceden oluşturma, WebView örneği düzeyinde bir işlemdir. Kullanıcı arayüzü iş parçacığından prerenderUrlAsync() numaralı telefonu WebViewCompat kullanarak arayın.

Kotlin

WebViewCompat.prerenderUrlAsync(
    webView,
    url,
    cancellationSignal,
    executor,
    params,
    object : PrerenderOperationCallback {
        override fun onPrerenderActivated() {
            // Called when the user navigates to the URL and the hidden page is swapped in
        }

        override fun onError(exception: Throwable) {
            // exception is an instance of PrerenderException
            // Handle prerender failure (for example, memory pressure or disallowed JavaScript APIs)
        }
    }
)

Java

WebViewCompat.prerenderUrlAsync(webView, url, cancellationSignal, executor, params, new PrerenderOperationCallback() {
    @Override
    public void onPrerenderActivated() {
        // Called when the user navigates to the URL and the hidden page is swapped in.
    }

    @Override
    public void onError(@NonNull Throwable exception) {
        // Handle prerender failure (for example, resource constraints or disallowed APIs).
    }
});

Hem önceden getirme hem de önceden oluşturma tamamen eşzamansızdır. prefetchUrlAsync() herhangi bir iş parçacığından çağrılabilirken prerenderUrlAsync(), kullanıcı arayüzü iş parçacığından başlatılmalıdır.

Teknik kısıtlamalar

WebView, anında gezinme ile sistem sağlığı arasında denge kurmak için aşağıdaki çalışma zamanı kısıtlamalarını uygular:

  • Bellek baskısı: Cihazın RAM'i düşükse WebView, önceden oluşturulmuş URL'leri iptal eder.
  • İzin verilmeyen API'ler: JavaScript'in arka plan bağlamında belirli API'lere (ör. ses çalma, uyarılar) erişme girişimleri, önceden oluşturma işlemini anında sonlandırır.
  • Örnek sınırı: WebView başına izin verilen etkin önceden oluşturulmuş URL'lerin sayısı sınırlıdır.

URL eşleştirme ve No-Vary-Search (NVS)

WebView, önceden yüklenmiş bir kaynağın yalnızca amaçlanan gezinme için yayınlanmasını sağlamak üzere güvenilir bir eşleştirme algoritması gerektirir.

Tam eşleme ve NVS eşleşmesi

Varsayılan olarak, önceden getirme ve önceden oluşturma için URL'nin tam olarak eşleşmesi gerekir. Gidilen URL, önceden yüklenen URL ile aynıysa hemen önbellekten sunulur. Sorgu parametreleri farklıysa WebView aşağıdaki No-Vary-Search (NVS) kurallarını kullanır:

  • İpucu: Geliştiriciler, başlatma sırasında setExpectedNoVarySearchHeader() ipucu verir. Gidilen URL, ipucu verilen parametreler hariç istek URL'siyle eşleşirse WebView, sunucunun gerçek başlıklarını beklemek için kısa bir süre engellenir.
  • Sunucu üstbilgisi: Sunucudan gelen NVS yanıt başlığı nihai yetkilidir. Sunucu, sorgu farklılıklarının yoksayılması gerektiğini onaylarsa eşleşme önbellekten sunulur. Aksi takdirde WebView, soğuk ağ yüklemesine geri döner.

No-Vary-Search (NVS), ileri düzey kullanım içindir ve çoğu geliştirici, hem önceden getirme hem de loadUrl() için tam olarak aynı URL'yi ilettiğinden bu özelliği kullanmak zorunda kalmayabilir. Bu yönergeler yalnızca önceden getirme URL'si ile loadUrl() URL'si arasında sorgu parametreleri açısından farklılıklar varsa gereklidir.

Genel yapılandırma

PrefetchCache sınırlarını ve maksimum önceden oluşturma sayısını yapılandırarak tahmine dayalı içerik yükleme davranışını profil düzeyinde ayarlayın. Özel önceden getirme sınırlarını sistem varsayılanlarına da sıfırlayabilirsiniz:

Kotlin

// Configure prefetch cache limits
profile.prefetchCache.setMaxPrefetches(10)
profile.prefetchCache.setPrefetchTtlSeconds(60)

// Reset to system defaults when needed
profile.prefetchCache.clearMaxPrefetches()

// Configure maximum active prerenders
profile.setMaxPrerenders(2)

Java

// Configure prefetch cache limits
PrefetchCache prefetchCache = profile.getPrefetchCache();
prefetchCache.setMaxPrefetches(10);
prefetchCache.setPrefetchTtlSeconds(60);

// Reset to system defaults when needed
prefetchCache.clearMaxPrefetches();

// Configure maximum active prerenders
profile.setMaxPrerenders(2);

Hata işleme ve istisnalar

Spekülatif işlemler, sonuçları bildirmek için OutcomeReceiverCompat veya PrerenderOperationCallback kullanır.

Birincil istisnalar

Tahmine dayalı içerik yükleme işlemi başarısız olduğunda hata işleyiciniz, belirli hata senaryolarını teşhis etmenize yardımcı olmak için aşağıdaki temel istisna türlerinden birini bildirir:

  • PrefetchException: Tüm eşzamansız önceden getirme hataları için temel sınıf.
  • PrefetchNetworkException: Ağ veya sunucu düzeyinde bir hata olduğunu gösterir. Sunucu tarafındaki sorunların teşhis edilmesine yardımcı olmak için httpStatusCode alanı (ör. 404 veya 503) içerebilir.
  • PrerenderException: Bellek baskısı veya arka planda izin verilmeyen API'lerin (ör. ses oynatma) kullanılması nedeniyle oluşan hatalar gibi önceden oluşturma ile ilgili tüm hataların üst sınıfı.

Optimizasyon stratejileri

Sistem kaynaklarını korurken tahmine dayalı içeriğin avantajlarından en iyi şekilde yararlanmak için aşağıdaki önerileri uygulayın:

  • Erken başlatma: Uygulama başlatılırken veya bir gezinme hedefi olası olduğu anda önceden getirme işlemini başlatın.
  • Entegre strateji: Önceden getirme önbelleğinde bulunan bir URL'yi önceden oluşturursanız önceden oluşturulan gezinme, bu önbellekten sunulur ve gereksiz ağ istekleri önlenir.
  • Kotaları izleme: Önceden oluşturma, kaynak açısından yoğun bir işlemdir. Birkaç olası aday için önceden getirmeyi tercih edin ve önceden oluşturmayı en olası tek gezinme için ayırın.
  • Şema desteği: Tüm URL'lerin zorunlu HTTPS şemasını kullandığından emin olun. Geçersiz şemalar veya boş girişler, senkronize bir IllegalArgumentException tetikler.

Ek kaynaklar

Web uygulamalarında hata ayıklama, WebView başlatma performansını optimize etme ve oluşturucu işlemi sonlandırmayı işleme hakkında daha fazla bilgi edinmek için aşağıdaki kaynaklara bakın: