WebViewCompat.navigate ile gelişmiş sayfa gezinme

WebViewCompat.navigate, WebView'te web sayfası yükleme, geçmiş yönetimi ve gezinme yaşam döngüsü takibi üzerinde ayrıntılı kontrol sağlayan, WebView.loadUrl'e göre gelişmiş bir alternatiftir.

Daha önce, loadUrl kullanılarak başlatılan sayfa gezinme işlemlerinde önemli sınırlamalar vardı:

  • Geçmiş girişini değiştirme yok: Geçerli geçmiş girişini değiştiremediğiniz için geri yığına giriş eklemeden yeni bir sayfaya gitmek mümkün olmuyordu.
  • Ayrılmış geri aramalar: WebViewClient içinde belirli bir loadUrl çağrısını sonraki geri arama etkinlikleriyle ilişkilendirmek için doğrudan bir mekanizma yoktu.
  • Ek üstbilgiler kaydedilmedi: loadUrl'ye iletilen özel üstbilgiler, WebView durumu kapsamında kaydedilmediği için durum geri yüklenirken kayboldu.

WebViewCompat.navigate API, aşağıdaki özellikleri sunarak bu sorunları çözer:

  • Gezinme geçmişi girişi değiştirme: WebViewGeçmiş yığınındaki mevcut sayfayı değiştirmenize olanak tanır.
  • Korelasyonlu geri arama izleme: Bir gezinme yaşam döngüsünün tüm aşamalarında benzersiz tanımlayıcı olarak kullanılan bir Navigation nesnesi döndürür.
  • Kaydedilmiş durum başlığı desteği: Ek başlıklar, durum geri yüklendiğinde yeniden kullanılabilmeleri için WebView durum paketinde güvenilir bir şekilde kaydedilir.

Temel özellikler ve sınırlamalar

WebViewCompat.navigate'yı kullanmadan önce aşağıdaki operasyonel kuralları ve kısıtlamaları göz önünde bulundurun:

  • İş parçacığı güvenliği: WebViewCompat.navigate işlevini kullanıcı arayüzü (ana) iş parçacığında çağırmanız gerekir.

  • İptal ve öncelik: Uçuş sırasındaki navigasyonlar açıkça iptal edilemez. Ancak aynı WebView üzerinde yeni bir navigate görüşmesi başlatmak, etkin olan tüm navigasyonları geçersiz kılar.

  • URI şeması desteği: Standart (ör. https: ve http:) ve özel URI şemaları desteklenir. javascript: şeması desteklenmiyor.

  • URL boyutu sınırı: Desteklenen maksimum URL dizesi uzunluğu 2 MB'tır.

  • Özellik kontrolü: Farklı WebView APK sürümleri arasında uyumluluğu korumak için API'yi çağırmadan önce her zaman WebViewFeature.isFeatureSupported kullanarak özellik kullanılabilirliğini kontrol edin.

Navigasyonu başlatma ve parça yaşam döngüsünü izleme

Gezinmeyi yapılandırmak ve yaşam döngüsünü izlemek için aşağıdakileri yapın:

  1. Yapılandırılmış yaşam döngüsü geri aramaları almak için WebView kurulumu sırasında WebViewCompat.addNavigationListener kullanarak NavigationListener uygulamasını kaydedin. Bellek sızıntılarını ve yinelenen geri çağırma yürütmelerini önlemek için dinleyiciyi her gezinme çağrısında değil, bir kez kaydedin.
  2. Geçmişi değiştirme veya özel HTTP üstbilgileri gibi isteğe bağlı davranışları belirtmek için NavigationParameters.Builder kullanarak bir NavigationParameters örneği oluşturun.
  3. WebViewCompat.navigate işlevini çağırarak WebView örneğinizi, hedef URL'yi ve parametreleri iletin.

WebViewCompat.navigate, isteği benzersiz şekilde tanımlayan bir Navigation nesnesi döndürür. Belirli bir gezinmeyi izlemek için NavigationListener geri çağırma işlemlerinizde bu nesneyi gelen Navigation parametresiyle karşılaştırın.

Uygulama örneği

Aşağıdaki örnekte gezinme parametrelerinin nasıl yapılandırılacağı, WebViewCompat.navigate işlevinin nasıl çağrılacağı ve gezinme yaşam döngüsü etkinliklerinin nasıl dinleneceği gösterilmektedir:

Kotlin

class WebNavigationManager(private val webView: WebView) {
    // Track the navigation instance returned by the API
    private var currentNavigation: Navigation? = null

    init {
        // 1. Define listener to observe navigation lifecycle events
        val listener = object : NavigationListener {
            override fun onNavigationStarted(navigation: Navigation) {
                if (navigation == currentNavigation) {
                    // Navigation started
                }
            }

            override fun onNavigationRedirected(navigation: Navigation) {
                if (navigation == currentNavigation) {
                    // Navigation encountered a redirect
                }
            }

            override fun onNavigationCompleted(navigation: Navigation) {
                if (navigation == currentNavigation) {
                    if (navigation.didCommit()) {
                        // Navigation committed successfully
                    } else if (navigation.didCommitErrorPage()) {
                        // Navigation committed an error page
                        val statusCode = navigation.statusCode
                        val error = navigation.webResourceError
                    }
                }
            }

            override fun onFirstContentfulPaintMillis(page: Page, durationMillis: Long) {
                // Match page with current navigation
                if (page == currentNavigation?.page) {
                    // Page rendering started (First Contentful Paint achieved)
                }
            }
        }

        // 2. Register listener on the main thread
        WebViewCompat.addNavigationListener(webView, listener)
    }

    @UiThread
    fun navigateToPage(url: String) {
        // Check feature availability
        if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEBVIEW_NAVIGATE_EXPERIMENTAL_V1)) {
            // Fall back to standard loadUrl if navigate API is unavailable
            webView.loadUrl(url)
            return
        }

        // 3. Configure navigation parameters
        val params = NavigationParameters.Builder()
            .setShouldReplaceCurrentEntry(true)
            .addAdditionalHeaders(
                mapOf("X-Test-Navigate-Header" to "TestValue")
            )
            .build()

        // 4. Initiate navigation on the UI thread
        currentNavigation = WebViewCompat.navigate(webView, url, params)
    }
}

Java

public class WebNavigationManager {

    private Navigation mCurrentNavigation;
    private final WebView mWebView;

    public WebNavigationManager(@NonNull WebView webView) {
        mWebView = webView;
        setupListener();
    }

    private void setupListener() {
        // 1. Define listener to observe navigation lifecycle events
        NavigationListener listener = new NavigationListener() {
            @Override
            public void onNavigationStarted(@NonNull Navigation navigation) {
                if (navigation.equals(mCurrentNavigation)) {
                    // Navigation started
                }
            }

            @Override
            public void onNavigationRedirected(@NonNull Navigation navigation) {
                if (navigation.equals(mCurrentNavigation)) {
                    // Navigation encountered a redirect
                }
            }

            @Override
            public void onNavigationCompleted(@NonNull Navigation navigation) {
                if (navigation.equals(mCurrentNavigation)) {
                    if (navigation.didCommit()) {
                        // Navigation committed successfully
                    } else if (navigation.didCommitErrorPage()) {
                        // Navigation committed an error page
                        int statusCode = navigation.getStatusCode();
                        WebResourceErrorCompat error = navigation.getWebResourceError();
                    }
                }
            }

            @Override
            public void onFirstContentfulPaintMillis(@NonNull Page page, long durationMillis) {
                if (mCurrentNavigation != null && page.equals(mCurrentNavigation.getPage())) {
                    // Page rendering started (First Contentful Paint achieved)
                }
            }
        };

        // 2. Register listener on the main thread
        WebViewCompat.addNavigationListener(mWebView, listener);
    }

    @UiThread
    public void navigateToPage(@NonNull String url) {
        // Check feature availability
        if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEBVIEW_NAVIGATE_EXPERIMENTAL_V1)) {
            // Fall back to standard loadUrl if navigate API is unavailable
            mWebView.loadUrl(url);
            return;
        }

        // 3. Configure navigation parameters
        NavigationParameters params = new NavigationParameters.Builder()
            .setShouldReplaceCurrentEntry(true)
            .addAdditionalHeaders(Collections.singletonMap(
                "X-Test-Navigate-Header", "TestValue"
            ))
            .build();

        // 4. Initiate navigation on the UI thread
        mCurrentNavigation = WebViewCompat.navigate(mWebView, url, params);
    }
}

Arıza modları ve hata işleme

WebViewCompat.navigate API, yapılandırma hatalarını ve çalışma zamanı gezinme hatalarını işlemek için farklı mekanizmalar sağlar:

Geçersiz bağımsız değişken istisnaları

Geçersiz bağımsız değişkenler iletildiğinde senkron bir IllegalArgumentException tetiklenir. Bu durumun yaygın nedenleri şunlardır:

  • Gerekli olan ve boş olmayan parametreler (webView, url veya params) için null değerini aktarma.
  • javascript: gibi desteklenmeyen bir URL şeması sağlama.
  • RFC 2616 spesifikasyonlarına uymayan hatalı biçimlendirilmiş HTTP üst bilgisi anahtarlarının veya değerlerinin iletilmesi.

Ağ isteği veya sayfa yükleme sırasında bir hata oluşursa (ör. HTTP 404 durum kodu, DNS çözümleme hatası veya SSL hatası), WebViewCompat.navigate yine de geçerli bir Navigation nesnesi döndürür.

Gezinme işlemi tamamlandığında, hatayı teşhis etmek için Navigation instance içinde onNavigationCompleted geri çağırma işleminizdeki aşağıdaki yöntemleri inceleyin:

  • getStatusCode: HTTP yanıt durum kodunu döndürür (örneğin, 404 veya 500).
  • getWebResourceError: Bağlantı zaman aşımları veya ana makine arama hataları gibi ağ hatalarını ayrıntılı olarak açıklayan bir WebResourceErrorCompat nesnesi döndürür.
  • didCommitErrorPage: WebView'ın hata işleyip kullanıcıya hata sayfası gösterip göstermediğini belirtir.
  • didCommit: Gezinmenin, iptal edilmeden hedef sayfaya başarıyla uygulanıp uygulanmadığını gösterir.

Durum paketi yönetimini kaydetme

NavigationParameters ile ek başlıklar ilettiğinizde WebView, bu başlıkları kaydedilmiş durum paketine kaydeder. Böylece, durum geri yüklendiğinde bu başlıklar yeniden kullanılabilir. Ancak büyük başlık koleksiyonları, kaydedilen durumun boyutunu önemli ölçüde artırabilir Bundle.

Android durumunu kaydederken TransactionTooLargeException olmasını önlemek için paket boyutunu sınırlamanız gerekiyorsa WebViewCompat.saveState kullanın. Bu yöntem, bayt cinsinden maksimum paket boyutu sınırı belirlemenize ve isteğe bağlı olarak ileri geçmiş öğelerini hariç tutmanıza olanak tanır:

Kotlin

// Save state with a maximum bundle size limit (for example, 64 KB)
val maxSizeBytes = 64 * 1024
val includeForwardState = false
val outState = Bundle()

WebViewCompat.saveState(webView, outState, maxSizeBytes, includeForwardState)

Java

// Save state with a maximum bundle size limit (for example, 64 KB)
int maxSizeBytes = 64 * 1024;
boolean includeForwardState = false;
Bundle outState = new Bundle();

WebViewCompat.saveState(webView, outState, maxSizeBytes, includeForwardState);

Elde edilen paket, standart WebView.restoreState yöntemiyle uyumlu olmaya devam eder.

Taşıma ve uygulama önerileri

WebView içinde gezinirken optimum performans ve kararlılık için şu önerileri uygulayın:

  • loadUrl'ten navigate'ye taşıma: Tüm eski WebView.loadUrl görüşmelerini WebViewCompat.navigate'ye taşıyın. Bu, geçmiş yönetiminin tek tip olmasını ve başlıkların her zaman kaydedilen durumun bir parçası olarak kaydedilmesini sağlar.

  • Özellik desteğini her zaman doğrulayın: API'yi çağırmadan önce, WebViewFeature.isFeatureSupported ile çalışma zamanı desteğini onaylayarak eski WebView sürümlerine karşı koruma sağlayın.

  • Gezinme örneklerini ilişkilendirme: Birden fazla WebView örneğini yönetirken eşzamanlı gezinmeleri ayırt etmek veya geri çağırmaları filtrelemek için döndürülen Navigation nesnesini kullanın.

  • Başlatma sırasında işleyiciyi bir kez kaydedin: WebViewCompat.addNavigationListener, mevcut bir işleyiciyi değiştirmek yerine işleyici eklediğinden, sonraki gezinmelerde bellek sızıntılarını ve yinelenen geri çağırma yürütmelerini önlemek için WebView kurulumu sırasında NavigationListener işleyicinizi bir kez kaydedin.

  • Kaydetme durumu boyutunu izleme: Büyük başlık yükleri aktarırken aşırı durum verilerinin kaydedilmesini önlemek için açık boyut sınırlarıyla WebViewCompat.saveState kullanın.

Ek kaynaklar

Yerleştirilmiş web özellikleri ve performans optimizasyonu hakkında daha fazla bilgi edinmek için aşağıdaki kılavuzlara bakın: