使用android项目,使用Kotlin开发Android项目-Kibo (一)

原标题:使用Kotlin开发Android项目-Kibo (一)

前言

这是安卓巴士举办的第三个活动了,每次我都是积极参与,一方面是想获得奖品,另一方面也是对自己最近一段时间学习的总结。上次讲的 Kotlin 开发 Android 《》只是皮毛,举了个小例子,接下来给大家讲的是自己之前做的一个 Android 项目 Kibo (新浪微博第三方客户端)。

项目概述

由于项目的内容比较多,所以本篇主要从项目的框架上来讲述。后面有可能会就细节分析。

首先来看下项目的效果吧:

5d61d964bd18c21df4d0dacb290a78f3.gif

从图片中也可以很清楚的看出,底部和官方的差不多,为主页、消息、发现、个人四个部分。而发布微博、分组、设置我则是使用悬浮窗实现的(主要是如果设计和官方一样的话上架会被驳回)。现在线上的 1.2.0 版本是以前的了。现在在做的是 1.3.2 了,只是一直没时间搞上线。

言归正传,Kibo使用的框架是 MVP + Dagger2 + Rxjava + Retrofit + GreenDao ,是大家可以好好学习的一个架构,学会之后会感觉安卓开发是如此的简单,再配合 Kotlin 这门优雅的语言,你会发现写出来的代码也很好理解。

项目结构

首先我粗略介绍下框架中每部分的作用吧,结合项目的目录结构来看可能会更好点:

b02fea9e7b0afdbd2ee336ebb4fd832a.png

也许大家看了目录结构后觉得有些包可以合并在一起的,比如用户账户数据可以放到 bean 里, glide 图片工具可以放到 utils 包中,其实这些我都想过,但是后来发现这些都需要单独作为一个功能来做,所以就所索性新开了一个包来写了。

整体框架大家看完之后我就先给大家具体讲下使用的每一个优秀的设计.讲完了大伙应该就知道是如何设计的了。

Dagger2Dagger2配合MVP使用

首先要说的是项目的两个大头,当时我学会了使用 MVP 后一直感觉在每一个 Activity 和 Fragment 中都需要 new 一个 Present 是一件很难受的事,然后就上网查了有什么方法可以解决,果然,就让我遇到了 Dagger2 。使用 MVP 时如果配合 Dagger 的话你会感觉到自己写起代码来得心应手,因为你只需要知道你需要添加什么功能,基本上你就可以按照已经搭建好的项目结构去继续开发。

由于项目中很多地方用到了注入,所以我先讲 Dagger 。Dagger 现在不知道发展到哪一个版本了,我当时用的就是2,感觉已经可以满足我的需求了。用过之后对其的理解就是帮你实现依赖注入!so cool!

依赖注入

讲到 Dagger 不得不提的就是这个,依赖注入( Dependency Injection )是用于实现控制反转( Inversion of Control )的最常见的方式之一。

为什么需要依赖注入?

控制反转用于解耦,解的究竟是谁和谁的耦?这是我在最初了解依赖注入时候产生的第一个问题。

我也不像网络上那样把代码贴出来给大家解释了,我就从项目中的举一个例子吧,而项目中使用 Dagger 是为了注入 Present 的,就从如何注入 P 讲起吧!

我们在 MVP 中使用的 P 也就是 Presenter ,如果在之前的开发中,想要在 Activity 中使用 P 来获取数据和显示 View 以及处理逻辑的话,我们最常用的方式可能就是形如下的代码段:

***ActivityPresenter ***ActivityPresenter = new ***ActivityPresenterImpl(...);

看上去没有什么问题,当然没有问题,我们也确实可以这么用,然后每个 Activity 或者 Fragment 在 onCreate 中去实例化即可。但是这其实是一种典型的硬编码!神马是硬编码呢?就是你的 Present构造方法中如果有任何参数发生变化的话,那么此时,不仅需要修改 P 的构造函数,还需要修改所有使用到这个 P 的类,这样子写代码就是所谓的硬编码啦!而我们开发中应该尽可能的避免这种写法,由此依赖注入就诞生咯!!

依赖注入先用简单的说法来描述,就是上面 Presenter 实例的获取是通过事先一个辅助类定义好,在具体使用的时候,通过这个辅助类去生成一个 Presenter 对象,这样子就好很多了,而这个辅助类就是 Dagger 中 Module。

所以使用 Dagger 能够达到的效果就是代码中不再需要 new *Presenter() 这样类似的代码实现,直接在定义 Presenter 实例时,用 @Inject 注解一下, Presenter 的实例就生成。

讲了 Dagger 的一个大概,最明了的讲解就是通过代码中贯穿的使用,下面我就通过项目中一个页面的实现来讲解下,至于 RxJava 和 Retrofit 在讲解的时候我会顺便做出解释。

我们就用最简单的个人页(第四张图)的实现来说吧,从图片中我们也可以看出来并没有什么特别的实现,我也是用的最简单的实现方法,大家看了布局文件就明白了( fragment_me.xml ),因为内容太多了我就不贴出来具体布局代码了,其实就是两个 CardView,然后 CardView 里再放其他的。

个人页的主要的实现就是 MeFragment ,首先看到有一个注解 @Inject 直接注入 MePresent :

override fun injectPresent(){ val uid = UserPrefs.get(activity).account.uid!! DaggerMePresentComponent.builder() .mePresentModule(MePresentModule(uid, this)) .managerAndApiComponent((activity.application as EMApplication).daggerManagerAndApiComponent) .build() .inject(this)}

这样就完成 Dagger 注入了,其实是重写了父类 BaseFragment 中的 injectPresent() 方法:

@Injectprotectedvar present: P? = nullprotectedopen fun injectPresent(){}

目的就是注入我们需要的特定的 Presenter ,然后我们就可以直接使用注入的MePresenter(present) ,如刷新时获取数据:

override fun onRefresh(){ present!!.loadData()}

我们不必再手动的去 new 一个对象,而具体是怎么实现的呢?

首先大概讲一下 Dagger 中的一些常用注解:

@Inject: 通常在需要依赖的地方使用这个注解。换句话说,你用它告诉 Dagger 这个类或者字段需要依赖注入。这样, Dagger 就会构造一个这个类的实例并满足他们的依赖。

@Module: Modules 类里面的方法专门提供依赖,所以我们定义一个类,用 @Module 注解,这样 Dagger 在构造类的实例的时候,就知道从哪里去找到需要的 依赖。 modules 的一个重要特征是它们设计为分区并组合在一起(比如说,在我们的 app 中可以有多个组成在一起的 modules)。

@Provide: 在 modules 中,我们定义的方法是用这个注解,以此来告诉 Dagger 我们想要构造对象并提供这些依赖。

@Component: Components 从根本上来说就是一个注入器,也可以说是 @Inject 和 @Module 的桥梁,它的主要作用就是连接这两个部分。

Components 可以提供所有定义了的类型的实例,比如:我们必须用 @Component 注解一个接口然后列出所有的 @Modules 组成该组件,如果缺失了任何一块都会在编译的时候报错。所有的组件都可以通过它的 modules 知道依赖的范围。

@Scope: Scopes 可是非常的有用, Dagger2 可以通过自定义注解限定注解作用域。后面会演示一个例子,这是一个非常强大的特点,因为就如前面说的一样,没必要让每个对象都去了解如何管理他们的实例。简单来说就是我们可以定义所有范围的粒度。

项目 Dagger 主要是在 di 目录下:

6bcd051192ba865c8c28d2136b3f3bbc.png

首选确定了一个 UIScope,这个我是为了限定注解作用域在UI域使用。

@Documented

@Scope

@Retention(RetentionPolicy.RUNTIME)annotation classUIScoped

简单和 Java 代码对比下:

@Documented

@Scope

@Retention(RetentionPolicy.RUNTIME)

public@interfaceUIScoped {}

其实看着感觉没多大差啦!!但是还是感觉酷酷的。(每次写东西都小跑题,还是继续关注MeFragment的实现吧:

DaggerMePresentComponent.builder() .mePresentModule(MePresentModule(uid, this)) .managerAndApiComponent((activity.application as EMApplication).daggerManagerAndApiComponent) .build() .inject(this)

在 MeFragment 完成 Dagger 的注入需要 MePresentComponent 、 MePresentModule 、 daggerManagerAndApiComponent :下面依次分析:

MePresentComponent:

@UIScoped

@Component(dependencies = arrayOf(ManagerAndApiComponent::class), modules= arrayOf(MePresentModule::class))

interfaceMePresentComponent{

fun inject(meFragment: MeFragment)

}

只有简单的一个 inject 方法,方法传参是 MeFragment ,注解提示依赖于 ManagerAndApiComponent 和 MePresentModule , MePresentComponent 这个类就是起到注入 MePresent 桥梁作用的东东啦!

然后我们再来看 ManagerAndApiComponent是什么:

@Singleton@Component(modules = arrayOf(ApplicationModule::class, ManagerModule::class, ApiModule::class))

interfaceManagerAndApiComponent{ fun provideDraftManager(): DraftManager fun provideGroupManager(): GroupManager fun provideMessageManager(): MessageManager fun provideNotifyManager(): NotifyManager fun provideStatusManager(): StatusManager fun provideStatusUploadImageManager(): StatusUploadImageManager fun provideUserManager(): UserManager

/**--------------------------------------------------------------------------------------- */fun provideAttitudeApi(): AttitudeApi fun provideCommentApi(): CommentApi fun provideGroupApi(): GroupApi fun provideInfoPageApi(): InfoPageApi fun provideLoginApi(): LoginApi fun provideMessageApi(): MessageApi fun provideNotifyApi(): NotifyApi fun provideSearchRecommendApi(): SearchRecommendSource fun provideStatusApi(): StatusApi fun provideUserApi(): UserApi fun provideUpdateApi(): UpdateApi}

从上面可以看出 ManagerAndApiComponent 有三个 Module ( ApiModule 、 ManagerModule 、 ApplicationModule ),前两个分别提供 Api 的接口和 Manager 的接口,分别获取数据和管理数据,具体可以看 ApiModule 和 ManagerModule :

ApiModule:

@ModuleclassApiModule{

@Provides@Singletoninternal fun provideAttitudeApi(weiCoService: WeiCoService, weiBoService: WeiBoService): AttitudeApi{

returnAttitudeApiImp(weiCoService) }

@Provides@Singletoninternal fun provideUserApi(weiCoService: WeiCoService, weiBoService: WeiBoService): UserApi{

returnUserApiImp(weiCoService) } //后面都是类似的返回获取对应Api..........}

ManagerModule:

@ModuleclassManagerModule{

@Provides@Singletoninternal fun provideDraftManager(): DraftManager{

returnDraftManagerImp() }

@Provides@Singletoninternal fun provideUserManager(): UserManager{

returnUserManagerImp() }

//省略类似的提供数据管理类(其实就是在每次获取和更改数据时存起来,方便下次获取和管理,不需要上面的Api联网获取了).............}

而第三个 ApplicationModule:

@ModuleclassApplicationModule(private val mContext: Context) { @Provides internal fun provideContext(): Context {

returnmContext } @Provides @Singleton internal fun provideOkHttpClient(): OkHttpClient {

returnOkHttpClientProvider.getDefaultOkHttpClient(true) .newBuilder() .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)//设置读取超时时间.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)//设置写的超时时间.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)//设置连接超时时间.addInterceptor { chain -> var request = chain.request() request = interceptRequest(request) LogUtil.d(TAG, "request "+ request.url()) chain.proceed(request) } .build() } @Provides @Singleton internal fun provideWeiBoService(okHttpClient: OkHttpClient): WeiBoService? {

returnWeiBoService.Factory.create(okHttpClient) } @Provides @Singleton internal fun provideWeiCoService(okHttpClient: OkHttpClient): WeiCoService? {

returnWeiCoService.WeiCoFactory.create(okHttpClient) } ......}

由 ApplicationModule可以看出主要是提供全局的 Context 和 ApiModule 中需要的两个 Service 。(通过 @Provides 都是可以认为是提供出去给别的类使用的)。

WeiboService 和 WeiCoService 讲起来涉及到 RxJava 和 Retrofit ,我就放到最后再讲吧,现在就理解成是为 ApiModule 提供数据的一个 okhttp 连接服务就行啦!

所以 MePresentComponent 中可以使用 dependcies = ManagerAndApiComponent 提供的 Api 和 Manager ,也可以注入 module = MePresentModule 中提供的 MePresentImp :

MePresentModule:

@ModuleclassMePresentModule(private val uid: Long, private val meView: MeView) { @Provides @UIScopedinternal fun providePresent(userApi: UserApi, userManager: UserManager, draftManager: DraftManager): MePresent {

returnMePresentImp(uid, userApi, userManager, draftManager, meView) }}

因为 ManagerAndApiComponent 中提供了 Api 和 Manager 的全部接口实现,所以在 MePresentModule 中所需要的传参如 UserApi , UserManager , DraftManager 等都不需要自己传入,而我们只需要传入 uid , MeView ,而这两个参数都是在注入时会带入构造函数中的( Kotlin 的构造函数都是直接写在类定义体中的。。感觉稍微参数多点就要好几行咯)。

上上面提到的的 MePresentComponent 中定义了一个 inject() 方法,参数是 MeFragment 。然后 rebuild 一下项目,会生成一个以 Dagger 为前缀的 Component 类,这里是前面的 DaggerMePresentComponent ,所以在 MeFragment 中使用如下代码就可以:

DaggerMePresentComponent.builder() .mePresentModule(MePresentModule(uid, this)) .managerAndApiComponent((activity.application as EMApplication).daggerManagerAndApiComponent) .build() .inject(this)

需要加入依赖的 Module 和 Component ,其中 ManagerAndApiComponent 使用( (activity.application as EMApplication).daggerManagerAndApiComponent )获取是因为其是在EMApplication 中初始化的:

classEMApplication:Application() { var daggerManagerAndApiComponent: ManagerAndApiComponent? = null

privatesetoverride fun onCreate() { super.onCreate() mApplication = thisCrashReport.initCrashReport(mApplication, Key.BUGLY_KEY, false)//BUG上传if(UserPrefs.get(this).token != null) { Init.instance.start(this, UserPrefs.get(this).account.uid!!) } if(SystemUtil.isMainProcess(this)) { registerTokenExpiredEvent() registerActivityLifecycleCallbacks() initNightMode() BackgroundUtil.instance.init(this) } initCrashReport() daggerManagerAndApiComponent = DaggerManagerAndApiComponent.builder() .apiModule(ApiModule()) .managerModule(ManagerModule()) .applicationModule(ApplicationModule(this)) .build() } companion object {

privateval DEFAULT_CHANNEL = "default"privatevar mApplication: Application? = null val instance: Application? get() = mApplication } //.......

}

MePresentModule 需要传入参数是用户的 uid 和 MeView ,用来提供依赖的 MePresenter !

接下来就是见证奇迹的时刻,因为 Dagger2 是在编译期注入的,所以我们需要小小的

build 一下下,然后我们就可以使用 MePresent 来加载数据了!

override fun onRefresh(){ present!!.loadData()}

下面简单说下 Dagger 的原理吧:

首先我们可以看出来 Module 是真正提供依赖的地方,但是要发挥作用的话还是要依靠 Component 类,而且一个 Component 类类可以包含多个 Module 类,用来提供多个依赖。

接着我们重新回顾一下上面的注入过程:首先 MeFragment 需要使用 MePresenter ,因此,我们在基类 BaseFragment 用 @Inject 对 *Presenter 进行标注 P,表明这是要注入的类。并且写了一个抽象方法 injectPresent 用于让子类来实现注入。

abstract classBaseFragment

:Fragment(), BaseView {

privatevar mLoadingDialog: Dialog? = null @Inject

protectedvar present: P? = null

setoverride fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) injectPresent()

if(present != null) { present!!.onCreate() } } protectedopen fun injectPresent() { }

//.....

}

后面的分析过程基本上可以从上面的讲解弄清楚,如果还有不清楚的可以给我提下,我再改下。

但是到此时,我们仍然没有看到实例化的过程,所以下面,就基于这个实例,分析 Dagger2 内部究竟做了什么。

Dagger2注入原理

Dagger2 与其他依赖注入框架不同,它是通过 apt 插件在编译阶段生成相应的注入代码,下面

我们就具体看看 Dagger2 生成了哪些注入代码?

我们先看 MePresenter 这个类, Dagger2 会在 appbuildgeneratedsourceaptarmeabireleasecomkibodipresentmodule 目录下生成一个对应的工厂类MePresentModule_ProvidePresentFactory,看下面具体代码:

@Generated( value = "dagger.internal.codegen.ComponentProcessor", comments = "https://google.github.io/dagger")

publicfinalclassMePresentModule_ProvidePresentFactoryimplementsFactory{

privatefinalMePresentModule module;

privatefinalProvider userApiProvider;

privatefinalProvider userManagerProvider;

privatefinalProvider draftManagerProvider;

publicMePresentModule_ProvidePresentFactory( MePresentModule module, Provider userApiProvider, Provider userManagerProvider, Provider draftManagerProvider){

assertmodule!= null;

this.module= module;

assertuserApiProvider != null;

this.userApiProvider = userApiProvider;

assertuserManagerProvider != null;

this.userManagerProvider = userManagerProvider;

assertdraftManagerProvider != null;

this.draftManagerProvider = draftManagerProvider; }

@OverridepublicMePresent get(){

returnPreconditions.checkNotNull(

module.providePresent( userApiProvider.get(), userManagerProvider.get(), draftManagerProvider.get()),

"Cannot return null from a non-@Nullable @Provides method"); } publicstaticFactory create( MePresentModule module, Provider userApiProvider, Provider userManagerProvider, Provider draftManagerProvider){

returnnewMePresentModule_ProvidePresentFactory(

module, userApiProvider, userManagerProvider, draftManagerProvider); }

/** Proxies {@linkMePresentModule#providePresent(UserApi, UserManager, DraftManager)}. */publicstaticMePresent proxyProvidePresent( MePresentModule instance, UserApi userApi, UserManager userManager, DraftManager draftManager){

returninstance.providePresent(userApi, userManager, draftManager); }}

对比MePresentModule:

@ModuleclassMePresentModule(private val uid: Long, private val meView: MeView) { @Provides @UIScopedinternal fun providePresent(userApi: UserApi, userManager: UserManager, draftManager: DraftManager): MePresent {

returnMePresentImp(uid, userApi, userManager, draftManager, meView) }}

看到上面的类名,我们发现了一种对应关系,在 MePresentModule 中定义的 @Provides 修饰的方法 providePresent 会对应的生成一个工厂类,这里是 MePresentModule_ProvidePresentFactory。我们看到这个类里有一个 get() 方法,其中调用了 ApIModule 里的 get() 方法来返回我们所需要的依赖 UserApi 以及 UserManager 和DraftManager 。

看下 ApiModule生成的 ApiModule_ProvideUserApiFactory中的 get() 方法,就是返回一个 UserApi :

@Generated( value = "dagger.internal.codegen.ComponentProcessor", comments = "https://google.github.io/dagger")

publicfinalclassApiModule_ProvideUserApiFactoryimplementsFactory{

privatefinalApiModule module;

privatefinalProvider weiCoServiceProvider;

privatefinalProvider weiBoServiceProvider;

publicApiModule_ProvideUserApiFactory( ApiModule module, Provider weiCoServiceProvider, Provider weiBoServiceProvider){

assertmodule!= null;

this.module= module;

assertweiCoServiceProvider != null;

this.weiCoServiceProvider = weiCoServiceProvider;

assertweiBoServiceProvider != null;

this.weiBoServiceProvider = weiBoServiceProvider; }

@OverridepublicUserApi get(){

returnPreconditions.checkNotNull(

module.provideUserApi(weiCoServiceProvider.get(), weiBoServiceProvider.get()),

"Cannot return null from a non-@Nullable @Provides method"); } publicstaticFactory create( ApiModule module, Provider weiCoServiceProvider, Provider weiBoServiceProvider){

returnnewApiModule_ProvideUserApiFactory(module, weiCoServiceProvider, weiBoServiceProvider); }

/** Proxies {@linkApiModule#provideUserApi(WeiCoService, WeiBoService)}. */publicstaticUserApi proxyProvideUserApi( ApiModule instance, WeiCoService weiCoService, WeiBoService weiBoService){

returninstance.provideUserApi(weiCoService, weiBoService); }}

所以获取了 uid , userApi , userManager , draftManager , meView 这五个参数注入到 MePresent 中,然后就可以提供 MePresent 来使用了!其他的都是类似的过程。还有一些其他层面的问题我在这不一一解答了,感觉问题还是比较多的,大家可以在网络上找到答案,或者直接提出自己的问题。总的来说去看生成的代码直接分析还是比较容易理解的。

看到这再看这张图应该会有点理解了:

08ecd1dc554560b3120fe85a463afbb6.png

未完待续...

大家都在看

欢迎大家到安卓巴士论坛博文专区》返回搜狐,查看更多

责任编辑:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值