@@ -33,6 +33,17 @@ public final class DynamicMessage extends AbstractMessage {
3333 private final UnknownFieldSet unknownFields ;
3434 private int memoizedSize = -1 ;
3535
36+ /**
37+ * Stores previously-computed {@code isInitialized} results. {@code isInitialized} can be
38+ * expensive to compute in situations where a large message is converted to a builder, modified,
39+ * and then rebuilt.
40+ */
41+ private transient byte isInitializedMemo = NO_MEMO_PRESENT ;
42+
43+ private static final byte NO_MEMO_PRESENT = -1 ;
44+ private static final byte IS_INITIALIZED_FALSE = 0 ;
45+ private static final byte IS_INITIALIZED_TRUE = 1 ;
46+
3647 /**
3748 * Construct a {@code DynamicMessage} using the given {@code FieldSet}. oneofCases stores the
3849 * FieldDescriptor for each oneof to indicate which field is set. Caller should make sure the
@@ -215,7 +226,20 @@ static boolean isInitialized(Descriptor type, FieldSet<FieldDescriptor> fields)
215226
216227 @ Override
217228 public boolean isInitialized () {
218- return isInitialized (type , fields );
229+ // This section departs slightly from idiomatic style because it is expected that this style
230+ // will optimize better on mobile after processing by technologies like AppReduce.
231+ if (isInitializedMemo == IS_INITIALIZED_TRUE ) {
232+ return true ;
233+ }
234+ if (isInitializedMemo == IS_INITIALIZED_FALSE ) {
235+ return false ;
236+ }
237+ if (isInitialized (type , fields )) {
238+ isInitializedMemo = IS_INITIALIZED_TRUE ;
239+ return true ;
240+ }
241+ isInitializedMemo = IS_INITIALIZED_FALSE ;
242+ return false ;
219243 }
220244
221245 @ Override
0 commit comments