如何使用Dagger为活动和片段以外的东西设置依赖注入?

时间:2013-09-20 01:00:08

标签: android dependency-injection android-asynctask android-contentprovider dagger

我开始使用Dagger设置依赖注入,如下所示。因为我可能有错误,所以请鼓励我更正我的实施!实现遵循项目提供的android-simple example。在下文中,您可以看到我是如何为ActivitiesFragments成功添加依赖项注入的。我现在试着保持简单,所以我决定将Timber作为Android's log util的记录器替换。

import android.app.Application;
import java.util.Arrays;
import java.util.List;
import dagger.ObjectGraph;
import com.example.debugging.LoggingModule;

public class ExampleApplication extends Application {

    private ObjectGraph mObjectGraph;

    protected List<Object> getModules() {
        return Arrays.asList(
                new AndroidModule(this),
                new ExampleModule(),
                new LoggingModule()
        );
    }

    private void createObjectGraphIfNeeded() {
        if (mObjectGraph == null) {
            Object[] modules = getModules().toArray();
            mObjectGraph = ObjectGraph.create(modules);
        }
    }

    public void inject(Object object) {
        createObjectGraphIfNeeded();
        mObjectGraph.inject(object);
    }
}

到目前为止AndroidModule并未在任何地方使用,但在需要ContextLayoutInflater时可能会有所帮助,例如在CursorAdapters

import android.content.Context;
import android.view.LayoutInflater;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;

/**
 * A module for Android-specific dependencies which require a {@link Context} 
 * or {@link android.app.Application} to create.
 */
@Module(library = true)
public class AndroidModule {
    private final ExampleApplication mApplication;

    public AndroidModule(ExampleApplication application) {
        mApplication = application;
    }

    /**
     * Allow the application context to be injected but require that it be 
     * annotated with {@link ForApplication @Annotation} to explicitly 
     * differentiate it from an activity context.
     */
    @Provides @Singleton @ForApplication Context provideApplicationContext() {
        return mApplication;
    }

    @Provides @Singleton LayoutInflater provideLayoutInflater() {
        return (LayoutInflater) mApplication
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
}

我不确定特定于应用程序的提供程序会在何处。我现在待记录

import dagger.Module;

@Module(
        complete = false
)
public class ExampleModule {
    public ExampleModule() {
         // TODO put your application-specific providers here!
    }
}

我准备了LoggingModule,可以访问 Timber

package com.example.debugging;

import javax.inject.Singleton;

import dagger.Module;
import dagger.Provides;
import com.example.BuildConfig;
import com.example.activities.BaseFragmentActivity;
import com.example.activities.DetailsActivity;
import com.example.fragments.BaseListFragment;
import com.example.fragments.ProfilesListFragment;
import timber.log.Timber;

@Module(injects = {
        // Activities
        BaseFragmentActivity.class,
        DetailsActivity.class,
        // Fragments
        BaseListFragment.class,
        ProfilesListFragment.class
})
public class LoggingModule {

    @Provides @Singleton Timber provideTimber() {
        return BuildConfig.DEBUG ? Timber.DEBUG : Timber.PROD;
    }
}

Activities的基类将自身注入对象图...

package com.example.activities;

import android.os.Bundle;    
import com.actionbarsherlock.app.SherlockFragmentActivity;    
import javax.inject.Inject;
import com.example.ExampleApplication;
import timber.log.Timber;

public abstract class BaseFragmentActivity extends SherlockFragmentActivity {

    @Inject Timber mTimber;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
        super.onCreate(savedInstanceState);
        ((ExampleApplication) getApplication()).inject(this);
    }
}

... Timber 已经存在的任何子类受益。

package com.example.activities;

import android.os.Bundle;
import com.example.R;

public class DetailsActivity extends BaseFragmentActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_details);
        mTimber.i("onCreate");
        // ...
    }
}

Fragments相同:基类执行脏工作...

package com.example.fragments;

import android.os.Bundle;
import com.actionbarsherlock.app.SherlockListFragment;
import javax.inject.Inject;
import com.example.ExampleApplication;
import timber.log.Timber;

public abstract class BaseListFragment extends SherlockListFragment {

    @Inject Timber mTimber;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ((ExampleApplication) getActivity().getApplication()).inject(this);
    }

}

......子类受益于超类。

package com.example.fragments;

import android.os.Bundle;

public class ProfilesListFragment extends BaseListFragment {

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // TODO This might be a good example to inject resources
        // in the base class. But how?
        setEmptyText(getResources()
           .getString(R.string.profiles_list_no_content));
        mTimber.i("onActivityCreated");
        // ...
    }

}

到目前为止一切顺利。但是如何将 Timber 注入BaseCursorAdapterBaseContentProviderBaseSQLiteOpenHelperBaseServiceBaseAsyncTaskstatic辅助方法?

已弃用的android example by Christopher Perry指出如何将Adapter注入ListFragment但不注明如何注入ContextResourcesLayoutInflaterCursor进入(Cursor)Adapter Timber


参考文献:

5 个答案:

答案 0 :(得分:8)

查看Andy Dennie在不同情况下注射的示例:

https://github.com/adennie/fb-android-dagger

我注入的一些要点:

  • ActivityServiceFragment子类:onCreate
  • BroadcastReceiver子类(包括,例如AppWidgetProvider):onReceive

答案 1 :(得分:5)

tl; dr在这个问题中有很多事情要发生,但Gist of mine可能会有所帮助。如果您可以在某个地方获得Context,则可以根据ObjectGraph课程维护的Application进行注入。


JJD编辑:

Gist中的ObjectGraph可以按如下方式整合:

public class ExampleApplication extends Application
        implements ObjectGraph.ObjectGraphApplication {

/* Application Lifecycle */
    @Override
    public void onCreate() {
        // Creates the dependency injection object graph
        _object_graph = ObjectGraph.create(...);
    }

/* ObjectGraphApplication Contract */
    @Override
    public void inject(@Nonnull Object dependent) {
        _object_graph.inject(dependent);
    }

    /** Application's object graph for handling dependency injection */
    private ObjectGraph _object_graph;
}

...

public abstract class BaseFragmentActivity extends SherlockFragmentActivity {
    @Inject Timber _timber;

    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        ObjectGraph.inject(this);
    }
}

...

public abstract class BaseListFragment extends SherlockListFragment {    
    @Inject Timber _timber;

    @Override
    public void onActivityCreated(Bundle icicle) {
        super.onActivityCreated(icicle);
        ObjectGraph.inject(this);
    }
}

BaseCursorAdapterBaseContentProviderBaseService的类似作品。

答案 2 :(得分:2)

我们必须做同样的事情(即在任何地方注入记录器)。我们最终创建了一个非常小的静态包装器(因此可以在任何地方使用),它包含一个静态注入匕首的类。

package com.example.secret;

import javax.inject.Inject;

import com.example.interfaces.Logger;

public class LoggerProvider {

    @Inject
    static Logger logger;

    public LoggerProvider() {

    }

    public static Logger getLogger() {
            return logger;
    }

}

Logger实现您的日志记录界面。要在应用程序级别注入它,您需要:

   graph = ObjectGraph.create(getModules().toArray());
   graph.injectStatics();

此处代码:https://github.com/nuria/android-examples/tree/master/dagger-logger-example

答案 3 :(得分:2)

注入ContextResourcesLayoutInflater(在应用程序中重新启动时传递应用程序上下文)。

@Module(complete = false)
public class AndroidServicesModule {
  private final Context context;

  public AndroidServicesModule(@ForApplication Context context) {
    this.context = context;
  }

  @Provides @Singleton Resources provideResources() {
    return context.getResources();
  }

  @Provides @Singleton LocationManager provideLocationManager() {
    return (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
  }

  @Provides @Singleton LayoutInflater provideLayoutInflater() {
    return (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  }

  @Provides @Singleton Resources provideResources() {
    return context.getResources();
  }

  @Provides @ForApplication Context provideContext() {
    return context;
  }
}

当然你应该qualify带有注释的上下文指定它是应用程序上下文(例如@ForApplication)。

如果你需要相当于Roboguice的@InjectResource(int)我无法想到任何事情。 Butterknife似乎是正确的lib添加它。见here

答案 4 :(得分:0)

您可以将这些构造添加到ExampleApplication类:

private static ExampleApplication INSTANCE;

@Override
public void onCreate() {
    super.onCreate();

    INSTANCE = this;
    mObjectGraph = ObjectGraph.create(getModules());
    mObjectGraph.injectStatics();
    mObjectGraph.inject(this);
}

public static ExampleApplication get() {
    return INSTANCE;
}

之后,你可以用一个简单的线注入任何对象(用this表示):

ExampleApplication.get().inject(this)

这应该在您手动创建的对象的构造函数中调用,或者对于由Android系统管理的对象调用onCreate(或模拟)。