Currently, Lynx is not suitable for building a new application from scratch. You need to integrate Lynx (engine) with your native mobile app or web app, and load Lynx apps through Lynx views. With a few steps, you can start developing with Lynx in your application.
Choose your target platform to view the specific integration steps:
Using Cocoapods can easily integrate Lynx into your application
The core capabilities of Lynx Engine include basic capabilities such as parsing Bundle, style parsing, layout, and rendering views
Get the latest version of Lynx from Cocoapods. Then add Lynx to your Podfile:
source 'https://cdn.cocoapods.org/'
platform :ios, '10.0'
target 'YourTarget' do
pod 'Lynx', '3.4.1', :subspecs => [
'Framework',
]
pod 'PrimJS', '2.14.1', :subspecs => ['quickjs', 'napi']
end
Lynx Service includes LynxImageService
, LynxLogService
, etc. It aims to provide the ability to strongly correlate some host App features, allowing the App to inject custom Services at runtime, or use the default implementation provided by the official. For example, LynxImageService
is implemented using the SDWebImage image library by default. Apps that do not integrate SDWebImage components can rely on other image libraries to implement Image Service.
Lynx provides standard native Image, Log, and Http service capabilities, which can be quickly accessed and used by the access party;
Get the latest version of Lynx Service from Cocoapods. Then add Lynx Service to your Podfile:
source 'https://cdn.cocoapods.org/'
platform :ios, '10.0'
target 'YourTarget' do
pod 'Lynx', '3.4.1', :subspecs => [
'Framework',
]
pod 'PrimJS', '2.14.1', :subspecs => ['quickjs', 'napi']
# integrate image-service, log-service, and http-service
pod 'LynxService', '3.4.1', :subspecs => [
'Image',
'Log',
'Http',
]
# ImageService dependencies:
pod 'SDWebImage','5.15.5'
pod 'SDWebImageWebPCoder', '0.11.0'
end
XElement is an extend native elements library maintained by the Lynx team. It provides richer component capabilities, enabling faster adoption of Lynx in production environments and enhancing the vibrancy of the Lynx ecosystem.
source 'https://cdn.cocoapods.org/'
platform :ios, '10.0'
target 'YourTarget' do
pod 'Lynx', '3.4.1', :subspecs => [
'Framework',
]
pod 'PrimJS', '2.14.1', :subspecs => ['quickjs', 'napi']
# integrate image-service, log-service, and http-service
pod 'LynxService', '3.4.1', :subspecs => [
'Image',
'Log',
'Http',
]
# ImageService
pod 'SDWebImage','5.15.5'
pod 'SDWebImageWebPCoder', '0.11.0'
pod 'XElement', '3.4.1'
end
Run pod install
to install dependencies, then open your Xcode project. Additionally, make sure to disable the Sandbox Scripting capability.
In order to disable the Sandbox scripting, in Xcode click on your app, then on build settings. Filter for script and set the User Script Sandboxing to NO.
LynxEnv provides a global initialization interface for the Lynx Engine. Please ensure that the initialization of LynxEnv occurs before any interface call of the Lynx Engine.
For example, it can be initialized in AppDelegate
#import <Lynx/LynxEnv.h>
#import <Lynx/LynxView.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[LynxEnv sharedInstance];
return YES;
}
LynxView is the basic rendering unit provided by Lynx Engine. LynxView is an implementation inherited from iOS native UIView. You can quickly construct a LynxView and add it to the ViewController.
Lynx Engine itself does not have the ability to load resources, so it is necessary to initialize LynxEnv, or when constructing LynxView to pass in the specific implementation of the LynxTemplateProvider
protocol. Lynx will use the injected resource loader to obtain the Bundle content
You can use various methods to obtain the contents of the Bundle. Here, we choose to embed the contents of the Bundle within the application.
Steps to embed files:
#import <Foundation/Foundation.h>
#import <Lynx/LynxTemplateProvider.h>
NS_ASSUME_NONNULL_BEGIN
@interface DemoLynxProvider : NSObject <LynxTemplateProvider>
@end
NS_ASSUME_NONNULL_END
#import <Foundation/Foundation.h>
#import "DemoLynxProvider.h"
@implementation DemoLynxProvider
- (void)loadTemplateWithUrl:(NSString*)url onComplete:(LynxTemplateLoadBlock)callback {
NSString *filePath = [[NSBundle mainBundle] pathForResource:url ofType:@"bundle"];
if (filePath) {
NSError *error;
NSData *data = [NSData dataWithContentsOfFile:filePath options:0 error:&error];
if (error) {
NSLog(@"Error reading file: %@", error.localizedDescription);
callback(nil, error);
} else {
callback(data, nil);
}
} else {
NSError *urlError = [NSError errorWithDomain:@"com.lynx"
code:400
userInfo:@{NSLocalizedDescriptionKey : @"Invalid URL."}];
callback(nil, urlError);
}
}
@end
you may construct a basic LynxView as follows:
#import <Lynx/LynxView.h>
#import "ViewController.h"
#import "DemoLynxProvider.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
LynxView *lynxView = [[LynxView alloc] initWithBuilderBlock:^(LynxViewBuilder *builder) {
builder.config = [[LynxConfig alloc] initWithProvider:[[DemoLynxProvider alloc] init]];
builder.screenSize = self.view.frame.size;
builder.fontScale = 1.0;
}];
lynxView.preferredLayoutWidth = self.view.frame.size.width;
lynxView.preferredLayoutHeight = self.view.frame.size.height;
lynxView.layoutWidthMode = LynxViewSizeModeExact;
lynxView.layoutHeightMode = LynxViewSizeModeExact;
}
@end
and then, adding the LynxView to the window.
#import <Lynx/LynxView.h>
#import "ViewController.h"
#import "DemoLynxProvider.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// ...
[self.view addSubview:lynxView];
}
@end
After completing the above steps, all the work of initializing LynxView have been completed. Call the lynxView.loadTemplateFromURL
method to render the corresponding Bundle onto the LynxView.
#import <Lynx/LynxView.h>
#import "ViewController.h"
#import "DemoLynxProvider.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// ...
[lynxView loadTemplateFromURL:@"main.lynx" initData:nil];
}
@end
Then you will see the following interface on the screen:
Congratulations, you have now completed all the work of integrating Lynx Engine!
At this stage, you have successfully integrated Lynx into your App. Refer to our developing and debugging docs for in-depth insights on working with Lynx.
The core capabilities of Lynx Engine include basic capabilities such as parsing Bundle, style parsing, layout, rendering views and the basic code of the javascript runtime that Lynx pages rely on
dependencies {
// lynx dependencies
implementation "org.lynxsdk.lynx:lynx:3.4.1"
implementation "org.lynxsdk.lynx:lynx-jssdk:3.4.1"
implementation "org.lynxsdk.lynx:lynx-trace:3.4.1"
implementation "org.lynxsdk.lynx:primjs:2.14.1"
}
Lynx Service includes LynxImageService
, LynxLogService
, etc. It aims to provide the ability to strongly correlate some host App features, allowing the App to inject custom Services at runtime, or use the default implementation provided by the official.For example, LynxImageService
is implemented using the Fresco image library by default. Apps that do not integrate Fresco components can rely on other image libraries, such as Glide to implement Image Service.
Lynx provides standard native Image, Log, and Http service capabilities, which can be quickly accessed and used by the access party;
dependencies {
// lynx dependencies
implementation "org.lynxsdk.lynx:lynx:3.4.1"
implementation "org.lynxsdk.lynx:lynx-jssdk:3.4.1"
implementation "org.lynxsdk.lynx:lynx-trace:3.4.1"
implementation "org.lynxsdk.lynx:primjs:2.14.1"
// integrating image-service
implementation "org.lynxsdk.lynx:lynx-service-image:3.4.1"
// image-service dependencies, if not added, images cannot be loaded; if the host APP needs to use other image libraries, you can customize the image-service and remove this dependency
implementation "com.facebook.fresco:fresco:2.3.0"
implementation "com.facebook.fresco:animated-gif:2.3.0"
implementation "com.facebook.fresco:animated-webp:2.3.0"
implementation "com.facebook.fresco:webpsupport:2.3.0"
implementation "com.facebook.fresco:animated-base:2.3.0"
implementation "com.squareup.okhttp3:okhttp:4.9.0"
// integrating log-service
implementation "org.lynxsdk.lynx:lynx-service-log:3.4.1"
// integrating http-service
implementation "org.lynxsdk.lynx:lynx-service-http:3.4.1"
}
The obfuscation rules for Lynx Engine
are as follows. It is recommended to refer to the latest source code configuration.
# LYNX START
# use @Keep to annotate retained classes.
-dontwarn android.support.annotation.Keep
-keep @android.support.annotation.Keep class **
-keep @android.support.annotation.Keep class ** {
@android.support.annotation.Keep <fields>;
@android.support.annotation.Keep <methods>;
}
-dontwarn androidx.annotation.Keep
-keep @androidx.annotation.Keep class **
-keep @androidx.annotation.Keep class ** {
@androidx.annotation.Keep <fields>;
@androidx.annotation.Keep <methods>;
}
# native method call
-keepclasseswithmembers,includedescriptorclasses class * {
native <methods>;
}
-keepclasseswithmembers class * {
@com.lynx.tasm.base.CalledByNative <methods>;
}
# to customize a module, you need to keep the class name and the method annotated as LynxMethod.
-keepclasseswithmembers class * {
@com.lynx.jsbridge.LynxMethod <methods>;
}
-keepclassmembers class * {
@com.lynx.tasm.behavior.LynxProp <methods>;
@com.lynx.tasm.behavior.LynxPropGroup <methods>;
@com.lynx.tasm.behavior.LynxUIMethod <methods>;
}
-keepclassmembers class com.lynx.tasm.behavior.ui.UIGroup {
public boolean needCustomLayout();
}
# in case R8 compiler may remove mLoader in bytecode.
# as mLoader is not used in java and passed as a WeakRef in JNI.
-keepclassmembers class com.lynx.tasm.LynxTemplateRender {
private com.lynx.tasm.core.LynxResourceLoader mLoader;
}
# the automatically generated setter classes use the class names of LynxBaseUI and ShadowNode and their subclasses.
-keep class com.lynx.tasm.behavior.ui.LynxBaseUI
-keep class com.lynx.tasm.behavior.shadow.ShadowNode
-keep class com.lynx.jsbridge.LynxModule { *; }
-keep class * extends com.lynx.tasm.behavior.ui.LynxBaseUI
-keep class * extends com.lynx.tasm.behavior.shadow.ShadowNode
-keep class * extends com.lynx.jsbridge.LynxModule { *; }
-keep class * extends com.lynx.jsbridge.LynxContextModule
-keep class * implements com.lynx.tasm.behavior.utils.Settable
-keep class * implements com.lynx.tasm.behavior.utils.LynxUISetter
-keep class * implements com.lynx.tasm.behavior.utils.LynxUIMethodInvoker
-keep class com.lynx.tasm.rendernode.compat.**{
*;
}
-keep class com.lynx.tasm.rendernode.compat.RenderNodeFactory{
*;
}
# LYNX END
Application#onCreate
lifecycle of the application.Please specify your custom Application class in the AndroidManifest.xml file, since Lynx needs to perform some global initialization operations when the application starts.
<application
android:name=".YourApplication">
</application>
import android.app.Application;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.facebook.imagepipeline.memory.PoolConfig;
import com.facebook.imagepipeline.memory.PoolFactory;
import com.lynx.service.http.LynxHttpService;
import com.lynx.service.image.LynxImageService;
import com.lynx.service.log.LynxLogService;
import com.lynx.tasm.service.LynxServiceCenter;
public class YourApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
initLynxService();
}
private void initLynxService() {
// init Fresco which is needed by LynxImageService
final PoolFactory factory = new PoolFactory(PoolConfig.newBuilder().build());
ImagePipelineConfig.Builder builder =
ImagePipelineConfig.newBuilder(getApplicationContext()).setPoolFactory(factory);
Fresco.initialize(getApplicationContext(), builder.build());
LynxServiceCenter.inst().registerService(LynxImageService.getInstance());
LynxServiceCenter.inst().registerService(LynxLogService.INSTANCE);
LynxServiceCenter.inst().registerService(LynxHttpService.INSTANCE);
}
}
LynxEnv provides the global initialization interface for the Lynx Engine. Please ensure that the initialization of LynxEnv occurs before any interface calls to the Lynx Engine. It is recommended to complete the initialization of LynxEnv during the Application#onCreate lifecycle of the application.
import com.lynx.tasm.LynxEnv;
public class YourApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
initLynxService();
initLynxEnv();
}
private void initLynxEnv() {
LynxEnv.inst().init(
this,
null,
null,
null
);
}
}
The parameters for the LynxEnv initialization method are described as follows:
Lynx Engine itself does not have the ability to integrate downloading resources, so the existing app needs to provide the specific implementation of AbsTemplateProvider
, and inject it when initializing LynxEnv or constructing LynxView. Lynx will use the injected resource loader to obtain the Bundle content
You can use various methods to obtain the contents of the Bundle. Here, we choose to embed the contents of the Bundle within the application.
app
└── src
└── main
├── java
├── res
└── assets
└── main.lynx.bundle
import android.content.Context;
import com.lynx.tasm.provider.AbsTemplateProvider;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class DemoTemplateProvider extends AbsTemplateProvider {
private Context mContext;
DemoTemplateProvider(Context context) {
this.mContext = context.getApplicationContext();
}
@Override
public void loadTemplate(String uri, Callback callback) {
new Thread(new Runnable() {
@Override
public void run() {
try (InputStream inputStream = mContext.getAssets().open(uri);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, length);
}
callback.onSuccess(byteArrayOutputStream.toByteArray());
} catch (IOException e) {
callback.onFailed(e.getMessage());
}
}
}).start();
}
}
LynxView
is the basic rendering view provided by Lynx Engine
. LynxView
inherits from the native Android View. You can quickly construct a LynxView and add it arbitrarily to the native Android view tree.
import android.app.Activity;
import android.os.Bundle;
import com.lynx.tasm.LynxView;
import com.lynx.tasm.LynxViewBuilder;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LynxView lynxView = buildLynxView();
setContentView(lynxView);
}
private LynxView buildLynxView() {
LynxViewBuilder viewBuilder = new LynxViewBuilder();
viewBuilder.setTemplateProvider(new DemoTemplateProvider(this));
return viewBuilder.build(this);
}
}
XElement is an extend native elements library maintained by the Lynx team. It provides richer component capabilities, enabling faster adoption of Lynx in production environments and enhancing the vibrancy of the Lynx ecosystem.
dependencies {
// lynx dependencies
implementation "org.lynxsdk.lynx:lynx:3.4.1"
implementation "org.lynxsdk.lynx:lynx-jssdk:3.4.1"
implementation "org.lynxsdk.lynx:lynx-trace:3.4.1"
implementation "org.lynxsdk.lynx:primjs:2.14.1"
// integrating image-service
implementation "org.lynxsdk.lynx:lynx-service-image:3.4.1"
// image-service dependencies, if not added, images cannot be loaded; if the host APP needs to use other image libraries, you can customize the image-service and remove this dependency
implementation "com.facebook.fresco:fresco:2.3.0"
implementation "com.facebook.fresco:animated-gif:2.3.0"
implementation "com.facebook.fresco:animated-webp:2.3.0"
implementation "com.facebook.fresco:webpsupport:2.3.0"
implementation "com.facebook.fresco:animated-base:2.3.0"
implementation "com.squareup.okhttp3:okhttp:4.9.0"
// integrating log-service
implementation "org.lynxsdk.lynx:lynx-service-log:3.4.1"
// integrating http-service
implementation "org.lynxsdk.lynx:lynx-service-http:3.4.1"
// integrating XElement
implementation "org.lynxsdk.lynx:xelement:3.4.1"
implementation "org.lynxsdk.lynx:xelement:3.4.1"
}
XElement requires additional importation in the LynxViewBuilder
:
import android.app.Activity;
import android.os.Bundle;
import com.lynx.tasm.LynxView;
import com.lynx.tasm.LynxViewBuilder;
import com.lynx.xelement.XElementBehaviors;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LynxView lynxView = buildLynxView();
setContentView(lynxView);
}
private LynxView buildLynxView() {
LynxViewBuilder viewBuilder = new LynxViewBuilder();
viewBuilder.addBehaviors(new XElementBehaviors().create());
viewBuilder.setTemplateProvider(new DemoTemplateProvider(this));
return viewBuilder.build(this);
}
}
After completing the above steps, all the work of initializing LynxView have been completed. Call the lynxView.renderTemplateUrl
method to render the corresponding Bundle onto the LynxView view.
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LynxView lynxView = buildLynxView();
setContentView(lynxView);
String url = "main.lynx.bundle";
lynxView.renderTemplateUrl(url, "");
}
}
Then you will see the following interface on the screen:
Congratulations, you have now completed all the work of rendering the LynxView!
At this stage, you have successfully integrated Lynx into your App. Refer to our developing and debugging docs for in-depth insights on working with Lynx.
The core capabilities of Lynx Engine include basic capabilities such as parsing Bundle, style parsing, layout, rendering views and the basic code of the javascript runtime that Lynx pages rely on
"dependencies": {
"@ohos/imageknife": "3.2.6",
"@lynx/lynx": "3.4.0",
"@lynx/primjs": "2.14.0",
},
Lynx Service includes LynxDevtoolService
, LynxLogService
, etc. It aims to provide the ability to strongly correlate some host App features, allowing the App to inject custom Services at runtime, or use the default implementation provided by the official. For example, LynxHttpService
is implemented using the built-in HTTP module of HarmonyOS by default. Lynx provides standard native Log and HTTP service capabilities, which can be quickly accessed and used by the access party;
"dependencies": {
"@ohos/imageknife": "3.2.6",
"@lynx/lynx": "3.4.1",
"@lynx/lynx_devtool": "3.4.1",
"@lynx/lynx_devtool_service": "3.4.1",
"@lynx/lynx_http_service": "3.4.1",
"@lynx/lynx_log_service": "3.4.1",
"@lynx/primjs": "2.14.1",
},
To import libc++_shared.so
, you need to configure Native C++. This requires defining a CMakeLists.txt
file.
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.5.0)
project(MyApplication)
And modify the buildOptions
in entry/build-profile.json5
:
{
buildOption: {
externalNativeOptions: {
path: './src/main/cpp/CMakeLists.txt',
arguments: '',
cppFlags: '',
},
},
}
If you need to request network resources, configure requestPermissions
in module.json5
to enable network requests.
{
module: {
requestPermissions: [
{
name: 'ohos.permission.INTERNET',
reason: '$string:network',
usedScene: {
abilities: ['FormAbility'],
when: 'inuse',
},
},
],
},
}
And configure the network
key field in entry/src/main/resources/base/element/string.json
:
{
"string": [
{
"name": "network",
"value": "Request network"
}
]
}
UIAbility#onCreate
lifecycle of the application.import { LLog, LynxServiceCenter, LynxEnv, LynxServiceType } from '@lynx/lynx';
import { LynxDevToolService } from '@lynx/lynx_devtool_service';
import { LynxLogService } from '@lynx/lynx_log_service';
import { LynxHttpService } from '@lynx/lynx_http_service';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// Init LynxDevtoolService
LynxServiceCenter.registerService(
LynxServiceType.DevTool,
LynxDevToolService.instance,
);
// Init LynxHttpService
LynxServiceCenter.registerService(
LynxServiceType.Http,
LynxHttpService.instance,
);
// Init LynxLogService
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
LynxServiceCenter.registerService(LynxServiceType.Log, LynxLogService.instance);
}
}
LynxEnv
provides the global initialization interface for the Lynx Engine
. Please ensure that the initialization of LynxEnv
occurs before any interface calls to the Lynx Engine
. It is recommended to complete the initialization of LynxEnv during the UIAbility#onCreate
lifecycle of the application.
import { LLog, LynxEnv } from '@lynx/lynx';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// Init LynxService
// ...
// Init LynxEnv
LLog.useSysLog(true);
LynxEnv.initialize(this.context);
let options = new Map<string, string>();
options.set('App', 'LynxExplorer');
options.set('AppVersion', '0.0.1');
LynxEnv.setAppInfo(options);
LynxEnv.enableDevtool(true);
}
}
Lynx Engine itself does not have the ability to integrate downloading resources, so the existing app needs to provide the specific implementation of LynxResourceProvider
, and inject it when constructing LynxView. Lynx will use the injected resource loader to obtain the Bundle content
You can use various methods to obtain the contents of the Bundle. Here, we choose to embed the contents of the Bundle within the application.
First, please place the Bundle file generated in the Quick Start stage in the src/main/resources/rawfile directory, or you can download the file below to your local machine and place it in the same directory:
entry
└── src
└── main
└── resources
└── rawfile
└── main.lynx.bundle
LynxTemplateResourceFetcher
provides the capability to load Bundle template resources. You need to implement the fetchTemplate
method to handle the loading.
import { LLog, LynxResourceRequest, LynxTemplateResourceFetcher, TemplateProviderResult } from '@lynx/lynx';
import { AsyncCallback, BusinessError } from '@ohos.base';
import http from '@ohos.net.http';
import resourceManager from '@ohos.resourceManager';
export class ExampleTemplateResourceFetcher extends LynxTemplateResourceFetcher {
fetchTemplate(request: LynxResourceRequest,
callback: AsyncCallback<TemplateProviderResult, void>) {
if (request.url.startsWith('http')) {
let httpRequest = http.createHttp();
httpRequest.request(
request.url, {
expectDataType: http.HttpDataType.ARRAY_BUFFER,
}, (err: BusinessError, data: http.HttpResponse) => {
callback(err, {
binary: data?.result as ArrayBuffer
});
httpRequest.destroy();
});
} else {
// local file
const context: Context = getContext(this);
const resourceMgr: resourceManager.ResourceManager = context.resourceManager;
resourceMgr.getRawFileContent(request.url, (err: BusinessError, data: Uint8Array) => {
callback(err, {
binary: data?.buffer as ArrayBuffer
})
});
}
}
fetchSSRData(request: LynxResourceRequest, callback: AsyncCallback<ArrayBuffer, void>) {
let httpRequest = http.createHttp();
httpRequest.request(request.url, {
expectDataType: http.HttpDataType.ARRAY_BUFFER
}, (err: BusinessError, data: http.HttpResponse) => {
callback(err, data?.result as ArrayBuffer)
httpRequest.destroy();
})
}
}
LynxMediaResourceFetcher
provides the capability to load media resources.
import { LynxMediaResourceFetcher, LynxResourceRequest, LynxOptionalBool } from '@lynx/lynx';
export class ExampleMediaResourceFetcher extends LynxMediaResourceFetcher {
shouldRedirectUrl(request: LynxResourceRequest): string {
// just return the input url;
return request.url;
}
isLocalResource(url: string): LynxOptionalBool {
return LynxOptionalBool.UNDEFINED;
}
}
LynxGenericResourceFetcher
provides the capability to load generic resources. You need to implement the fetchResource
method to handle the loading.
import { LynxError, LynxSubErrorCode, LynxGenericResourceFetcher, LynxResourceRequest, LynxResourceType, LynxStreamDelegate } from '@lynx/lynx';
import { AsyncCallback, BusinessError } from '@ohos.base';
import http from '@ohos.net.http';
import { ImageKnife, ImageKnifeOption, CacheStrategy } from '@ohos/imageknife';
export class ExampleGenericResourceFetcher extends LynxGenericResourceFetcher {
fetchResource(request: LynxResourceRequest, callback: AsyncCallback<ArrayBuffer, void>): void {
let httpRequest = http.createHttp();
httpRequest.request(request.url, {
expectDataType: http.HttpDataType.ARRAY_BUFFER
}, (err: BusinessError, data: http.HttpResponse) => {
callback(err, data?.result as ArrayBuffer)
httpRequest.destroy();
})
}
fetchResourcePath(request: LynxResourceRequest, callback: AsyncCallback<string, void>): void {
if (request.type === LynxResourceType.LYNX_RESOURCE_TYPE_IMAGE) {
let option = new ImageKnifeOption();
option.loadSrc = request.url;
option.writeCacheStrategy = CacheStrategy.File;
let error: BusinessError | undefined = undefined;
ImageKnife.getInstance().preLoadCache(option).then((data: string) => {
if (data.length > 0) {
callback(error, data);
} else {
error = {
code: LynxSubErrorCode.E_RESOURCE_IMAGE_PIC_SOURCE,
message: 'Image path is invalid',
name: 'Image Error',
}
callback(error, '');
}
}).catch((e: string) => {
error = {
code: LynxSubErrorCode.E_RESOURCE_IMAGE_FROM_NETWORK_OR_OTHERS,
message: e,
name: 'Image Error',
}
callback(error, '');
})
} else {
callback({
code: LynxError.LYNX_ERROR_CODE_RESOURCE,
message: 'unsupported type: ' + request.type,
name: 'Resource Error',
}, '');
}
}
fetchStream(request: LynxResourceRequest, delegate: LynxStreamDelegate): void {
// TODO(Lynx): support fetching stream.
delegate.onStart(100);
let a = new ArrayBuffer(10);
delegate.onData(a, 0, 10);
delegate.onEnd();
}
cancel(request: LynxResourceRequest): void {
// TODO(Lynx)
}
}
Once you have completed the steps above, you have finished all the necessary work for creating the LynxView
and reading its resources. You can now render the corresponding Bundle content onto the LynxView
.
import {
LynxTemplateResourceFetcher,
LynxMediaResourceFetcher,
LynxGenericResourceFetcher,
LynxView,
} from '@lynx/lynx';
import { ExampleTemplateResourceFetcher } from '../provider/ExampleTemplateResourceFetcher';
import { ExampleMediaResourceFetcher } from '../provider/ExampleMediaResourceFetcher';
import { ExampleGenericResourceFetcher } from '../provider/ExampleGenericResourceFetcher';
@Entry
@Component
struct Index {
templateResourceFetcher: LynxTemplateResourceFetcher = new ExampleTemplateResourceFetcher();
mediaResourceFetcher: LynxMediaResourceFetcher = new ExampleMediaResourceFetcher();
genericResourceFetcher: LynxGenericResourceFetcher = new ExampleGenericResourceFetcher();
private url: string = 'your bundle file';
build() {
Column() {
LynxView({
templateResourceFetcher: this.templateResourceFetcher,
mediaResourceFetcher: this.mediaResourceFetcher,
genericResourceFetcher: this.genericResourceFetcher,
url: this.url,
}).width('100%').height('100%');
}
.size({ width: '100%', height: '100%' })
}
}
You will then see the following content on your screen:
Congratulations! You have now completed the full integration of the Lynx Engine.
Now that you have integrated Lynx into your application, please refer to the Development and Debugging documentation to further explore the world of Lynx!
Lynx for Web implements the Lynx engine in web browsers. With Lynx for Web, you can easily integrate Lynx apps into any existing web project, regardless of whether the project uses React, Vue, Svelte, or plain HTML.
We need you to have read and created a Lynx project according to Quick Start.
cd <lynx-project-name>
environments.web
) to lynx.config.ts
:import { defineConfig } from '@lynx-js/rspeedy';
import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';
export default defineConfig({
plugins: [pluginReactLynx()],
environments: {
web: {
output: {
assetPrefix: '/',
},
},
lynx: {},
},
});
Run:
npm run build
You will see an additional dist/main.web.bundle
file in this project, which is the final web build artifact.
Now that you have a Lynx for Web build artifact, we need to create a web project to use it. Here we use Rsbuild.
Create a new project at the same level as the Lynx project above and run:
npm create rsbuild@latest
Follow the prompts to create a React project.
cd <web-project-name>
npm install @lynx-js/web-core @lynx-js/web-elements
src/app.tsx
import './App.css';
import '@lynx-js/web-core/index.css';
import '@lynx-js/web-elements/index.css';
import '@lynx-js/web-core';
import '@lynx-js/web-elements/all';
const App = () => {
return (
<lynx-view
style={{ height: '100vh', width: '100vw' }}
url="/main.web.bundle"
></lynx-view>
);
};
export default App;
rsbuild.config.ts
server.publicDir
needs to be replaced with your actual Lynx project path.
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export default defineConfig({
plugins: [pluginReact()],
server: {
publicDir: [
{
name: path.join(
__dirname,
'../',
// Please replace this with your actual Lynx project name
'lynx-project',
'dist',
),
},
],
},
});
Run:
npm run dev
Visit http://localhost:3000
to see your Lynx application.
After completing the above integration, you can achieve more flexible interaction control through the APIs provided by Lynx for Web. Here is a detailed description of the core APIs:
Name | Required | Description |
---|---|---|
url | Yes | URL of the Rspeedy output (URLs of other chunks will be automatically injected and launched during compilation) |
globalProps | No | GlobalProps for card initialization |
initData | No | InitData for card initialization |
overrideLynxTagToHTMLTagMap | No | Custom mapping relationship from Lynx tags to HTML tags. React Components are not supported, only HTMLElements (can be web components or native tags) |
Custom NativeModule where key is the module name and value is the module implementation (an ESM URL):
type NativeModulesMap = Record<string, string>;
Example:
const nativeModulesMap = {
CustomModule: URL.createObjectURL(
new Blob(
[
`export default function(NativeModules, NativeModulesCall) {
return {
async getColor(data, callback) {
const color = await NativeModulesCall('getColor', data);
callback(color);
},
}
};`,
],
{ type: 'text/javascript' },
),
),
};
lynxView.nativeModulesMap = nativeModulesMap;
Entry point for handling NativeModules (JSB, etc.) related calls:
(name: string, data: any, moduleName: string) => Promise<any> | any;
Example:
// Handle NativeModule.bridge.call('request')
lynxView.onNativeModulesCall = (name, data, moduleName) => {
if (moduleName === 'bridge') {
if (name === 'request') {
// ...
// return data will be automatically processed as callback data
return {};
}
}
};
Allows users to implement custom template loading functionality (default is fetch):
lynxView.customTemplateLoader = (url) => {
return await(
await fetch(url, {
method: 'GET',
}),
).json();
};
Error message notification:
type LynxError = CustomEvent<{
error: Error;
sourceMap: {
offset: {
// Line offset
line: number;
// Column offset
col: number;
};
};
release: string;
fileName: 'lepus.js' | 'app-service.js';
}>;
lynxView.addEventListener('error', (err: LynxError) => {
// ...
});
export type Cloneable<T = string | number | null | boolean | undefined> =
| T
| Record<string, T>
| T[];
updateData(
data: Cloneable,
updateDataType: UpdateDataType,
callback?: () => void,
): void
updateGlobalProps(data: Cloneable): void;
sendGlobalEvent(eventName: string, params: Cloneable[]): void;
The internal layout of lynx-view will be forced out of the external layout flow
We will force all lynx-view elements to apply CSS Containment.
That is, by default, you need to set a width and height for lynx-view. The width and height can be allocated by flex-grow
or specified as a percentage, but cannot be "stretched". Setting width and height is a strongly recommended practice and also a best practice for performance.
In some cases where you really need the width or height to be determined by the content of lynx-view, you can set height="auto"
or width="auto"
to enable the automatic width/height listener. In this case, the internal layout of lynx-view remains independent of the external layout flow.
The recommended configuration is: Chrome > 118, Safari > 18, Firefox NSR
If you want to support browsers with Chrome < 118 and Safari < 18, you need to do the following:
import '@lynx-js/web-elements-compat/LinearContainer';
@lynx-js
dependencies. If your project uses Rsbuild, modify the configuration as follows:// rsbuild.config.ts
export default {
source: {
include: [/@lynx-js/],
},
output: {
polyfill: 'usage',
},
};
Uncaught SecurityError: Failed to construct 'Worker': Script at 'xxx' cannot be accessed from origin 'xxx'.
This is because Worker loading remote scripts needs to comply with the Same-Origin Policy, and the JS resources of the project are generally deployed on CDN, causing cross-origin issues.
This can be solved by introducing remote-web-worker:
// The import position must be before @lynx-js/web-core
import 'remote-web-worker';
import '@lynx-js/web-core';
import '@lynx-js/web-core/index.css';
import '@lynx-js/web-elements/all';
import '@lynx-js/web-elements/index.css';
document.body.innerHTML = `
<lynx-view
style="height:100vh; width:100vw;"
url="http://localhost:3000/main/index.main.bundle"
>
</lynx-view>`;
We provide an RSBuild plugin for performance optimization. You can introduce this plugin in your web project.