2017-04-13 87 views
0

我正在嘗試關注MVP/Dagger2/RxJava項目的this template匕首2作用域環境問題

我無法將活動上下文注入到演示者中,因爲我知道子組件對所有父提供的邏輯都有開放訪問權限,所以其他注入都會通過。

應用程序組件構建在Application類中,然後在基本演示者活動中進行訪問,然後注入演示者和其他人的相關依賴關係。配置持久組件主要用於保存演示者狀態。

如果我只是手動將上下文從活動傳遞給演示者,那麼DI的目的就是失敗。

我試圖添加範圍到所有組件和模塊,以確保依賴關係可以從圖中正確訪問,但是這不起作用。

我想使用上下文的構造函數注入,我實際上接收到演示者與之通信的活動中的上下文,但演示者沒有,引發錯誤。所以我想知道爲什麼這個活動可以訪問活動上下文,但是演示者卻沒有。

任何指導,將不勝感激。

錯誤

Error:(13, 8) error: [<packageName>.injection.component.ActivityComponent.inject(<packageName>.login.LoginActivity)] android.app.Activity cannot be provided without an @Inject constructor or from an @Provides-annotated method. 
android.app.Activity is injected at 
<packageName>.login.presenter.LoginActivityPresenter.<init>(activity, …) 
<packageName>.login.presenter.LoginActivityPresenter is injected at 
<packageName>.login.LoginActivity.presenter 
<packageName>.login.LoginActivity is injected at 
<packageName>.injection.component.ActivityComponent.inject(loginActivity) 
A binding with matching key exists in component: <packageName>.injection.component.ActivityComponent 

我的組件/模塊如下所示:

應用程序組件

@Singleton 
@Component(modules = {ApplicationModule.class, BusModule.class, PrefsModule.class, NetModule.class}) 
public interface ApplicationComponent { 
    @ApplicationContext Context context(); 
    Application application(); 
    EventBus bus(); 
    SharedPreferences prefs(); 
    Gson gson(); 
} 

應用模塊

@Module 
public class ApplicationModule { 
    protected final Application app; 

    public ApplicationModule(Application app) { 
     this.app = app; 
    } 

    @Provides 
    Application providesApplication() { 
     return app; 
    } 

    @Provides 
    @ApplicationContext 
    Context providesContext(){ 
     return app; 
    } 
} 

配置持久性組件

@ConfigPersistent 
@Component(dependencies = ApplicationComponent.class) 
public interface ConfigPersistentComponent { 

    ActivityComponent plus(ActivityModule activityModule); 

} 

活性成分

@PerActivity 
@Subcomponent(modules = ActivityModule.class) 
public interface ActivityComponent { 

    void inject(LoginActivity loginActivity); 

} 

活動模塊

@Module 
public class ActivityModule { 

    private Activity activity; 

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

    @Provides 
    @PerActivity 
    Activity providesActivity() { 
    return activity; 
    } 

} 

應用類

public class App extends Application { 

    private ApplicationComponent applicationComponent; 

    @Override public void onCreate() { 
    super.onCreate(); 
    Timber.plant(new Timber.DebugTree()); 
    } 

    public static App get(Context context) { 
    return (App) context.getApplicationContext(); 
    } 

    public ApplicationComponent getComponent() { 
    if (applicationComponent == null) { 
     applicationComponent = DaggerApplicationComponent.builder() 
       .applicationModule(new ApplicationModule(this)) 
       .busModule(new BusModule()) 
       .netModule(new NetModule()) 
       .prefsModule(new PrefsModule()) 
       .build(); 
    } 
    return applicationComponent; 
    } 

} 

MainPresenter

@ConfigPersistent 
public class LoginActivityPresenter extends BasePresenter<LoginContract.View> { 

    Context context; 
    Gson gson; 

    @Inject 
    public LoginActivityPresenter(Activity activity, Gson gson) { 
    this.context = activity; 
    this.gson = gson; 
    } 

    @Override public void attachView(LoginContract.View view) { 
    super.attachView(view); 
    Timber.d("onAttach"); 
    } 

    @Override public void detachView() { 
    super.detachView(); 
    Timber.d("onDettach"); 
    disposableSubscriber.dispose(); 
    } 
} 

基地演示活動

public abstract class BasePresenterActivity extends AppCompatActivity { 

    private static final String KEY_ACTIVITY_ID = "KEY_ACTIVITY_ID"; 
    private static final AtomicLong NEXT_ID = new AtomicLong(0); 
    private static final Map<Long, ConfigPersistentComponent> sComponentsMap = new HashMap<>(); 

    private ActivityComponent activityComponent; 
    private long activityId; 

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

     // Create the ActivityComponent and reuses cached ConfigPersistentComponent if this is 
     // being called after a configuration change. 
     activityId = savedInstanceState != null ? savedInstanceState.getLong(KEY_ACTIVITY_ID) : NEXT_ID.getAndIncrement(); 
     ConfigPersistentComponent configPersistentComponent; 
     if (!sComponentsMap.containsKey(activityId)) { 
      Timber.i("Creating new ConfigPersistentComponent id=%d", activityId); 
      configPersistentComponent = DaggerConfigPersistentComponent.builder() 
        .applicationComponent(App.get(this).getComponent()) 
        .build(); 
      sComponentsMap.put(activityId, configPersistentComponent); 
     } else { 
      Timber.i("Reusing ConfigPersistentComponent id=%d", activityId); 
      configPersistentComponent = sComponentsMap.get(activityId); 
     } 
     activityComponent = configPersistentComponent.plus(new ActivityModule(this)); 
    } 

主要活動

public class LoginActivity extends BasePresenterActivity implements LoginContract.View { 

    @Inject 
    EventBus bus; 
    @Inject 
    SharedPreferences prefs; 
    @Inject 
    Activity activity; 
    @Inject 
    LoginActivityPresenter presenter; 

    @Override 
    protected void onCreate(@Nullable Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    activityComponent().inject(this); 
    setContentView(R.layout.activity_login); 
    presenter.attachView(this); 
    } 

    @Override protected void onDestroy() { 
    super.onDestroy(); 
    presenter.detachView(); 
    } 
} 

/********** ******** EDIT **/

爲了幫助保存演示者的狀態並仍然允許激活vity上下文被傳入,我的解決方案在下面發佈。歡迎任何反饋。

 @ActivityContext 
    public class LoginActivityPresenter extends BasePresenter<LoginContract.View> { 

     Context context; 

     @Inject 
     LoginStateHolder loginStateHolder; 

     @Inject 
     public LoginActivityPresenter(Context context, Gson gson) { 
     this.context = activity; 
     this.gson = gson; 
     } 
} 

@ConfigPersistent 
public class LoginStateHolder { 

    String title; 

    @Inject 
    public LoginStateHolder(Context context) { 
    title = "Save me"; 
    } 

    public void setTitle(String title) { 
    this.title = title; 
    } 

    public String getTitle() { 
    return title; 
    } 
} 

/*********** 編輯 - 21_5_17 *********/

例外:

Error:(13, 8) error: [<packagename>.injection.component.ActivityComponent.inject(<packagename>.login.ui.activity.LoginActivity)] android.app.Activity cannot be provided without an @Provides-annotated method. 
android.app.Activity is injected at 
<packagename>.login.presenter.LoginActivityPresenter.<init>(activity, …) 
<packagename>.login.presenter.LoginActivityPresenter is injected at 
<packagename>.login.ui.activity.LoginActivity.presenter 
<packagename>.login.ui.activity.LoginActivity is injected at 
<packagename>.injection.component.ActivityComponent.inject(loginActivity) 

登錄活動

public class LoginActivity extends BasePresenterActivity implements LoginContract.View { 

    @Inject 
    EventBus bus; 
    @Inject 
    SharedPreferences prefs; 
    @Inject 
    LoginActivityPresenter presenter; 

    @Override 
    protected void onCreate(@Nullable Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    activityComponent().inject(this); 
    setContentView(R.layout.activity_login); 
    ButterKnife.bind(this); 
    presenter.attachView(this); 
    presenter.setupValidation(username, companyId, password); 

} 

登錄活動主持人

@ActivityContext 
public class LoginActivityPresenter extends BasePresenter<LoginContract.View> { 

    @Inject LoginPresenterStorage loginPresenterStorage; 

    Gson gson; 

    @Inject 
    public LoginActivityPresenter(Activity activity, Gson gson) { 
    this.context = activity; 
    this.gson = gson; 
    } 
} 

登錄演示存儲

@ConfigPersistent 
public class LoginPresenterStorage { 

    private String test = ""; 

    @Inject 
    public LoginPresenterStorage(Activity activity) { 
    test = "I didn't die"; 
    } 

    public String getTest() { 
    return test; 
    } 
} 

應用組件

@Singleton 
@Component(modules = {ApplicationModule.class, BusModule.class, PrefsModule.class, NetModule.class}) 
public interface ApplicationComponent { 

    @ApplicationContext Context context(); 
    Application application(); 
    EventBus bus(); 
    SharedPreferences prefs(); 
    Gson gson(); 

} 

活動構件

@ActivityContext 
@Subcomponent(modules = ActivityModule.class) 
public interface ActivityComponent { 

    void inject(LoginActivity loginActivity); 

} 

ConfigPersistentComponent

@ConfigPersistent 
@Component(dependencies = ApplicationComponent.class) 
public interface ConfigPersistentComponent { 

    ActivityComponent plus(ActivityModule activityModule); 

} 

活動模塊

@Module 
public class ActivityModule { 

    private Activity activity; 

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

    @Provides 
    @ActivityContext 
    Activity providesActivity() { 
    return activity; 
    } 

} 

編輯

我提出使用的錯誤與活動模塊相同的活動上下文。因此,我沒有把活動注入主持人。更改活動模塊到原來的@peractivity範圍,請按照以下的答案將會使活動場景注射。

回答

2
@ConfigPersistent 
public class LoginActivityPresenter extends BasePresenter<LoginContract.View> { 

通過與@ConfigPersistent範圍,你告訴匕首「在ConfigPersistentComponent管理這一類的實例」標記LoginActivityPresenter(即總是從一個給定的ConfigPersistentComponent實例返回相同的實例),這意味着它不應該訪問到像@ActivityScope這樣的狹義範圍內的任何東西。畢竟,ConfigPersistentComponent會活得比ActivityComponent,所以注射用活動的LoginPresenter沒有意義:你現在的樣子,你會得到相同的LoginPresenter實例與不同的活動。

「不能沒有@Inject構造被提供android.app.Activity」的消息來自代ConfigPersistentComponent的,它不具有活性結合。當然,你的ActivityComponent做,但這不是它正試圖存儲LoginPresenter其當前的註釋。

將該聲明切換到@ActivityScope,一切都會好:您將爲每個創建的Activity創建一個不同的LoginActivityPresenter,並且您還可以訪問@Singleton範圍(ApplicationComponent)和@ConfigPersistent範圍內的所有內容(ConfigPersistentComponent)。

@ActivityScope 
public class LoginActivityPresenter extends BasePresenter<LoginContract.View> { 
+0

嗨,傑夫,現在可以工作,但我有一個保存配置更改演示者的問題,沒有活動注入的原始示例存儲演示者罰款重新使用和保存狀態。然而,通過使用更狹窄的範圍,每次爲相同的活動創建演示者。我有沒有想過這個錯誤,我只是想知道我是否需要一個活動環境,特別是如果它不得不比活動活躍。當我分離主持人時,我總是可以刪除上下文。也許我應該使用應用程序上下文,對此你有什麼想法? – TheSunny

+1

您需要在這裏決定實例的角色。在Activity的生命週期之外存儲狀態的部分不應該注入Activity,因此如果刪除該Context參考,則可能會很好。另一方面,演示者通常具有特定於Activity的實現,所以也許您確實需要一些密切協作的Activity範圍的演示者 - 或者您的LoginActivityPresenter可能保持活動範圍,併爲持久數據創建ConfigPersistent持有者。無論哪種方式都很好,你只需要決定最適合你的需求。 –

+0

我該如何着手創建ConfigPersistent持有者以注入Presenter? ConfigPersistentComponent是在基本活動中重用以允許重用的通用組件,我需要持有者對象的特定實例來恢復狀態,因爲我必須爲每個演示者執行此操作。我如何通過DI去解決這個問題?我只用了一個星期的匕首,而且從我知道的我不認爲我可以將更廣泛的範圍對象注入更窄的範圍,任何指導或示例都會很棒,歡呼聲 – TheSunny