Dagger 注入 - 何时调用 provide 方法

Dagger injection - when do the provide methods get called

我正在 Android 上对 Dagger 进行一些试验,这似乎是一个很好的隔离依赖项的工具。首先,我从 GitHub 复制了 android-activity-graphs 示例:https://github.com/square/dagger/tree/master/examples/android-activity-graphs

然后我向 ActivityModule

添加了几个 classes
@Module(
    injects = {
            HomeActivity.class,
            HomeFragment.class
    },
    addsTo = AndroidModule.class,
    library = true
)

public class ActivityModule {
    private static final String TAG = "Activity_Module";
    private final DemoBaseActivity activity;

    public ActivityModule(DemoBaseActivity activity) {
        this.activity = activity;
    }

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

    @Provides
    @Singleton
    ActivityTitleController provideTitleController() {
        return new ActivityTitleController(activity);
    }

    //My addition from here
    @Provides
    @Singleton
    Player providePlayer() {
        Log.i(TAG, "in providePlayer()");
        return new MyAndroidTestPlayer(activity);
    }

    @Provides
    RandomNumberGenerator provideRandomNumberGenerator() {
        Log.i(TAG, "in provideRandomNumberGenerator()");
        return new RealRandomNumberGenerator();
    }
}

图形初始化的其余部分与 github 中的示例相同。

令我困惑的是,注入的对象在 class 被注入到 (HomeFragment) 之后是空的......有一段时间了。 同样,HomeFragment 与 github 中示例中的 HomeFragment 或多或少相同,只是我自己添加了一些内容。 如果我在 HomeFragment 的 onCreateView() 中调用注入的 Player 或 RandomNumberGenerator 对象中的任何一个,我会收到一条错误消息,指出它们为空。 但是,如果我在内部 OnClickListener - onClick() 中调用它们,它们将按预期工作。

任何人都可以指出我所缺少的知识以了解这里发生的事情吗?

public class HomeFragment extends DemoBaseFragment {
public static final String TAG = "HOME_FRAGMENT";

public static HomeFragment newInstance() {
    return new HomeFragment();
}

@Inject
ActivityTitleController titleController;
@Inject
Player player;
@Inject
RandomNumberGenerator randomNumberGenerator;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    TextView tv = new TextView(getActivity());
    if (randomNumberGenerator != null) {
        Log.i(TAG, "randomNumberGenerator is NOT null");
    } else {
        Log.e(TAG, "randomNumberGenerator is NULL!");
    }
    if (player != null) {
        Log.i(TAG, "player is NOT null");
    } else {
        Log.e(TAG, "player is NULL!");
    }
    //int randomNumber = randomNumberGenerator.getIntegerInRange(48, 50);
    //player.playTestNote();
    tv.setGravity(CENTER);
    tv.setText("Play test note");
    tv.setTextSize(40);
    tv.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            player.playTestNote();
            int randomNumber = randomNumberGenerator.getIntegerInRange(48, 50);
            Log.i(TAG, "Text view clicked, random number is: " + randomNumber);
        }
    });
    return tv;
}

我用来测试的 classes 非常微不足道(RandomNumberGenerator 比 Player class 更重要)。我将跳过 RandomNumberGenerator。这是实现 Player 的 MyAndroidTestPlayer(只有一个 playTestNote() 方法)。

public class MyAndroidTestPlayer implements Player {

SoundPool soundPool;
private static final int MAX_STREAMS = 10;
private static final int DEFAULT_SRC_QUALITY = 0;
private static final int HARDCODED_SOUND_RESOURCE_C3 = R.raw.midi_48_c3;
private static final int DEFAULT_PRIORITY = 1;
private static final String TAG = "MyAndroidTestPlayer";
private Context context;
private boolean isLoaded = false;
private int streamId;
private int soundId;

public MyAndroidTestPlayer(Context context) {
    this.context = context;
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        createNewSoundPool();
    } else {
        createOldSoundPool();
    }
}

protected void createOldSoundPool() {
    soundPool = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC, DEFAULT_SRC_QUALITY);
    Log.i(TAG, "created old sound pool");
    loadSoundPool();
}

protected void createNewSoundPool() {
    AudioAttributes attributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).build();
    soundPool = new SoundPool.Builder().setAudioAttributes(attributes).build();

    Log.i(TAG, "created new sound pool");
    loadSoundPool();
}

private void loadSoundPool() {
    soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {

        @Override
        public void onLoadComplete(SoundPool soundPool, int sampleId,
                                   int status) {
            isLoaded = true;
            Log.i(TAG, "Loaded");
            Log.i(TAG, "Status: " + status);
        }
    });
    soundId = soundPool.load(context, HARDCODED_SOUND_RESOURCE_C3, DEFAULT_PRIORITY);
}

@Override
public void playTestNote() {
    Log.i(TAG, "before loaded check");
    if (isLoaded) {
        streamId = soundPool.play(soundId, 1, 1, 1, 0, 1f);
        Log.i(TAG, "Played Sound");
        Log.i(TAG, "streamId: " + streamId);
    }
}

}

提前谢谢你。

我想我明白了。 HomeFragment 必须 @Override onCreate 并使用以下行注入自身

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

    ((DemoBaseActivity) getActivity()).inject(this);
}

那对我有用。

我希望这对我理解水平的其他用户有所帮助。