Upload
naver-d2
View
10.116
Download
3
Embed Size (px)
Citation preview
WebView 뛰어 넘기부제: 고성능 WebView 만들기
박창현
About Us
http://www.skplanet.comhttp://readme.skplanet.com
About Me
단말 플랫폼 개발 Specialist웹 플랫폼 개발 SpecialistAndroid 보안 시스템 개발 SpecialistAndroid/HTML5/Hybrid App Framework/...현) SK planet / Mobile SW 개발 1팀 / 팀장
•••••
Why High-performance WebView?Basic Tech. Idea for High-performance
WebViewDemoLessons Learned
••
••
Contents
Why High-performance WebView?
Hybrid Apps. Become More Popular
What is Hybrid Application?
Native Web
WebView
Frag
mentat
ion
WebView Is ...
A Part of AndroidFrameworkDifferent from Web
Browser (Chrome)Less Powerful Than Web
Browser (Chrome...)Web Standard
CompatibilityDepend On Android OS
Version
•
•
•
•
-
A Part of iOSDifferent from Web
Browser (Safari)Less Powerful Than Web
Browser (Safari)Web Standard
CompatibilityDepend On iOS Version
••
•
•
-
Frag
mentat
ion
Frag
mentat
ion
Web Standard Compatibility forWebView
CanvasAudio/Vi
deo
WebWorker
s
WebSockets
WebAudio
WebNotificat
ionWebGL
GB O △ X X X X X
ICS O △ X X X X X
JB O △ △ X X X X
Web Standard Compatibility forWebView
CanvasAudio/Vi
deo
WebWorker
s
WebSockets
WebAudio
WebNotificat
ionWebGL
5.0 O △ O △ X X X
6.0 O △ O O O X X
7.0 O △ O O O X X
Hybrid App’s Problems Are ...
“We have to make several versions of in-appweb contents for each android/iOS version.”
“We need Web Worker feature for ourcontents. But because only iOS can support
Web Worker spec at this time, we can’ssupport android devices.”
Fragmentation
Performance
We Need To Have SpecialWebView
Provide The Same Web StandardsIndependent of OS version and Manufacturers
Should Be Faster Than Native
Basic Technical Ingredient
We Need To Know ...
How To Call JavaScript FunctionFrom Native
•
How To Call Native Function FromJavaScript
•
AndroidonPageFinished + bookmarklet•
webview.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { // evaluate your javascript code here! view.loadUrl(“javascript:.... “); }});
AndroidaddJavascriptInterface•
//Define Bridge Classclass MyBridgeClass { public void foo(final String args) { // do something }}
webview.addJavascriptInterface(new MyBridgeClass(), “MyBridge”);
// In javascript codewindow.MyBride.foo(“hello world”);
iOSwebViewDidFinishLoad +
stringByEvaluatingJavaScriptFromString•
//Insert my own javascript codes like this:- (void)webViewDidFinishLoad:(UIWebView *)webView{ outputString = [webViewstringByEvaluatingJavaScriptFromString:@"whatever js code I wantto evaluate"]; }
iOSshouldStartLoadWithRequest•
//Insert my own javascript codes like this:- (BOOL)webView:(UIWebView *)theWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType: UIWebViewNavigationType)navigationType { if ([[[request URL] absoluteString] hasPrefix:@"skp:"]) { // do my job return NO; } return YES;}
//Javascript codevar iframe = document.createElement("IFRAME");iframe.setAttribute("src", "skp:myBridgeFunction";document.documentElement.appendChild(iframe);iframe.parentNode.removeChild(iframe);iframe = null;
Add & Replace
Web Sockets and Canvas 2D
Web SocketsAndroid WebView Does Not SupportAdd Web Sockets Features Into WebView
•--
Canvas 2DAndroid WebView’s Canvas 2D Is Extremely SlowReplace Canvas 2D Features With My Own
Implementation (with SurfaceView)
•--
Add Web Socket (Android)
WebSocket_shim.js
NativeWebSocket
NativeWebSocketImplNativeWebSocketImpl...
WebSocket WebSocket ...
NativeWebSocket
WebView
Add Web Socket (Android)
// websocket_shim.js(function() { var store = {}; // Websocket constructor var WebSocket = window.WebSocket = function(url) { // Initialize web socket this.socketId = nativewebsocket.getInstance(url); WebSocket.store[this.socketId] = this; } // declare prototype method WebSocket.prototype.send = function(data) { // bind to native nativewebsocket.send(data, this.socketId); } ... // event dispatcher WebSocket.onopen = function(evt) { // dispatch event to proper instance WebSocket.store[evt.id].onopen(evt); }...})();
Define Web Socket (JavaScript)•
Add Web Socket (Android)
// Define Bridge Interfaceclass NativeWebSocket { class NativeWebSocketImpl { public String id; public void send(String data) { } public void onOpen(...) { webview.post(new Runnable() { @override public void run() { webview.loadUrl(“javascript:WebSocket.onopen({targetId:“ + id + “,data:” + “data + “});”); } }); } }; ... public void send(String data, String id) { } public String getInstance(String url) { hash.put(getId(), newNativeWebSocketImpl(url, id)); }}...// Register NativeWebSocketwebview.addJavascriptInterface(new NativeWebSocket(), “nativewebsocket”);
Define Web Socket / Register /Callback(Java)
•
Add Web Socket (Android)Install WebSocket_shim.js!•
webview.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { // evaluate your javascript code here! view.loadUrl(“javascript:“ + <WebSocket_shim.js 파일 내용>); }});
Replace Canvas 2D (Android)
WebKit
Original Canvas(JavaScript)
Canvas Canvas ...
CanvasRenderingContext2DCanvasRenderingContext2D...
Image Image ...
WebView
NativeCanvas2DContext
canvas_shim.js(JavaScript)
Canvas Canvas ...
CanvasRenderingContext2DCanvasRenderingContext2D...
Image Image ...
SurfaceView
Bitmap
SurfaceView
Bitmap
...
...
WebKit
NativeCanvas2DContext
WebView
Replace Canvas 2D (Android)
HTML
WebView
SurfaceView
Canvas
Replace Canvas 2D (Android)
// canvas2d_shim.js
// Replace built-in function prototype with your ownHTMLCanvasElement.prototype.getContext = function(getCtxOld) { return function (val) { var ctx; ctx = getCtxOld.apply(this, arguments); if (val == ‘2d’) { // make Native Canvas NativeCanvas2DContext.createNativeCanvas(...); // Object Instance Mixin } return ctx; };}(HTMLCanvasElement.prototype.getContext);
Override Canvas.getContext(“2d”)•
Replace Canvas 2D (Android)
// Replace built-in CanvasRenderingContext2D prototype with yourown
CanvasRenderingContext2D.prototype.fillText = function(..) { NativeCanvas2DContext.fillText(...);};
CanvasRenderingContext2D.prototype.drawImage = function(..) { if (arguments.length == 9) { NativeCanvas2DContext.drawImage(...); } else if (arguments.length == 3) { ... } else { }};
Override CanvasRendering2DContext•
Replace Canvas 2D (Android)
// Replace built-in constructor of Image object with my ownImage = function() { var image = {}; var imageId = NativeCanvas2DContext.getImageId();
image.__defineSetter__("src", function(val) { NativeCanvas2DContext.setImageSrc(imageId, val); this._src = val; });
image.__defineGetter__("width", function() { return NativeCanvas2DContext.getImageWidth(imageId); }); image.__defineGetter__("height", function() { return NativeCanvas2DContext.getImageHeight(imageId); }); ...
return image;};
Override Image•
// Define Bridge Interfaceclass NativeCanvas2DContext { public SurfaceView nativeView; public Bitmap image; public void createCanvas(int width, int height) { ... } public void fillText(..) { ... } public void drawImage(..) { ... } public void drawArc(..) { ...} ... public String getImageId() { ... } public void setImageSrc(..) { ... } ...}...// Register NativeCanvas2DContextwebview.addJavascriptInterface(new NativeCanvas2DContext(),“NativeCanvas2DContext”);
Replace Canvas 2D (Android)Define Native Canvas / Register•
Replace Canvas 2D (Android)Install canvas2d_shim.js!•
webview.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { // evaluate your javascript code here! view.loadUrl(“javascript:“ + <canvas2d_shim.js 파일 내용>); }});
Demo
Lessons Learned
Do as many implementations as possible injavascript worldMinimize Javascript ⬌ Native
communicationsWatch out threads (PostMessage)
•
•
•
General Lessons
We Can’t Override Native Properties InWebView
Object.defineProperty Doesn’t Work for NativeProperties
•
-
Android Specific Lessons
Object Instance MixinAdd Getter/Setter into CanvasRendering2DContext’s
Instance
Object.defineProperty bugsUse __defineGetter__, __defineSetter__ instead
•-
•-
Q&A