Подтвердить что ты не робот

Как настроить инъекцию зависимостей с помощью кинжала для вещей, отличных от действий и фрагментов?

Я начал настраивать инъекцию зависимостей, используя Dagger следующим образом. Пожалуйста, имейте в виду, что я исправляю свою реализацию, поскольку у меня могут быть ошибки! Реализация следует андроид-простой пример, предоставляемый проектом. Ниже вы можете увидеть, как я успешно добавил инъекцию зависимостей для Activities и Fragments. Я стараюсь, чтобы это было легко, поэтому я решил ввести Timber в качестве замены регистратора для Android 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 нигде не используется, но может оказаться полезным, когда нужны Context и LayoutInflater, например. в 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 в методы BaseCursorAdapter, BaseContentProvider, BaseSQLiteOpenHelper, BaseService, BaseAsyncTask и static?

Устаревший андроидный пример Кристофера Перри указывает, как ввести Adapter в ListFragment, но не как ввести Context, Resources, LayoutInflater, Cursor в (Cursor)Adapter или просто Timber.


Литература:

4b9b3361

Ответ 1

Ознакомьтесь с примерами Энди Денни для инъекций в разных сценариях:

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

Некоторые точки, в которые я вставляю:

  • Activity, Service и Fragment подклассы: в onCreate Подклассы
  • BroadcastReceiver (включая, например, AppWidgetProvider): in onReceive

Ответ 2

tl; dr В этом вопросе многое происходит, но это может помочь с Gist of mine. Если вы можете получить Context где-нибудь, вы можете ввести его на основе ObjectGraph, поддерживаемого вашим классом Application.


Изменить JJD:

ObjectGraph в Gist можно интегрировать следующим образом:

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 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);
    }
}

Аналогичные работы для BaseCursorAdapter, BaseContentProvider и BaseService.

Ответ 3

Мы должны были сделать то же самое (т.е. ввести логгер всюду). Мы закончили создание очень маленькой статической обертки (доступной везде), которая обертывает класс, который вводится через кинжал статически.

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

Ответ 4

Инъекция Context, Resources и LayoutInflater (передача контекста приложения при его обновлении в приложении).

@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;
  }
}

Конечно, вы должны квалифицировать контекст с аннотацией, определяющей его контекст приложения (например, @ForApplication).

Если вам нужен эквивалент Roboguice @InjectResource(int), я ничего не могу придумать. Butterknife кажется правильным lib, чтобы добавить это. См. здесь

Ответ 5

Вы можете добавить эти конструкции в класс 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)

Это нужно вызвать в конструкторе для объектов, созданных вручную, или onCreate (или аналоговых) для тех, которые управляются системой Android.