
Giải pháp tích hợp native và webview trong Hybrid Native App
Mình xin phép share giải pháp tích hợp tích hợp Native layer và Webview layer trong Hybrid Native Mobile application.
Thường mình tham khảo 1 số dự án thì đang thấy solution integration đang kiểu spam code cho nhiều mobile platform như bên dưới.
document.dispatchEvent(new CustomEvent('getData_fromSDK_android'));
document.dispatchEvent(new CustomEvent('getData_fromSDK_iOS'));
document.addEventListener('getData_fromSDK_android', (customEvent: CustomEvent) => this.handleDataAndroid(customEvent));
document.addEventListener('getData_fromSDK_iOS', (event: CustomEvent) => this.handleDataIOS(event));
Đoạn code tích hợp trên có rất nhiều vấn đề :
– spam call cho cả Android lẫn IOS
– Các event thì lại đăng ký vào global event sẽ dễ dàng xảy ra memory leak nhất là mô hình SPA
– việc call native không trực tiếp mà lại thông qua dispatch event.
– khó để control event vì call global event ở khắp nơi trong src code.
– mỗi khi thêm, sửa event phải code khá nhiều từ tầng tích hợp cho đến business
Cơ chế kết nối native thì mình đã code dưới dạng helper class NativeCallHelper, native-script.js. Và các vấn đề trên đã được giải quyết hết
– gọi trực tiếp native và phân biệt sẵn Android, IOS.
– dễ dàng quản lý và debug vì các event đều thông qua 1 method duy nhất.
– Cơ chế tích hợp chỉ cần code 1 lần duy nhất không cần sửa lại khi có api mới.
– tự động release memory(event) ngay khi nhận được response từ tầng Native
– dễ dàng mở rộng vì mỗi phần implement mới chỉ việc đăng ký thêm event, thay vì phải code thêm phần method integration
Dưới đây là diagram mô ta overview về solution

Dưới đây là detail cụ thể code implementation:
1) Script kết nối tầng native file “native-script.js” có thể cho vào Index.html hoặc scripts trong angular.json
native-script.js
var deviceCache = -1;
function nativeCallback(eventName, data) {
document.dispatchEvent(new CustomEvent(eventName, {
detail: data
}));
}
// return 0 // Android, 1 // IOS
function getMobileOperatingSystem() {
var userAgent = navigator.userAgent || navigator.vendor || window.opera;
if (/android/i.test(userAgent)) {
return 0;
}
if (!window.MSStream) {
if (/iPhone/i.test(userAgent) ||
/iPad/i.test(userAgent) ||
/iPod/i.test(userAgent)) {
return 1;
}
}
return -1;
}
document.addEventListener("nativeCall", function (event) {
var device = deviceCache;
if (device === -1) {
deviceCache = getMobileOperatingSystem();
device = deviceCache;
}
switch (device) {
case 0:
callAndroidNative(event);
break;
case 1:
callIOSNative(event);
break;
default:
console.log("device not support");
}
});
function callAndroidNative(eventInfo) {
try {
if (nativeInterface) {
nativeInterface.invokeNativeMethod(eventInfo.detail.name, eventInfo.detail.data);
}
} catch (err) {
console.log("android SdkController not define " + eventInfo.detail.name);
}
}
function callIOSNative(eventInfo) {
try {
var data = { event: eventInfo.detail.name, data: eventInfo.detail.data };
webkit.messageHandlers.invokeNativeMethod.postMessage(JSON.stringify(data));
} catch (err) {
console.log('IOS webkit not define' + eventInfo.detail.name);
}
}
2) Native Call helper để send và nhận event giữa tầng web và native
native-call.helper.ts
/**
* Native API name defination
*/
export enum NativeRequest {
REQUEST_GET_TOKEN = "getToken",
REQUEST_GET_USER_INFORMATION = "getUserInformation"
}
/**
* Native API event defination
*/
export enum NativeEvent{
EVENT_BACK = "clickBackNative",
EVENT_SHOW_HOME_SCREEN = "showHomeScreen"
}
/**
* Helper class to call method or register event from native layer
*/
export class NativeCallHelper {
private static readonly EVENT_NATIVE_CALL = "nativeCall";
private static readonly EVENT_NATIVE_CALLBACK_SUFFIX = "response";
private requests: Array<any> = [];
/**
* api to call function from native layer
*
* @param functionName function name from NativeRequest
* @param parameters parameter of native function
* @param callback callback with json data from native layer
*/
public callNative(functionName: string, parameters: string, callback: (data: string) => any) {
if (callback) {
var responseEventName = functionName + NativeCallHelper.EVENT_NATIVE_CALLBACK_SUFFIX;
var localCallback = (event: CustomEvent) => {
callback(event.detail);
this.removeEvent(responseEventName);
}
var event = { name: responseEventName, listener: localCallback };
document.addEventListener(responseEventName, localCallback);
this.requests.push(event);
}
document.dispatchEvent(new CustomEvent(NativeCallHelper.EVENT_NATIVE_CALL, { detail: { name: functionName, data: parameters } }));
}
/**
* register event from native layer
*
* @param functionName function name from NativeEvent
* @param parameters parameter of native function
* @param callback callback with json data from native layer
*/
public registerEvent(eventName: string, callback: (data: string) => any) {
if (callback) {
var localCallback = (event: CustomEvent) => {
callback(event.detail);
}
var event = { name: name, listener: localCallback };
document.addEventListener(name, localCallback);
this.requests.push(event);
}
}
/**
* unregister event from native layer
*
* @param eventName function name from NativeEvent
*/
public unRegisterEvent(eventName: string) {
this.removeEvent(eventName);
}
/**
* destroy all event to make sure no memory leak
*
*/
public destroy() {
this.requests.forEach(function (event, index, object) {
document.removeEventListener(event.name, event.listener, false);
object.splice(index, 1);
});
}
private removeEvent(name: string) {
this.requests.forEach(function (event, index, object) {
if (event.name === name) {
document.removeEventListener(event.name, event.listener, false);
object.splice(index, 1);
}
});
}
}
3) Tích hợp Android/IOS native
Tầng native sẽ cần implement thêm như sau
– share native api với function name là “invokeNativeMethod” cho web layer, Android thì thông qua Javascript inteface, IOS thì qua Webkit
– các reponse trả về từ tầng native bao gồm api và event thì thông qua method “nativeCallback” của web layer
Note: trong bất kỳ trường hợp nào tầng native bắt buộc phải call “nativeCallback” để tầng Web biết được method dã complete .
Những phần trên thì là theo standard của native Android, IOS nên mình ko share detail ở đây
Sau khi tích hợp xong Web layer chỉ cần gọi qua duy nhất từ api của nativeCallHelper. Example:
nativeCallHelper.callNative(NativeRequest .REQUEST_GET_USER_INFORMATION , “”, this.handeUserInformation(data));
nativeCallHelper.registerEvent(NativeEvent.EVENT_SHOW_HOME_SCREEN , “”, this.handeShowHomeSceen(data));
// gọi trong ondestroy của component hoặc khi thấy ko cần đến nữa.
nativeCallHelper.destroy();
Post Comment