2017-08-16 44 views
0

我正在学习Dagger2 + MVP并在Kotlin上做。而且我在理解Dagger2或MVP或那里的组合方面存在问题。Kotlin上的Dagger2 + MVP

构建一个应用程序和想法应该如何工作非常简单。该应用程序由MenuActivity与左侧导航和几个Fragments(比方说3)应改变在FrameLayoutactivity_menu.xml

我已经阅读了几篇文章,花了几天的时间学习Dagger2。 (1)AppComponent,(2)MenuActivityComponent和(3)AccountFragmentComponent。在我的想法中,Dagger体系结构应该由三个@Component s组成:(1)AppComponent,(2)MenuActivityComponent和(3)AccountFragmentComponent。 而从我的理解和架构的文章中的图片我的体系结构可以是这样的:(3)取决于 - >(2)取决于 - >(1)

每个@Component具有@Module:(1 )AppModule,(2)MenuActivityModule和(3)AccountFragmentModule。就我所知,从MVP的意识形态角度来看,(2)MenuActivityModule和(3)AccountFragmentModule都应该是@ProvidePresenter,而在MenuActivity和其他Fragments,比如AccountFragment中应该是@Inject

的AppModule

@Module 
class AppModule(val app : App){ 

    @Provides @Singleton 
    fun provideApp() = app 

} 

AppComponent

@Singleton @Component(modules = arrayOf(AppModule::class)) 
interface AppComponent{ 

    fun inject(app : App) 

    fun plus(menuActivityModule: MenuActivityModule): MenuActivityComponent 
} 

MenuActivityModule

@Module 
class MenuActivityModule(val activity : MenuActivity) { 

    @Provides 
    @ActivityScope 
    fun provideMenuActivityPresenter() = 
     MenuActivityPresenter(activity) 

    @Provides 
    fun provideActivity() = activity 
} 

MenuActivityComponent

AccountsFragmentModule

@Module 
class AccountsFragmentModule(val fragment: AccountsFragment){ 

    @FragmentScope 
    @Provides 
    fun provideAccountsFragmentPresenter() = 
     AccountsFragmentPresenter(fragment) 
} 

AccountsFragmentComponent

@FragmentScope 
@Subcomponent(modules = arrayOf(AccountsFragmentModule::class)) 
interface AccountsFragmentComponent { 

    fun inject(fragment: AccountsFragment) 
} 

而且我有两个@Scope S:ActivityScope和FragmentScope,使我明白,这将保证在应用程序中需要每个组件的时间仅存在一个Component。

ActivityScope

@Scope 
annotation class ActivityScope 

FragmentScope

@Scope 
annotation class FragmentScope 

在应用I类创建的@Singleton依赖性的曲线图。

class App : Application(){ 

    val component : AppComponent by lazy { 
     DaggerAppComponent 
      .builder() 
      .appModule(AppModule(this)) 
      .build() 
    } 

    companion object { 
     lateinit var instance : App 
      private set 
    } 

    override fun onCreate() { 
     super.onCreate() 
     component.inject(this) 
    } 

} 

在MenuActivity:

class MenuActivity: AppCompatActivity() 

    @Inject lateinit var presenter : MenuActivityPresenter 

    val Activity.app : App 
    get() = application as App 

    val component by lazy { 
     app.component.plus(MenuActivityModule(this)) 
    } 

    override fun onCreate(savedInstanceState: Bundle?) { 
     super.onCreate(savedInstanceState) 
     setContentView(R.layout.activity_menu) 
     /* setup dependency injection */ 
     component.inject(this) 
     /* setup UI */ 
     setupMenu() 
     presenter.init() 
    } 

private fun setupMenu(){ 
    navigationView.setNavigationItemSelectedListener({ 
     menuItem: MenuItem -> selectDrawerItem(menuItem) 
     true 
    }) 

    /* Hamburger icon for left-side menu */ 
    supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_menu_white_24dp) 
    supportActionBar?.setDisplayHomeAsUpEnabled(true) 

    drawerToggle = ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close); 
    drawerLayout.addDrawerListener(drawerToggle as ActionBarDrawerToggle) 
    } 
private fun selectDrawerItem(menuItem: MenuItem){ 

    presenter.menuItemSelected(menuItem) 

    // Highlight the selected item has been done by NavigationView 
    menuItem.isChecked = true 
    // Set action bar title 
    title = menuItem.title 
    // Close the navigation drawer 
    drawerLayout.closeDrawers() 
} 

@SuppressLint("CommitTransaction") 
override fun showFragment(fragment: Fragment, isReplace: Boolean, 
          backStackTag: String?, isEnabled: Boolean) 
{ 
    /* Defining fragment transaction */ 
    with(supportFragmentManager.beginTransaction()){ 

     /* Select if to replace or add a fragment */ 
     if(isReplace) 
      replace(R.id.frameLayoutContent, fragment, backStackTag) 
     else 
      add(R.id.frameLayoutContent, fragment) 

     backStackTag?.let { this.addToBackStack(it) } 

     commit() 
    } 

    enableDrawer(isEnabled) 
} 

private fun enableDrawer(isEnabled: Boolean) { 
    drawerLayout.setDrawerLockMode(if(isEnabled) DrawerLayout.LOCK_MODE_UNLOCKED 
            else DrawerLayout.LOCK_MODE_LOCKED_CLOSED) 
    drawerToggle?.onDrawerStateChanged(if(isEnabled) DrawerLayout.LOCK_MODE_UNLOCKED 
             else DrawerLayout.LOCK_MODE_LOCKED_CLOSED) 
    drawerToggle?.isDrawerIndicatorEnabled = isEnabled 
    drawerToggle?.syncState() 
} 

override fun onOptionsItemSelected(item: MenuItem?): Boolean { 
    if (drawerToggle!!.onOptionsItemSelected(item)) { 
     return true 
    } 
    return super.onOptionsItemSelected(item) 
} 

override fun onPostCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) { 
    super.onPostCreate(savedInstanceState, persistentState) 
    drawerToggle?.syncState() 
} 

override fun onConfigurationChanged(newConfig: Configuration?) { 
    super.onConfigurationChanged(newConfig) 
    drawerToggle?.onConfigurationChanged(newConfig) 
} 
} 

MainActivityPresenter

class MenuActivityPresenter(val menuActivity: MenuActivity){ 

    fun init(){ 
     menuActivity.showFragment(AccountsFragment.newInstance(), isReplace = false) 
    } 

    fun menuItemSelected(menuItem: MenuItem){ 

     val fragment = when(menuItem.itemId){ 
      R.id.nav_accounts_fragment -> { 
       AccountsFragment.newInstance() 
      } 
      R.id.nav_income_fragment -> { 
       IncomeFragment.newInstance() 
      } 
      R.id.nav_settings -> { 
       IncomeFragment.newInstance() 
      } 
      R.id.nav_feedback -> { 
       OutcomeFragment.newInstance() 
      } 
      else -> { 
       IncomeFragment.newInstance() 
      } 
     } 

     menuActivity.showFragment(fragment) 
    } 

} 

activity_menu.xml

<android.support.v4.widget.DrawerLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:app="http://schemas.android.com/apk/res-auto" 
android:id="@+id/drawerLayout" 
android:layout_width="match_parent" 
android:layout_height="match_parent"> 

<!-- This LinearLayout represents the contents of the screen --> 
<LinearLayout 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical"> 

    <!-- The ActionBar displayed at the top --> 
    <include 
     layout="@layout/toolbar" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" /> 

    <!-- The main content view where fragments are loaded --> 
    <FrameLayout 
     android:id="@+id/frameLayoutContent" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" /> 

</LinearLayout> 

<!-- The navigation drawer that comes from the left --> 
<!-- Note that `android:layout_gravity` needs to be set to 'start' --> 
<android.support.design.widget.NavigationView 
    android:id="@+id/navigationView" 
    android:layout_width="wrap_content" 
    android:layout_height="match_parent" 
    android:layout_gravity="start" 
    android:background="@android:color/white" 
    app:menu="@menu/main_menu" 
    app:headerLayout="@layout/nav_header" 
    /> 

</android.support.v4.widget.DrawerLayout> 

而且在那里我有在我的理解一个突破点的地方:

class AccountsFragment : Fragment() { 

    companion object { 
     fun newInstance() = AccountsFragment() 
    } 

    val Activity.app : App 
     get() = application as App 

    val component by lazy { 
     app.component 
      .plus(MenuActivityModule(activity as MenuActivity)) 
      .plus(AccountsFragmentModule(this)) 
    } 

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { 
     val view = inflater?.inflate(R.layout.fragment_accounts, container, false) 
     setHasOptionsMenu(true) 
     component.inject(this) 
     return view 
    } 

} 

我在component值这最后一部分的误解。我来到这种情况,我需要plus MenuActivityComponent的子组件,并给出一个构造函数变量MenuActivity,但我明白这是错误的,即使我希望实例应该只有一个在应用程序中,我也不能创建另一个实例。

问题:我哪里有错?在我的代码中,在架构中,在理解依赖注入方面还是在所有三方面?感谢您的帮助!

回答

1

我会分开你的头脑中的概念,并单独工作。一旦你在这两个概念中变得流利/优秀,你可以尝试将它们结合起来。

例如,尝试构建一个简单的多活动/片段应用程序MVP设计模式。使用MVP,您将在Presenter(一个包含视图逻辑并控制视图的对象,以及处理视图收集和转发的行为的对象)之间编写双向接口契约,以及View(视图对象,通常是像Fragment或Activity这样的本地组件,负责显示视图并处理用户输入,如触摸事件)。

使用Dagger2,您将学习依赖注入设计模式/架构风格。您将构建组合的模块,以形成组件,然后使用这些组件来注入对象。

将两者结合起来就是从理解每个概念开始。

查看Google Architectural Blueprint存储库以获取MVP和Dagger2的示例。 https://github.com/googlesamples/android-architecture

1

而且我有两个@Scopes:ActivityScope和FragmentScope,所以按照我的理解,这将保证只有一个组件的存在,每一个部件需要在应用

匕首的范围AREN时间一些神奇的仙尘,会为你管理生命。范围的使用仅仅是验证帮助,它可以帮助您不依赖不同的生命周期。您仍然必须自己管理组件和模块对象,并将正确的参数传递给它们的构造函数。由不同组件实例管理的依赖关系图是独立的,并绑定到它们的@Component对象。请注意,我在某种意义上说“绑定”,它们是由它们(通过构造函数)创建的,并且可选地存储在它们内部,所以在幕后工作绝对没有其他魔法。因此,如果您需要在应用程序的各个部分之间共享一堆依赖关系,则可能需要传递一些组件和模块实例。

在碎片的情况下,对复杂的生命周期有很强的依赖性 - 活动。您在onAttach期间收到该依赖项,并在onDetach期间丢失该依赖项。所以如果你真的想将一些Activity依赖的东西传递给Fragments,你必须传递那些依赖于Activity的组件/模块,就像你在没有MVP的情况下传递Activity实例一样。

同样的,如果你的活动通过意向接收一些依赖,你将不得不反序列化的依赖性和它,并创建一个模块的基础上,意向内容...

活动的复杂性和片段的生命周期加重的问题,一般在使用匕首注射时会见。 Android框架围绕假设建立,活动和片段以序列化的形式接收所有的依赖关系。最后,一个写得很好的应用程序不应该在模块之间有太多依赖关系(查找“紧耦合”)。由于这些原因,如果您不遵循严格的单一活动范例,则在本地化的活动/ Fagment范围中使用Dagger在您的项目中可能根本不值得。许多人仍然这样做,即使这不值得,但海事组织,这只是个人喜好的问题。