Ricardo's Blog

  • 03:43:16 pm on January 19, 2011 | 3
    Tags: , ,

    The Problem: Adapters cannot be injected into Activities because the BaseAdapter constructor requires a Context be passed to it.

    The Solution: Create a new Guice @InjectAdapter annotation and TypeListener that dynamically creates an adapter on injection, passing the inject Activity to the adapter’s constructor.

    So, our activity code might look something like this:

    public class SampleActivity extends GuiceListActivity {
    
     @Inject
     List<Integer> items;
    
     @InjectAdapter
     @Override
     public void setListAdapter(ListAdapter adapter) {
      super.setListAdapter(adapter);
     }
    
     @Override
     public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      Log.i(TAG, "searching for local profiles");
      setContentView(R.layout.main);
     }
    }
    

    Out module might then have the following code:

    bindListener(Matchers.any(), new AdapterTypeListener(binder()));
    bind(ListAdapter.class).to(LocalProfilesAdapter.class);
    

    Where AdapterTypeListener is defined as follows:

    public <T> void hear(TypeLiteral<T> typeLiteral, TypeEncounter<T> typeEncounter) {
    
    Class<T> type = typeLiteral.getRawType();
    
    for (Field field : type.getDeclaredFields()) {
    	if (Adapter.class.isAssignableFrom(field.getType()) && field.isAnnotationPresent(InjectAdapter.class)) {
    		typeEncounter.register(new AdapterMembersInjector<T>(field));
    	}
    }
    
    for (Method method : type.getDeclaredMethods()) {
    	if(method.isAnnotationPresent(InjectAdapter.class)) {
    		typeEncounter.register(new AdapterMembersInjector<T>(method));
    	}
    }
    }
    

    Last we could define AdapterMembersInjector as follows:

    public class AdapterMembersInjector<T> implements MembersInjector<T> {
    
    	private Field field;
    	private Method setter;
    	TypeEncounter<T> typeEncounter;
    
    	AdapterMembersInjector(Field field, TypeEncounter<T> typeEncounter) {
    		this.field = field;
    		field.setAccessible(true);
    	}
    
    	AdapterMembersInjector(Method setter, TypeEncounter<T> typeEncounter) {
    		this.setter = setter;
    		setter.setAccessible(true);
    	}
    
    	public void injectMembers(T activity) {
    		Adapter adapter;
    
    		if(field != null) {
    			try {
    				field.set(activity, adapter);
    			} catch (IllegalAccessException e) {
    				throw new RuntimeException(e);
    			}
    		} else if(setter != null) {
    			try {
    				setter.invoke(activity, adapter);
    			} catch (IllegalArgumentException e) {
    				throw new RuntimeException(e);
    			} catch (IllegalAccessException e) {
    				throw new RuntimeException(e);
    			} catch (InvocationTargetException e) {
    				throw new RuntimeException(e);
    			}
    		}
    	}
    
    }
    

    The problem is how do we create a reference to Adapter in the above MembersInjector, with all the correct dependencies including the Activity context injected into it’s constructor?

     

Comments


Leave a comment