Skip to content

Commit 459bfc6

Browse files
committed
[BugFix][Android] Report image load events in legacy image path
- Add shared image event reporting helpers for Android image implementations. - Report memory usage, image origin, and load timing from the legacy image path. - Keep the full image implementation wrapper API compatible while reusing the shared helper.
1 parent 24d95a9 commit 459bfc6

6 files changed

Lines changed: 467 additions & 6 deletions

File tree

platform/android/api/lynx_android.api

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,8 @@ public class com::lynx::tasm::behavior::ui::image::FlattenUIImage : com.lynx.tas
19281928
public com.lynx.tasm.behavior.ui.image.FlattenUIImage.FlattenUIImage(LynxContext context);
19291929
public com.lynx.tasm.behavior.ui.image.FlattenUIImage.FlattenUIImage(LynxContext context, Object params);
19301930
public MeaningfulContentStatus com.lynx.tasm.behavior.ui.image.FlattenUIImage.getMeaningfulContentStatus();
1931+
public long com.lynx.tasm.behavior.ui.image.FlattenUIImage.getMemoryUsageBytes();
1932+
public Map< String, String > com.lynx.tasm.behavior.ui.image.FlattenUIImage.getMemoryUsageDetail();
19311933
public void com.lynx.tasm.behavior.ui.image.FlattenUIImage.pauseAnimation(ReadableMap params, com.lynx.react.bridge.Callback callback);
19321934
public void com.lynx.tasm.behavior.ui.image.FlattenUIImage.resumeAnimation(ReadableMap params, com.lynx.react.bridge.Callback callback);
19331935
public void com.lynx.tasm.behavior.ui.image.FlattenUIImage.stopAnimation(ReadableMap params, com.lynx.react.bridge.Callback callback);
@@ -2889,11 +2891,21 @@ public class com::lynx::tasm::image::ImageErrorCodeUtils : {
28892891
public static int com.lynx.tasm.image.ImageErrorCodeUtils.checkImageExceptionCategory(int errorCode);
28902892
}
28912893

2894+
public class com::lynx::tasm::image::ImageEventHelper : {
2895+
public static void com.lynx.tasm.image.ImageEventHelper.monitorReporterV2(final LynxContext context, final int sign, final String url, final boolean isSuccess, final boolean hitCache, final long startTimeStamp, final long getImageTimeStamp, final int memoryCost, final JSONObject info);
2896+
public static void com.lynx.tasm.image.ImageEventHelper.monitorReporter(final LynxContext context, final String url, final boolean isSuccess, final boolean hitCache, final long startTimeStamp, final long getImageTimeStamp, final int memoryCost, final JSONObject info);
2897+
public static JSONObject com.lynx.tasm.image.ImageEventHelper.getReportData(LynxContext context, String url, boolean isSuccess, boolean hitCache, long startTimeStamp, long fetchDuration, long completeDuration, long finishTimeStamp, int memoryCost, JSONObject info);
2898+
public static void com.lynx.tasm.image.ImageEventHelper.reportImageInfo(final LynxContext context, final String url, final boolean isSuccess, final boolean hitCache, final long startTimeStamp, final long finishTimeStamp, final int memoryCost, final int errorCode);
2899+
public static void com.lynx.tasm.image.ImageEventHelper.reportImageEvent(final LynxContext context, final String url, final int errorCode, final boolean hitMemoryCache, final int imageOrigin, final long startTimeStamp, final long finishTimeStamp, final boolean isFlattenUI, final int width, final int height, Map< String, String > customParams);
2900+
}
2901+
28922902
public class com::lynx::tasm::image::model::ImageInfo : {
28932903
public com.lynx.tasm.image.model.ImageInfo.ImageInfo(int width, int height, boolean isAnim);
2904+
public com.lynx.tasm.image.model.ImageInfo.ImageInfo(int width, int height, boolean isAnim, int origin);
28942905
public int com.lynx.tasm.image.model.ImageInfo.getWidth();
28952906
public int com.lynx.tasm.image.model.ImageInfo.getHeight();
28962907
public boolean com.lynx.tasm.image.model.ImageInfo.isAnim();
2908+
public int com.lynx.tasm.image.model.ImageInfo.getOrigin();
28972909
}
28982910

28992911
public interface com::lynx::tasm::behavior::ImageInterceptor {
@@ -11149,6 +11161,8 @@ public class com::lynx::tasm::behavior::ui::image::UIImage : com.lynx.tasm.behav
1114911161
public com.lynx.tasm.behavior.ui.image.UIImage.UIImage(LynxContext context);
1115011162
public com.lynx.tasm.behavior.ui.image.UIImage.UIImage(LynxContext context, Object params);
1115111163
public MeaningfulContentStatus com.lynx.tasm.behavior.ui.image.UIImage.getMeaningfulContentStatus();
11164+
public long com.lynx.tasm.behavior.ui.image.UIImage.getMemoryUsageBytes();
11165+
public Map< String, String > com.lynx.tasm.behavior.ui.image.UIImage.getMemoryUsageDetail();
1115211166
public void com.lynx.tasm.behavior.ui.image.UIImage.pauseAnimation(ReadableMap params, com.lynx.react.bridge.Callback callback);
1115311167
public void com.lynx.tasm.behavior.ui.image.UIImage.resumeAnimation(ReadableMap params, com.lynx.react.bridge.Callback callback);
1115411168
public void com.lynx.tasm.behavior.ui.image.UIImage.stopAnimation(ReadableMap params, com.lynx.react.bridge.Callback callback);

platform/android/lynx_android/src/main/java/com/lynx/tasm/behavior/ui/image/FlattenUIImage.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.lynx.tasm.behavior.ui.ViewInfo;
2020
import com.lynx.tasm.behavior.ui.utils.BackgroundDrawable;
2121
import com.lynx.tasm.event.EventsListener;
22+
import java.util.HashMap;
2223
import java.util.Map;
2324

2425
public class FlattenUIImage extends LynxFlattenUI {
@@ -50,6 +51,27 @@ public MeaningfulContentStatus getMeaningfulContentStatus() {
5051
return MeaningfulContentStatus.PENDING;
5152
}
5253

54+
@Override
55+
public long getMemoryUsageBytes() {
56+
long size = super.getMemoryUsageBytes();
57+
if (mLynxImageManager == null) {
58+
return size;
59+
}
60+
return mLynxImageManager.getBitmapMemorySizeBytes() + size;
61+
}
62+
63+
@Override
64+
public Map<String, String> getMemoryUsageDetail() {
65+
long size = getMemoryUsageBytes();
66+
String url = mLynxImageManager != null ? mLynxImageManager.getSrc() : null;
67+
if (size == 0 || url == null || url.isEmpty() || url.startsWith("data:image")) {
68+
return null;
69+
}
70+
HashMap<String, String> detail = new HashMap<>();
71+
detail.put(url, String.valueOf(size));
72+
return detail;
73+
}
74+
5375
@Override
5476
protected MeaningfulPaintingArea convertToMeaningfulPaintingArea(int offsetX, int offsetY) {
5577
if (getMeaningfulContentStatus() == MeaningfulContentStatus.IRRELEVANT) {

platform/android/lynx_android/src/main/java/com/lynx/tasm/behavior/ui/image/LynxImageManager.java

Lines changed: 151 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.lynx.react.bridge.Dynamic;
2323
import com.lynx.react.bridge.ReadableMap;
2424
import com.lynx.react.bridge.ReadableMapKeySetIterator;
25+
import com.lynx.tasm.LynxEnv;
2526
import com.lynx.tasm.LynxError;
2627
import com.lynx.tasm.base.LLog;
2728
import com.lynx.tasm.base.TraceEvent;
@@ -32,6 +33,7 @@
3233
import com.lynx.tasm.behavior.render.RoundedRectangle;
3334
import com.lynx.tasm.behavior.shadow.ShadowNode;
3435
import com.lynx.tasm.behavior.ui.LynxBaseUI;
36+
import com.lynx.tasm.behavior.ui.LynxFlattenUI;
3537
import com.lynx.tasm.behavior.ui.ViewInfo;
3638
import com.lynx.tasm.behavior.ui.utils.BackgroundDrawable;
3739
import com.lynx.tasm.core.LynxThreadPool;
@@ -41,8 +43,10 @@
4143
import com.lynx.tasm.image.AutoSizeImage;
4244
import com.lynx.tasm.image.ImageContent;
4345
import com.lynx.tasm.image.ImageErrorCodeUtils;
46+
import com.lynx.tasm.image.ImageEventHelper;
4447
import com.lynx.tasm.image.ImageUtils;
4548
import com.lynx.tasm.image.LynxImageConfig;
49+
import com.lynx.tasm.image.LynxImageLoadInfo;
4650
import com.lynx.tasm.image.LynxImageMediaFetcherProxy;
4751
import com.lynx.tasm.image.LynxScaleTypeDrawable;
4852
import com.lynx.tasm.image.ScalingUtils;
@@ -186,6 +190,8 @@ public class LynxImageManager implements Drawable.Callback {
186190

187191
private int mImageHeight;
188192

193+
private long mBitmapMemorySizeBytes;
194+
189195
private ShadowNode mAutoSizeShadowNode = null;
190196

191197
private boolean mNeedRetryAutoSize;
@@ -224,6 +230,11 @@ public class LynxImageManager implements Drawable.Callback {
224230
public static final String EVENT_LOAD = "load";
225231

226232
public static final String EVENT_ERROR = "error";
233+
234+
private static final String HTTP_PREFIX = "http";
235+
236+
private static final int IMAGE_ORIGIN_UNKNOWN = -1;
237+
227238
private ColorFilter mColorFilter = null;
228239

229240
private ImageRequestInfo mPreImageRequestInfo = null;
@@ -240,12 +251,20 @@ public class LynxImageManager implements Drawable.Callback {
240251

241252
private boolean mEnableReportInfo = false;
242253

254+
private boolean mEnableImageEventReport = false;
255+
256+
private boolean mEnableImageLoadCallback = false;
257+
243258
private float mImageSRScale = 0;
244259

245260
private boolean mCacheKeyPathOnly = false;
246261

247262
private @Nullable Rect mRegionToDecode;
248263

264+
private long mStartTimeStamp = 0;
265+
266+
private long mFinishTimeStamp = 0;
267+
249268
private static class ImageRequestHandle implements ImageLoadListener {
250269
private final ImageLoadListener mSrcLoadListenerImpl;
251270
private final ArrayList<Runnable> mRunnableList = new ArrayList<>();
@@ -353,6 +372,7 @@ public void onSuccess(
353372
}
354373
mImageWidth = imageInfo.getWidth();
355374
mImageHeight = imageInfo.getHeight();
375+
mBitmapMemorySizeBytes = calculateBitmapMemorySizeBytes();
356376
ILynxViewRuntimeCacheManager cacheManager = mContext.getRuntimeCacheManager();
357377
if (cacheManager != null) {
358378
cacheManager.setBitmapSizeCache(requestInfo.getUrl(), mImageWidth, mImageHeight);
@@ -363,6 +383,7 @@ public void onSuccess(
363383
}
364384
configureBounds(mImageDrawable);
365385
onImageLoadSuccess(mImageWidth, mImageHeight);
386+
reportImageSuccess(imageInfo);
366387
invalidate();
367388
}
368389

@@ -381,6 +402,7 @@ public void onFailure(int errorCode, Throwable throwable) {
381402
categoryCode, "Android LynxImageManager loading image failed", "", LynxError.LEVEL_ERROR);
382403
lynxError.setRootCause(error);
383404
onImageLoadError(lynxError, categoryCode, errorCode);
405+
reportImageFailure(errorCode, error);
384406
}
385407

386408
@Override
@@ -453,7 +475,9 @@ public LynxImageManager(LynxContext context) {
453475
if (imageConfig.getImageCustomParam() != null) {
454476
mCustomParams.putAll(imageConfig.getImageCustomParam());
455477
}
478+
mEnableImageLoadCallback = imageConfig.getEnableImageLoadCallback();
456479
}
480+
mEnableImageEventReport = LynxEnv.inst().enableImageEventReport();
457481
}
458482

459483
// region setProps
@@ -484,6 +508,10 @@ public void setSrc(String src) {
484508
}
485509
}
486510

511+
String getSrc() {
512+
return mSrc;
513+
}
514+
487515
public void setPlaceholder(String placeholder) {
488516
if (!TextUtils.equals(placeholder, mPlaceholder)) {
489517
mPlaceholder = placeholder;
@@ -959,6 +987,7 @@ public void updateNodeProps() {
959987
mCurImageRequest = null;
960988
mImageWidth = 0;
961989
mImageHeight = 0;
990+
mBitmapMemorySizeBytes = 0;
962991
} else {
963992
mPreImageRequestInfo = mCurImageRequest;
964993
}
@@ -1057,6 +1086,7 @@ public void tryFetchImageFromService(int width, int height) {
10571086
ImageRequestInfo requestInfo = createImageRequest(width, height, mSrc);
10581087
if (requestInfo != null) {
10591088
mCurImageRequest = requestInfo;
1089+
mStartTimeStamp = System.currentTimeMillis();
10601090
mImageLoader.fetchImage(requestInfo, mSrcLoadListener, animationListener, mContext);
10611091
}
10621092
}
@@ -1083,6 +1113,9 @@ private void releaseAllImage() {
10831113
releaseDrawable(mPlaceholderDrawable);
10841114
mCurImageRequest = null;
10851115
mCurPlaceholderRequest = null;
1116+
mImageWidth = 0;
1117+
mImageHeight = 0;
1118+
mBitmapMemorySizeBytes = 0;
10861119

10871120
if (mImageDrawable != null) {
10881121
mImageDrawable.releaseImageSource();
@@ -1357,14 +1390,15 @@ private void sendLoadWithReportInfo(JSONObject monitorInfo) {
13571390
}
13581391
event.addDetail(key, value);
13591392
}
1360-
int memoryCost = ImageUtils.getSizeInByteForBitmap(mImageWidth, mImageHeight,
1361-
mBitmapConfig == null ? Bitmap.Config.ARGB_8888 : mBitmapConfig);
13621393
event.addDetail("src", mSrc);
13631394
event.addDetail("width", mImageWidth);
13641395
event.addDetail("height", mImageHeight);
13651396
event.addDetail("view_width", mViewWidth);
13661397
event.addDetail("view_height", mViewHeight);
1367-
event.addDetail("memoryCost", memoryCost);
1398+
event.addDetail("memoryCost", mBitmapMemorySizeBytes);
1399+
event.addDetail("load_start", mStartTimeStamp);
1400+
event.addDetail("load_finish", mFinishTimeStamp);
1401+
event.addDetail("cost", mFinishTimeStamp - mStartTimeStamp);
13681402

13691403
mContext.getEventEmitter().sendCustomEvent(event);
13701404
}
@@ -1395,6 +1429,100 @@ private void handlerFailure(LynxError lynxError, int categorizedCode, int imageE
13951429
}
13961430
}
13971431

1432+
private void reportImageSuccess(ImageInfo imageInfo) {
1433+
mFinishTimeStamp = System.currentTimeMillis();
1434+
JSONObject info = getSizeInfo(imageInfo.isAnim());
1435+
int memoryCost = (int) mBitmapMemorySizeBytes;
1436+
monitorReporter(mSrc, true, false, mStartTimeStamp, mFinishTimeStamp, memoryCost, info);
1437+
reportImageInfo(mSrc, true, false, mStartTimeStamp, mFinishTimeStamp, 0, memoryCost);
1438+
reportImageEvent(mSrc, 0, false, imageInfo.getOrigin(), mStartTimeStamp, mFinishTimeStamp,
1439+
isFlattenUI(), mImageWidth, mImageHeight);
1440+
notifyImageLoadedIfNeeded(mViewWidth, mViewHeight, mImageWidth, mImageHeight, mStartTimeStamp,
1441+
mFinishTimeStamp, imageInfo.getOrigin(), 0, null);
1442+
}
1443+
1444+
private void reportImageFailure(int errorCode, @Nullable String errorMessage) {
1445+
mFinishTimeStamp = System.currentTimeMillis();
1446+
monitorReporter(mSrc, false, false, mStartTimeStamp, mFinishTimeStamp, 0, null);
1447+
reportImageInfo(mSrc, false, false, mStartTimeStamp, mFinishTimeStamp, errorCode, 0);
1448+
reportImageEvent(mSrc, errorCode, false, IMAGE_ORIGIN_UNKNOWN, mStartTimeStamp,
1449+
mFinishTimeStamp, isFlattenUI(), 0, 0);
1450+
notifyImageLoadedIfNeeded(0, 0, 0, 0, mStartTimeStamp, mFinishTimeStamp, IMAGE_ORIGIN_UNKNOWN,
1451+
errorCode, errorMessage);
1452+
}
1453+
1454+
private void monitorReporter(String url, boolean isSuccess, boolean hitCache, long startTimeStamp,
1455+
long finishTimeStamp, int memoryCost, JSONObject info) {
1456+
if (LynxEnv.inst().enableImageMemoryReport()) {
1457+
ImageEventHelper.monitorReporterV2(mContext, getEventSign(), url, isSuccess, hitCache,
1458+
startTimeStamp, finishTimeStamp, memoryCost, info);
1459+
} else if (mEnableImageEventReport) {
1460+
ImageEventHelper.monitorReporter(
1461+
mContext, url, isSuccess, hitCache, startTimeStamp, finishTimeStamp, memoryCost, info);
1462+
}
1463+
}
1464+
1465+
private void reportImageInfo(String url, boolean isSuccess, boolean hitCache, long startTimeStamp,
1466+
long finishTimeStamp, int errorCode, int memoryCost) {
1467+
if (mEnableImageEventReport) {
1468+
ImageEventHelper.reportImageInfo(mContext, url, isSuccess, hitCache, startTimeStamp,
1469+
finishTimeStamp, memoryCost, errorCode);
1470+
}
1471+
}
1472+
1473+
private void reportImageEvent(String url, int errorCode, boolean hitMemoryCache, int imageOrigin,
1474+
long startTimeStamp, long finishTimeStamp, boolean isFlattenUI, int width, int height) {
1475+
if (mEnableImageEventReport && url != null && url.startsWith(HTTP_PREFIX)) {
1476+
ImageEventHelper.reportImageEvent(mContext, url, errorCode, hitMemoryCache, imageOrigin,
1477+
startTimeStamp, finishTimeStamp, isFlattenUI, width, height, mCustomParams);
1478+
}
1479+
}
1480+
1481+
private JSONObject getSizeInfo(boolean isAnim) {
1482+
try {
1483+
JSONObject info = new JSONObject();
1484+
info.put("viewWidth", mViewWidth);
1485+
info.put("viewHeight", mViewHeight);
1486+
info.put("width", mImageWidth);
1487+
info.put("height", mImageHeight);
1488+
info.put("config", mBitmapConfig == null ? Bitmap.Config.ARGB_8888 : mBitmapConfig);
1489+
info.put("isFlattenAnim", isAnim && isFlattenUI() ? 1 : 0);
1490+
return info;
1491+
} catch (JSONException e) {
1492+
LLog.e(TAG, "getSizeInfo error: " + e.getMessage());
1493+
}
1494+
return null;
1495+
}
1496+
1497+
private boolean isFlattenUI() {
1498+
return mUI instanceof LynxFlattenUI;
1499+
}
1500+
1501+
private boolean isNetworkUrl() {
1502+
return mSrc != null && mSrc.startsWith(HTTP_PREFIX);
1503+
}
1504+
1505+
private void notifyImageLoadedIfNeeded(int viewWidth, int viewHeight, int imageWidth,
1506+
int imageHeight, long startTime, long finishTime, int origin, int errorCode,
1507+
@Nullable String errorMessage) {
1508+
if (!mEnableImageLoadCallback || !isNetworkUrl()) {
1509+
return;
1510+
}
1511+
LynxImageLoadInfo imageLoadInfo = new LynxImageLoadInfo(
1512+
LynxResourceRequest.LynxResourceType.LynxResourceTypeImage, errorCode, errorMessage);
1513+
imageLoadInfo.setSrc(mSrc);
1514+
if (errorCode == 0) {
1515+
imageLoadInfo.setWidth(imageWidth)
1516+
.setHeight(imageHeight)
1517+
.setViewWidth(viewWidth)
1518+
.setViewHeight(viewHeight)
1519+
.setLoadStart(startTime)
1520+
.setLoadFinish(finishTime)
1521+
.setOrigin(origin);
1522+
}
1523+
mContext.notifyResourceLoaded(imageLoadInfo);
1524+
}
1525+
13981526
public void setBorderRadius(float[] borderRadius, boolean radiusSizeChanged) {
13991527
if (mBorderRadius == null || radiusSizeChanged) {
14001528
dirtyFlags |= BORDER_RADIUS_CHANGED;
@@ -1406,6 +1534,26 @@ public Drawable getSrcImageDrawable() {
14061534
return mImageDrawable;
14071535
}
14081536

1537+
long getBitmapMemorySizeBytes() {
1538+
if (mImageDrawable == null) {
1539+
return 0;
1540+
}
1541+
return mBitmapMemorySizeBytes;
1542+
}
1543+
1544+
private long calculateBitmapMemorySizeBytes() {
1545+
if (mImageWidth <= 0 || mImageHeight <= 0) {
1546+
return 0;
1547+
}
1548+
try {
1549+
return ImageUtils.getSizeInByteForBitmap(mImageWidth, mImageHeight,
1550+
mBitmapConfig == null ? Bitmap.Config.ARGB_8888 : mBitmapConfig);
1551+
} catch (UnsupportedOperationException e) {
1552+
LLog.w(TAG, "Unsupported bitmap config when reporting image memory usage: " + mBitmapConfig);
1553+
return 0;
1554+
}
1555+
}
1556+
14091557
public Boolean getHasContent() {
14101558
return mImageDrawable != null || mPlaceholderDrawable != null;
14111559
}

platform/android/lynx_android/src/main/java/com/lynx/tasm/behavior/ui/image/UIImage.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import com.lynx.tasm.behavior.ui.ViewInfo;
1818
import com.lynx.tasm.behavior.ui.view.UIView;
1919
import com.lynx.tasm.event.EventsListener;
20+
import java.util.HashMap;
2021
import java.util.Map;
2122

2223
public class UIImage extends UIView {
@@ -71,6 +72,27 @@ public MeaningfulContentStatus getMeaningfulContentStatus() {
7172
return MeaningfulContentStatus.PENDING;
7273
}
7374

75+
@Override
76+
public long getMemoryUsageBytes() {
77+
long size = super.getMemoryUsageBytes();
78+
if (mLynxImageManager == null) {
79+
return size;
80+
}
81+
return mLynxImageManager.getBitmapMemorySizeBytes() + size;
82+
}
83+
84+
@Override
85+
public Map<String, String> getMemoryUsageDetail() {
86+
long size = getMemoryUsageBytes();
87+
String url = mLynxImageManager != null ? mLynxImageManager.getSrc() : null;
88+
if (size == 0 || url == null || url.isEmpty() || url.startsWith("data:image")) {
89+
return null;
90+
}
91+
HashMap<String, String> detail = new HashMap<>();
92+
detail.put(url, String.valueOf(size));
93+
return detail;
94+
}
95+
7496
private void ensureLynxImageManager() {
7597
if (mLynxImageManager != null) {
7698
return;

0 commit comments

Comments
 (0)