Capture the application on create event before

33 views
Skip to first unread message

Mingfai

unread,
Jun 28, 2013, 3:23:08 PM6/28/13
to tran...@googlegroups.com
hi John,

For @Application, what do you think if there is an event that got triggered at the beginning of onCreate before any Transfuse stuff is ran?

e.g. I want to register a crash report or analytics function at the earliest possible time with a reference to the Application context, so any issue during the Module init stage can be captured. What do you think?

If there is such a hook point, I may just store a reference to the Application for use in other places. :-)

regards,
mingfai

John Ericksen

unread,
Jun 28, 2013, 7:45:31 PM6/28/13
to tran...@googlegroups.com
Mingfai,

If you want to use the Transfuse @Application, the earliest you can
execute something in the generated onCreate() method is in the
constructor of an @Application:

@Application
public class ExampleApp{

@Inject
public ExampleApp(android.app.Application app){
System.out.println("starting");
}
}

That being said, the @OnCreate is also triggered first in the generated
onCreate() method.

If you require something sooner, you would have to resort to the legacy
Application.

John
> --
> You received this message because you are subscribed to the Google
> Groups "Transfuse" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to transfuse+...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

John Ericksen

unread,
Jun 28, 2013, 7:59:36 PM6/28/13
to tran...@googlegroups.com
This brings to mind another idea.  For the purposes of 3rd party library integration, I could see some sort of pre-, post- event hooks for all of the registered events in Transfuse.  This would help in tying in libraries that use subclasses (IE: SherlockActivity) to weave in their functionality.  I think this would be reserved for library integration only as pre and post hooks may cause confusion.

John

Mingfai

unread,
Jun 29, 2013, 1:18:36 PM6/29/13
to tran...@googlegroups.com
replying to group.

On Sun, Jun 30, 2013 at 1:05 AM, John Ericksen <johnc...@gmail.com> wrote:
So, here's some information on @Bootstrap.  I use Transfuse within Transfuse for Dependency Injection.  @Bootstrap tells the internal engine to generate a factory for it, so Boostraps.inject() knows about the class. Otherwise, Transfuse would have to generate the object graph on demand during runtime.

Im thinking of just copying the feature into a new one, here's the api Im thinking about:

@Injectable
public void Example{
    @Inject ....
    public void inject(){
        Injectors.inject(this);
    }
}

which would generate an Injector for Example (Example$Injector?) and associate it with the Injectors utility class.

@Injectable is somehow better than @Bootstrap, but still we got to document clearly it's not for automated injection but require calling the API to do injection. It's great to support this so that for cases such as using legacy Application or Activity, we can quickly inject all the fields.

Thanks,
Mingfai

 

By the way, would you like to have this conversation on the group?

John



On 06/29/2013 10:33 AM, Mingfai wrote:
it's obviously better. (the less code, the better, to me) So @Bootstrap basically enable manually bootstrapping. i.e. use of Bootstrap.inject()

I'm thinking about whether it is a good name. Some people expect @Bootstrap works just like @Activity or @Application or @Fragment that will do automated bootstrapping. We'll need to document it clearly telling people @Bootstrap won't get your class automatically bootstrapped, and it is created because in some scenarios, we cannot use automated injection (like legacy support).


John Ericksen

unread,
Jun 30, 2013, 8:06:43 PM6/30/13
to tran...@googlegroups.com
A handful of thoughts on this.

First, I think Injectors.inject() could be a really natural and straight forward way to support injecting unit tests, as well as a good option for legacy conversions.  Expect to see this soon.

Second,, should we reinitialize the scope for every inject() call as Guice does?  We could also do the opposite, drawing from the generated Scopes utility.  I guess this boils down to if we truly want scopes (like singleton) to be per-application or per-injection.  I think we may want to at least have an option to reinitialize.

Third, should we combine Factories and the proposed Injector?  This would further parallel Guice and Dagger for this sort of manual setup.  I'm leaning towards combining the two into a single Injector class with the following api:

class Injector{
    T get(Class<T> clazz);
    <T> T inject(T instance);
}

What do you think?

John

Mingfai

unread,
Jul 1, 2013, 6:35:29 PM7/1/13
to John Ericksen, tran...@googlegroups.com
On Mon, Jul 1, 2013 at 8:06 AM, John Ericksen <johnc...@gmail.com> wrote:
A handful of thoughts on this.

First, I think Injectors.inject() could be a really natural and straight forward way to support injecting unit tests, as well as a good option for legacy conversions.  Expect to see this soon.

Second,, should we reinitialize the scope for every inject() call as Guice does?  We could also do the opposite, drawing from the generated Scopes utility.  I guess this boils down to if we truly want scopes (like singleton) to be per-application or per-injection.  I think we may want to at least have an option to reinitialize.

You mean reinitialize scope per every createInjector call? and have singleton per injector?
Injector injector = Guice.createInjector(new BillingModule());
 My initial thought is Guice can do it because that happen in runtime, but it is not really true as Dagger supports exactly the same as it's compile time generated. 
ObjectGraph objectGraph = ObjectGraph.create(new DripCoffeeModule());
The difference are:
  • Guice "installs" other modules at Injector initialization time, programmatically
  • Dagger "includes" other modules in compile time, in annotation
Notice that Dagger also requires to use "injects" attribute in some/many cases for its to know a module support injection to a class. Roughly speaking, when there is a Injector.injects( target ), the target class is required to be in the "injects" attribute array. Obviously, code is generated in compile time to support that inject call. 
@Module( injects = CoffeeApp.class )
Both of them are Java DI framework. JSE doesn't have any default scope... other than the JVM process and classloader. Say in another way, traditional Java (unscoped) singleton is per classloader. 

Transfuse is Android specific. Android normally (or always?) has an Application scope. AFAIK we can't use classloader. For the Android usages that one start a service in another process, or allow support IPC calls or calling a IPC interface, the application has always be created first, right?

If it's the case, I suppose, by default, any unscoped singleton is actually bound to Android Application scope.

I suppose if Transfuse doesn't provide a way to start another scope (injector/object graph), it would be too limited to anyone with IoC/DI framework experience. I wish:
  • by default, any created scope extends from the default Android Application (assume my assumption about Android is correct), so we can always inject the Application to anywhere 

  • by default, every Activity has its own scope extends from Application scope.
    If there are more than one default scopes, there should annotations to define it.
    If a @Singleton is used by two different activities, it may be the same singleton (i.e. application scope) or two different Activity Scope singelton.

    In Android, a singleton can be injected to Application or Service. 

    I think by default, any unscoped singleton shall be in Application scope.(with my assumption about Android that everything are under Application anyway) If one want to make a singleton per activity/service, then he needs to use an annotation to clarify the scope.

  • As a future idea, I think Fragment in the same FragmentStatePagerAdapter may potentially be a default scope, if the fragment is managed by Transfuse but not started programmatically, for sure. But let's point this aside first.
I think start new scope refers to starting a scope other than the above default scopes. e.g. within an activity, one may want to start different object graphs. I guess we may want to get the Android specific scope first. 



Third, should we combine Factories and the proposed Injector?  This would further parallel Guice and Dagger for this sort of manual setup.  I'm leaning towards combining the two into a single Injector class with the following api:

class Injector{
    T get(Class<T> clazz);
    <T> T inject(T instance);
}

What do you think?


I didn't use Factory and don't really not what exactly it is for now. :-) I would use when I need to use AssistedInject that is useful to me. What exactly factory does other than AssistedInject? it's similar to Provider. 

For the above signature, the get may needs to have variance like
  T get(Class<T>clazz, ??? scope); 

Scope handle Activity scope, @Named etc. There is also the TypeLiteral case that, e.g. when one want to inject a "List<String>". 

Mingfai

 

Mingfai

unread,
Jul 1, 2013, 6:42:35 PM7/1/13
to John Ericksen, tran...@googlegroups.com
On Tue, Jul 2, 2013 at 6:35 AM, Mingfai <mingf...@gmail.com> wrote:
On Mon, Jul 1, 2013 at 8:06 AM, John Ericksen <johnc...@gmail.com> wrote:
A handful of thoughts on this.
= 

.... AFAIK we can't use classloader. ...


Just to clarify we can use custom classloader in Android. And there is DexClassLoader.


Mingfai

unread,
Jul 1, 2013, 6:45:08 PM7/1/13
to John Ericksen, tran...@googlegroups.com
And I don't think Transfuse should care about it. :-)

 

John Ericksen

unread,
Jul 1, 2013, 11:27:07 PM7/1/13
to tran...@googlegroups.com
Right, Guice scopes singletons to the Injector.  There should probably be the same functionality with Transfuse's Injector.  I like the idea though of having a single injector for the entire application.  This supports the Singleton scope across the entire application.

Transfuse, although it targets Android specifically, is not necessarily Android specific.  Transfuse could be adopted to support other frameworks.  In fact, my coworkers and I were talking about using it behind a Swing application.  With that in mind, Singletons are not tied directly to the Android Application.  I decided to take this route to support JSR330 and from some advice in the Android documentation.  The documentation stated that singletons should be written using a static final holder variable instead of using the Application.  (nearest thing I could find recently: " There is normally no need to subclass Application. In most situation, static singletons can provide the same functionality in a more modular way." http://developer.android.com/reference/android/app/Application.html

From my understanding of Android, Applications are just Singletons in your application.  This means that they should be compatible with the singleton scope I described above.  In other words, you don't risk a memory leak if you hold a reference to them in a singleton.  You should be able to inject an Application into a singleton (which is a bug I'm trying to fix currently :-) ).

Have you looked at the custom scopes functionality available in Transfuse?  I believe it supports the cases you mentioned.  I've purposefully left it open so you can define scopes around anything you like, as long as they implement the org.androidtransfuse.scope.Scope interface.  Each scope is a Singleton in Transfuse, but you can base the backing code on whatever you like (location, time, credentials, etc).  This makes it extremely flexible.

Yes, @Factory is basically an analogy for Assisted Injection.  Technically you could use it to write providers as well if necessary (ie: no @Assisted arguments).

For the case you gave:

T get(Class<T>clazz, ??? scope);
Each object's scope is defined on the module level.  This gives Transfuse the necessary opportunity to wire up the graph accordingly.

Im not sure type literals are necessary in Transfuse, because you can define the generics in a @Provides method:

@Provides
List<String> getStringList()...

Transfuse is smart enough to recognize the entire type, and qualifier annotations.

And I agree, Transfuse should not mess around with the Classloader.  Transfuse should already be done with its work by the time classloading happens.

John

Mingfai

unread,
Jul 1, 2013, 11:32:50 PM7/1/13
to John Ericksen, tran...@googlegroups.com
On Tue, Jul 2, 2013 at 11:27 AM, John Ericksen <johnc...@gmail.com> wrote:

Im not sure type literals are necessary in Transfuse, because you can define the generics in a @Provides method:

@Provides
List<String> getStringList()...

Transfuse is smart enough to recognize the entire type, and qualifier annotations.

ic. Guice works in runtime that Java doesn't provide a way to get the generic type, but compile time DI frameworks do not have such problem. 

John Ericksen

unread,
Jul 1, 2013, 11:38:44 PM7/1/13
to tran...@googlegroups.com
Actually, you'd be surprised what generics you can access during Runtime.  See this StackOverflow post I made a while back:

http://stackoverflow.com/questions/9548779/java-generics-accessing-generic-type-at-runtime

John Ericksen

unread,
Jul 1, 2013, 11:41:55 PM7/1/13
to tran...@googlegroups.com
Mingfai,  how often do you truly need objects to be context-scoped?

Mingfai

unread,
Jul 2, 2013, 12:02:43 AM7/2/13
to John Ericksen, tran...@googlegroups.com
On Tue, Jul 2, 2013 at 11:41 AM, John Ericksen <johnc...@gmail.com> wrote:
Mingfai,  how often do you truly need objects to be context-scoped?

For my Android project, the majority of injection are (application scope) singleton and "prototype" instance with @Inject attributes, and the only "context-scoped" is the Activity itself, as in some cases the Activity is required.  So I never use custom scope at all. 

The way i use singleton isn't the most efficient way to use memory. In the future I may review it and move stuff to activity scope properly so as to allow killing an activity to free up memory.

IMHO, in general, if there are a lot of data (e.g. a few Mb), it should flush to disk instead of keep in heap anyway. 

Reply all
Reply to author
Forward
0 new messages