Android实践 — ikan项目(三)

  • 转载时请注明出处,谢谢
  • / 0评 / 0

      本篇简述下项目的domain层,domain层在项目中担负的职责是处理presentation层下发的数据存取过程。因为data层仅是对数据存取并不包含对数据进一步处理工作,所以对数据处理应是项目的业务逻辑层职责。
      在domain层,不关心数据的来源,只需定义Repository接口然后data层实现该接口实现即可,先看项目中FeedRepository的定义:

    interface FeedRepository {
    
    fun getHomeFeeds(feedParamProvider: FeedParamProvider): Observable<List<FeedEntity>>
        // xxx
    }
    

    FeedRepository在data层的实现FeedDataRepository如下:

    @Singleton
    class FeedDataRepository @Inject constructor(private var feedDataStoreFactory: FeedDataStoreFactory)
        : FeedRepository {
    
        override fun getHomeFeeds(feedParamProvider: FeedParamProvider) = feedDataStoreFactory
                .createCloudDataStore()
                .getHomeFeeds(feedParamProvider)
        // xxx
    }
    

    在data层需要确定数据具体来源,这里是通过DataStoreFactory创建相应的DataStore实例,继而通过DataStore实例获取数据。FeedDataStoreFactory提供创建CloudFeedDataStore和LocalFeedDataStore实例的方法,看下实现代码:

    @Singleton
    class FeedDataStoreFactory @Inject internal constructor(private val apiConnector: Connector,
                                                            private val tokenInterceptor: TokenInterceptor,
                                                            private val feedCache: FeedCache) {
    
        fun createCloudDataStore(): FeedDataStore = CloudFeedDataStore(apiConnector, tokenInterceptor, feedCache)
    
        fun createLocalDataStore(): FeedDataStore = LocalFeedDataStore(feedCache)
    }
    

    在domain层通过repository接口和data层建立连接,但是创建连接的过程并不包含具体业务逻辑。基于职责从data层获取的数据并不一定等于presentation需要的数据,比如首页的视频列表,产品的需求是用户屏蔽的视频不做显示,需要在domain层就需要将屏蔽的视频过滤掉。其次为了与domain层与presentation层进行交互,项目在domain层为每个Repository创建对应的管理类,比如FeedRepositoryManager,具体代码如下:

    @Singleton
    class FeedRepositoryManager @Inject constructor(threadExecutor: ThreadExecutor,
                                                    postExecutionThread: PostExecutionThread,
                                                    private var feedDataRepository: FeedDataRepository) :
            BaseRepositoryManager(threadExecutor, postExecutionThread) {
    
        fun executeGetHomeFeeds(feedParamProvider: FeedParamProvider, subscriber: Observer<PagerEntity<List<FeedEntity>>>) {
            feedDataRepository.getHomeFeeds(feedParamProvider)
                    .subscribeOn(Schedulers.from(threadExecutor))
                    .observeOn(postExecutionThread.scheduler)
                    .subscribe(subscriber)
        }
    
        // xxx
    }
    

    如你所见,executeGetHomeFeeds是间接通过FeedRepository获取数据比较好理解,然后subscribeOn限定订阅的线程,observeOn限定观察的线程,两者分别依赖threadExecutor和postExecutionThread对象,对此稍作讲解,先看下代码:

    @Singleton
    class DefaultExecutor @Inject constructor() : ThreadExecutor {
    
        private val workQueue: BlockingQueue<Runnable>
        private val threadPoolExecutor: ThreadPoolExecutor
        private val threadFactory: ThreadFactory
    
        init {
            workQueue = LinkedBlockingQueue()
            threadFactory = DefaultThreadFactory()
            threadPoolExecutor = ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE,
                    KEEP_ALIVE_TIME.toLong(), KEEP_ALIVE_TIME_UNIT, this.workQueue, this.threadFactory)
        }
    
        override fun execute(runnable: Runnable) {
            threadPoolExecutor.execute(runnable)
        }
    
        private class DefaultThreadFactory : ThreadFactory {
    
            private var counter = 0
    
            override fun newThread(runnable: Runnable): Thread {
                return Thread(runnable, THREAD_NAME + counter++)
            }
    
            companion object {
    
                private const val THREAD_NAME = "android_thread_"
            }
        }
    
        companion object {
    
            private const val CORE_POOL_SIZE = 5
            private const val MAX_POOL_SIZE = 20
            private const val KEEP_ALIVE_TIME = 10
    
            private val KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS
        }
    }
    

    简述下该类的实现:首先初始化BlockingQueue对象,其负责生产的线程不断的制造新对象并插入到阻塞队列中,直到达到这个队列的上限值。队列达到上限值之后生产线程将会被阻塞,直到消费的线程对这个队列进行消费。初始化ThreadFactory对象,其负责创建线程对象。之后ThreadPoolExecutor通过BlockingQueue对象、ThreadFactory对象、设定核心线程数、设定线程池所能容纳的最大线程数、设定非核心线程的闲置超时时间、设定keepAliveTime的单位共同创建一个线程池。其实就是一个线程池的配置过程 0.0

    PostExecutionThread实现类UIThread的代码如下:

    @Singleton
    class UIThread @Inject constructor() : PostExecutionThread {
    
        override val scheduler: Scheduler
            get() = AndroidSchedulers.mainThread()
    }
    

    UIThread比较容易理解,直接使用AndroidSchedulers.mainThread(),不再赘述。控制流向和数据流向入下图所示:

    最后,再次明确下domain层职责是衔接data层以获取源数据,同时通过RepositoryManager向presentation层提供所需要的数据。

    发表评论

    电子邮件地址不会被公开。 必填项已用*标注