Android開源組件化開發框架Android-Sun-Framework

xiongrq 7年前發布 | 17K 次閱讀 安卓開發 Android開發 移動開發

一. 寫在前面

Android-Sun-Framework是一個Android組件化開發框架,可用于中大型項目。

二. 框架結構

遵循高內聚低耦合理念,Module之間沒有強依賴,具體結構如下圖:

三. 框架依賴

//Rx系列
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
    compile 'com.trello.rxlifecycle2:rxlifecycle:2.0.1'
    compile 'com.trello.rxlifecycle2:rxlifecycle-android:2.0.1'
    compile 'com.trello.rxlifecycle2:rxlifecycle-components:2.0.1'

//Retrofit
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
//路由
compile 'com.github.mzule.activityrouter:activityrouter:1.2.2'

//圖片加載
compile 'com.github.bumptech.glide:glide:3.7.0'

//下拉刷新
compile 'com.lcodecorex:tkrefreshlayout:1.0.7'

compile 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.15'</code></pre> 

三. 詳細說明

下面我會根據代碼,詳細講解框架結構及其使用說明。

A. 配置中心config.gradle

ext {
    android = [
            compileSdkVersion: 25,
            buildToolsVersion: "25.0.2",
            minSdkVersion    : 19,
            targetSdkVersion : 25,
            versionCode      : 1,
            versionName      : "1.0.0",
            isModule         : false, //是否是獨立模塊開發
            isDebug          : "true",//是否是調試模式
            scheme           : "\"xpai\"" //應用scheme
    ]
    //依賴配置
    dependencies = [
            "supportVersion": "25.2.0"
    ]
}

當前配置項還不多,后面會根據實際開發需要,優化配置。

這里我單獨說明一下isModule的作用:

  1. 當isModule為真時,除了library子模塊為library,其他子模塊均為application,我們以login模塊為例,看一下是如何實現的
if (rootProject.ext.android.isModule) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

android { compileSdkVersion rootProject.ext.android.compileSdkVersion buildToolsVersion rootProject.ext.android.buildToolsVersion

defaultConfig {
    if (rootProject.ext.android.isModule) {
        applicationId "com.ody.login"
    }
    minSdkVersion rootProject.ext.android.minSdkVersion
    targetSdkVersion rootProject.ext.android.targetSdkVersion
    versionCode rootProject.ext.android.versionCode
    versionName rootProject.ext.android.versionName
}

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

sourceSets {
    main {
        if (rootProject.ext.android.isModule) {
            manifest.srcFile 'src/main/debug/AndroidManifest.xml'
        } else {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            //release模式下排除debug文件夾中的所有Java文件
            java {
                exclude 'debug/**'
            }
        }
    }
}

}

dependencies { compile fileTree(include: ['*.jar'], dir: 'libs')

annotationProcessor 'com.github.mzule.activityrouter:compiler:1.1.7'
compile project(':library')

}</code></pre>

我們主要看一下rootProject.ext.android.isModule的if判斷,這里根據它來配置當前模塊plugin為library還是application。當為application時,下面會增加applicationId的配置。

仔細的同學可能會問,sourceSets的配置是干嘛的?由于application 必須要有默認啟動Activity,所以這里我們需要根據isModule使用不同的AndroidManifest.xml,每個模塊可能會有測試代碼,所以只要把測試代碼寫在debug包下面,正式編譯的時候debug下面的java文件不會參與編譯。

同樣的我們來看一下主Module的配置:

apply plugin: 'com.android.application'

android { ...... }

dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') testCompile 'junit:junit:4.12' //路由 annotationProcessor 'com.github.mzule.activityrouter:compiler:1.1.7' if (rootProject.ext.android.isModule) { compile project(':library') } if (!rootProject.ext.android.isModule) { compile project(':guide') } if (!rootProject.ext.android.isModule) { compile project(':login') } if (!rootProject.ext.android.isModule) { compile project(':trade') } if (!rootProject.ext.android.isModule) { compile project(':usercenter') } }</code></pre>

這里我們只關注dependencies,根據isModule來配置是否依賴該Module.

  1. 當isModule為真時,Module可以單獨運行,這樣做的好處是:一:大大縮減編譯時間;二:可以跨部門,跨團隊協作。

    B. Module間跳轉

    對比 ARouter 與 ActivityRouter ,我決定使用ActivityRouter,主要原因有兩個:
  2. 由于ARouter加入分組概念,和我們公司當前已有設計相違背;
  3. ActivityRouter比ARouter精簡,后面我會分享一下ActivityRouter和ARouter的實現原理, 敬請期待!

ActivityRouter具體如何使用我就不再贅述,詳細可以查看 ActivityRouter ,里面有詳細的說明和demo。我在這里要強調一下ActivityRouter多模塊需要如何實現?

實現步驟:

a. 在子Module中新建一個Module類,比如我這里的login模塊:

@Module("login")
public class LoginModule {
}

b. 同樣在主Module中也需要新建一個類

@Module("app")
public class AppModule {
}

c. Module注冊

@Modules({"app", "login"})
public class OdyApplication extends BaseApplication {
}

d. 界面跳轉

為了能統一處理,自己把ActivityRouter的跳轉封裝了一層:

public class JumpUtils {
    public final static String LOGIN_URL = "login";

public static void open(Context context, String url) {
    Routers.open(context, BuildConfig.SCHEME + "://" + url);
}

public static void open(Context context, String url, RouterCallback callback) {
    Routers.open(context, BuildConfig.SCHEME + "://" + url, callback);
}

}</code></pre>

JumpUtils里的常量就是跳轉注解路徑

@Router(JumpUtils.LOGIN_URL)
public class LoginActivity extends BaseActivity {
    ......
}

以上都是為了能更好的 統一管理,避免后期修改多處

C. 網絡庫封裝

做Android開發的想必大家都知道Retrofit和Rxjava,我們也是使用的他們,為了更好的控制網絡請求,這里我同時引入了Rxlifecycle。接下來我們看看具體是如何實現的

  1. 添加依賴
//Rx系列
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
    compile 'com.trello.rxlifecycle2:rxlifecycle:2.0.1'
    compile 'com.trello.rxlifecycle2:rxlifecycle-android:2.0.1'
    compile 'com.trello.rxlifecycle2:rxlifecycle-components:2.0.1'

//Retrofit
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'</code></pre> 

  1. Retrofit接口定義
public interface BaseNetApi {
    @GET("/api/dolphin/list?&platform=3&platformId=0&pageCode=APP_HOME&adCode=ad_banner&areaCode=310115")
    Observable<AdBean> getAd(@QueryMap Map<String, String> params);
}
  1. Retrofit初始化和具體實現
public class SingletonNet {
    public static final String BASE_URL = "

private SingletonNet() {
    //手動創建一個OkHttpClient并設置超時時間
    OkHttpClient.Builder builder = new OkHttpClient.Builder();
    builder
            //添加公共header
            .addInterceptor(new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request.Builder builder = chain.request().newBuilder();
                    builder.addHeader("token", "123");
                    return chain.proceed(builder.build());
                }
            })
            .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
            .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);

    Retrofit.Builder b = new Retrofit.Builder()
            .client(builder.build())
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .baseUrl(BASE_URL);

    retrofit = b.build();
}

public static SingletonNet getSingleton() {
    if (INSTANCE == null) {
        synchronized (SingletonNet.class) {
            if (INSTANCE == null) {
                INSTANCE = new SingletonNet();
            }
        }
    }
    return INSTANCE;
}

public <T> T getNetService(Class<T> t) {
    return retrofit.create(t);
}

}</code></pre>

我們使用泛型,為各個模塊創建自己的API,這樣做能更好的解耦。同時我們也在基礎庫里實現了BaseNetApi,這里是項目中重復使用的接口,避免多處實現同一個接口。我們再來看一下其他Module里是如何創建的

public class MainHttpClient extends BaseHttpClient {

private static class SingletonHolder {
    private static final MainApi API = SingletonNet.getSingleton().getNetService(MainApi.class);
}

public static Observable<AdBean> get() {
    Map<String, String> params = new HashMap<>();
    return SingletonHolder.API.get(params);
}

}</code></pre>

這里采用靜態內部類實現了單例,和SingletonNet的單例有區別。

  1. 具體使用
MainHttpClient.get()
                .compose(RxSchedulers.<AdBean>compose())
                .compose(this.<AdBean>bindToLifecycle())
                .subscribe(new HttpObserver<AdBean>(mContext) {

                @Override
                protected void success(AdBean bean) {
                    super.success(bean);
                }

            });</code></pre> 

.compose(RxSchedulers.<AdBean>compose())線程調度等重復操作放在這里;

.compose(this.<AdBean>bindToLifecycle()) 網絡請求和Activity生命周期想關聯。

HttpObserver的實現如下:

public abstract class HttpObserver<T> implements Observer<T> {
    private Context mContext;

    protected HttpObserver(Context context) {
        mContext = context.getApplicationContext();
    }

    @Override
    public final void onSubscribe(Disposable d) {

    }

    @Override
    public final void onNext(T value) {
        success(value);
    }

    @Override
    public final void onError(Throwable e) {
        error(e.toString());
    }

    @Override
    public final void onComplete() {
        complete();
    }


    protected void success(T t) {
    }

    protected void error(String msg) {
    }

    protected void complete() {

    }
}

final是為了避免用戶重寫,強制重寫后面自定義的幾個方法。

TOTO

  1. 引入dagger2
  2. 開啟混淆
  3. 分享基礎庫
  4. 圖片選擇
  5. 下拉刷新
  6. 輪播
  7. 客服
  8. 推送

 

項目主頁:http://www.baiduhome.net/lib/view/home/1494400907579

 

 本文由用戶 xiongrq 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!