无法获得作用域组件的相同实例-Dagger 2 Clean体系结构

问题描述 投票:0回答:1

我在干净的体系结构项目中使用Dagger 2,我有2个片段。应该将这两个片段的范围限定为共享相同的实例,但是不幸的是,我在第二个片段中得到了空对象。应用程序组件

@ApplicationScope
@Component(modules = [ContextModule::class, RetrofitModule::class])
interface ApplicationComponent {
    fun exposeRetrofit(): Retrofit
    fun exposeContext(): Context

}

数据层-存储库

class MoviesParsableImpl @Inject constructor(var moviesLocalResult: MoviesLocalResult): MoviesParsable {
private val TAG = javaClass.simpleName
private val fileUtils = FileUtils()
override fun parseMovies() {
    Log.d(TAG,"current thread is ".plus(Thread.currentThread().name))
    val gson = Gson()
    val fileName = "movies.json"
    val jsonAsString = MyApplication.appContext.assets.open(fileName).bufferedReader().use{
        it.readText()
    }
    val listType: Type = object : TypeToken<MoviesLocalResult>() {}.type
    moviesLocalResult  = gson.fromJson(jsonAsString,listType)
    Log.d(TAG,"result size ".plus(moviesLocalResult.movies?.size))
}

override fun getParsedMovies(): Results<MoviesLocalResult> {
    return Results.Success(moviesLocalResult)
}
}

回购模块

    @Module
interface RepoModule {
    @DataComponentScope
    @Binds
    fun bindsMoviesParsable(moviesParsableImpl: MoviesParsableImpl): MoviesParsable
}

MoviesLocalResultsModule(结果需要跨不同片段的实例)

    @Module
class MoviesLocalResultModule {
    @DataComponentScope
    @Provides
    fun provideMovieLocalResults(): MoviesLocalResult{
        return MoviesLocalResult()
    }
}

用例

class AllMoviesUseCase @Inject constructor(private val moviesParsable: MoviesParsable){
fun parseMovies(){
    moviesParsable.parseMovies()
}

fun getMovies(): Results<MoviesLocalResult> {
    return moviesParsable.getParsedMovies()
}

}

演示组件

    @PresentationScope
@Component(modules = [ViewModelFactoryModule::class],dependencies = [DataComponent::class])
interface PresentationComponent {

    fun exposeViewModel(): ViewModelFactory

}

第一个ViewModel,在需要的地方我可以与其他片段共享结果

class AllMoviesViewModel @Inject constructor(private val useCase: AllMoviesUseCase):ViewModel() {
    private val moviesMutableLiveData = MutableLiveData<Results<MoviesLocalResult>>()
    init {
        moviesMutableLiveData.postValue(Results.Loading())
    }
    fun parseJson(){
        viewModelScope.launch(Dispatchers.Default){
            useCase.parseMovies()
            moviesMutableLiveData.postValue(useCase.getMovies())
        }
    }

    fun readMovies(): LiveData<Results<MoviesLocalResult>> {
        return  moviesMutableLiveData
    }
}

第二个ViewModel,它不需要再次请求数据,因为它的作用域是预期的

class MovieDetailsViewModel @Inject constructor(private val useCase: AllMoviesUseCase): ViewModel() {

    var readMovies = liveData(Dispatchers.IO){
        emit(Results.Loading())
        val result = useCase.getMovies()
        emit(result)
    }
}

第一个片段,应在其中请求数据:

class AllMoviesFragment : Fragment() {
    private val TAG = javaClass.simpleName
    private lateinit var viewModel: AllMoviesViewModel
    private lateinit var adapter: AllMoviesAdapter
    private lateinit var layoutManager: LinearLayoutManager
    private var ascendingOrder = true

    @Inject
    lateinit var viewModelFactory: ViewModelFactory
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)
        DaggerAllMoviesComponent.builder()
            .presentationComponent(
                DaggerPresentationComponent.builder()
                    .dataComponent(
                        DaggerDataComponent.builder()
                            .applicationComponent(MyApplication.applicationComponent).build()
                    )
                    .build()
            ).build()inject(this)

        viewModel = ViewModelProvider(this, viewModelFactory).get(AllMoviesViewModel::class.java)
        startMoviesParsing()
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_all_movies, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        setupRecyclerView()
        viewModel.readMovies().observe(viewLifecycleOwner, Observer {
            if (it != null) {
                when (it) {
                    is Loading -> {
                        showResults(false)
                    }

                    is Success -> {
                        showResults(true)
                        Log.d(TAG, "Data observed ".plus(it.data))
                        addMoviesList(it.data)

                    }

                    is Error -> {
                        moviesList.snack(getString(R.string.error_fetch_movies))
                    }
                }
            }
        })
    }

第二个片段,我希望在第一个片段中获得与它们作用域相同的实例请求。

class MovieDetailsFragment: Fragment() {
    val TAG = javaClass.simpleName
    @Inject
    lateinit var viewModelFactory: ViewModelFactory
    lateinit var viewModel: MovieDetailsViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val depend = DaggerAllMoviesComponent.builder()
            .presentationComponent(
                DaggerPresentationComponent.builder()
                    .dataComponent(
                        DaggerDataComponent.builder()
                            .applicationComponent(MyApplication.applicationComponent).build())
                    .build()
            ).build()

        depend.inject(this)
        viewModel = ViewModelProvider(this, viewModelFactory).get(MovieDetailsViewModel::class.java)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        viewModel.readMovies.observe(this, Observer {
            if (it!=null){
                Log.d(TAG,"Movies returned successfully")
            }
        })
        return super.onCreateView(inflater, container, savedInstanceState)
    }
}
android dependency-injection scope dagger-2 clean-architecture
1个回答
0
投票

范围告诉组件缓存绑定的结果。它与缓存任何组件的实例无关。因此,您始终在片段的DataComponent方法中创建新的PresentationComponentAllMoviesComponentonCreate

为了重用相同的AllMoviesComponent实例,您需要将其存储在某个地方。它的存储位置取决于您的应用程序体系结构,但是某些选项包括MyApplication本身,托管Activity或以某种方式位于您的导航图中。


即使解决了这个问题,也不能保证已经调用了parseMovies。 Android系统可能会随时终止您的应用程序,包括MoviesDetailFragment是当前片段时。如果发生这种情况,并且用户稍后导航回您的应用程序,则将重新创建任何活动的片段,并且您仍然会得到null。

© www.soinside.com 2019 - 2024. All rights reserved.