Kotlin—dagger2+retrofit+mvp实战

上一篇我们学习了anko库的使用,到这里我们来做一个简单的小项目吧!
那使用什么架构来写呢,这里我们使用Kotlin+dagger2+retrofit+MVP架构来撸码

下面分享一下大牛写的不错的文章以及自己在开发时候用到的开源接口

dagger2 http://www.jianshu.com/p/39d1df6c877d
retrofit http://www.jianshu.com/p/308f3c54abdd
API http://gank.io/api

正文

因为项目中用的Dagger2比较多,这里来讲解一下项目中用到的几个注解,更多的详情可以看上面的地址

Component:组件、管理器、注入器

功能就是将类中使用@Inject标记的属性和在对应属性类中使用@Inject标记的构造方法,然后将它们关联起来。

而如果构造方法需要参数,或者我们没法再需要注入的对象的构造方法加入@Inject注解的时候就需要使用Module

该注解里面的参数有两个,一个是设置modules所关联的Module类,第二个是dependency所依赖的Component

image
Module:提供者、依赖对象工厂

功能是与被@Inject标记的构造方法一样提供生成依赖的对象

因为对于第三方库我们没有办法将它的构造方法加入@Inject标记,这时候我们需要使用Module生成,并提供@provide注解,Component会去查找Module类中@provide的方法获取到对象并通过component返回目标类需要的对象并注入到目标类

Qualifier:限定符

功能是在同一纬度下存在多个依赖对象的提供方式(多个构造方法),则会迷失。这时,可以使用Qualifier

提供依赖对象有两种方式

(1)通过使用Inject注解标注的构造函数来创建

(2)通过工厂模式的Module来创建

如果一个依赖对象以上两种方式都能够提供,它会优先使用Module。Qualifier有一个@Named 指定相同的参数和自定义Qualifier注解一样的效果

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {

    /** The name. */
    String value() default "";
}
Scope:作用域

管理创建的类实例的生命周期。
可以通过Scope来限定通过Module和Inject方式创建的类的实例的生命周期能够与目标类的生命周期相同。
Scope本身没有制定生命周期的能力,它的存在一是为了可读性,二是更好的管理Component和Module的关系

如果你想了解Dagger2具体调用的流程可以查看

image

接下来我们开始看一下要做的小项目的效果图

image
image

ok,项目比较low,废话不多说,开始我们撸代码时间

我是这样分包的

image

在项目中我定义了一个全局的Component类

@Singleton
@Component(modules = arrayOf(DataSourceModule::class)) //注入器对象提供工厂
interface AppComponent{
    /**
     * 全局注入器能够提供的对象
     */
    fun dataManager(): DataManager
}

这个类为DataSourceModule用来提供DataManager对象的生成,使用DataManager来对网络请求
会去查询DataSourceModule类中去找生成DataManager对象的方法

@Module
class DataSourceModule {

    @Singleton
    @Provides
    fun provideGankService(): GankService {
        return Retrofit.Builder().addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(BASE_URL).build().create(GankService::class.java)
    }

    @Singleton
    @Local
    @Provides
    fun provideLocal(): IDataSource{
        return LocalDataSource()
    }

    @Singleton
    @Remote
    @Provides
    fun provideRemote(service: GankService): IDataSource {
        return RemoteDataSource(service)
    }

    @Singleton
    @Provides
    fun provideDataManager(@Remote remote: IDataSource, @Local local: IDataSource): DataManager {
        return DataManager(remote,local)
    }
}

这里推荐使用provide开头,在方法参数里面我有定义两个注解Remote和Local用来区分对应哪个一个IDataSource

@Qualifier
annotation class Remote

provideGankService方法里面的写法使用了Gson转换用于将json转化为对象、Rxjava回调用来对网络请求的结果做不同的处理

然后我们在来看一下DataManager类

fun getWelfareList(page: Int): Flowable<WelfareEntity> {
    return remote.getWelfareList(page).onErrorResumeNext(local.getWelfareList(page))
}

主要是定义相应的规则,先在网络请求获取,当失败的时候获取本地的数据

ok,前期的工作准备完毕,为了更好去使用APPComponent,我们在Application自定一个单例

class MyApplication: Application(){

    /**
     * 提供全局注入器的获得
     */
    lateinit private var appComponent: AppComponent

    companion object {
        lateinit var app: MyApplication
        fun getApplication(): MyApplication{
            return app
        }
    }

    override fun onCreate() {
        super.onCreate()
        app = this
        appComponent = DaggerAppComponent.builder().dataSourceModule(DataSourceModule()).build()
    }

    fun getAppComponent(): AppComponent = appComponent
}

开始我们的主界面,当然这个项目也就一个Activity,既然是MVP就少不了Persenter

@Inject
lateinit var presenter: MainPersenter

使用Inject注解来自动去创建MainPersenter对象,然后定义MainAppComponent

@ActivityScope
@Component(modules = arrayOf(MainActivityModule::class),dependencies = arrayOf(AppComponent::class))
interface MainAppComponent{
    fun inject(activity: MainActivity)
}

这里对APPComponent进行了依赖,因为在MainPersenter类里面使用到了DataManager的对象

@Module
class MainActivityModule(val view: MainActivity){

    @ActivityScope
    @Provides
    fun provide1Presenter(dataManager: DataManager): MainPersenter{
        return MainPersenter(view,dataManager)
    }

}

然后我们在来看一下MainPersenter

class MainPersenter(val view: MainViews, val dataManager: DataManager) {
    //福利
    fun getWelfarmList(page: Int) {
        dataManager.getWelfareList(page)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe { view.startLoading() }
                .doOnError { view.stopLoading() }
                .subscribe {
                    view.stopLoading()
                    view.showWefareList(it.results)
                }
}

在这个类中主要是使用DataManager的对象用来获取数据并针对获取成功和失败调用View层的方法

最后我们需要初始化Dagger2注入器

//初始化Dagger2注入器
DaggerMainAppComponent.builder()
        .appComponent(MyApplication.getApplication().getAppComponent())
        .mainActivityModule(MainActivityModule(this))
        .build().inject(this)

另外在写生成ItemView的时候写了两种方式,这里也一并贴出来和大家一起分享

第一种使用with表达式

val view = with(context){
verticalLayout {
    gravity = Gravity.CENTER_HORIZONTAL
    imageView {
        id = R.id.welfare_item_iv
        imageResource = R.mipmap.ic_launcher
        scaleType = ImageView.ScaleType.FIT_XY
        lparams {
            height = dip(250)
            width = matchParent
            leftMargin = dip(20)
            rightMargin = dip(20)
            topMargin = dip(15)
            bottomMargin = dip(15)
             }
        }
    }
}

当然这样写感觉代码太多了,我们需要将生成布局的代码单独使用一个类,我们可以定义一个类来继承AnkoComponent来写独立的一个布局,如果安装插件还可以预览界面的效果

class RecyclerUI: AnkoComponent<AndroidAdapter>{
override fun createView(ui: AnkoContext<AndroidAdapter>): View = with(ui){
    verticalLayout {
        orientation = LinearLayout.HORIZONTAL
        lparams {
            topMargin = dip(10)
            leftMargin = dip(15)
            rightMargin = dip(15)
            bottomMargin = dip(10)
        }
        imageView {
            id = R.id.android_item_iv
            imageResource = R.mipmap.android_icon
            lparams {
                width = dip(90)
                height = dip(90)
            }
        }
        verticalLayout {
            textView {
                id = R.id.android_item_tv1
                textSize = 18.toFloat()

            }

            textView {
                id = R.id.android_item_tv2
                textSize = 16.toFloat()
                textColor = Color.RED
                }
            }
        }
    }
}

至此,小项目的讲解就结束了。

坚持总会有结果,总会有收获的
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容