Android開源組件化開發框架Android-Sun-Framework
一. 寫在前面
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的作用:
- 當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.
- 當isModule為真時,Module可以單獨運行,這樣做的好處是:一:大大縮減編譯時間;二:可以跨部門,跨團隊協作。
B. Module間跳轉
對比 ARouter 與 ActivityRouter ,我決定使用ActivityRouter,主要原因有兩個:
- 由于ARouter加入分組概念,和我們公司當前已有設計相違背;
- 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。接下來我們看看具體是如何實現的
- 添加依賴
//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>
- 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);
}
- 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的單例有區別。
- 具體使用
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
- 引入dagger2
- 開啟混淆
- 分享基礎庫
- 圖片選擇
- 下拉刷新
- 輪播
- 客服
- 推送
項目主頁:http://www.baiduhome.net/lib/view/home/1494400907579