Firemonkey增强浏览器在Android平台上与JavaScript的交互
来源:互联网 发布:淘宝网页如何用英语 编辑:程序博客网 时间:2024/05/18 01:53
本文背景:delphi XE10.1
Firemonkey自带的TWebBrower对于JavaScript的交互支持一直不是很好,仅仅提供了一个本地执行JavaScript的方法EvaluateJavaScript,而且该方法不提供JS执行的返回结果。在安卓平台上,EvaluateJavaScript是通过WebView的loadUrl('javascript:' + JavaScript)实现的。
在Android 4.4之后,WebView提供了一个新的执行JS的接口:
procedure evaluateJavascript(script: JString; resultCallback: JValueCallback); cdecl;
该接口可以注册一个获取JS执行结果的回调函数以便在JS异步执行完时返回结果。
TJavaScriptCallBack = procedure(const AResult: string) of object;TJSResultCallback = class(TJavaLocal, JValueCallback)private fCallBack: TJavaScriptCallBack;public procedure onReceiveValue(value: JObject); cdecl;end;{ TResultCallback }procedure TJSResultCallback.onReceiveValue(value: JObject);begin if Assigned(fCallBack) and (value<>nil) then fCallBack(JStringToString(value.toString));end;
这样我们只需创建 一个TJSResultCallBack对象, 并作为WebView.evaluateJavascript接口的第二个参数,就可以异步获取本地执行JS代码的结果。
WebView还提供一个本地代码扩展JavaScript功能的接口:
procedure addJavascriptInterface(object_: JObject; name: JString); cdecl;
该接口注册一个本地实现类,在JS中可以使用指定的名称直接调用该类实现的本地方法。 由于目前不知道怎么直接用Delphi的JNI直接产生一个Java本地对象【注1】,而且Android在高版本中基于安全JS只支持调用本地实现类中标记了“@JavascriptInterface”属性的方法【注2】,所以这里直接使用Java编写了一个代理类:
package tu2.com.jshellper;import android.webkit.JavascriptInterface;/** * Created by tutu on 2017-01-01. */public class JavaScriptHelper { public interface LocalCallBack { public String executeCustomJavaScript(String cmd, String param); } protected LocalCallBack mLocalCallBack; public void setLocalCallBack(LocalCallBack callBack) { mLocalCallBack = callBack; } @JavascriptInterface public String executeCmd(String cmd, String param) { String sRet= ""; if (mLocalCallBack != null) { sRet = mLocalCallBack.executeCustomJavaScript(cmd, param); } return sRet; }}
将这个Java实现类编译成Jar包,就可以在Delphi中使用。
[JavaSignature('tu2/com/jshellper/JavaScriptHelper$LocalCallBack')] JJavaScriptHelper_LocalCallBack = interface(IJavaInstance) ['{74BA78C9-00E5-474A-8FAE-D9DE8D1219F3}'] function executeCustomJavaScript(cmd: JString; param: JString): JString; cdecl; end; TJavaScriptHelper_LocalCallBack = class(TJavaLocal, JJavaScriptHelper_LocalCallBack) private FCallBack: TLocalCallBack; public function executeCustomJavaScript(cmd: JString; param: JString): JString; cdecl; end; [JavaSignature('tu2/com/jshellper/JavaScriptHelper')] JJavaScriptHelper = interface(JObject) ['{B70ED7E1-37E5-4F7A-B26B-478A9D36A128}'] procedure setLocalCallBack(callBack: JJavaScriptHelper_LocalCallBack); cdecl; function executeCmd(cmd: JString; param: JString): JString; cdecl; end; JJavaScriptHelperClass = interface(JObjectClass) ['{9A522A31-9D8A-4748-9704-1E97C84E5657}'] function init: JJavaScriptHelper; cdecl; end; TJJavaScriptHelper = class(TJavaGenericImport<JJavaScriptHelperClass, JJavaScriptHelper>) end;
基于上面的介绍,可以扩展TWebBrower在android平台上的实现,这里为了不引起其他单元代码的修改,采取外部主动注册的方式。
RegisterJavaScriptCallBack方法为浏览器控件注册本地扩展JS方法回调和本地异步执行JS的结果回调,EvaluateJavaScriptAsync方法使浏览器异步调用JS,如果JS有返回值将触发TJavaScriptCallBack方法的执行。
unit FMX.WebBrowser.Android;interface{$SCOPEDENUMS ON}uses FMX.WebBrowser;type TLocalCallBack = procedure(const ACmd, AParam: string; var AResult: string) of object; TJavaScriptCallBack = procedure(const AResult: string) of object; procedure RegisterJavaScriptCallBack(const AWebControl: TCustomWebBrowser; const ALocal: TLocalCallBack; const AJavaScript: TJavaScriptCallBack); procedure EvaluateJavaScriptAsync(const AWebControl: TCustomWebBrowser; const JavaScript: string);
在TAndroidWebBrowserService的字段部分增加:
FBounds: TRect; FRealBounds: TRect; //[Add By Tu2 FJSHelper: JJavaScriptHelper; FJSLocal: TJavaScriptHelper_LocalCallBack; FJSResult: TJSResultCallback; procedure DoEvaluateJavaScriptAsync(const JavaScript: string); //Add By Tu2] procedure InitUIThread; procedure CalcRealBorder;
方法InitUIThread中增加:
procedure TAndroidWebBrowserService.InitUIThread;begin FJWebBrowser := TJWebBrowser.JavaClass.init(TAndroidHelper.Activity); FJWebBrowser.getSettings.setJavaScriptEnabled(True); FListener := TWebBrowserListener.Create(Self); FJWebBrowser.SetWebViewListener(FListener); FJNativeLayout := TJNativeLayout.JavaClass.init(TAndroidHelper.Activity, MainActivity.getWindow.getDecorView.getWindowToken); FJNativeLayout.setPosition(100,100); FJNativeLayout.setSize(300,300); FJNativeLayout.setControl(FJWebBrowser); FFocusChangeListener := TFocusChangeListener.Create(Self); FJNativeLayout.setOnFocusChangeListener(FFocusChangeListener); FJWebBrowser.getSettings.setGeolocationEnabled(True); FJWebBrowser.getSettings.setAppCacheEnabled(True); FJWebBrowser.getSettings.setDatabaseEnabled(True); FJWebBrowser.getSettings.setDomStorageEnabled(True); FJWebBrowser.getSettings.setBuiltInZoomControls(True); FJWebBrowser.getSettings.setDisplayZoomControls(False); //Add By TU2 FJSResult := TJSResultCallback.Create; FJSHelper := TJJavaScriptHelper.JavaClass.init; FJSLocal := TJavaScriptHelper_LocalCallBack.Create; FJSHelper.setLocalCallBack(FJSLocal); FJWebBrowser.addJavascriptInterface(FJSHelper, StringToJString('TU2JSHelper'));end;
最后实现开头定义的注册扩展方法:
procedure TAndroidWebBrowserService.DoEvaluateJavaScriptAsync(const JavaScript: string);begin CallInUIThread(procedure begin FJWebBrowser.evaluateJavascript(StringToJString(JavaScript), FJSResult); end);end;{ TAndroidWebBrowserService.TResultCallback }procedure TAndroidWebBrowserService.TJSResultCallback.onReceiveValue(value: JObject);begin if Assigned(fCallBack) and (value<>nil) then begin TThread.Queue(nil, procedure begin fCallBack(JStringToString(value.toString)); end); end;end;{ TAndroidWebBrowserService.TJavaScriptHelper_LocalCallBack }function TAndroidWebBrowserService.TJavaScriptHelper_LocalCallBack.executeCustomJavaScript( cmd, param: JString): JString;var AResult: string;begin if Assigned(FCallBack) then begin AResult := ''; FCallBack(JStringToString(cmd), JStringToString(param), AResult); Result := StringToJString(AResult); end;end;type TMyCustomWebBrowser = class(TControl) private FWeb: ICustomBrowser; end;procedure RegisterJavaScriptCallBack(const AWebControl: TCustomWebBrowser; const ALocal: TLocalCallBack; const AJavaScript: TJavaScriptCallBack);begin with (TMyCustomWebBrowser(AWebControl).FWeb as TAndroidWebBrowserService) do begin FJSLocal.FCallBack := ALocal; FJSResult.fCallBack := AJavaScript; end;end;procedure EvaluateJavaScriptAsync(const AWebControl: TCustomWebBrowser; const JavaScript: string);begin if TOSVersion.Check(4,4) then (TMyCustomWebBrowser(AWebControl).FWeb as TAndroidWebBrowserService). DoEvaluateJavaScriptAsync(JavaScript) else begin //暂未实现 end;end;
至此,TWebBrower在android平台上的JS扩展交互就实现了。只需为WebBrowser控件调用一次RegisterJavaScriptCallBack,就可以在JS代码中如下使用:
function CallHost() {document.getElementById("demoHost").innerHTML = window.TU2JSHelper.executeCmd("JSCallLocal","读取Label的Tag值:");}
完整的FMX.WebBrowser.Android和Demo: http://download.csdn.net/detail/tht2009/9730534
注1:Delphi使用两种方式创建Java对象,一种是TJavaGenericImport导入Java中的类,一种是使用(TJavaLocal, JXXX)创建本地实例对象,两种都需要已知的签名接口,不能直接用delphi定义一个新Java Object对象。但在 turbococoa 中介绍了可以直接使用TJavaObject继承扩展Java类,只是这是一个第三方商业项目,不知道其实现原理。
注2:详情参见android相关开发文档。
- Firemonkey增强浏览器在Android平台上与JavaScript的交互
- 【Android】安卓平台上Javascript和Java的交互
- Javascript与Java在浏览器中的交互
- javascript与浏览器的数据交互
- Android学习笔记:在Android平台上通过JSON格式与服务器端进行数据交互
- Android学习笔记:在Android平台上通过JSON格式与服务器端进行数据交互
- Firemonkey扩展增强:Android 浏览器支持Input file标签上传功能
- android与javascript的交互
- android与javascript的交互
- Android 与JavaScript的交互
- javascript:用户与浏览器交互
- JavaScript在浏览器上的调试技巧
- Firemonkey TListView在Android上的滚动流畅度与其所在的Form里显示的元件数有关
- JavaScript 与 Android 交互
- JavaScript与Android交互
- Android与Javascript交互
- Android与Javascript交互
- Android与Javascript交互
- 欢迎使用CSDN-markdown编辑器
- 简单的Android启动页——闪屏(2)
- Unity3D: Cache Mechanism
- 软件研发部经理岗位职责
- typedef
- Firemonkey增强浏览器在Android平台上与JavaScript的交互
- spring-boot配置文件-DataSource(中文翻译)
- 记录一个析构顺序错误导致程序中断的问题
- 分治解决假硬币问题
- OpenCV中的神器Image Watch
- LeetCode 419. Battleships in a Board
- NULL Object
- MYSQL Too many connections错误的解决办法
- boost之tuple