Skip to content
This repository was archived by the owner on Feb 26, 2023. It is now read-only.
This repository was archived by the owner on Feb 26, 2023. It is now read-only.

Provide a Context scope for @EBean #205

Description

@pyricau

This issue is based on feedback in #197, where a user wanted to shared a ViewHolder between various components tied to an Activity.

Currently, @EBean classes can have a default scope or a singleton scope. The default scope means a new instance is created for each injection, and the singleton scope means there's only one instance for the whole application life.

We need a Context scope for cases where two beans depends on the same bean and want to manipulate the same instance. The beans will have the same lifespan as the context instance.

They may be saved and reinjected in an activity when annotated with @NonConfigurationInstance.

@EActivity
public class MyActivity extends Activity {

  @Bean
  BeanA beanA;

  @Bean
  BeanB beanB;
}

BeanA also references BeanB, and we want the same BeanB instance to be in BeanA and MyActivity

@EBean
public class BeanA {
  @Bean
  BeanB beanB;
}

So we use the Context scope :

@EBean(scope = Scopes.Context)
public class BeanB {
}

How do we implement that ?

We'll let each generated activity (or service, or application) implement an interface with the following contract (may change):

public interface BeanHolder {
  // May return null if the bean hasn't been created yet
  <T> T getBean_(Class<T> key);
  <T> void putBean_(Class<T> key, T value);
}

The implementation should be along those lines:

public class MyActivity_ extends MyActivity implements BeanHolder {

  private final Map<Class<?>, Object> beans_ = new HashMap<Class<?>, Object>();

  public void <T> T getBean_(Class<T> key) {
    return (T) beans_.get(key);
  }

  public void <T> void putBean_(Class<T> key, T value) {
    beans_.put(key, value);
  }
}

And the build method for a bean with Context scope :

public class MyBean_ extends MyBean {

    public MyBean_ build(Context context) {
      if (context instanceof BeanHolder) {
         BeanHolder beanHolder = (BeanHolder) context;
         MyBean_ bean = beanHolder.getBean_(MyBean_.class);
        if (bean == null) {
          bean = new MyBean_(context);
          beanHolder.putBean_(bean);
        }
        return bean;
      } else {
        // TODO => what should we do ?
      }
    }
}

Notice that we handle the case where the given context is not a BeanHolder. This may happen when singletons depend on beans with context scope, or when singleton depends on beans with default scope which themselves depend on beans with context scope. In either cases, we know that our bean will have an "application" / "singleton" scope, because it now depends on the application context. So we should probably have a singleton like construct, and return the singleton reference in that case.

Activity

  1. pyricau commented on May 27, 2012

    @pyricau
    ContributorAuthor

    Once thing to take care of is to make sure that view injection only happens once, and doesn't occurs again if afterSetContentView_() is called more then once.

  2. fc1943s commented on Jun 15, 2012

    @fc1943s

    (I just saw this on 2.7 milestone, sorry for the "topic reviving")
    Damn, this is almost what i was trying to explain to you on google groups.
    A way to register beans manually, to retrieve later by @bean. Something like ComponentFactory/Provider.
    But for work with singletons (what i need), would be necessary create another generated class, AA specific.
    example.

    My specific usage:

    // I need this because only daoHolder can have @EBean. My DAO classes are in another project.
    ProductDao productDao = daoHolder.getDao(ProductDao.class);
    AABeanHolderSomething.putBean(productDao);
    

    On activity/enhanced etc:

    // Or @Bean
    @RegisteredBean
    ProductDao productDao;
    

    Code:

    public class AABeanHolderSomething
    {
        private static final Map<Class<?>, Object>  beans   = new HashMap<Class<?>, Object>();
    
        public static void putBean(Object obj)
        {
            beans.put(obj.getClass(), obj);
        }
    
        // Generated classes usage
        @SuppressWarnings("unchecked")
        public static <T> T getBean(Class<T> cls)
        {
            return (T)beans.get(cls);
        }
    }
    
  3. pyricau commented on Jun 21, 2012

    @pyricau
    ContributorAuthor

    Hi @stewunknown

    I don't think this is the same issue, although it has similarities. This issue is mainly about introducing a Context scope, for existing @EBean annotated classes.

    What you want is injection support for non @EBean annotated classes, which is a complete different issue (regardless of the solution, that might look similar).

    I'm not saying it can't be done, but I'm not really fond of this because there's no way to check at compile time that you actually registered the bean with AABeanHolderSomething.putBean(productDao);.

  4. fc1943s commented on Jun 21, 2012

    @fc1943s

    This could be done implementing some @EBeanProvider that works like an R.java.
    I guess this way you can be sure that the beans are registered, checking for the method names, like this:

    //
    // Provider
    //
    
    @EBeanProvider
    public class BeanRegistration
    {
        @Bean
        DaoHolder   daoHolder;
    
        UserDao getUserDao()
        {
            return daoHolder.getDao(UserDao.class);
        }
    
        ProductDao getProductSomethingDao()
        {
            return daoHolder.getDao(ProductDao.class);
        }
    }
    
    //
    // Activity
    //
    
    @EActivity(R.layout.product_form_activity)
    public class ProductFormActivity extends Activity
    {
        @RegisteredBean
        UserDao     userDao;
    
        @RegisteredBean
        ProductDao  productSomethingDao;
    }
    

    and sorry for the bad english, I'm brazilian ;)

  5. ghost assigned on Feb 27, 2013
  6. pyricau commented on Mar 3, 2013

    @pyricau
    ContributorAuthor

    I gave it a few more thoughts and I think this is overengineering for an edge case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions