You need to enable JavaScript to run this app.
导航

Hybrid 同层渲染(Beta)

最近更新时间2024.02.23 17:31:47

首次发布时间2023.09.20 16:23:44

Hybrid 同层渲染能力处于 Beta 内测阶段,如有需求请 提交工单 联系技术支持申请试用并获取帮助信息。

Hybrid 同层渲染(Hybrid Same-Layer Rendering)是指在图形渲染中将基于 CPU 的软件渲染及基于 GPU 的硬件渲染结合在一起,以实现更高效的渲染和呈现效果。请参考以下内容先接入双端 SDK 后开启端上 hybrid 指定加载页面地址,再接入 WEB 端并指定具体加载图片的处理配置。

应用场景

适用于客户端 App 中 Hybrid 页面(以 Hybrid 技术开发的页面)加载图片。

功能优势

  • 支持 HEIF、WEBP 等高级格式的图片加载及显示,可节省图片传输流量和加载耗时,提升用户体验。

  • 支持监控各种场景下图片元素的加载情况,通过上报图片加载数据,助力您分析图片加载耗时、成功率、分辨率等数据。

环境要求

平台版本限制
Android 端系统版本:Android 8 及以上版本

iOS 端

  • 开发版本:Xcode 11 及以上版本(推荐使用最新版本)
  • 系统版本:iOS 9.0 及以上版本
Web 端系统版本:React 16 及以上版本

接入双端同层渲染

您可在根据实际情况开启 Android 或 iOS 同层渲染后,使用 Web 端加载能力。

开通插件

  1. 登录 veImageX 控制台

  2. 单击左侧导航栏 SDK管理 > 应用管理,进入应用管理页面。

  3. 选择一个 App 类应用,单击卡片进入应用详情页面。

  4. 开启 Android Hybrid-HEIF TTwebview 插件 的开关。

    说明

    • 开通后将为此 AppID 创建 1 个域名,用于该 App 下发插件。插件包含 Hybrid-HEIF 安卓端 TTwebview 插件,插件总大小约为 30 MB。请注意,专用插件下发域名的流量/带宽费用需要由您自行承担。开通后无法关闭。
    • 域名格式:{AppID}.plugin.volcimagex.com

添加 maven 仓库

确保 project 根目录下的 build.gradle 下配置服务,代码示例如下所示:

maven {
    url 'https://artifact.bytedance.com/repository/Volcengine/'
}

添加 SDK 依赖

请在 module 目录下的 build.gradle 文件中的 dependencies 中添加 Hybrid 加载 SDK 依赖,X.X.X 为技术支持提供的具体版本号。代码示例如下所示:

implementation "com.bytedance.fresco:pia-image:X.X.X"    // 用于加载和显示 Pia 格式的图像
implementation "com.bytedance.fresco:ttweb-wrapper:X.X.X"   // 用于在应用程序中加载和展示 TTWebView
implementation "com.bytedance.fresco:fresco:X.X.X"  // Fresco 图片加载库,提供了基本的图像加载和显示功能
implementation "com.bytedance.fresco:fresco-api:X.X.X"  // Fresco API
implementation "com.bytedance.fresco:animated-gif:X.X.X" // 用于支持加载和显示 GIF 格式的动态图像
implementation "com.bytedance.fresco:animated-webp:X.X.X" // 用于支持加载和显示 WebP 格式动态图像 
implementation "com.bytedance.fresco:webpsupport:X.X.X" // 提供对低版本 webp 的支持
implementation "com.bytedance.fresco:drawee:X.X.X" // fresco 组件,用于显示和管理图像的视图组件
implementation "com.bytedance.fresco:heif:X.X.X" // 用于解码和显示 HEIF 格式的静态图像
implementation "com.bytedance.fresco:animated-heif:X.X.X" // 用于解码和显示HEIF格式的动画图像
implementation "com.bytedance.fresco:authorization:X.X.X"  // 授权

初始化 SDK

请参考以下内容完成 SDK 的初始化。

public class BDFrescoApplication extends Application {
    
    @Override
    public void onCreate() {
        super.onCreate();
        // TTWebView Render进程和GPU进程不走端上初始化流程
        // 使用步骤 2:TTWebView 子进程初始化
        if (TTWebSdk.needInitIndependent(this)) {
            initALog(this);
            TTWebWrapper.initTTWebViewProcess(this);
            return;
        }

        // 日志打印
        FLog.setMinimumLoggingLevel(FLog.VERBOSE);
        Logger.INSTANCE.setEnableDefaultLog(true);
        initDataReport(this);

        // 使用步骤 3:主进程初始化
        initALog(this);
        // HostAbi 需要与 abiFilters 的值相对应:
        // abiFilters 是 "armeabi-v7a",则传 "32"
        // abiFilters 是 "arm64-v8a",则传 "64"
        CloudControl.setHostAbi(BuildConfig.HOSTABI);
        TTWebWrapper.initAppProcess(this);

        // 初始化 applog 和 fresco,参考帮助文档:https://www.volcengine.com/docs/508/176049#%E5%88%9D%E5%A7%8B%E5%8C%96
        initAppLog();
        initFresco();
        initMonitor();

        // 使用步骤 4:其他必要调用,可放入子线程,优化启动时间
        new Thread(() -> {
            // 同层渲染埋点
            TTWebWrapper.initEmbedLog();
            // 配置 settings
            TTWebWrapper.setTTWebSettings();
            // 同层渲染初始化
            TTWebMixRender.INSTANCE.initialize();
        }).start();
}

    @Override
    public Context getApplicationContext() {
        // 使用步骤 1:返回 Context
        return this;
    }
    
    
    private void initALog(final Context context) {
        TTWebDataCenter.registerLogexCallback(context, new LogExCallback() {
            @Override
            public void onLogExe(String tag, String msg) {
                Log.e(TAG, tag + " " + msg);
            }

            @Override
            public void onLogExi(String tag, String msg) {
                Log.i(TAG, tag + " " + msg);
            }

            @Override
            public void onLogExd(String tag, String msg) {
                Log.d(TAG, tag + " " + msg);
            }
        });
}

    private void initDataReport(Context context) {
        TTWebDataCenter.registerEventListener(context, new EventListener() {
            @Override
            public void onCommonEvent(int eventCode, JSONObject jsonValue, JSONObject exjs) {
                Log.i(TAG, "onCommonEvent " + eventCode + " " + jsonValue.toString());
                switch (eventCode) {
                    case 443: // 加载结果
                        Log.i(TAG,
                            "loadso_result:" + jsonValue.optString("sdk_loadso_result"));
                        break;
                    case 201: // 开始下载
                        Log.i(TAG, "start download");
                        break;
                    case 299: // 下载完成
                        Log.i(TAG, "finish download");
                        break;
                    case 399: //解压完成
                        Log.i("[onCommonEvent]", "finish decompress");
                        break;
                }
            }

            @Override
            public void onCrucialEvent(int eventCode, JSONObject jsonValue, JSONObject exjs) {
                Log.i(TAG, "onCrucialEvent " + eventCode + " " + jsonValue.toString());
                switch (eventCode) {
                    case 1100: // 加载内核版本号
                        Log.i(TAG, "loadso: " + jsonValue.optString("LoadSo"));
                        break;
                }
            }
        });
    }
}

开启 Hybrid 同层渲染

使用 WebView 加载网页的应用程序,代码示例如下所示:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
        WebView webView = findViewById(R.id.main_webview);

        // 使用步骤 5:允许同层渲染 2.0,添加自定义组件。
        TTWebMixRender.INSTANCE.enableForWebView(webView, Collections.singletonList(TTWebMixRender.EmbedRuntimeType));
        TTWebMixRender.INSTANCE.addComponent(BDImageView.class);

        webView.setWebViewClient(new WebViewClient());
        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            WebView.setWebContentsDebuggingEnabled(true);
        }

        // 使用步骤 6:调用 loadUrl 方法加载指定的网址到 WebView 中
        webView.loadUrl("您自己的应用程序地址");
    }
}

自定义插件

参考 BDImageView、BDImageViewFactory 代码自定义插件。

同层渲染并在一个 Web 页面滑动时,即使图片不可见,SimpleDraweeView 也不会自动释放,此时内存会累加。同一个页面中存在大量图片时,您可以在图片处于不可见位置时,从 Web 端将图片节点直接删除。

接入 Web 同层渲染

请确保已完成 Android/iOS SDK 接入后,再按照以下步骤接入 Web 同层渲染。

安装 SDK

运行该命令后,npm 将会下载 @volcengine/imagex-hybrid-react 包及其所有的依赖项,并将其记录在 package.json 文件中的 dependencies 字段中,以便在项目中使用该包。

npm install @volcengine/imagex-hybrid-react -S

集成 SDK

引入了 Viewer 组件后并在代码中使用 <Viewer> 标签来创建 Viewer 组件,并传递相应的参数。

import { Viewer } from "@volcengine/imagex-hybrid-react";

//具体参数说明请见功能接入
<Viewer
    width={680}
    height={376}
    formats={["heic", "avif", "webp"]}
    loader={({ src, format }) => `//www.example.com/${src}~tplv-serviceid-image.${format}`}
    src='xxx'
/>

功能接入

具体支持的参数如下所示:

属性名类型是否必填说明
srcstring图片路径,可访问的图片 URL。
widthnumber | string图片渲染宽度,值类型和 css width 一致
heightnumber | string图片渲染高度,值类型和 css height 一致

objectFit

"none" | "contain" | "cover" | "fill"

用于指定图片元素如何适应容器,与 css 属性 object-fit 相同。其中容器范围由上述 width、height 定义。
默认值为 'none'。

cornorRadius

string

圆角半径,支持以下两种类型:

  • 具体像素值,如:1px
  • 百分比值,如:10%,在 iOS 同层场景下,百分比会相对组件宽度计算

formats

string[]

自适应格式列表,结合 loader 方法实现格式自适应,在同层渲染场景下优先采用 heic 格式,不支持同层渲染时走格式自适应的方式。默认值为 ['heic', 'avif', 'webp']。取值如下所示:

  • heic
  • avif
  • webp

说明

指定 ['webp']时,表示仅支持 webp 格式自适应,并采用原图格式兜底;
若指定为 ['heic', 'avif', 'webp'] 则表示优先 heic 格式自适应,其次是 avif、webp 格式自适应,最后用原图格式兜底。

loader

(props: ImageLoaderProps) => string

图片 URL 生成函数。函数入参包含 domain, src, format 等参数,返回拼接处理参数后的 url。格式自适应依赖该函数实现。
格式自适应:结合当前环境支持性以及 formats 属性中指定的格式列表,选择最优的格式传递至函数的入参 format,函数返回相应格式的图片 url;
loader 配置示例如下所示:

import { ImageLoader } from '@volcengine/imagex-react';
// 域名/src~模板:模板参数:q质量参数.图片格式
const myLoader: ImageLoader = {({ src, format }) => `//www.example.com/${src}~tplv-serviceid-image.${format}`}

ssrConfig

ISSRConfig

SSR (服务端渲染)配置,该配置用于在 SSR 配置场景提升图片加载速度。参与了服务端渲染的图片需要配置该属性,使用方式详见 SSR 配置说明。具体配置参数如下所示:

  • isSSR:表示是否在服务器进行渲染。需要在服务端渲染的图片请指定为 true;否则指定为 false

  • hybridRender:表示是否使用混合渲染。需要在支持同层渲染的端内加载时请指定为 true;否则指定为 false

    说明

    混合渲染是指在服务器端渲染和客户端渲染之间进行切换的一种渲染策略。

  • platform:当前所处的渲染平台,支持取值为AndroidiOS

loader 入参类型如下所示:

type IImageLoaderProps = {
  src: string; // 图片访问 path 部分,不包含域名,如:imagex-common/59*****************
  format: string; // 图片格式
  extra: {
    domain?: string; // 从图片访问 url 解析出的域名
    protocol?: 'http:' | 'https:'; // 从图片访问 url 解析出的协议
    template?: string; // 从图片访问 url 解析出的 veImageX 模板
    suffix?: string; // 从图片访问 url 解析出的格式后缀
    search?: string; // 从图片访问 url 解析出的query部分
    origin: string; // 图片访问 url
  };
};

type ISSRConfig = {
  isSSR?: boolean;  //是否在服务器进行渲染
  hybridRender?: boolean; //是否使用混合渲染
  platform?: 'Android' | 'iOS';  //当前所处的渲染平台
};

SSR 配置

说明

在 CSR(客户端渲染)的场景中,您无需关注此配置。

以下仅适用于服务端渲染场景。在完成以下配置后,本组件将自动在服务端提前生成图片占位标识。获取并应用这些占位标识到客户端后,可以帮助尽可能提前图片渲染的时间,减少图片加载的延迟,从而提升图片加载速度。具体流程如下所示:

  1. 根据接入说明引入 Web 同层渲染组件后,请参考属性说明配置 ssrConfig 字段。

  2. 在服务端完成页面渲染之后(例如在执行 renderToString 后),请调用 Web SDK 提供的 exportInitScript 方法生成触发同层渲染逻辑的 JS 字符串。

  3. 将生成的 JS 字符串以 <script> 标签的形式插入到 HTML 模板中。

代码示例如下所示。

// 以下 demo 描述了 SSR 场景下的使用流程,请您根据业务实际情况进行配置
import { exportInitScript } from '@volcengine/imagex-hybrid-react';

app.get('/', (req, res) => {
  // 1. 业务组件渲染成初始 HTML
  let domContent = renderToString(<Home />);
  // 2. 导出用于初始化的 script 字符串
  const script = exportInitScript();
  // 3. 拼接生成完整的 HTML 结构
  let html = `
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
        <title>react-ssr</title>
        <script>${script}</script>
      </head>
      <body>
        <div id="root">${domContent}</div>
        <script src="/client.js"></script>
      </body>
    </html>
    `;
   // 4. 响应客户端请求
   res.send(html);
});