Skip to content

Commit 2e1a83a

Browse files
committed
Retaining fields on configuration changes with @NonConfiguration. Also rebinds context in non singleton @ebean classes. Lacks tests for configuration changes. androidannotations#76
1 parent 60555b9 commit 2e1a83a

17 files changed

Lines changed: 429 additions & 17 deletions

File tree

‎AndroidAnnotations/androidannotations/src/main/java/com/googlecode/androidannotations/AndroidAnnotationProcessor.java‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,17 @@
4949
import com.googlecode.androidannotations.annotations.Extra;
5050
import com.googlecode.androidannotations.annotations.FromHtml;
5151
import com.googlecode.androidannotations.annotations.Fullscreen;
52+
import com.googlecode.androidannotations.annotations.InstanceState;
5253
import com.googlecode.androidannotations.annotations.ItemClick;
5354
import com.googlecode.androidannotations.annotations.ItemLongClick;
5455
import com.googlecode.androidannotations.annotations.ItemSelect;
5556
import com.googlecode.androidannotations.annotations.LongClick;
5657
import com.googlecode.androidannotations.annotations.NoTitle;
58+
import com.googlecode.androidannotations.annotations.NonConfigurationInstance;
5759
import com.googlecode.androidannotations.annotations.OptionsItem;
5860
import com.googlecode.androidannotations.annotations.OptionsMenu;
5961
import com.googlecode.androidannotations.annotations.RoboGuice;
6062
import com.googlecode.androidannotations.annotations.RootContext;
61-
import com.googlecode.androidannotations.annotations.InstanceState;
6263
import com.googlecode.androidannotations.annotations.SystemService;
6364
import com.googlecode.androidannotations.annotations.Touch;
6465
import com.googlecode.androidannotations.annotations.Trace;
@@ -121,20 +122,21 @@
121122
import com.googlecode.androidannotations.processing.ExtraProcessor;
122123
import com.googlecode.androidannotations.processing.FromHtmlProcessor;
123124
import com.googlecode.androidannotations.processing.FullscreenProcessor;
125+
import com.googlecode.androidannotations.processing.InstanceStateProcessor;
124126
import com.googlecode.androidannotations.processing.ItemClickProcessor;
125127
import com.googlecode.androidannotations.processing.ItemLongClickProcessor;
126128
import com.googlecode.androidannotations.processing.ItemSelectedProcessor;
127129
import com.googlecode.androidannotations.processing.LongClickProcessor;
128130
import com.googlecode.androidannotations.processing.ModelProcessor;
129131
import com.googlecode.androidannotations.processing.NoTitleProcessor;
132+
import com.googlecode.androidannotations.processing.NonConfigurationInstanceProcessor;
130133
import com.googlecode.androidannotations.processing.OptionsItemProcessor;
131134
import com.googlecode.androidannotations.processing.OptionsMenuProcessor;
132135
import com.googlecode.androidannotations.processing.PrefProcessor;
133136
import com.googlecode.androidannotations.processing.ResProcessor;
134137
import com.googlecode.androidannotations.processing.RestServiceProcessor;
135138
import com.googlecode.androidannotations.processing.RoboGuiceProcessor;
136139
import com.googlecode.androidannotations.processing.RootContextProcessor;
137-
import com.googlecode.androidannotations.processing.InstanceStateProcessor;
138140
import com.googlecode.androidannotations.processing.SharedPrefProcessor;
139141
import com.googlecode.androidannotations.processing.SystemServiceProcessor;
140142
import com.googlecode.androidannotations.processing.TouchProcessor;
@@ -171,12 +173,14 @@
171173
import com.googlecode.androidannotations.validation.ExtraValidator;
172174
import com.googlecode.androidannotations.validation.FromHtmlValidator;
173175
import com.googlecode.androidannotations.validation.FullscreenValidator;
176+
import com.googlecode.androidannotations.validation.InstanceStateValidator;
174177
import com.googlecode.androidannotations.validation.ItemClickValidator;
175178
import com.googlecode.androidannotations.validation.ItemLongClickValidator;
176179
import com.googlecode.androidannotations.validation.ItemSelectedValidator;
177180
import com.googlecode.androidannotations.validation.LongClickValidator;
178181
import com.googlecode.androidannotations.validation.ModelValidator;
179182
import com.googlecode.androidannotations.validation.NoTitleValidator;
183+
import com.googlecode.androidannotations.validation.NonConfigurationInstanceValidator;
180184
import com.googlecode.androidannotations.validation.OptionsItemValidator;
181185
import com.googlecode.androidannotations.validation.OptionsMenuValidator;
182186
import com.googlecode.androidannotations.validation.PrefValidator;
@@ -185,7 +189,6 @@
185189
import com.googlecode.androidannotations.validation.RoboGuiceValidator;
186190
import com.googlecode.androidannotations.validation.RootContextValidator;
187191
import com.googlecode.androidannotations.validation.RunnableValidator;
188-
import com.googlecode.androidannotations.validation.InstanceStateValidator;
189192
import com.googlecode.androidannotations.validation.SharedPrefValidator;
190193
import com.googlecode.androidannotations.validation.SystemServiceValidator;
191194
import com.googlecode.androidannotations.validation.TouchValidator;
@@ -263,6 +266,7 @@
263266
EProvider.class, //
264267
Trace.class, //
265268
InstanceState.class, //
269+
NonConfigurationInstance.class, //
266270
EApplication.class //
267271
})
268272
@SupportedSourceVersion(SourceVersion.RELEASE_6)
@@ -425,6 +429,7 @@ private ModelValidator buildModelValidator(IRClass rClass, AndroidSystemServices
425429
modelValidator.register(new RunnableValidator(UiThread.class, processingEnv));
426430
modelValidator.register(new RunnableValidator(Background.class, processingEnv));
427431
modelValidator.register(new InstanceStateValidator(processingEnv));
432+
modelValidator.register(new NonConfigurationInstanceValidator(processingEnv));
428433
return modelValidator;
429434
}
430435

@@ -496,6 +501,7 @@ private ModelProcessor buildModelProcessor(IRClass rClass, AndroidSystemServices
496501
modelProcessor.register(new BackgroundProcessor());
497502
modelProcessor.register(new AfterInjectProcessor());
498503
modelProcessor.register(new InstanceStateProcessor(processingEnv));
504+
modelProcessor.register(new NonConfigurationInstanceProcessor());
499505
return modelProcessor;
500506
}
501507

‎AndroidAnnotations/androidannotations/src/main/java/com/googlecode/androidannotations/annotations/Bean.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
* Injects an instance of a class annotated with {@link EBean}. You can specify
2525
* the type to inject using the value attribute, otherwise
2626
*/
27-
@Retention(RetentionPolicy.SOURCE)
27+
@Retention(RetentionPolicy.CLASS)
2828
@Target(ElementType.FIELD)
2929
public @interface Bean {
3030
Class<?> value() default Void.class;

‎AndroidAnnotations/androidannotations/src/main/java/com/googlecode/androidannotations/annotations/InstanceState.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import java.lang.annotation.Target;
2222

2323
/**
24-
* Use it on fields in activities to save and restore their values when the
24+
* Use on activity fields to save and restore their values when the
2525
* system calls onSaveInstanceState(Bundle) and onCreate(Bundle).
2626
*/
2727
@Retention(RetentionPolicy.SOURCE)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Copyright (C) 2010-2011 eBusiness Information, Excilys Group
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed To in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.googlecode.androidannotations.annotations;
17+
18+
import java.lang.annotation.ElementType;
19+
import java.lang.annotation.Retention;
20+
import java.lang.annotation.RetentionPolicy;
21+
import java.lang.annotation.Target;
22+
23+
/**
24+
* Use on activity fields to retain instances that are intensive to
25+
* compute, on configuration changes.
26+
*
27+
* See <a href=
28+
* "http://developer.android.com/guide/topics/resources/runtime-changes.html#RetainingAnObject"
29+
* >RetainingAnObject</a> in the Android Documentation.<br />
30+
* <br />
31+
* Caution: While you can annotate any field, you should never annotate a field
32+
* that is tied to the Activity, such as a Drawable, an Adapter, a View or any
33+
* other object that's associated with a Context. If you do, it will leak all
34+
* the views and resources of the original activity instance. (Leaking resources
35+
* means that your application maintains a hold on them and they cannot be
36+
* garbage-collected, so lots of memory can be lost.)<br />
37+
* <br />
38+
* This caution doesn't apply to beans annotated with {@link Bean}, because
39+
* AndroidAnnotations automatically takes care of rebinding their context.
40+
*/
41+
@Retention(RetentionPolicy.SOURCE)
42+
@Target(ElementType.FIELD)
43+
public @interface NonConfigurationInstance {
44+
}

‎AndroidAnnotations/androidannotations/src/main/java/com/googlecode/androidannotations/processing/EBeanHolder.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ public class EBeanHolder {
7575
* Only defined in activities
7676
*/
7777
public JFieldVar intentField;
78+
79+
/**
80+
* Only defined in activities
81+
*/
82+
public NonConfigurationHolder nonConfigurationHolder;
7883

7984
public JClass refClass(String fullyQualifiedClassName) {
8085

‎AndroidAnnotations/androidannotations/src/main/java/com/googlecode/androidannotations/processing/EBeanProcessor.java‎

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHo
138138
constructorBody.invoke(holder.init);
139139
}
140140

141+
EBean eBeanAnnotation = element.getAnnotation(EBean.class);
142+
Scope eBeanScope = eBeanAnnotation.scope();
143+
boolean hasSingletonScope = eBeanScope == Scope.Singleton;
144+
141145
{
142146
// Factory method
143147

@@ -147,24 +151,39 @@ public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHo
147151

148152
JBlock factoryMethodBody = factoryMethod.body();
149153

150-
EBean eBeanAnnotation = element.getAnnotation(EBean.class);
151-
152-
Scope eBeanScope = eBeanAnnotation.scope();
153-
154-
if (eBeanScope == Scope.Singleton) {
154+
/*
155+
* Singletons are bound to the application context
156+
*/
157+
if (hasSingletonScope) {
155158

156159
JFieldVar instanceField = holder.eBean.field(PRIVATE | STATIC, holder.eBean, "instance_");
157160

158161
factoryMethodBody //
159162
._if(instanceField.eq(_null())) //
160163
._then() //
161164
.assign(instanceField, _new(holder.eBean).arg(factoryMethodContextParam.invoke("getApplicationContext")));
162-
165+
163166
factoryMethodBody._return(instanceField);
164167
} else {
165168
factoryMethodBody._return(_new(holder.eBean).arg(factoryMethodContextParam));
166169
}
167170
}
168171

172+
{
173+
// rebind(Context)
174+
JMethod rebindMethod = holder.eBean.method(PUBLIC, codeModel.VOID, "rebind");
175+
JVar contextParam = rebindMethod.param(contextClass, "context");
176+
177+
/*
178+
* No rebinding of context for singletons, their are bound to the
179+
* application context
180+
*/
181+
if (!hasSingletonScope) {
182+
JBlock body = rebindMethod.body();
183+
body.assign(contextField, contextParam);
184+
body.invoke(holder.init);
185+
}
186+
}
187+
169188
}
170189
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.googlecode.androidannotations.processing;
2+
3+
import com.sun.codemodel.JBlock;
4+
import com.sun.codemodel.JDefinedClass;
5+
import com.sun.codemodel.JInvocation;
6+
import com.sun.codemodel.JMethod;
7+
import com.sun.codemodel.JVar;
8+
9+
public class NonConfigurationHolder {
10+
11+
public JDefinedClass holderClass;
12+
13+
public JMethod holderConstructor;
14+
15+
public JInvocation newHolder;
16+
17+
public JBlock initIfNonConfiguration;
18+
19+
public JVar initNonConfigurationInstance;
20+
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/**
2+
* Copyright (C) 2010-2011 eBusiness Information, Excilys Group
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed To in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.googlecode.androidannotations.processing;
17+
18+
import static com.sun.codemodel.JExpr._new;
19+
import static com.sun.codemodel.JExpr._null;
20+
import static com.sun.codemodel.JExpr._super;
21+
import static com.sun.codemodel.JExpr._this;
22+
import static com.sun.codemodel.JExpr.cast;
23+
import static com.sun.codemodel.JMod.FINAL;
24+
import static com.sun.codemodel.JMod.PUBLIC;
25+
26+
import java.lang.annotation.Annotation;
27+
28+
import javax.lang.model.element.Element;
29+
30+
import com.googlecode.androidannotations.annotations.Bean;
31+
import com.googlecode.androidannotations.annotations.NonConfigurationInstance;
32+
import com.googlecode.androidannotations.helper.APTCodeModelHelper;
33+
import com.sun.codemodel.JBlock;
34+
import com.sun.codemodel.JClass;
35+
import com.sun.codemodel.JClassAlreadyExistsException;
36+
import com.sun.codemodel.JCodeModel;
37+
import com.sun.codemodel.JFieldVar;
38+
import com.sun.codemodel.JMethod;
39+
import com.sun.codemodel.JMod;
40+
import com.sun.codemodel.JVar;
41+
42+
public class NonConfigurationInstanceProcessor implements ElementProcessor {
43+
44+
private APTCodeModelHelper aptCodeModelHelper;
45+
46+
public NonConfigurationInstanceProcessor() {
47+
aptCodeModelHelper = new APTCodeModelHelper();
48+
}
49+
50+
@Override
51+
public Class<? extends Annotation> getTarget() {
52+
return NonConfigurationInstance.class;
53+
}
54+
55+
@Override
56+
public void process(Element element, JCodeModel codeModel, EBeansHolder activitiesHolder) throws JClassAlreadyExistsException {
57+
EBeanHolder holder = activitiesHolder.getEnclosingEBeanHolder(element);
58+
59+
NonConfigurationHolder ncHolder = holder.nonConfigurationHolder;
60+
61+
if (ncHolder == null) {
62+
63+
ncHolder = new NonConfigurationHolder();
64+
holder.nonConfigurationHolder = ncHolder;
65+
66+
ncHolder.holderClass = holder.eBean._class(JMod.PRIVATE | JMod.STATIC, "NonConfigurationInstancesHolder");
67+
68+
JFieldVar superNonConfigurationInstanceField = ncHolder.holderClass.field(PUBLIC | FINAL, Object.class, "superNonConfigurationInstance");
69+
70+
ncHolder.holderConstructor = ncHolder.holderClass.constructor(PUBLIC);
71+
72+
JVar superNonConfigurationInstanceParam = ncHolder.holderConstructor.param(Object.class, "superNonConfigurationInstance");
73+
74+
ncHolder.holderConstructor.body() //
75+
.assign(_this().ref(superNonConfigurationInstanceField), superNonConfigurationInstanceParam);
76+
77+
{
78+
// init()
79+
JBlock initBody = holder.init.body();
80+
ncHolder.initNonConfigurationInstance = initBody.decl(ncHolder.holderClass, "nonConfigurationInstance", cast(ncHolder.holderClass, _super().invoke("getLastNonConfigurationInstance")));
81+
ncHolder.initIfNonConfiguration = initBody._if(ncHolder.initNonConfigurationInstance.ne(_null()))._then();
82+
}
83+
84+
{
85+
// getLastNonConfigurationInstance()
86+
JMethod getLastNonConfigurationInstance = holder.eBean.method(PUBLIC, Object.class, "getLastNonConfigurationInstance");
87+
getLastNonConfigurationInstance.annotate(Override.class);
88+
JBlock body = getLastNonConfigurationInstance.body();
89+
90+
JVar nonConfigurationInstance = body.decl(ncHolder.holderClass, "nonConfigurationInstance", cast(ncHolder.holderClass, _super().invoke(getLastNonConfigurationInstance)));
91+
92+
body._if(nonConfigurationInstance.eq(_null()))._then()._return(_null());
93+
94+
body._return(nonConfigurationInstance.ref(superNonConfigurationInstanceField));
95+
}
96+
97+
{
98+
// onRetainNonConfigurationInstance()
99+
JMethod onRetainNonConfigurationInstance = holder.eBean.method(PUBLIC, ncHolder.holderClass, "onRetainNonConfigurationInstance");
100+
onRetainNonConfigurationInstance.annotate(Override.class);
101+
ncHolder.newHolder = _new(ncHolder.holderClass);
102+
ncHolder.newHolder.arg(_super().invoke(onRetainNonConfigurationInstance));
103+
onRetainNonConfigurationInstance.body()._return(ncHolder.newHolder);
104+
}
105+
}
106+
107+
String fieldName = element.getSimpleName().toString();
108+
JClass fieldType = aptCodeModelHelper.typeMirrorToJClass(element.asType(), holder);
109+
JFieldVar field = ncHolder.holderClass.field(PUBLIC | FINAL, fieldType, fieldName);
110+
111+
JVar constructorParam = ncHolder.holderConstructor.param(fieldType, fieldName);
112+
113+
ncHolder.holderConstructor.body() //
114+
.assign(_this().ref(field), constructorParam);
115+
116+
ncHolder.newHolder.arg(field);
117+
118+
ncHolder.initIfNonConfiguration.assign(field, ncHolder.initNonConfigurationInstance.ref(field));
119+
120+
boolean hasBeanAnnotation = element.getAnnotation(Bean.class) != null;
121+
if (hasBeanAnnotation) {
122+
JClass fieldGeneratedBeanClass = holder.refClass(fieldType.fullName() + "_");
123+
124+
ncHolder.initIfNonConfiguration.invoke(cast(fieldGeneratedBeanClass, field), "rebind").arg(_this());
125+
}
126+
127+
}
128+
129+
}

0 commit comments

Comments
 (0)