-
Notifications
You must be signed in to change notification settings - Fork 43
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
Add ScopeFactory API #136
Add ScopeFactory API #136
Conversation
aa8933d
to
a455430
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leaving open for pending questions
|
||
private ScopeFactory() {} | ||
|
||
private static final Map<Class<?>, Constructor<?>> scopeClassToCreateMethod = new HashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this caching layer might be redundant post android 7.0 where the reflection apis where optimized with native invocations. At that point the methods are already cached in the native representation of the class Object so the vm maintains its own cache for them and we don't need to tax the heap with it.
Do you want to spend time to look into that and decide if this needs to be api gated ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting - didn't know about this. Do you remember where you read this or the relevant source code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
http://androidxref.com/9.0.0_r3/xref/art/runtime/native/java_lang_Class.cc
I guess we will still incur the overhead of the result array
So the tradeoff of the heap cache is that we are taxing the heap with
the map vs allocating a new array every time we need to create a root scope
either to get the result of getDeclaredConstructors or to pass params to getDeclaredConstructor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it - in this case I'll move forward with the current approach since it's the simplest and safest.
if (constructor == null) { | ||
Class<?> scopeImplClass = getScopeImplClass(scopeClass); | ||
constructor = scopeImplClass.getDeclaredConstructors()[0]; | ||
scopeClassToCreateMethod.put(scopeClass, constructor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this second field access is redundant just hoist it to a local var at the top
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what you mean here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scopeClass is a field reference so its doing a double field read
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather tackle this as a post processing step at the binary level if we're concerned about field access performance.
private static Class<?> getScopeImplClass(Class<?> scopeClass) { | ||
String scopeImplClassName = getScopeImplClassName(scopeClass); | ||
try { | ||
return Class.forName(scopeImplClassName, false, scopeClass.getClassLoader()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a reason that you avoid the initialization here?
If it hasn't been done already It will probably have to do it anyway immediately on the new instance
unless you are considering warming the cache separately for some reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which initialization are you referring to here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the class initialization controlled by the false parameter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha - will update
|
||
// Inspired by https://github.com/square/javapoet/issues/295 | ||
private static String getScopeImplClassName(Class<?> scopeClass) { | ||
StringBuilder sb = new StringBuilder(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
smarter init value than 16 chars ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would be a better initial value?
d27718c
to
90f6bf3
Compare
a455430
to
ba23e63
Compare
ba23e63
to
9c15c79
Compare
9c15c79
to
f4f6add
Compare
6cd2d97
to
e00e899
Compare
f4f6add
to
25004d6
Compare
e00e899
to
dca40c2
Compare
25004d6
to
a208b19
Compare
dca40c2
to
c6e8526
Compare
a208b19
to
00ba8f8
Compare
c6e8526
to
423d3b5
Compare
d921921
to
21343e5
Compare
6485ab2
to
8de6db6
Compare
9c24149
to
9fd42d8
Compare
|
||
private static final Map<Class<?>, Constructor<?>> scopeClassToCreateMethod = new HashMap<>(); | ||
|
||
public static <S extends Creatable<NoDependencies>> S create(Class<S> scopeClass) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andreasnomikos @ugonna @Ericliu001 Heads up that I've added this convenience API for Scopes that don't have any dependencies. Let me know if you have any concerns with this.
} else { | ||
return (S) constructor.newInstance(dependencies); | ||
} | ||
} catch (IllegalAccessException e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you're handling them all the same, do you not want to:
try {
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private static final Map<Class<?>, Constructor<?>> scopeClassToCreateMethod = new HashMap<>(); | ||
|
||
public static <S extends Creatable<NoDependencies>> S create(Class<S> scopeClass) { | ||
return create(scopeClass, NO_DEPENDENCIES); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And if someone sends in a Creatable
with a non-zero arg constructor to this method? We would be passing NO_DEPENDENCIES
to that constructor. That's legal, but incorrect usage. Do we want to warn or fail against that case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The class that we end up instantiating is the ScopeImpl associated with the Scope class that is passed in. If someone passes in a Creatable that is not a Scope then we will fail to find the ScopeImpl and crash at runtime. We could add an explicit error if the passed class is not annotated with @Scope
but that adds some runtime overhead. Might be worth it - we can discuss this as a potential follow up.
9fd42d8
to
76780a4
Compare
Resolves #125