125 고성능 web view-deview 2013 발표...

Preview:

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