JavaScript Değerlendirmesi
JavaScriptEngine Jetpack kitaplığı, bir uygulamanın WebView örneği oluşturmadan JavaScript kodunu değerlendirebilirsiniz.
Etkileşimli olmayan JavaScript değerlendirmesi gerektiren uygulamalarda JavaScriptEngine kitaplığının kullanılmasının avantajları şunlardır:
WebView örneği ayırmaya gerek olmadığından daha düşük kaynak tüketimi.
Bir Service (WorkManager görevi) içinde yapılabilir.
Uygulamanın aynı anda birden fazla JavaScript snippet'i çalıştırmasına olanak tanıyan, düşük yükü olan birden fazla yalıtılmış ortam.
API çağrısı kullanarak büyük miktarda veri iletme özelliği.
Temel Kullanım
Başlamak için JavaScriptSandbox
örneği 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ün, JavaScript değerlendirmesine ihtiyaç duyan bileşenin yaşam döngüsüyle uyumlu hale getirilmesi önerilir.
Örneğin, korumalı alanı barındıran bir bileşen Activity
veya Service
olabilir. JavaScript değerlendirmesini kapsamak için tek bir Service
kullanılabilir
sahip olmayabilir.
Tahsis etme oranı 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, kaynak boşaltmak 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, JavaScript kodunu yürütme bağlamını temsil eder. Gerekirse ayrılabilirler. Böylece farklı kaynaklardan gelen komut dosyaları için zayıf güvenlik sınırları sağlanabilir veya JavaScript doğası gereği tek iş parçacıklı olduğundan eşzamanlı JavaScript yürütme etkinleştirilebilir. 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 bir izole örneğin kapatılması (tamamlanmamış bir Future
'ye sahip olması) 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üzelce biçimlendirilmiş şekilde:
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 iletilir ve sonuç String
olarak yayınlanır.
evaluateJavaScriptAsync()
çağrısının, JavaScript kodundaki son ifadenin değerlendirilmiş sonucunu döndürdüğünü unutmayın. 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ık, AssetFileDescriptor
veya ParcelFileDescriptor
biçimindeki komut dosyalarının değerlendirilmesini de destekler. Daha fazla bilgi için evaluateJavaScriptAsync(AssetFileDescriptor)
ve evaluateJavaScriptAsync(ParcelFileDescriptor)
başlıklı makalelere göz atın.
Bu API'ler, diskteki veya uygulama dizinlerindeki bir dosyadan değerlendirme yapmak için daha uygundur.
Kitaplık, hata ayıklama için kullanılabilecek konsol günlük kaydını da destekler
amaçlar. Bu, setConsoleCallback()
kullanılarak ayarlanabilir.
Bağlam devam ettiği için kod yükleyebilir ve JavaScriptIsolate
'ün kullanım süresi boyunca birkaç kez çalıştırabilirsiniz:
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ı olduğundan önceki snippet'e şu şekilde devam edebilirsiniz:
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 ve bir JavaScript kodunu yürütmek için gereken snippet'in tamamı 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);
Tüm görevlerin ayrılmış olduğundan emin olmak için
kaynak serbest bırakılır ve artık kullanılmaz. Korumalı alanı kapatmak, tüm JavaScriptIsolate
örneklerinde bekleyen tüm değerlendirmelerin SandboxDeadException
ile başarısız olmasına neden olur. JavaScript değerlendirmesi,
hata verirse JavaScriptException
oluşturulur. Alt sınıflarına başvurma
inceleyin.
Korumalı Alan kilitlenmelerini işleme
Tüm JavaScript, JavaScript'inizin dışında, korumalı alana alınmış ayrı bir işlemde yürütülür ana işlemidir. JavaScript kodu, korumalı alan içindeki bu işlemin kilitlenmesine neden olursa (ör. bellek sınırını aşarak) uygulamanın ana işlemi bundan etkilenmez.
Korumalı alan kilitlenmesi, söz konusu korumalı alandaki tüm izolelerin sonlandırılmasına neden olur. Bunun en belirgin belirtisi, tüm değerlendirmelerin IsolateTerminatedException
ile başarısız olmaya başlamasıdır. Duruma bağlı olarak SandboxDeadException
veya MemoryLimitExceededException
gibi daha spesifik istisnalar atılabilir.
Her değerlendirme için ayrı ayrı kilitlenmelerle ilgilenmek 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. Kilitlenme işleme mantığı, JavaScriptIsolate.addOnTerminatedCallback()
kullanılarak geri çağırma işlevi ekleyerek merkezileştirilebilir.
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);
İsteğe bağlı korumalı alan özellikleri
Temel WebView sürümüne bağlı olarak, korumalı alan uygulamasında farklı özellik grupları kullanılabilir. Bu nedenle, gerekli her özelliği JavaScriptSandbox.isFeatureSupported(...)
kullanarak sorgulamanız gerekir. Bu,
bu özellikleri temel alan yöntemleri çağırmadan önce özellik durumunu kontrol edin.
Her yerde kullanılamayabilecek JavaScriptIsolate
yöntemleri, RequiresFeature
ek açıklamayla notlandırılır. Bu sayede, bu çağrıları kodda tespit etmek kolaylaşır.
Parametreleri Geçirme
JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT
destekleniyorsa JavaScript motoruna gönderilen değerlendirme istekleri, bağlayıcı işlem sınırlarıyla sınırlı değildir. Özellik desteklenmiyorsa JavaScriptEngine'e gönderilen tüm veriler bir Binder 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 JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT
desteklenmiyorsa Binder işleminin maksimum boyut sınırına tabidir. Dize olmayan değerler açıkça JavaScript dizesi biçimine 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 Kodu Ç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 hiçbir şey paylaşmaz. Aşağıdaki snippet,
Hi from AAA!5
ve
Uncaught Reference Error: a is not defined
"jsTwo
" örneği, "jsOne
" içinde oluşturulan nesneleri göremez.
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 coroutine'leriyle kullanmak için kotlinx-coroutines-guava
bağımlılık ekleyin. Bu, ListenableFuture
ile entegrasyon sağlar.
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.0"
}
Jetpack kitaplığı API'leri artık aşağıdaki gibi bir iş parçacığı kapsamından çağrılabilir:
// 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.