JavaScript Değerlendirmesi
JavaScriptEngine Jetpack kitaplığı, bir uygulamanın WebView örneği oluşturmadan JavaScript kodunu değerlendirebilirsiniz.
Etkileşimsiz JavaScript değerlendirmesi gerektiren uygulamalarda JavaScriptEngine kitaplığı aşağıdaki avantajlara sahiptir:
WebView tahsis etmeye gerek olmadığından daha düşük kaynak tüketimi kullanır.
Bir Service (WorkManager görevi) içinde yapılabilir.
Düşük ek yük sunan birden çok izole ortam, uygulamanın birkaç JavaScript snippet'ini aynı anda çalıştırabilir.
API çağrısı kullanarak büyük miktarda veri iletme özelliği.
Temel Kullanım
Başlamak için JavaScriptSandbox
öğesinin bir örneğini oluşturun. Bu,
devre dışı JavaScript motoruyla bağlantı kurulmasını sağlar.
ListenableFuture<JavaScriptSandbox> jsSandboxFuture =
JavaScriptSandbox.createConnectedInstanceAsync(context);
Korumalı alanın yaşam döngüsünü, gereken bir JavaScript bileşenidir.
Örneğin, korumalı alanı barındıran bir bileşen bir Activity
veya
Service
. JavaScript değerlendirmesini kapsamak için tek bir Service
kullanılabilir
sahip olmayabilir.
Tahsis etme düzeyi oldukça yüksek olduğu için JavaScriptSandbox
örneğini koruma
Pahalı. Uygulama başına yalnızca bir JavaScriptSandbox
örneğine izin verilir.
Bir uygulama,IllegalStateException
ikinci JavaScriptSandbox
örneği. Ancak birden fazla yürütme ortamı varsa
gerekli olduğundan, birden fazla JavaScriptIsolate
örneği ayrılabilir.
Artık kullanılmadığında, kaynakları boşa çıkarmak için korumalı alan örneğini kapatın. İlgili içeriği oluşturmak için kullanılan
JavaScriptSandbox
örneği, AutoCloseable
arayüzü uygular.
, basit engelleme kullanım alanları için "kaynaklarla deneme" kullanımına olanak tanır.
Alternatif olarak, JavaScriptSandbox
örneğin yaşam döngüsünün
barındırma bileşenini bir etkinlik için onStop()
geri çağırmasında kapatarak
onDestroy()
sırasında bir Hizmet:
jsSandbox.close();
JavaScriptIsolate
örneği, yürütülecek bir bağlamı temsil eder
JavaScript kodu. Gerektiğinde bunlara ayrılabilir ve güvenlik zayıftır
farklı kaynaklı komut dosyaları için sınırlar veya eşzamanlı JavaScript'i etkinleştirme
yapısı gereği tek iş parçacıklı olduğundan yürütmelidir. Sonraki çağrılar
aynı durum aynı durumu paylaştığından bazı veriler oluşturulabilir.
ve daha sonra aynı JavaScriptIsolate
örneğinde işleyin.
JavaScriptIsolate jsIsolate = jsSandbox.createIsolate();
close()
yöntemini çağırarak JavaScriptIsolate
öğesini açıkça yayınlayın.
JavaScript kodu çalıştıran izole bir örneği kapatma
(eksik bir Future
öğesi yoksa) IsolateTerminatedException
ile sonuçlanır. İlgili içeriği oluşturmak için kullanılan
arka planda temizlenir.
aşağıdaki adımları izleyin: JS_FEATURE_ISOLATE_TERMINATION
korumalı alan kilitlenmelerini ele alma bölümünü
sayfasını ziyaret edin. Aksi takdirde, bekleyen tüm değerlendirmeler tamamlanana kadar temizleme işlemi ertelenir.
ya da korumalı alan kapatıldı.
Bir uygulama, şuradan bir JavaScriptIsolate
örneği oluşturabilir ve örneğe erişebilir:
herhangi bir ileti dizisinde.
Artık uygulama bazı JavaScript kodlarını yürütmeye hazırdır:
final String code = "function sum(a, b) { let r = a + b; return r.toString(); }; sum(3, 4)";
ListenableFuture<String> resultFuture = jsIsolate.evaluateJavaScriptAsync(code);
String result = resultFuture.get(5, TimeUnit.SECONDS);
Aynı JavaScript snippet'i güzel biçimlendirilmiş:
function sum(a, b) {
let r = a + b;
return r.toString(); // make sure we return String instance
};
// Calculate and evaluate the expression
// NOTE: We are not in a function scope and the `return` keyword
// should not be used. The result of the evaluation is the value
// the last expression evaluates to.
sum(3, 4);
Kod snippet'i String
olarak, sonuç olarak ise String
olarak iletilir.
evaluateJavaScriptAsync()
çağrısının, değerlendirilen
işlevinin sonucu. Bu olmalıdır:
String
JavaScript türü; Aksi takdirde, kitaplık API'si boş bir değer döndürür.
JavaScript kodunda return
anahtar kelimesi kullanılmamalıdır. Korumalı alan
belirli özellikleri ve ek iade türlerini destekler (örneğin, Promise
String
olarak çözümlenen) mümkün olabilir.
Kitaplıkta şu tür e-postalara sahip komut dosyalarının değerlendirilmesi de desteklenir.
AssetFileDescriptor
veya ParcelFileDescriptor
. Görüntüleyin
evaluateJavaScriptAsync(AssetFileDescriptor)
ve
Ayrıntılı bilgi için evaluateJavaScriptAsync(ParcelFileDescriptor)
.
Bu API'ler, diskteki veya uygulama içindeki bir dosyadan değerlendirme yapmaya daha uygundur.
dizin oluşturabilirsiniz.
Kitaplık, hata ayıklama için kullanılabilecek konsol günlük kaydını da destekler
amaçlar. Bu ayar, setConsoleCallback()
kullanılarak yapılabilir.
Bağlam devam ettiği için kod yükleyip birkaç kez çalıştırabilirsiniz
JavaScriptIsolate
ömrü boyunca:
String jsFunction = "function sum(a, b) { let r = a + b; return r.toString(); }";
ListenableFuture<String> func = js.evaluateJavaScriptAsync(jsFunction);
String twoPlusThreeCode = "let five = sum(2, 3); five";
ListenableFuture<String> r1 = Futures.transformAsync(func,
input -> js.evaluateJavaScriptAsync(twoPlusThreeCode)
, executor);
String twoPlusThree = r1.get(5, TimeUnit.SECONDS);
String fourPlusFiveCode = "sum(4, parseInt(five))";
ListenableFuture<String> r2 = Futures.transformAsync(func,
input -> js.evaluateJavaScriptAsync(fourPlusFiveCode)
, executor);
String fourPlusFive = r2.get(5, TimeUnit.SECONDS);
Elbette değişkenler de kalıcıdır. Dolayısıyla, önceki şununla snippet:
String defineResult = "let result = sum(11, 22);";
ListenableFuture<String> r3 = Futures.transformAsync(func,
input -> js.evaluateJavaScriptAsync(defineResult)
, executor);
String unused = r3.get(5, TimeUnit.SECONDS);
String obtainValue = "result";
ListenableFuture<String> r4 = Futures.transformAsync(func,
input -> js.evaluateJavaScriptAsync(obtainValue)
, executor);
String value = r4.get(5, TimeUnit.SECONDS);
Örneğin, gerekli tüm nesneleri ayırmak için tam snippet ve JavaScript kodu çalıştırmak aşağıdaki gibi görünebilir:
final ListenableFuture<JavaScriptSandbox> sandbox
= JavaScriptSandbox.createConnectedInstanceAsync(this);
final ListenableFuture<JavaScriptIsolate> isolate
= Futures.transform(sandbox,
input -> (jsSandBox = input).createIsolate(),
executor);
final ListenableFuture<String> js
= Futures.transformAsync(isolate,
isolate -> (jsIsolate = isolate).evaluateJavaScriptAsync("'PASS OK'"),
executor);
Futures.addCallback(js,
new FutureCallback<String>() {
@Override
public void onSuccess(String result) {
text.append(result);
}
@Override
public void onFailure(Throwable t) {
text.append(t.getMessage());
}
},
mainThreadExecutor);
Ayrılan tüm kaynakların doğru bir şekilde
toplandığından emin olmak için
kaynak serbest bırakılır ve artık kullanılmaz. Korumalı alan sonuçları kapatılıyor
JavaScriptIsolate
örneğin tamamı başarısız olan bekleyen tüm değerlendirmelerde
SandboxDeadException
ile. JavaScript değerlendirmesi,
hata verirse JavaScriptException
oluşturulur. Alt sınıflarına başvurma
inceleyin.
Korumalı Alan Kilitlenmelerini Yönetme
Tüm JavaScript, JavaScript'inizin dışında, korumalı alana alınmış ayrı bir işlemde yürütülür ana işlemi. Korumalı alana alınmış bu işleme JavaScript kodu neden oluyorsa kilitlenebilir. Örneğin, bellek sınırını tükettiğinde uygulamanın ana bu değişiklikten etkilenmeyecektir.
Korumalı alan kilitlenmesi, söz konusu korumalı alandaki tüm izolelerin sonlandırılmasına neden olur. En
Bunun bariz bir belirtisi, tüm değerlendirmelerin
IsolateTerminatedException
. Koşullara bağlı olarak daha fazla
SandboxDeadException
veya
MemoryLimitExceededException
atılabilir.
Her bir değerlendirmede kilitlenmeleri ele almak her zaman pratik değildir.
Ayrıca, bir izole etme işlemi, açık bir şekilde istenen
arka plan görevleri veya diğer izole gruplardaki değerlendirmeler nedeniyle yapılan değerlendirmedir. Çökme
işleme mantığı, API'leri kullanan bir geri çağırma eklenerek
JavaScriptIsolate.addOnTerminatedCallback()
.
final ListenableFuture<JavaScriptSandbox> sandboxFuture =
JavaScriptSandbox.createConnectedInstanceAsync(this);
final ListenableFuture<JavaScriptIsolate> isolateFuture =
Futures.transform(sandboxFuture, sandbox -> {
final IsolateStartupParameters startupParams = new IsolateStartupParameters();
if (sandbox.isFeatureSupported(JavaScriptSandbox.JS_FEATURE_ISOLATE_MAX_HEAP_SIZE)) {
startupParams.setMaxHeapSizeBytes(100_000_000);
}
return sandbox.createIsolate(startupParams);
}, executor);
Futures.transform(isolateFuture,
isolate -> {
// Add a crash handler
isolate.addOnTerminatedCallback(executor, terminationInfo -> {
Log.e(TAG, "The isolate crashed: " + terminationInfo);
});
// Cause a crash (eventually)
isolate.evaluateJavaScriptAsync("Array(1_000_000_000).fill(1)");
return null;
}, executor);
ile izolasyonu kapatırken tetikleyin.
İsteğe Bağlı Korumalı Alan Özellikleri
Temel WebView sürümüne bağlı olarak bir korumalı alan uygulaması
farklı özellikler bulunuyor. Bu yüzden, gerekli her bir
özelliğini kullanarak JavaScriptSandbox.isFeatureSupported(...)
öğrenebilirsiniz. Bu,
bu özellikleri temel alan yöntemleri çağırmadan önce özellik durumunu kontrol edin.
Her yerde kullanılamayabilecek JavaScriptIsolate
yöntem
RequiresFeature
ek açıklamasıyla işaretlenmiştir ve bu şekilde, bunlar kolayca ayırt edilebilir
çağrısının nasıl yapılacağını göstereceğim.
Parametreleri Geçirme
JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT
destekleniyorsa JavaScript motoruna gönderilen değerlendirme istekleri bağlı değil
tarafından uygulanır. Özellik desteklenmiyorsa
JavaScriptEngine, bir Bağlayıcı işlemi aracılığıyla gerçekleşir. Genel
işlem boyutu sınırı, veri veya işlem aracılığıyla iletilen her çağrı için geçerlidir.
verileri döndürür.
Yanıt her zaman Dize olarak döndürülür ve Bağlayıcı
işlem maksimum boyut sınırı:
JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT
değil
desteklenir. Dize olmayan değerler açık bir şekilde JavaScript Dizesine dönüştürülmelidir.
Aksi takdirde boş bir dize döndürülür. JS_FEATURE_PROMISE_RETURN
ise
özelliği desteklendiğinde, JavaScript kodu da alternatif olarak bir Promise değeri döndürebilir.
String
ile çözümleniyor.
JavaScriptIsolate
örneğine büyük bayt dizileri iletmek için:
provideNamedData(...)
API'yi kullanabilir. Bu API'nin kullanımı şunlara bağlı değildir:
Bağlayıcı işlem sınırları belirleyin. Her bayt dizisi, benzersiz bir
ve tekrar kullanılamayacaktır.
if (sandbox.isFeatureSupported(JavaScriptSandbox.JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER)) {
js.provideNamedData("data-1", "Hello Android!".getBytes(StandardCharsets.US_ASCII));
final String jsCode = "android.consumeNamedDataAsArrayBuffer('data-1').then((value) => { return String.fromCharCode.apply(null, new Uint8Array(value)); });";
ListenableFuture<String> msg = js.evaluateJavaScriptAsync(jsCode);
String response = msg.get(5, TimeUnit.SECONDS);
}
Wasm Kodunu Çalıştırma
WebAssembly (Wasm) kodu, provideNamedData(...)
kullanılarak iletilebilir
API'den daha sonra aşağıda gösterildiği gibi normal şekilde derlenir ve yürütülür.
final byte[] hello_world_wasm = {
0x00 ,0x61 ,0x73 ,0x6d ,0x01 ,0x00 ,0x00 ,0x00 ,0x01 ,0x0a ,0x02 ,0x60 ,0x02 ,0x7f ,0x7f ,0x01,
0x7f ,0x60 ,0x00 ,0x00 ,0x03 ,0x03 ,0x02 ,0x00 ,0x01 ,0x04 ,0x04 ,0x01 ,0x70 ,0x00 ,0x01 ,0x05,
0x03 ,0x01 ,0x00 ,0x00 ,0x06 ,0x06 ,0x01 ,0x7f ,0x00 ,0x41 ,0x08 ,0x0b ,0x07 ,0x18 ,0x03 ,0x06,
0x6d ,0x65 ,0x6d ,0x6f ,0x72 ,0x79 ,0x02 ,0x00 ,0x05 ,0x74 ,0x61 ,0x62 ,0x6c ,0x65 ,0x01 ,0x00,
0x03 ,0x61 ,0x64 ,0x64 ,0x00 ,0x00 ,0x09 ,0x07 ,0x01 ,0x00 ,0x41 ,0x00 ,0x0b ,0x01 ,0x01 ,0x0a,
0x0c ,0x02 ,0x07 ,0x00 ,0x20 ,0x00 ,0x20 ,0x01 ,0x6a ,0x0b ,0x02 ,0x00 ,0x0b,
};
final String jsCode = "(async ()=>{" +
"const wasm = await android.consumeNamedDataAsArrayBuffer('wasm-1');" +
"const module = await WebAssembly.compile(wasm);" +
"const instance = WebAssembly.instance(module);" +
"return instance.exports.add(20, 22).toString();" +
"})()";
// Ensure that the name has not been used before.
js.provideNamedData("wasm-1", hello_world_wasm);
FluentFuture.from(js.evaluateJavaScriptAsync(jsCode))
.transform(this::println, mainThreadExecutor)
.catching(Throwable.class, e -> println(e.getMessage()), mainThreadExecutor);
}
JavaScript Yalıtımı Ayırma
Tüm JavaScriptIsolate
örnekleri birbirinden bağımsızdır ve
paylaşmanın iyi bir yoludur. Aşağıdaki snippet,
Hi from AAA!5
ve
Uncaught Reference Error: a is not defined
çünkü ”jsTwo
” örneği şurada oluşturulan nesnelerin görünürlüğüne sahip değildir:
"jsOne
".
JavaScriptIsolate jsOne = engine.obtainJavaScriptIsolate();
String jsCodeOne = "let x = 5; function a() { return 'Hi from AAA!'; } a() + x";
JavaScriptIsolate jsTwo = engine.obtainJavaScriptIsolate();
String jsCodeTwo = "a() + x";
FluentFuture.from(jsOne.evaluateJavaScriptAsync(jsCodeOne))
.transform(this::println, mainThreadExecutor)
.catching(Throwable.class, e -> println(e.getMessage()), mainThreadExecutor);
FluentFuture.from(jsTwo.evaluateJavaScriptAsync(jsCodeTwo))
.transform(this::println, mainThreadExecutor)
.catching(Throwable.class, e -> println(e.getMessage()), mainThreadExecutor);
Kotlin Desteği
Bu Jetpack kitaplığını Kotlin eş yordamlarıyla kullanmak için
kotlinx-coroutines-guava
. Bu sayede, Google Analytics 4'te
ListenableFuture
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.0"
}
Jetpack kitaplığı API'leri artık eş düzey bir kapsamdan çağrılabilir: gösterilmektedir:
// Launch a coroutine
lifecycleScope.launch {
val jsSandbox = JavaScriptSandbox
.createConnectedInstanceAsync(applicationContext)
.await()
val jsIsolate = jsSandbox.createIsolate()
val resultFuture = jsIsolate.evaluateJavaScriptAsync("PASS")
// Await the result
textBox.text = resultFuture.await()
// Or add a callback
Futures.addCallback<String>(
resultFuture, object : FutureCallback<String?> {
override fun onSuccess(result: String?) {
textBox.text = result
}
override fun onFailure(t: Throwable) {
// Handle errors
}
},
mainExecutor
)
}
Yapılandırma Parametreleri
Yalıtılmış ortam örneği isteğinde bulunurken
yapılandırma. Yapılandırmayı değiştirmek için
IsolateStartupParameters örneğini
JavaScriptSandbox.createIsolate(...)
.
Şu anda parametreler maksimum yığın boyutunun ve maksimum boyutun belirtilmesine izin verir ve hataları kontrol edin.