Extract native methods into isolated classes
StaticLayout/MeasuredParagraph uses native methods which is a pain point
of porting TextView to JetPack.
To make minimize the dependency to the native methods, extract native
methods and put them into a thin wrapper class.
The performance impact is limited:
android.text.StaticLayoutPerfTest:
PrecomputedText Balanced Hyphenation : 602 -> 644: ( +42, +7.0%)
PrecomputedText Balanced NoHyphenation: 457 -> 476: ( +19, +4.2%)
PrecomputedText Greedy Hyphenation : 397 -> 412: ( +15, +3.8%)
PrecomputedText Greedy NoHyphenation : 397 -> 411: ( +14, +3.5%)
RandomText Balanced Hyphenation : 17,594 -> 17,715: (+121, +0.7%)
RandomText Balanced NoHyphenation : 7,146 -> 7,236: ( +90, +1.3%)
RandomText Greedy Hyphenation : 7,125 -> 7,196: ( +71, +1.0%)
RandomText Greedy NoHyphenation : 7,099 -> 7,187: ( +88, +1.2%)
draw
PrecomputedText NoStyle : 614 -> 628: ( +14, +2.3%)
PrecomputedText Style : 778 -> 826: ( +48, +6.2%)
RandomText NoStyle : 537 -> 540: ( +3, +0.6%)
RandomText Style : 786 -> 759: ( -27, -3.4%)
Bug: N/A
Test: atest CtsWidgetTestCases:EditTextTest
CtsWidgetTestCases:TextViewFadingEdgeTest
FrameworksCoreTests:TextViewFallbackLineSpacingTest
FrameworksCoreTests:TextViewTest FrameworksCoreTests:TypefaceTest
CtsGraphicsTestCases:TypefaceTest CtsWidgetTestCases:TextViewTest
CtsTextTestCases FrameworksCoreTests:android.text
CtsWidgetTestCases:TextViewPrecomputedTextTest
Change-Id: I976df4db63be241af395dd30dd94182f76bdae53
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index c2c3182..9bf8cd2 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -30,10 +30,6 @@
import android.text.style.ReplacementSpan;
import android.util.Pools.SynchronizedPool;
-import dalvik.annotation.optimization.CriticalNative;
-
-import libcore.util.NativeAllocationRegistry;
-
import java.util.Arrays;
/**
@@ -62,9 +58,6 @@
public class MeasuredParagraph {
private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';
- private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- MeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024);
-
private MeasuredParagraph() {} // Use build static functions instead.
private static final SynchronizedPool<MeasuredParagraph> sPool = new SynchronizedPool<>(1);
@@ -128,24 +121,7 @@
private @Nullable IntArray mFontMetrics = new IntArray(4 * 4);
// The native MeasuredParagraph.
- // See getNativePtr comments.
- // Do not modify these members directly. Use bindNativeObject/unbindNativeObject instead.
- private /* Maybe Zero */ long mNativePtr = 0;
- private @Nullable Runnable mNativeObjectCleaner;
-
- // Associate the native object to this Java object.
- private void bindNativeObject(/* Non Zero*/ long nativePtr) {
- mNativePtr = nativePtr;
- mNativeObjectCleaner = sRegistry.registerNativeAllocation(this, nativePtr);
- }
-
- // Decouple the native object from this Java object and release the native object.
- private void unbindNativeObject() {
- if (mNativePtr != 0) {
- mNativeObjectCleaner.run();
- mNativePtr = 0;
- }
- }
+ private @Nullable NativeMeasuredParagraph mNativeMeasuredParagraph;
// Following two objects are for avoiding object allocation.
private @NonNull TextPaint mCachedPaint = new TextPaint();
@@ -173,7 +149,7 @@
mWidths.clear();
mFontMetrics.clear();
mSpanEndCache.clear();
- unbindNativeObject();
+ mNativeMeasuredParagraph = null;
}
/**
@@ -267,10 +243,10 @@
* Returns the native ptr of the MeasuredParagraph.
*
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
- * Returns 0 in other cases.
+ * Returns null in other cases.
*/
- public /* Maybe Zero */ long getNativePtr() {
- return mNativePtr;
+ public NativeMeasuredParagraph getNativeMeasuredParagraph() {
+ return mNativeMeasuredParagraph;
}
/**
@@ -283,7 +259,7 @@
* @param end the exclusive end offset of the target region in the text
*/
public float getWidth(int start, int end) {
- if (mNativePtr == 0) {
+ if (mNativeMeasuredParagraph == null) {
// We have result in Java.
final float[] widths = mWidths.getRawArray();
float r = 0.0f;
@@ -293,7 +269,7 @@
return r;
} else {
// We have result in native.
- return nGetWidth(mNativePtr, start, end);
+ return mNativeMeasuredParagraph.getWidth(start, end);
}
}
@@ -305,7 +281,16 @@
*/
public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull Rect bounds) {
- nGetBounds(mNativePtr, mCopiedBuffer, start, end, bounds);
+ mNativeMeasuredParagraph.getBounds(mCopiedBuffer, start, end, bounds);
+ }
+
+ /**
+ * Returns a width of the character at the offset.
+ *
+ * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
+ */
+ public float getCharWidthAt(@IntRange(from = 0) int offset) {
+ return mNativeMeasuredParagraph.getCharWidthAt(offset);
}
/**
@@ -364,7 +349,7 @@
if (mt.mSpanned == null) {
// No style change by MetricsAffectingSpan. Just measure all text.
mt.applyMetricsAffectingSpan(
- paint, null /* spans */, start, end, 0 /* native static layout ptr */);
+ paint, null /* spans */, start, end, null /* native builder ptr */);
} else {
// There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
int spanEnd;
@@ -374,7 +359,7 @@
MetricAffectingSpan.class);
spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
mt.applyMetricsAffectingSpan(
- paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */);
+ paint, spans, spanStart, spanEnd, null /* native builder ptr */);
}
}
return mt;
@@ -406,25 +391,16 @@
@Nullable MeasuredParagraph recycle) {
final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
mt.resetAndAnalyzeBidi(text, start, end, textDir);
+ final NativeMeasuredParagraph.Builder builder = new NativeMeasuredParagraph.Builder();
if (mt.mTextLength == 0) {
// Need to build empty native measured text for StaticLayout.
// TODO: Stop creating empty measured text for empty lines.
- long nativeBuilderPtr = nInitBuilder();
- try {
- mt.bindNativeObject(
- nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
- computeHyphenation, computeLayout));
- } finally {
- nFreeBuilder(nativeBuilderPtr);
- }
- return mt;
- }
-
- long nativeBuilderPtr = nInitBuilder();
- try {
+ mt.mNativeMeasuredParagraph = builder.build(mt.mCopiedBuffer, computeHyphenation,
+ computeLayout);
+ } else {
if (mt.mSpanned == null) {
// No style change by MetricsAffectingSpan. Just measure all text.
- mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr);
+ mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, builder);
mt.mSpanEndCache.append(end);
} else {
// There may be a MetricsAffectingSpan. Split into span transitions and apply
@@ -437,15 +413,12 @@
MetricAffectingSpan.class);
spans = TextUtils.removeEmptySpans(spans, mt.mSpanned,
MetricAffectingSpan.class);
- mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd,
- nativeBuilderPtr);
+ mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd, builder);
mt.mSpanEndCache.append(spanEnd);
}
}
- mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
- computeHyphenation, computeLayout));
- } finally {
- nFreeBuilder(nativeBuilderPtr);
+ mt.mNativeMeasuredParagraph = builder.build(mt.mCopiedBuffer, computeHyphenation,
+ computeLayout);
}
return mt;
@@ -517,13 +490,13 @@
private void applyReplacementRun(@NonNull ReplacementSpan replacement,
@IntRange(from = 0) int start, // inclusive, in copied buffer
@IntRange(from = 0) int end, // exclusive, in copied buffer
- /* Maybe Zero */ long nativeBuilderPtr) {
+ @Nullable NativeMeasuredParagraph.Builder builder) {
// Use original text. Shouldn't matter.
// TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
// backward compatibility? or Should we initialize them for getFontMetricsInt?
final float width = replacement.getSize(
mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
- if (nativeBuilderPtr == 0) {
+ if (builder == null) {
// Assigns all width to the first character. This is the same behavior as minikin.
mWidths.set(start, width);
if (end > start + 1) {
@@ -531,24 +504,22 @@
}
mWholeWidth += width;
} else {
- nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
- width);
+ builder.addReplacementRun(mCachedPaint, start, end, width);
}
}
private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer
@IntRange(from = 0) int end, // exclusive, in copied buffer
- /* Maybe Zero */ long nativeBuilderPtr) {
+ @Nullable NativeMeasuredParagraph.Builder builder) {
if (mLtrWithoutBidi) {
// If the whole text is LTR direction, just apply whole region.
- if (nativeBuilderPtr == 0) {
+ if (builder == null) {
mWholeWidth += mCachedPaint.getTextRunAdvances(
mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
mWidths.getRawArray(), start);
} else {
- nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
- false /* isRtl */);
+ builder.addStyleRun(mCachedPaint, start, end, false /* isRtl */);
}
} else {
// If there is multiple bidi levels, split into individual bidi level and apply style.
@@ -558,14 +529,13 @@
for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) {
if (levelEnd == end || mLevels.get(levelEnd) != level) { // transition point
final boolean isRtl = (level & 0x1) != 0;
- if (nativeBuilderPtr == 0) {
+ if (builder == null) {
final int levelLength = levelEnd - levelStart;
mWholeWidth += mCachedPaint.getTextRunAdvances(
mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
isRtl, mWidths.getRawArray(), levelStart);
} else {
- nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart,
- levelEnd, isRtl);
+ builder.addStyleRun(mCachedPaint, levelStart, levelEnd, isRtl);
}
if (levelEnd == end) {
break;
@@ -582,12 +552,12 @@
@Nullable MetricAffectingSpan[] spans,
@IntRange(from = 0) int start, // inclusive, in original text buffer
@IntRange(from = 0) int end, // exclusive, in original text buffer
- /* Maybe Zero */ long nativeBuilderPtr) {
+ @Nullable NativeMeasuredParagraph.Builder builder) {
mCachedPaint.set(paint);
// XXX paint should not have a baseline shift, but...
mCachedPaint.baselineShift = 0;
- final boolean needFontMetrics = nativeBuilderPtr != 0;
+ final boolean needFontMetrics = builder != null;
if (needFontMetrics && mCachedFm == null) {
mCachedFm = new Paint.FontMetricsInt();
@@ -610,15 +580,14 @@
final int startInCopiedBuffer = start - mTextStart;
final int endInCopiedBuffer = end - mTextStart;
- if (nativeBuilderPtr != 0) {
+ if (builder != null) {
mCachedPaint.getFontMetricsInt(mCachedFm);
}
if (replacement != null) {
- applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer,
- nativeBuilderPtr);
+ applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, builder);
} else {
- applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr);
+ applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, builder);
}
if (needFontMetrics) {
@@ -689,59 +658,6 @@
* This only works if the MeasuredParagraph is computed with buildForStaticLayout.
*/
public @IntRange(from = 0) int getMemoryUsage() {
- return nGetMemoryUsage(mNativePtr);
+ return mNativeMeasuredParagraph.getMemoryUsage();
}
-
- private static native /* Non Zero */ long nInitBuilder();
-
- /**
- * Apply style to make native measured text.
- *
- * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
- * @param paintPtr The native paint pointer to be applied.
- * @param start The start offset in the copied buffer.
- * @param end The end offset in the copied buffer.
- * @param isRtl True if the text is RTL.
- */
- private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
- /* Non Zero */ long paintPtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- boolean isRtl);
-
- /**
- * Apply ReplacementRun to make native measured text.
- *
- * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
- * @param paintPtr The native paint pointer to be applied.
- * @param start The start offset in the copied buffer.
- * @param end The end offset in the copied buffer.
- * @param width The width of the replacement.
- */
- private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
- /* Non Zero */ long paintPtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @FloatRange(from = 0) float width);
-
- private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr,
- @NonNull char[] text,
- boolean computeHyphenation,
- boolean computeLayout);
-
- private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
-
- @CriticalNative
- private static native float nGetWidth(/* Non Zero */ long nativePtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end);
-
- @CriticalNative
- private static native /* Non Zero */ long nGetReleaseFunc();
-
- @CriticalNative
- private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
-
- private static native void nGetBounds(long nativePtr, char[] buf, int start, int end,
- Rect rect);
}