Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/image data key #3441

Merged
merged 9 commits into from
Aug 17, 2023
6 changes: 4 additions & 2 deletions docs/development/native-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@

- 配置 build.gradle

下面引用Hippy最新版本号可在上述链接中查询

```java
// implementation 'com.tencent.hippy:hippy-debug:1.0.0'
implementation 'com.tencent.hippy:hippy-release:1.0.0'
// implementation 'com.tencent.hippy:hippy-debug:3.0.1'
implementation 'com.tencent.hippy:hippy-release:3.0.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.viewpager:viewpager:1.0.0'
Expand Down
14 changes: 11 additions & 3 deletions docs/feature/feature3.0/render-node-snapshot.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,25 @@
public void recordSnapshot(@NonNull View rootView, @NonNull final Callback<byte[]> callback)
```

记录指定root view的节点缓存,SDK内部会完成节点遍历,序列化相关的任务,最终通过Callback返回节点序列化后的buffer,节点数据存储,生命周期管理由宿主自行完成,由于序列化节点数据有一定耗时,为了防止卡UI线程,序列化数据的操作是分发到子线程异步执行的,最终也在子线程callback buffer
记录指定root view的节点缓存,SDK内部会完成节点遍历,序列化相关的任务,最终通过Callback返回节点序列化后的buffer,节点数据存储,生命周期管理由宿主自行完成

```java
public View replaySnapshot(@NonNull Context context, byte[] buffer)
```

传入之前record的buffer回放节点数据,由于render node和view的操作都必须在UI线程完成,所有这个接口调用将同步返回还原的view
传入之前record的buffer回放节点数据,这个接口调用将同步返回还原的view

```java
public View replaySnapshot(@NonNull Context context, @NonNull Map<String, Object> snapshotMap)
```

传入decode后的节点Map数据,同步返回还原的view,必须在UI线程调用,由于节点数据decode有一定耗时,为了最大程度减少UI线程的占用,这个接口用于在子线程提前decode buffer后调用
传入decode后的节点Map数据,同步返回还原的view

```java
public View removeSnapshotView()
```

从view tree中移除snapshot view及其子view,同时删除所有对应的render node节点,调用该接口的时机需要根据不同的业务场景做调整,保证真正页面完整显示后再移除snapshot view,不会产生页面的跳变视觉体验

### NativeRenderer Public static methods

Expand All @@ -48,6 +54,8 @@ public static Map<String, Object> decodeSnapshot(@NonNull byte[] buffer)

NativeRenderer静态方法,对节点数据进行decode操作,支持子线程异步调用,返回的Map可以直接用作 replaySnapshot接口的输入参数

> 注意:replaySnapshot接口调用必须在Hippy engine初始化完成以后,可以在引擎回调的子线程中执行,也可以在UI主线程执行,但必须在replay调用结束后再调用loadModule,否则有可能会有多线程的问题,在子线程执行好处就是不卡UI线程,但需要post到UI线程挂载到父容器,中间还是会有短暂白屏过程,在主线程直接replay并挂载到父容器,虽然会卡UI,但可以保证页面切换无白屏效果

## Performance

在中低端机型华为M10上对比demo首屏还原耗时:3.0 replay耗时47.8ms,2.0 relplay耗时146.9ms,优化后耗时下降67.5%
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ public View replaySnapshot(@NonNull Context context, @NonNull Map<String, Object
return (mRenderer != null) ? mRenderer.replaySnapshot(context, snapshotMap) : null;
}

@Override
public void removeSnapshotView() {
if (mRenderer != null) {
mRenderer.removeSnapshotView();
}
}

@Override
public void setFrameworkProxy(@NonNull Object proxy) {
if (mRenderer != null && proxy instanceof FrameworkProxy) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public interface RenderConnector extends Connector {

View replaySnapshot(@NonNull Context context, @NonNull Map<String, Object> snapshotMap);

void removeSnapshotView();

void setFrameworkProxy(@NonNull Object proxy);

View createRootView(@NonNull Context context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ public int getEngineId() {

public abstract View replaySnapshot(@NonNull Context context, @NonNull Map<String, Object> snapshotMap);

public abstract void removeSnapshotView();

public interface BackPressHandler {

void handleBackPress();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,12 @@ public View replaySnapshot(@NonNull Context context, @NonNull Map<String, Object
return null;
}

public void removeSnapshotView() {
if (mEngineContext != null) {
mEngineContext.getRenderer().removeSnapshotView();
}
}

public void addControllers(@NonNull List<HippyAPIProvider> providers) {
if (mEngineContext != null) {
List<Class<?>> controllers = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,26 @@ class HippyEngineHelper {
private val hippyEngineList: MutableList<HippyEngineWrapper> = mutableListOf()
private val abandonHippyEngineList: MutableList<HippyEngineWrapper> = mutableListOf()


fun createHippyEngine(
driverType: PageConfiguration.DriverMode,
rendererType: PageConfiguration.RenderMode,
isDebugMode: Boolean,
isSnapshotMode: Boolean,
debugServerHost: String
): HippyEngineWrapper {
val hippyEngineWrapper = HippyEngineWrapper(driverType, rendererType, isDebugMode, debugServerHost)
val hippyEngineWrapper = HippyEngineWrapper(
driverType,
rendererType,
isDebugMode,
isSnapshotMode,
debugServerHost
)
hippyEngineList.add(hippyEngineWrapper)
return hippyEngineWrapper
}

fun getHippyEngineList() : MutableList<HippyEngineWrapper> {
fun getHippyEngineList(): MutableList<HippyEngineWrapper> {
return hippyEngineList
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package com.openhippy.example
import android.app.Activity
import android.content.Context
import android.graphics.Bitmap
import android.os.Handler
import android.os.Looper
import android.view.View
import android.view.ViewGroup
import android.view.ViewParent
Expand All @@ -32,25 +34,32 @@ import com.tencent.mtt.hippy.common.HippyMap
import com.tencent.mtt.hippy.utils.LogUtils
import com.tencent.mtt.hippy.utils.UIThreadUtils

class HippyEngineWrapper {
class HippyEngineWrapper//TODO: Coming soon
(
dm: PageConfiguration.DriverMode,
rm: PageConfiguration.RenderMode,
isDebug: Boolean,
useNodeSnapshot: Boolean,
debugServerHost: String
) {

var hippyEngine: HippyEngine
var hippyRootView: ViewGroup? = null
var hippySnapshotView: ViewGroup? = null
var devButton: View? = null
var snapshot: Bitmap? = null
var pageItem: View? = null
var isDebugMode: Boolean = false
val driverMode: PageConfiguration.DriverMode
val renderMode: PageConfiguration.RenderMode
var isDebugMode: Boolean = isDebug
var isSnapshotMode: Boolean = useNodeSnapshot
val driverMode: PageConfiguration.DriverMode = dm
val renderMode: PageConfiguration.RenderMode = rm
val engineId: Int

constructor(dm: PageConfiguration.DriverMode,
rm: PageConfiguration.RenderMode,
isDebug: Boolean,
debugServerHost: String) {
driverMode = dm
renderMode = rm
isDebugMode = isDebug
companion object {
val renderNodeSnapshot: HashMap<PageConfiguration.DriverMode, ByteArray> = HashMap()
}

init {
val initParams = EngineInitParams()
initParams.context = applicationContext
initParams.debugServerHost = debugServerHost
Expand Down Expand Up @@ -123,6 +132,18 @@ class HippyEngineWrapper {
hippyEngine.destroyEngine()
}
hippyRootView = null
hippySnapshotView = null
}

fun recordRenderNodeSnapshot() {
hippyEngine.recordSnapshot(hippyRootView as View) {
buffer, e ->
run {
buffer?.let {
renderNodeSnapshot[driverMode] = buffer
}
}
}
}

fun load(context: Context, callback: HippyEngineLoadCallback) {
Expand Down Expand Up @@ -154,6 +175,16 @@ class HippyEngineWrapper {
"msgFromNative",
"Hi js developer, I come from native code!"
)
var snapshotView: View? = null
if (!isDebugMode && isSnapshotMode) {
var buffer = renderNodeSnapshot[driverMode]
buffer?.let {
snapshotView = hippyEngine.replaySnapshot(context, it)
}
snapshotView?.let {
hippySnapshotView = snapshotView as ViewGroup
}
}
hippyRootView = hippyEngine.loadModule(loadParams, object : ModuleListener {
override fun onLoadCompleted(statusCode: ModuleLoadStatus, msg: String?) {
callback.onLoadModuleCompleted(statusCode, msg)
Expand All @@ -164,14 +195,26 @@ class HippyEngineWrapper {
}

override fun onFirstViewAdded() {
LogUtils.e("MyActivity", "onFirstViewAdded")
snapshotView?.let {
val handler = Handler(Looper.getMainLooper())
handler.postDelayed({
hippyEngine.removeSnapshotView()
}, 400)
}
}
})
if (UIThreadUtils.isOnUiThread()) {

var loadCallbackTask = Runnable {
callback.onCreateRootView(hippyRootView)
snapshotView?.let {
callback.onReplaySnapshotViewCompleted(snapshotView as ViewGroup)
}
}
if (UIThreadUtils.isOnUiThread()) {
loadCallbackTask.run()
} else {
UIThreadUtils.runOnUiThread {
callback.onCreateRootView(hippyRootView)
loadCallbackTask.run()
}
}
}
Expand All @@ -182,6 +225,7 @@ class HippyEngineWrapper {
interface HippyEngineLoadCallback {
fun onInitEngineCompleted(statusCode: EngineInitStatus, msg: String?)
fun onCreateRootView(hippyRootView: ViewGroup?)
fun onReplaySnapshotViewCompleted(snapshotView: ViewGroup)
fun onLoadModuleCompleted(statusCode: ModuleLoadStatus, msg: String?)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class PageConfiguration : AppCompatActivity(), View.OnClickListener {
private var hasRunOnCreate = false
private var hippyEngineWrapper: HippyEngineWrapper? = null
private var debugMode: Boolean = false
private var snapshotMode: Boolean = false
private var debugServerHost: String = "localhost:38989"
private var dialog: Dialog? = null
private var driverMode: DriverMode = DriverMode.JS_REACT
Expand All @@ -71,7 +72,8 @@ class PageConfiguration : AppCompatActivity(), View.OnClickListener {
pageConfigurationRoot = layoutInflater.inflate(R.layout.activity_page_configuration, null)
pageConfigurationContainer =
pageConfigurationRoot.findViewById(R.id.page_configuration_container)
pageConfigurationSetting = pageConfigurationRoot.findViewById(R.id.page_configuration_setting)
pageConfigurationSetting =
pageConfigurationRoot.findViewById(R.id.page_configuration_setting)
pageConfigurationTitle =
pageConfigurationRoot.findViewById(R.id.page_configuration_navigation_title)

Expand Down Expand Up @@ -121,9 +123,11 @@ class PageConfiguration : AppCompatActivity(), View.OnClickListener {
}

override fun onBackPressed() {
val goBack: () -> Unit = { buildSnapshot {
moveTaskToBack(true)
} }
val goBack: () -> Unit = {
buildSnapshot {
moveTaskToBack(true)
}
}
hippyEngineWrapper?.apply {
if (hippyEngine.onBackPressed(goBack)) {
return
Expand All @@ -137,6 +141,11 @@ class PageConfiguration : AppCompatActivity(), View.OnClickListener {
if (rootView == null || currentEngineId == -1) {
runnable.run()
} else {
hippyEngineWrapper?.let {
if (!it.isDebugMode) {
hippyEngineWrapper?.recordRenderNodeSnapshot()
}
}
generateBitmapFromView(rootView, object : SnapshotBuildCallback {
override fun onSnapshotReady(bitmap: Bitmap?) {
hippyEngineWrapper?.snapshot = bitmap
Expand All @@ -153,34 +162,43 @@ class PageConfiguration : AppCompatActivity(), View.OnClickListener {
pageConfigurationRoot.findViewById<View>(R.id.page_configuration_driver_setting)
driverSettingText =
pageConfigurationRoot.findViewById(R.id.page_configuration_driver_setting_title)
driverSettingButton.setOnClickListener { v ->
driverSettingButton.setOnClickListener {
onDriverSettingClick()
}

val rendererSettingButton =
pageConfigurationRoot.findViewById<View>(R.id.page_configuration_renderer_setting)
rendererSettingText =
pageConfigurationRoot.findViewById(R.id.page_configuration_renderer_setting_title)
rendererSettingButton.setOnClickListener { v ->
rendererSettingButton.setOnClickListener {
onRendererSettingClick()
}

val debugButton =
pageConfigurationRoot.findViewById<View>(R.id.page_configuration_debug_setting_image)
val debugServerHost =
pageConfigurationRoot.findViewById<View>(R.id.page_configuration_debug_server_host)
debugButton.setOnClickListener { v ->
debugButton.setOnClickListener {
if (debugMode) {
(debugButton as ImageView).setImageResource(R.drawable.page_config_debug_off_2x)
debugMode = false
debugServerHost.visibility = View.GONE
debugMode = false
} else {
(debugButton as ImageView).setImageResource(R.drawable.page_config_debug_on_2x)
debugMode = true
debugServerHost.visibility = View.VISIBLE
debugMode = true
}
}
val snapshotButton =
pageConfigurationRoot.findViewById<View>(R.id.page_configuration_snapshot_setting_image)
snapshotButton.setOnClickListener {
snapshotMode = if (snapshotMode) {
(snapshotButton as ImageView).setImageResource(R.drawable.page_config_debug_off_2x)
false
} else {
(snapshotButton as ImageView).setImageResource(R.drawable.page_config_debug_on_2x)
true
}
}

val createButton =
pageConfigurationRoot.findViewById<View>(R.id.page_configuration_create_image)
createButton.setOnClickListener { v ->
Expand Down Expand Up @@ -240,6 +258,7 @@ class PageConfiguration : AppCompatActivity(), View.OnClickListener {
driverMode,
renderMode,
debugMode,
snapshotMode,
debugServerHost
)
hippyEngineWrapper?.let {
Expand All @@ -256,10 +275,13 @@ class PageConfiguration : AppCompatActivity(), View.OnClickListener {
override fun onCreateRootView(hippyRootView: ViewGroup?) {
hippyRootView?.let {
(pageConfigurationContainer as ViewGroup).addView(hippyRootView)
hippyEngineWrapper?.hippyRootView = hippyRootView
}
}

override fun onReplaySnapshotViewCompleted(snapshotView: ViewGroup) {
(pageConfigurationContainer as ViewGroup).addView(snapshotView)
}

override fun onLoadModuleCompleted(
statusCode: HippyEngine.ModuleLoadStatus,
msg: String?
Expand Down Expand Up @@ -338,7 +360,12 @@ class PageConfiguration : AppCompatActivity(), View.OnClickListener {
R.id.page_configuration_driver_vl -> {
val text = resources.getText(R.string.setting_not_available)
val span = SpannableString(text)
span.setSpan(ForegroundColorSpan(Color.BLACK), 0, text.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
span.setSpan(
ForegroundColorSpan(Color.BLACK),
0,
text.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
val toast: Toast =
Toast.makeText(
this,
Expand Down
Loading
Loading