0

我在干净的架构项目中使用 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)
    }
}
4

1 回答 1

0

范围告诉组件缓存绑定的结果。它与缓存任何组件的实例无关。因此,您总是在片段的方法中创建新DataComponentPresentationComponent, 和。AllMoviesComponentonCreate

为了重用同一个AllMoviesComponent实例,您需要将它存储在某个地方。存储它的位置可能取决于您的应用架构,但一些选项包括MyApplication它本身、托管Activity或以某种方式在您的导航图中。


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

于 2020-05-18T19:25:28.577 回答