Android項目框架之MVP+Dagger.Android+LiveData

個人博客:haichenyi.com。感謝關(guān)注

??之前的那個系列從零開始搭建一個主流的項目框架(一?~?八),盡管把dagger2中inject activity 優(yōu)化了一下,但是依然顯得繁瑣,每一個activity里面都要寫一個inject方法。Dagger.Android只用在基類里面inject一次,子類里不用管,直接用。

??還有就是之前的MVP架構(gòu),每次更新頁面的時候都要在每個頁面對應(yīng)的Contract——View里面寫方法,在P層去實現(xiàn)然后在Activity里面也要實現(xiàn)一遍,這樣太繁瑣,現(xiàn)在用上LiveData,解決了這個問題,并且LiveData的優(yōu)點:

  1. 能保證UI與數(shù)據(jù)保持一致。LiveData是觀察者模式,與EventBus一樣,一個地方注冊observer,另一個地方去響應(yīng),調(diào)用setValue()方法通知頁面更新。
  2. 與activity的生命周期同步,不會出現(xiàn)內(nèi)存泄漏。LiveData的observer對象是與lifeCycle綁定的,當(dāng)lifeCycle對象被onDestory的時候,observer對象也被clean up了
  3. 當(dāng)activity處于stop activity狀態(tài)時,不會崩潰。當(dāng)observer處于inactive狀態(tài)時,例如,activity被按了返回鍵,observer將不會響應(yīng)來自LiveData的事件
  4. 不需要更多的手動管理生命周期。LiveData負(fù)責(zé)自動管理生命周期
  5. 保證數(shù)據(jù)是最新的。比如處于后臺的activity,返回到前臺是時,他會自動獲取最新數(shù)據(jù)。
  6. 響應(yīng)屏幕旋轉(zhuǎn)等配置變化。
  7. 共享資源。新建一個專門的LiveData管理類,通過單例模式,在項目的任意位置都可以訪問的到。

官方介紹

Dagger.Android

依賴的庫

    implementation 'com.google.dagger:dagger:2.17'
    implementation 'com.google.dagger:dagger-android:2.16'
    implementation 'com.google.dagger:dagger-android-support:2.16'
    annotationProcessor 'com.google.dagger:dagger-android-processor:2.16'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.16'

集成

  1. 集成到application中
  2. 集成到activity中
  3. 集成到fragment中

Application

新建Application
package com.haichenyi.aloe.demo1.base;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.support.multidex.MultiDex;

import com.haichenyi.aloe.demo1.di.component.DaggerAppComponent;
import com.haichenyi.aloe.tools.LogUtils;
import com.haichenyi.aloe.tools.ToastUtils;

import javax.inject.Inject;

import dagger.android.AndroidInjector;
import dagger.android.DispatchingAndroidInjector;
import dagger.android.HasActivityInjector;

/**
 * @Title: BaseApp
 * @Des: Application
 * @Author: haichenyi
 * @Date: 2018/11/20 0020
 * @Email: haichenyi@yeah.net
 */
public class BaseApp extends Application implements HasActivityInjector {

    @Inject
    DispatchingAndroidInjector<Activity> dispatchingActivityInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        setInstance(this);
        DaggerAppComponent.create().inject(this);
    }
    
    @Override
    public AndroidInjector<Activity> activityInjector() {
        return dispatchingActivityInjector;
    }
}

??實現(xiàn)HasActivityInjector接口,這部分可以直接復(fù)制過去就可以了

新建AppComponent,AppModule
@Singleton
@Component(modules = {AndroidInjectionModule.class, AppModule.class})
public interface AppComponent {
    void inject(BaseApp baseApp);
}

@Module
public class AppModule {

}

這都沒有什么要說的,看好注解就行了。

Activity

新建BaseActivity
public class BaseActivity extends SupportActivity{
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        AndroidInjection.inject(this);
        super.onCreate(savedInstanceState);
    }
}

??在super前面添加AndroidInjection.inject(this);

新建ActComponent
package com.haichenyi.aloe.demo1.di.component;

import com.haichenyi.aloe.demo1.base.BaseActivity;

import dagger.Subcomponent;
import dagger.android.AndroidInjectionModule;
import dagger.android.AndroidInjector;

/**
 * @Title: ActComponent
 * @Des:
 * @Author: haichenyi
 * @Date: 2018/11/21 0021
 * @Email: haichenyi@yeah.net
 */
@Subcomponent(modules = {AndroidInjectionModule.class})
public interface ActComponent extends AndroidInjector<BaseActivity> {
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<BaseActivity> {

    }
}

??這里實現(xiàn)AndroidInjector<T>接口,這里的泛型傳你的BaseActivity,然后添加抽象的Builder類

新建AllActivitiesModule
package com.haichenyi.aloe.demo1.di.module;

import com.haichenyi.aloe.demo1.di.component.ActComponent;

import dagger.Module;
import dagger.android.ContributesAndroidInjector;

/**
 * @Title: AllActivitiesModule
 * @Des:
 * @Author: haichenyi
 * @Date: 2018/11/21 0021
 * @Email: haichenyi@yeah.net
 */
@Module(subcomponents = ActComponent.class)
public abstract class AllActivitiesModule {

}

??這里的module注解里面?zhèn)髂闵厦鎰?chuàng)建的ActivityComponent類

重要的事情說三遍,不然報錯都不知道為什么

重要的事情說三遍,不然報錯都不知道為什么

重要的事情說三遍,不然報錯都不知道為什么

??接下來,在你創(chuàng)建的AppComponent類里面,的注解@Component,添加AllActivitiesModule.class,如下:

@Singleton
@Component(modules = {AndroidInjectionModule.class, AppModule.class, AllActivitiesModule.class})
public interface AppComponent {
    void inject(BaseApp baseApp);
}

??然后,你新建Activity就直接繼承你創(chuàng)建的BaseActivity,然后,在你上面創(chuàng)建的AllActivitiesModule里面添加如下代碼即可:

package com.haichenyi.aloe.demo1.di.module;

import com.haichenyi.aloe.demo1.di.component.ActComponent;
import com.haichenyi.aloe.demo1.ui.activity.FragmentActivity;
import com.haichenyi.aloe.demo1.ui.activity.SwipeBackActivity;
import com.haichenyi.aloe.demo1.ui.activity.WelcomeActivity;

import dagger.Module;
import dagger.android.ContributesAndroidInjector;

/**
 * @Title: AllActivitiesModule
 * @Des:
 * @Author: haichenyi
 * @Date: 2018/11/21 0021
 * @Email: haichenyi@yeah.net
 */
@Module(subcomponents = ActComponent.class)
public abstract class AllActivitiesModule {

    @ContributesAndroidInjector
    abstract WelcomeActivity contributeWelcomeActivityInjector();

    @ContributesAndroidInjector
    abstract FragmentActivity contributeFragmentActivityInjector();

    @ContributesAndroidInjector
    abstract SwipeBackActivity contributeSwipeBackActivityInjector();

}

??這里是我的demo里面創(chuàng)建的三個activity:WelcomeActivity,F(xiàn)ragmentActivity,SwipeBackActivity,這樣就可以用了。

Fragment

重要的事情說三遍,不然報錯都不知道為什么

重要的事情說三遍,不然報錯都不知道為什么

重要的事情說三遍,不然報錯都不知道為什么

??官方文檔上面介紹的是app包下面fragment的集成方式,實現(xiàn)的是HasFragmentInjector接口。

??我這里介紹的是v4包下面的fragment的集成方式,絕大多數(shù)情況下,我們用的都是v4包下面的fragment,實現(xiàn)的是HasSupportFragmentInjector接口。

??強調(diào)一遍,一個是HasSupportFragmentInjector,一個是HasFragmentInjector,帶Support的是v4包下的,不帶的是app包下的。

??進入正題,fragment依賴activity存在,我們這里,需要對包含fragment的activity做額外的操作。如下:

新建fragment需要依賴的activity
package com.haichenyi.aloe.demo1.ui.activity;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;

import com.haichenyi.aloe.demo1.R;
import com.haichenyi.aloe.demo1.base.BaseActivity;
import com.haichenyi.aloe.demo1.base.BaseFragment;
import com.haichenyi.aloe.demo1.presenter.EmptyPresenter;
import com.haichenyi.aloe.demo1.ui.frag.Fragment01;
import com.haichenyi.aloe.demo1.ui.frag.Fragment02;
import com.haichenyi.aloe.demo1.ui.frag.Fragment03;
import com.haichenyi.aloe.demo1.ui.frag.Fragment04;
import com.jaeger.library.StatusBarUtil;

import java.util.ArrayList;

import javax.inject.Inject;

import dagger.android.AndroidInjector;
import dagger.android.DispatchingAndroidInjector;
import dagger.android.support.HasSupportFragmentInjector;

/**
 * @Title: FragmentActivity
 * @Des:
 * @Author: haichenyi
 * @Date: 2018/11/29 0029
 * @Email: haichenyi@yeah.net
 */
public class FragmentActivity extends BaseActivity<EmptyPresenter> implements HasSupportFragmentInjector {
    @Inject
    DispatchingAndroidInjector<Fragment> fragmentInjector;

    ArrayList<BaseFragment> list;
    ViewPager viewPager;

    @Override
    protected int layout(@Nullable Bundle savedInstanceState) {
        setAttachToolbar(false);
        setFullScreen(false);
        return R.layout.activity_fragment;
    }

    @Override
    protected void initView() {
        super.initView();
        viewPager = findViewById(R.id.viewPager);
    }

    @Override
    protected void initData() {
        super.initData();
        StatusBarUtil.setTranslucentForImageViewInFragment(this, null);
        StatusBarUtil.hideFakeStatusBarView(this);

        list = new ArrayList<>();
        list.add(new Fragment01());
        list.add(new Fragment02());
        list.add(new Fragment03());
        list.add(new Fragment04());
        viewPager.setOffscreenPageLimit(3);
        viewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int position) {
                return list.get(position);
            }

            @Override
            public int getCount() {
                return list.size();
            }
        });
    }

    @Override
    public AndroidInjector<Fragment> supportFragmentInjector() {
        return fragmentInjector;
    }
}

??如上,實現(xiàn)的是HasSupportFragmentInjector,然后實現(xiàn)supportFragmentInjector方法即可。

??記得,這里繼承的是BaseActivity,要在前面創(chuàng)建的AllActivitiesModule中,把這個FragmentActivity添加進去。

新建FragmentActivity,需要的Component
package com.haichenyi.aloe.demo1.di.component;

import com.haichenyi.aloe.demo1.ui.activity.FragmentActivity;

import dagger.Subcomponent;
import dagger.android.AndroidInjectionModule;
import dagger.android.AndroidInjector;

/**
 * @Title: FragActivityComponent
 * @Des:
 * @Author: haichenyi
 * @Date: 2018/11/29 0029
 * @Email: haichenyi@yeah.net
 */
@Subcomponent(modules = {AndroidInjectionModule.class})
public interface FragActivityComponent extends AndroidInjector<FragmentActivity> {
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<FragmentActivity> {

    }
}

??這里的泛型傳你上面創(chuàng)建的FragmentActivity

新建FragmentActivity,需要的Module
package com.haichenyi.aloe.demo1.di.module;

import android.app.Activity;

import com.haichenyi.aloe.demo1.di.component.FragActivityComponent;
import com.haichenyi.aloe.demo1.ui.activity.FragmentActivity;

import dagger.Binds;
import dagger.Module;
import dagger.android.ActivityKey;
import dagger.android.AndroidInjector;
import dagger.multibindings.IntoMap;

/**
 * @Title: FragActivityModule
 * @Des:
 * @Author: haichenyi
 * @Date: 2018/11/29 0029
 * @Email: haichenyi@yeah.net
 */
@Module(subcomponents = FragActivityComponent.class)
public abstract class FragActivityModule {

    @Binds
    @IntoMap
    @ActivityKey(FragmentActivity.class)
    abstract AndroidInjector.Factory<? extends Activity> bindDaggerFragmentActivityInjectorFactory(FragActivityComponent.Builder builder);

}
新建BaseFragment
public class BaseFragment extends Fragment{

    @Override
    public void onAttach(Context context) {
        AndroidSupportInjection.inject(this);
        super.onAttach(context);
    }

}

??在super前面添加AndroidSupportInjection.inject(this);

新建BaseFragment需要的Component
package com.haichenyi.aloe.demo1.di.component;

import com.haichenyi.aloe.demo1.base.BaseFragment;

import dagger.Subcomponent;
import dagger.android.AndroidInjectionModule;
import dagger.android.AndroidInjector;

/**
 * @Title: BaseFragComponent
 * @Des:
 * @Author: haichenyi
 * @Date: 2018/11/21 0021
 * @Email: haichenyi@yeah.net
 */
@Subcomponent(modules = {AndroidInjectionModule.class})
public interface BaseFragComponent extends AndroidInjector<BaseFragment>{
    @Subcomponent.Builder
    public abstract class Builder extends AndroidInjector.Builder<BaseFragment>{

    }
}
新建BaseFragment需要的Module
package com.haichenyi.aloe.demo1.di.module;

import com.haichenyi.aloe.demo1.di.component.BaseFragComponent;
import com.haichenyi.aloe.demo1.ui.frag.Fragment01;
import com.haichenyi.aloe.demo1.ui.frag.Fragment02;
import com.haichenyi.aloe.demo1.ui.frag.Fragment03;
import com.haichenyi.aloe.demo1.ui.frag.Fragment04;

import dagger.Module;
import dagger.android.ContributesAndroidInjector;

/**
 * @Title: AllFragmentModule
 * @Des:
 * @Author: haichenyi
 * @Date: 2018/11/29 0029
 * @Email: haichenyi@yeah.net
 */
@Module(subcomponents = BaseFragComponent.class)
public abstract class AllFragmentModule {

    @ContributesAndroidInjector
    abstract Fragment01 contributeFragment01Injector();

    @ContributesAndroidInjector
    abstract Fragment02 contributeFragment02Injector();

    @ContributesAndroidInjector
    abstract Fragment03 contributeFragment03Injector();

    @ContributesAndroidInjector
    abstract Fragment04 contributeFragment04Injector();
}

??跟activity一樣,把繼承BaseFragment的fragment,都添加到這里來

??然后就是把這個AllFragmentModule添加到AppComponent的@Component注解上就可以了。

至此

至此

至此

??到這里,這個dagger.android就添加依賴完了,后面我們新建activity,直接繼承BaseActivity就可以直接用了。新建fragment,直接繼承BaseFragment就可以直接用了

LiveData

依賴的庫

現(xiàn)在項目創(chuàng)建完,都是自帶的。lifecycle,不需要額外添加依賴庫

用法

??很簡單,觀察者模式,類似于EventBus的用法。一個地方注冊,一個地方觸發(fā)。

MutableLiveData<String> liveData1 = new MutableLiveData<>();
liveData1.observe(this, new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {
                Log.v("wz",s);
            }
        });
liveData1.setValue("123");

??簡單的來說,就像上面這樣,新建一個LiveData對象,注冊一個observer,然后,setValue去觸發(fā)這個onChange方法。

??LiveData提供了兩個方法觸發(fā)onChange方法,一個是setValue:在當(dāng)前線程觸發(fā)回調(diào);另一個postValue:在主線程觸發(fā)回調(diào)。

??前面我們說過共享資源,LiveData是全局的,我們可以新建一個管理類,我這里給出來了。通過map簡單的封裝了一下添加,移除的方法(通過鍵,或者通過類名稱移除當(dāng)前類的所有觀察者),項目里面有,有點長,就不貼出來了,我貼出來方法

??在我的BaseActivity或者BaseFragment,添加如下方法:

/**
     * 設(shè)置LiveData的Observer(需要通過類名稱手動移除Observer)
     *
     * @param aClass   當(dāng)前類對象
     * @param key      鍵
     * @param observer 監(jiān)聽器
     * @param <T>      數(shù)據(jù)類型
     */
    protected synchronized <T> void setObserver(final Class aClass, final String key, final Observer<T> observer) {
        BaseLiveData<T> liveData = new BaseLiveData<>();
        liveData.observe(this, observer);
        LiveDataManager.getInstance().putLiveData(aClass, key, liveData);
    }

    /**
     * 設(shè)置LiveData的Observer(不需要通過類名稱手動移除Observer)
     *
     * @param key      鍵
     * @param observer 監(jiān)聽器
     * @param <T>      數(shù)據(jù)類型
     */
    protected synchronized <T> void setObserver(final String key, final Observer<T> observer) {
        BaseLiveData<T> liveData = new BaseLiveData<>();
        liveData.observe(this, observer);
        LiveDataManager.getInstance().putLiveData(key, liveData);
    }

??在子類里面調(diào)用:

@Override
    protected void initData() {
        super.initData();
        this.<String>setObserver("internet", ToastUtils::showTipMsg);
        this.<String>setObserver("internet2", ToastUtils::showTipMsg);
    }

??在P層里面去觸發(fā)回調(diào):

package com.haichenyi.aloe.demo1.presenter;

import com.haichenyi.aloe.demo1.base.BasePresenter;
import com.haichenyi.aloe.demo1.base.BaseView;
import com.haichenyi.aloe.demo1.impl.LiveDataManager;

import javax.inject.Inject;

/**
 * @Title: WelcomePresenter
 * @Des:
 * @Author: haichenyi
 * @Date: 2018/11/21 0021
 * @Email: haichenyi@yeah.net
 */
public class WelcomePresenter extends BasePresenter<BaseView> {

    @Inject
    public WelcomePresenter() {
        //Dagger2 generate object.
    }

    public void getData() {
        LiveDataManager.getInstance().getLiveData("internet").callbackUI("123");
    }

    public void getData2() {
        LiveDataManager.getInstance().getLiveData("internet2").callbackUI("321");
    }
}

??如上所示,我們的P層代碼與之前的框架相比,簡潔了很多。

項目鏈接

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內(nèi)容