Android开源组件化开发框架Android-Sun-Framework

xiongrq 7年前
   <h2>一. 写在前面</h2>    <p>Android-Sun-Framework是一个Android组件化开发框架,可用于中大型项目。</p>    <h2>二. 框架结构</h2>    <p>遵循高内聚低耦合理念,Module之间没有强依赖,具体结构如下图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/6fd1e0e037a77d373264f7b8cfab2be7.png"></p>    <h2>三. 框架依赖</h2>    <pre>  <code class="language-java">//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>    <h2>三. 详细说明</h2>    <p>下面我会根据代码,详细讲解框架结构及其使用说明。</p>    <h3>A. 配置中心config.gradle</h3>    <pre>  <code class="language-java">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"      ]  }</code></pre>    <p>当前配置项还不多,后面会根据实际开发需要,优化配置。</p>    <p>这里我单独说明一下isModule的作用:</p>    <ol>     <li>当isModule为真时,除了library子模块为library,其他子模块均为application,我们以login模块为例,看一下是如何实现的</li>    </ol>    <pre>  <code class="language-java">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>    <p>我们主要看一下rootProject.ext.android.isModule的if判断,这里根据它来配置当前模块plugin为library还是application。当为application时,下面会增加applicationId的配置。</p>    <p>仔细的同学可能会问,sourceSets的配置是干嘛的?由于application 必须要有默认启动Activity,所以这里我们需要根据isModule使用不同的AndroidManifest.xml,每个模块可能会有测试代码,所以只要把测试代码写在debug包下面,正式编译的时候debug下面的java文件不会参与编译。</p>    <p>同样的我们来看一下主Module的配置:</p>    <pre>  <code class="language-java">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>    <p>这里我们只关注dependencies,根据isModule来配置是否依赖该Module.</p>    <ol>     <li>当isModule为真时,Module可以单独运行,这样做的好处是:一:大大缩减编译时间;二:可以跨部门,跨团队协作。 <h3>B. Module间跳转</h3> 对比 ARouter 与 ActivityRouter ,我决定使用ActivityRouter,主要原因有两个:</li>     <li>由于ARouter加入分组概念,和我们公司当前已有设计相违背;</li>     <li>ActivityRouter比ARouter精简,后面我会分享一下ActivityRouter和ARouter的实现原理, <strong>敬请期待!</strong></li>    </ol>    <p>ActivityRouter具体如何使用我就不再赘述,详细可以查看 ActivityRouter ,里面有详细的说明和demo。我在这里要强调一下ActivityRouter多模块需要如何实现?</p>    <p>实现步骤:</p>    <p>a. 在子Module中新建一个Module类,比如我这里的login模块:</p>    <pre>  <code class="language-java">@Module("login")  public class LoginModule {  }</code></pre>    <p>b. 同样在主Module中也需要新建一个类</p>    <pre>  <code class="language-java">@Module("app")  public class AppModule {  }</code></pre>    <p>c. Module注册</p>    <pre>  <code class="language-java">@Modules({"app", "login"})  public class OdyApplication extends BaseApplication {  }</code></pre>    <p>d. 界面跳转</p>    <p>为了能统一处理,自己把ActivityRouter的跳转封装了一层:</p>    <pre>  <code class="language-java">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>    <p>JumpUtils里的常量就是跳转注解路径</p>    <pre>  <code class="language-java">@Router(JumpUtils.LOGIN_URL)  public class LoginActivity extends BaseActivity {      ......  }</code></pre>    <p>以上都是为了能更好的 <strong>统一管理,避免后期修改多处</strong> 。</p>    <h3>C. 网络库封装</h3>    <p>做Android开发的想必大家都知道Retrofit和Rxjava,我们也是使用的他们,为了更好的控制网络请求,这里我同时引入了Rxlifecycle。接下来我们看看具体是如何实现的</p>    <ol>     <li>添加依赖</li>    </ol>    <pre>  <code class="language-java">//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>    <ol>     <li>Retrofit接口定义</li>    </ol>    <pre>  <code class="language-java">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);  }</code></pre>    <ol>     <li>Retrofit初始化和具体实现</li>    </ol>    <pre>  <code class="language-java">public class SingletonNet {      public static final String BASE_URL = "http://api.laiyifen.com";      private static final int DEFAULT_TIMEOUT = 30;      private Retrofit retrofit;      private volatile static SingletonNet INSTANCE = null;        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>    <p>我们使用泛型,为各个模块创建自己的API,这样做能更好的解耦。同时我们也在基础库里实现了BaseNetApi,这里是项目中重复使用的接口,避免多处实现同一个接口。我们再来看一下其他Module里是如何创建的</p>    <pre>  <code class="language-java">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>    <p>这里采用静态内部类实现了单例,和SingletonNet的单例有区别。</p>    <ol>     <li>具体使用</li>    </ol>    <pre>  <code class="language-java">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>    <p>.compose(RxSchedulers.<AdBean>compose())线程调度等重复操作放在这里;</p>    <p>.compose(this.<AdBean>bindToLifecycle()) 网络请求和Activity生命周期想关联。</p>    <p>HttpObserver的实现如下:</p>    <pre>  <code class="language-java">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() {        }  }</code></pre>    <p>final是为了避免用户重写,强制重写后面自定义的几个方法。</p>    <h2>TOTO</h2>    <ol>     <li>引入dagger2</li>     <li>开启混淆</li>     <li>分享基础库</li>     <li>图片选择</li>     <li>下拉刷新</li>     <li>轮播</li>     <li>客服</li>     <li>推送</li>    </ol>    <p> </p>    <p>项目主页:<a href="http://www.open-open.com/lib/view/home/1494400907579">http://www.open-open.com/lib/view/home/1494400907579</a></p>    <p> </p>