Browse Source

完成搭建组件化框架

guiying712 8 years ago
parent
commit
432369a372
27 changed files with 1814 additions and 31 deletions
  1. 2 3
      Girls/build.gradle
  2. 34 10
      News/build.gradle
  3. 47 8
      app/build.gradle
  4. 3 3
      build.gradle
  5. 48 0
      common/src/main/java/com/guiying/common/base/BaseActivity.java
  6. 146 0
      common/src/main/java/com/guiying/common/base/BaseApplication.java
  7. 14 0
      common/src/main/java/com/guiying/common/base/BasePresenter.java
  8. 14 0
      common/src/main/java/com/guiying/common/base/BaseView.java
  9. 50 0
      common/src/main/java/com/guiying/common/http/ApiService.java
  10. 105 0
      common/src/main/java/com/guiying/common/http/DataParseUtil.java
  11. 338 0
      common/src/main/java/com/guiying/common/http/HttpClient.java
  12. 201 0
      common/src/main/java/com/guiying/common/http/HttpsUtil.java
  13. 141 0
      common/src/main/java/com/guiying/common/http/LoggerInterceptor.java
  14. 37 0
      common/src/main/java/com/guiying/common/http/OnResultListener.java
  15. 51 0
      common/src/main/java/com/guiying/common/loader/OkHttpGlideModule.java
  16. 78 0
      common/src/main/java/com/guiying/common/loader/OkHttpStreamFetcher.java
  17. 73 0
      common/src/main/java/com/guiying/common/loader/OkHttpUrlLoader.java
  18. 14 0
      common/src/main/res/values-v21/styles.xml
  19. 70 0
      common/src/main/res/values/colors.xml
  20. 240 0
      common/src/main/res/values/dimens.xml
  21. 51 0
      common/src/main/res/values/styles.xml
  22. 8 3
      girls/src/main/debug/AndroidManifest.xml
  23. 12 0
      girls/src/main/java/com/guiying/girls/Girls.java
  24. 8 0
      girls/src/main/release/AndroidManifest.xml
  25. 8 4
      news/src/main/debug/AndroidManifest.xml
  26. 13 0
      news/src/main/java/com/guiying/news/news.java
  27. 8 0
      news/src/main/release/AndroidManifest.xml

+ 2 - 3
Girls/build.gradle

@@ -1,4 +1,3 @@
-println isModule.toBoolean()
 if (isModule.toBoolean()) {
     apply plugin: 'com.android.application'
 } else {
@@ -28,9 +27,9 @@ android {
     sourceSets {
         main {
             if (isModule.toBoolean()) {
-                Manifest.srcFile 'src/main/debug/AndroidManifest.xml'
+                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
             } else {
-                Manifest.srcFile 'src/main/release/AndroidManifest.xml'
+                manifest.srcFile 'src/main/release/AndroidManifest.xml'
                 //release模式下排除debug文件夹中的所有Java文件
                 java {
                     exclude 'debug/**'

+ 34 - 10
News/build.gradle

@@ -1,26 +1,50 @@
-apply plugin: 'com.android.application'
+if (isModule.toBoolean()) {
+    apply plugin: 'com.android.application'
+} else {
+    apply plugin: 'com.android.library'
+}
+
+apply plugin: 'com.neenbedankt.android-apt'
 
 android {
-    compileSdkVersion 25
-    buildToolsVersion "25.0.2"
+    compileSdkVersion rootProject.ext.compileSdkVersion
+    buildToolsVersion rootProject.ext.buildToolsVersion
 
     defaultConfig {
-        applicationId "com.guiying.news"
-        minSdkVersion 15
-        targetSdkVersion 25
-        versionCode 1
-        versionName "1.0"
-
+        minSdkVersion rootProject.ext.minSdkVersion
+        targetSdkVersion rootProject.ext.targetSdkVersion
+        versionCode rootProject.ext.versionCode
+        versionName rootProject.ext.versionName
     }
+
     buildTypes {
         release {
             minifyEnabled false
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
     }
+
+    sourceSets {
+        main {
+            if (isModule.toBoolean()) {
+                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
+            } else {
+                manifest.srcFile 'src/main/release/AndroidManifest.xml'
+                //release模式下排除debug文件夹中的所有Java文件
+                java {
+                    exclude 'debug/**'
+                }
+            }
+        }
+    }
+    //设置了resourcePrefix值后,所有的资源名必须以指定的字符串做前缀,否则会报错。
+    //但是resourcePrefix这个值只能限定xml里面的资源,并不能限定图片资源,所有图片资源仍然需要手动去修改资源名。
+    //resourcePrefix "girls_"
 }
 
 dependencies {
     compile fileTree(dir: 'libs', include: ['*.jar'])
-    compile 'com.android.support:appcompat-v7:25.1.0'
+    compile project(':common')
+    //router
+    apt "com.github.mzule.activityrouter:compiler:$rootProject.aptCompilerVersion"
 }

+ 47 - 8
app/build.gradle

@@ -1,24 +1,63 @@
 apply plugin: 'com.android.application'
+apply plugin: 'com.neenbedankt.android-apt'
+
+def buildTime() {
+    return new Date().format("yyyyMMdd");
+}
 
 android {
-    compileSdkVersion 25
-    buildToolsVersion "25.0.2"
+    compileSdkVersion rootProject.ext.compileSdkVersion
+    buildToolsVersion rootProject.ext.buildToolsVersion
     defaultConfig {
         applicationId "com.guiying.androidmodulepattern"
-        minSdkVersion 15
-        targetSdkVersion 25
-        versionCode 1
-        versionName "1.0"
+        minSdkVersion rootProject.ext.minSdkVersion
+        targetSdkVersion rootProject.ext.targetSdkVersion
+        versionCode rootProject.ext.versionCode
+        versionName rootProject.ext.versionName
+        //multiDexEnabled true
+        //打包时间
+        resValue "string", "build_time", buildTime()
     }
+
     buildTypes {
         release {
-            minifyEnabled false
+            //更改AndroidManifest.xml中预先定义好占位符信息
+            //manifestPlaceholders = [app_icon: "@drawable/icon"]
+            // 不显示Log
+            buildConfigField "boolean", "LEO_DEBUG", "false"
+            //是否zip对齐
+            zipAlignEnabled true
+            // 缩减resource文件
+            shrinkResources true
+            //Proguard
+            minifyEnabled true
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+            //签名
+            //signingConfig signingConfigs.release
+        }
+
+        debug {
+            //给applicationId添加后缀“.debug”
+            applicationIdSuffix ".debug"
+            //manifestPlaceholders = [app_icon: "@drawable/launch_beta"]
+            buildConfigField "boolean", "LOG_DEBUG", "true"
+            zipAlignEnabled false
+            shrinkResources false
+            minifyEnabled false
+            debuggable true
         }
     }
 }
 
 dependencies {
     compile fileTree(dir: 'libs', include: ['*.jar'])
-    compile 'com.android.support:appcompat-v7:25.1.0'
+
+    if (!isModule.toBoolean()) {
+        compile project(':girls')
+        compile project(':news')
+    } else {
+        compile project(':common')
+    }
+    //router
+    apt "com.github.mzule.activityrouter:compiler:$rootProject.aptCompilerVersion"
 }

+ 3 - 3
build.gradle

@@ -52,7 +52,7 @@ ext {
 
     //不成熟开源库,需经常检查升级版本
     aptCompilerVersion = "1.1.7"
-    routerVersion = "1.2.1"
-    easyRecyclerVersion = "4.3.4"
-    cookieVersion = "v1.0.0"
+    routerVersion = "1.2.2"
+    easyRecyclerVersion = "4.3.8"
+    cookieVersion = "v1.0.1"
 }

+ 48 - 0
common/src/main/java/com/guiying/common/base/BaseActivity.java

@@ -0,0 +1,48 @@
+package com.guiying.common.base;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+
+/**
+ * <p>Activity基类 </p>
+ *
+ * @author 2016/12/2 17:33
+ * @version V1.0.0
+ * @name BaseActivity
+ */
+public abstract class BaseActivity extends AppCompatActivity {
+
+    /**
+     * 处理Intent,防止开发人员没做Intent判空
+     */
+    protected void handleIntent(Intent intent) {
+    }
+
+    /**
+     * 封装的findViewByID方法
+     */
+    @SuppressWarnings("unchecked")
+    protected <T extends View> T $(@IdRes int id) {
+        return (T) super.findViewById(id);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        BaseApplication.getIns().addActivity(this);
+        //强制在基类Intent判空
+        if (null != getIntent()) {
+            handleIntent(getIntent());
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        BaseApplication.getIns().finishActivity(this);
+    }
+
+}

+ 146 - 0
common/src/main/java/com/guiying/common/base/BaseApplication.java

@@ -0,0 +1,146 @@
+package com.guiying.common.base;
+
+import android.app.Activity;
+import android.app.Application;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import com.common.util.StringUtils;
+import com.orhanobut.logger.LogLevel;
+import com.orhanobut.logger.Logger;
+
+import java.util.Stack;
+
+/**
+ * 要想使用BaseApplication,必须在组件中实现自己的Application,并且继承BaseApplication;
+ * 组件中实现的Application必须在AndroidManifest.xml中注册,否则无法使用;
+ * 组件的Application需置于java/debug文件夹中,不得放于主代码;
+ * 组件中获取Context的方法必须为:BaseApplication.context,不允许其他写法;
+ * BaseApplication主要有如下功能:
+ * 1、全局获取Context;
+ * 2、用来管理全局Activity;
+ *
+ * @author 2016/12/2 17:02
+ * @version V1.0.0
+ * @name BaseApplication
+ */
+public class BaseApplication extends Application {
+
+    private static BaseApplication sInstance;
+
+    public static Context context;
+
+    private static Stack<Activity> activityStack;
+
+    public static BaseApplication getIns() {
+        return sInstance;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        sInstance = this;
+        context = this.getApplicationContext();
+        if (isAppDebug(context)) {
+            //只有debug模式才会打印日志
+            Logger.init("Petrel").logLevel(LogLevel.FULL);
+        } else {
+            Logger.init("Petrel").logLevel(LogLevel.NONE);
+        }
+    }
+
+    /**
+     * 添加指定Activity到堆栈
+     */
+    public void addActivity(Activity activity) {
+        if (activityStack == null) {
+            activityStack = new Stack<>();
+        }
+        activityStack.add(activity);
+    }
+
+    /**
+     * 获取当前Activity
+     */
+    public Activity currentActivity() {
+        return activityStack.lastElement();
+    }
+
+    /**
+     * 结束当前Activity
+     */
+    public void finishActivity() {
+        Activity activity = activityStack.lastElement();
+        finishActivity(activity);
+    }
+
+    /**
+     * 结束指定的Activity
+     */
+    public void finishActivity(Activity activity) {
+        if (activity != null) {
+            activityStack.remove(activity);
+            activity.finish();
+            activity = null;
+        }
+    }
+
+    /**
+     * 结束指定Class的Activity
+     */
+    public void finishActivity(Class<?> cls) {
+        for (Activity activity : activityStack) {
+            if (activity.getClass().equals(cls)) {
+                finishActivity(activity);
+                return;
+            }
+        }
+    }
+
+    /**
+     * 结束全部的Activity
+     */
+    public void finishAllActivity() {
+        for (int i = 0, size = activityStack.size(); i < size; i++) {
+            if (null != activityStack.get(i)) {
+                activityStack.get(i).finish();
+            }
+        }
+        activityStack.clear();
+    }
+
+    /**
+     * 退出应用程序
+     */
+    public void exitApp(Context context) {
+        try {
+            finishAllActivity();
+            android.app.ActivityManager activityMgr = (android.app.ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+            activityMgr.restartPackage(context.getPackageName());
+            System.exit(0);
+        } catch (Exception e) {
+            Log.e("ActivityManager", "app exit" + e.getMessage());
+        }
+    }
+
+    /**
+     * 判断App是否是Debug版本
+     *
+     * @param context 上下文
+     * @return {@code true}: 是<br>{@code false}: 否
+     */
+    public static boolean isAppDebug(Context context) {
+        if (StringUtils.isSpace(context.getPackageName())) return false;
+        try {
+            PackageManager pm = context.getPackageManager();
+            ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), 0);
+            return ai != null && (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+}

+ 14 - 0
common/src/main/java/com/guiying/common/base/BasePresenter.java

@@ -0,0 +1,14 @@
+/*
+ *  @ProjectName: ISMS_Petrel_MCU
+ *  @Copyright: 2016 HangZhou Hikvision System Technology Co., Ltd. All Right Reserved.
+ *  @address: http://www.hikvision.com
+ *  @Description: 本内容仅限于杭州海康威视系统技术公有限司内部使用,禁止转发.
+ */
+
+package com.guiying.common.base;
+
+public interface BasePresenter {
+
+    void start();
+
+}

+ 14 - 0
common/src/main/java/com/guiying/common/base/BaseView.java

@@ -0,0 +1,14 @@
+/*
+ *  @ProjectName: ISMS_Petrel_MCU
+ *  @Copyright: 2016 HangZhou Hikvision System Technology Co., Ltd. All Right Reserved.
+ *  @address: http://www.hikvision.com
+ *  @Description: 本内容仅限于杭州海康威视系统技术公有限司内部使用,禁止转发.
+ */
+
+package com.guiying.common.base;
+
+public interface BaseView<T> {
+
+    void setPresenter(T presenter);
+
+}

+ 50 - 0
common/src/main/java/com/guiying/common/http/ApiService.java

@@ -0,0 +1,50 @@
+package com.guiying.common.http;
+
+import java.util.Map;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.http.FieldMap;
+import retrofit2.http.FormUrlEncoded;
+import retrofit2.http.GET;
+import retrofit2.http.HeaderMap;
+import retrofit2.http.POST;
+import retrofit2.http.Streaming;
+import retrofit2.http.Url;
+
+/**
+ * <p>
+ * 注意:如果方法的泛型指定的类不是ResponseBody,retrofit会将返回的string用json转换器自动转换该类的一个对象,转换不成功就报错
+ * 如果不需要Gson转换,那么就指定泛型为ResponseBody,只能是ResponseBody,子类都不行.
+ * </p>
+ *
+ * @author 张华洋 2016/12/5 15:22
+ * @version V1.0.0
+ * @name HttpParams
+ */
+public interface ApiService {
+
+
+    @GET
+    Call<ResponseBody> executeGet(@Url String url);
+
+    /**
+     * POST方式将以表单的方式传递键值对作为请求体发送到服务器
+     * 其中@FormUrlEncoded 以表单的方式传递键值对
+     * 其中 @Path:所有在网址中的参数(URL的问号前面)
+     * 另外@FieldMap 用于POST请求,提交多个表单数据,@Field:用于POST请求,提交单个数据
+     * 使用@url是为了防止URL被转义为https://10.33.31.200:8890/msp%2Fmobile%2Flogin%3
+     */
+    @FormUrlEncoded
+    @POST
+    Call<ResponseBody> executePost(@Url String url, @FieldMap Map<String, String> map);
+
+
+    /**
+     * 流式下载,不加这个注解的话,会整个文件字节数组全部加载进内存,可能导致oom
+     */
+    @Streaming
+    @GET
+    Call<ResponseBody> download(@Url String fileUrl, @HeaderMap Map<String, String> headers);
+
+}

+ 105 - 0
common/src/main/java/com/guiying/common/http/DataParseUtil.java

@@ -0,0 +1,105 @@
+/*
+ *  @ProjectName: ISMS_Petrel_MCU
+ *  @Copyright: 2016 HangZhou Hikvision System Technology Co., Ltd. All Right Reserved.
+ *  @address: http://www.hikvision.com
+ *  @Description: 本内容仅限于杭州海康威视系统技术公有限司内部使用,禁止转发.
+ */
+
+package com.guiying.common.http;
+
+import android.text.TextUtils;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.gson.reflect.TypeToken;
+
+import org.simpleframework.xml.Serializer;
+import org.simpleframework.xml.core.Persister;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * <p>
+ * 服务器返回数据的解析工具;
+ * 支持XML,json对象,json数组
+ * </p>
+ *
+ * @author 张华洋 2017/1/9 16:00
+ * @version V1.2.0
+ * @name DataParseUtil
+ */
+
+
+public class DataParseUtil {
+
+    private DataParseUtil() {
+        throw new UnsupportedOperationException("u can't instantiate me...");
+    }
+
+    /**
+     * 解析json对象
+     *
+     * @param string 要解析的json
+     * @param clazz  解析类
+     */
+    public static <T> T parseObject(String string, Class<T> clazz) {
+        return new Gson().fromJson(string, clazz);
+    }
+
+    /**
+     * 解析json数组为ArrayList
+     *
+     * @param json  要解析的json
+     * @param clazz 解析类
+     * @return ArrayList
+     */
+    public static <T> ArrayList<T> parseToArrayList(String json, Class<T> clazz) {
+        Type type = new TypeToken<ArrayList<JsonObject>>() {
+        }.getType();
+        ArrayList<JsonObject> jsonObjects = new Gson().fromJson(json, type);
+        ArrayList<T> arrayList = new ArrayList<>();
+        for (JsonObject jsonObject : jsonObjects) {
+            arrayList.add(new Gson().fromJson(jsonObject, clazz));
+        }
+        return arrayList;
+    }
+
+    /**
+     * 解析json数组为List
+     *
+     * @param json  要解析的json
+     * @param clazz 解析类
+     * @return List
+     */
+    public static <T> List<T> parseToList(String json, Class<T[]> clazz) {
+        Gson gson = new Gson();
+        T[] array = gson.fromJson(json, clazz);
+        return Arrays.asList(array);
+    }
+
+
+    /**
+     * 解析Xml格式数据
+     *
+     * @param json  要解析的json
+     * @param clazz 解析类
+     */
+    public static Object parseXml(String json, Class<?> clazz) {
+        try {
+            if (!TextUtils.isEmpty(json) && clazz != null) {
+                Serializer serializer = new Persister();
+                InputStreamReader is = new InputStreamReader(new ByteArrayInputStream(json.getBytes("UTF-8")), "utf-8");
+                return serializer.read(clazz, is);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+}

+ 338 - 0
common/src/main/java/com/guiying/common/http/HttpClient.java

@@ -0,0 +1,338 @@
+package com.guiying.common.http;
+
+import android.text.TextUtils;
+
+import com.common.base.BaseApplication;
+import com.common.data.InfoCache;
+import com.common.util.NetworkUtils;
+import com.common.util.StringUtils;
+import com.common.util.ToastUtils;
+import com.franmontiel.persistentcookiejar.ClearableCookieJar;
+import com.franmontiel.persistentcookiejar.PersistentCookieJar;
+import com.franmontiel.persistentcookiejar.cache.SetCookieCache;
+import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;
+import com.hikvision.isms.petrel.common.R;
+import com.orhanobut.logger.Logger;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import okhttp3.OkHttpClient;
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+
+/**
+ * <p>类说明</p>
+ *
+ * @author 张华洋 2016/12/5 14:09
+ * @version V1.0.0
+ * @name HttpClient
+ */
+public class HttpClient {
+
+    /*The certificate's password*/
+    private static final String STORE_PASS = "ivms8700";
+    /*返回数据为String*/
+    public static final int STRING = 0;
+    /*返回数据为json对象*/
+    public static final int OBJECT = 1;
+    /*返回数据为json数组*/
+    public static final int ARRAY = 2;
+    /*返回数据为xml类型*/
+    public static final int XML = 3;
+    /*用户设置的BASE_URL*/
+    public static String BASE_URL = InfoCache.getIns().getBaseUrl();
+    /*本地使用的baseUrl*/
+    private String baseUrl = "";
+    private static OkHttpClient okHttpClient;
+    private Builder mBuilder;
+    private Retrofit retrofit;
+    private Call<ResponseBody> mCall;
+    private static final Map<String, Call> CALL_MAP = new HashMap<>();
+
+    /**
+     * 获取HttpClient的单例
+     *
+     * @return HttpClient的唯一对象
+     */
+    private static HttpClient getIns() {
+        return HttpClientHolder.sInstance;
+    }
+
+    /**
+     * 单例模式中的静态内部类写法
+     */
+    private static class HttpClientHolder {
+        private static final HttpClient sInstance = new HttpClient();
+    }
+
+    private HttpClient() {
+        ClearableCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(BaseApplication.getIns()));
+        //HttpsUtil.SSLParams sslParams = HttpsUtil.getSslSocketFactory(BaseApplication.context, new int[0], R.raw.ivms8700, STORE_PASS);
+        okHttpClient = new OkHttpClient.Builder()
+                .connectTimeout(10000L, TimeUnit.MILLISECONDS)
+                .sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager)
+                .hostnameVerifier(HttpsUtil.getHostnameVerifier())
+                .addInterceptor(new LoggerInterceptor(null, true))
+                .cookieJar(cookieJar)
+                .build();
+    }
+
+    public Builder getBuilder() {
+        return mBuilder;
+    }
+
+    private void setBuilder(Builder builder) {
+        this.mBuilder = builder;
+    }
+
+    /**
+     * 获取的Retrofit的实例,
+     * 引起Retrofit变化的因素只有静态变量BASE_URL的改变。
+     */
+    private void getRetrofit() {
+        if (!BASE_URL.equals(baseUrl) || retrofit == null) {
+            baseUrl = BASE_URL;
+            retrofit = new Retrofit.Builder()
+                    .baseUrl(baseUrl)
+                    .client(okHttpClient)
+                    .build();
+        }
+    }
+
+
+    public void post(final OnResultListener onResultListener) {
+        Builder builder = mBuilder;
+        mCall = retrofit.create(ApiService.class)
+                .executePost(builder.url, builder.params);
+        putCall(builder, mCall);
+        request(builder, onResultListener);
+    }
+
+
+    public void get(final OnResultListener onResultListener) {
+        Builder builder = mBuilder;
+        if (!builder.params.isEmpty()) {
+            String value = "";
+            for (Map.Entry<String, String> entry : builder.params.entrySet()) {
+                String mapKey = entry.getKey();
+                String mapValue = entry.getValue();
+                String span = value.equals("") ? "" : "&";
+                String part = StringUtils.buffer(span, mapKey, "=", mapValue);
+                value = StringUtils.buffer(value, part);
+            }
+            builder.url(StringUtils.buffer(builder.url, "?", value));
+        }
+        mCall = retrofit.create(ApiService.class).executeGet(builder.url);
+        putCall(builder, mCall);
+        request(builder, onResultListener);
+    }
+
+
+    private void request(final Builder builder, final OnResultListener onResultListener) {
+        if (!NetworkUtils.isConnected()) {
+            ToastUtils.showLongToastSafe(R.string.current_internet_invalid);
+            onResultListener.onFailure("");
+            return;
+        }
+        mCall.enqueue(new Callback<ResponseBody>() {
+            @Override
+            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
+                if (200 == response.code()) {
+                    try {
+                        String result = response.body().string();
+                        parseJson(result, builder.clazz, builder.bodyType, onResultListener);
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+                if (!response.isSuccessful() || 200 != response.code()) {
+                    onResultListener.onError(response.code(), response.message());
+                }
+                if (null != builder.tag) {
+                    removeCall(builder.url);
+                }
+            }
+
+            @Override
+            public void onFailure(Call<ResponseBody> call, Throwable t) {
+                t.printStackTrace();
+                onResultListener.onFailure(t.getMessage());
+                if (null != builder.tag) {
+                    removeCall(builder.url);
+                }
+            }
+
+        });
+    }
+
+
+    /**
+     * 添加某个请求
+     */
+    private synchronized void putCall(Builder builder, Call call) {
+        if (builder.tag == null)
+            return;
+        synchronized (CALL_MAP) {
+            CALL_MAP.put(builder.tag.toString() + builder.url, call);
+        }
+    }
+
+
+    /**
+     * 取消某个界面都所有请求,或者是取消某个tag的所有请求;
+     * 如果要取消某个tag单独请求,tag需要传入tag+url
+     *
+     * @param tag 请求标签
+     */
+    public synchronized void cancel(Object tag) {
+        if (tag == null)
+            return;
+        List<String> list = new ArrayList<>();
+        synchronized (CALL_MAP) {
+            for (String key : CALL_MAP.keySet()) {
+                if (key.startsWith(tag.toString())) {
+                    CALL_MAP.get(key).cancel();
+                    list.add(key);
+                }
+            }
+        }
+        for (String s : list) {
+            removeCall(s);
+        }
+
+    }
+
+    /**
+     * 移除某个请求
+     *
+     * @param url 添加的url
+     */
+    private synchronized void removeCall(String url) {
+        synchronized (CALL_MAP) {
+            for (String key : CALL_MAP.keySet()) {
+                if (key.contains(url)) {
+                    url = key;
+                    break;
+                }
+            }
+            CALL_MAP.remove(url);
+        }
+    }
+
+    /**
+     * Build a new HttpClient.
+     * url is required before calling. All other methods are optional.
+     */
+    public static final class Builder {
+        private String builderBaseUrl = BASE_URL;
+        private String url;
+        private Object tag;
+        private Map<String, String> params = new HashMap<>();
+        /*返回数据的类型*/
+        private int bodyType = STRING;
+        /*解析类*/
+        private Class clazz;
+
+        public Builder() {
+        }
+
+        /**
+         * 如需设置baseUrl请使用这种方法:HttpClient.BASE_URL = "https://10.33.31.200:8890/";
+         * 不推荐使用下面的方法改变baseUrl的值。
+         * 请求地址的baseUrl,最后会被赋值给HttpClient的静态变量BASE_URL;
+         * 例如:"https://10.33.31.200:8890/"
+         *
+         * @param baseUrl 请求地址的baseUrl
+         */
+        @Deprecated
+        public Builder baseUrl(String baseUrl) {
+            this.builderBaseUrl = baseUrl;
+            return this;
+        }
+
+        /**
+         * 除baseUrl以外的部分,
+         * 例如:"msp/mobile/login"
+         *
+         * @param url path路径
+         */
+        public Builder url(String url) {
+            this.url = url;
+            return this;
+        }
+
+        /**
+         * 给当前网络请求添加标签,用于取消这个网络请求
+         *
+         * @param tag 标签
+         */
+        public Builder tag(Object tag) {
+            this.tag = tag;
+            return this;
+        }
+
+        /**
+         * 添加请求参数
+         *
+         * @param key   键
+         * @param value 值
+         */
+        public Builder params(String key, String value) {
+            this.params.put(key, value);
+            return this;
+        }
+
+        /**
+         * 响应体类型设置
+         *
+         * @param bodyType 响应体类型,分别为STRING,OBJECT,ARRAY,XML
+         * @param clazz    指定的解析类
+         * @param <T>      解析类
+         */
+        public <T> Builder bodyType(int bodyType, Class<T> clazz) {
+            this.bodyType = bodyType;
+            this.clazz = clazz;
+            return this;
+        }
+
+        public HttpClient build() {
+            if (!TextUtils.isEmpty(builderBaseUrl)) {
+                BASE_URL = builderBaseUrl;
+            }
+            HttpClient client = HttpClient.getIns();
+            client.getRetrofit();
+            client.setBuilder(this);
+            return client;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void parseJson(String data, Class clazz, int bodyType, OnResultListener onResultListener) {
+        switch (bodyType) {
+            case STRING:
+                onResultListener.onSuccess(data);
+                break;
+            case OBJECT:
+                onResultListener.onSuccess(DataParseUtil.parseObject(data, clazz));
+                break;
+            case ARRAY:
+                onResultListener.onSuccess(DataParseUtil.parseToArrayList(data, clazz));
+                break;
+            case XML:
+                onResultListener.onSuccess(DataParseUtil.parseXml(data, clazz));
+                break;
+            default:
+                Logger.e("http parse tip:", "if you want return object, please use bodyType() set data type");
+                break;
+        }
+    }
+
+}

+ 201 - 0
common/src/main/java/com/guiying/common/http/HttpsUtil.java

@@ -0,0 +1,201 @@
+package com.guiying.common.http;
+
+import android.content.Context;
+import android.support.annotation.RawRes;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * HttpsUtils来自于鸿洋的: https://github.com/hongyangAndroid/okhttputils;
+ * 增加了主机名校验方法getHostnameVerifier();
+ * 其他参考的文章有:http://android.jobbole.com/83787/;
+ */
+public class HttpsUtil {
+
+    public static class SSLParams {
+        public SSLSocketFactory sSLSocketFactory;
+        public X509TrustManager trustManager;
+    }
+
+    public static SSLParams getSslSocketFactory(Context context, @RawRes int[] certificatesId, @RawRes int bksFileId, String password) {
+        if (context == null) {
+            throw new NullPointerException("context == null");
+        }
+        SSLParams sslParams = new SSLParams();
+        try {
+            TrustManager[] trustManagers = prepareTrustManager(context, certificatesId);
+
+            KeyManager[] keyManagers = prepareKeyManager(context.getResources().openRawResource(bksFileId), password);
+            //创建TLS类型的SSLContext对象,that uses our TrustManager
+            SSLContext sslContext = SSLContext.getInstance("TLS");
+            X509TrustManager x509TrustManager;
+            if (trustManagers != null) {
+                x509TrustManager = new MyTrustManager(chooseTrustManager(trustManagers));
+            } else {
+                x509TrustManager = new UnSafeTrustManager();
+            }
+            //用上面得到的trustManagers初始化SSLContext,这样sslContext就会信任keyStore中的证书
+            sslContext.init(keyManagers, new TrustManager[]{x509TrustManager}, null);
+            //通过sslContext获取SSLSocketFactory对象
+            sslParams.sSLSocketFactory = sslContext.getSocketFactory();
+            sslParams.trustManager = x509TrustManager;
+            return sslParams;
+        } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    private static TrustManager[] prepareTrustManager(Context context, int[] certificatesId) {
+        if (certificatesId == null || certificatesId.length <= 0) return null;
+        try {
+            //创建X.509格式的CertificateFactory
+            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+            // 创建一个默认类型的KeyStore,存储我们信任的证书
+            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+            keyStore.load(null);
+            int index = 0;
+            for (int certificateId : certificatesId) {
+                //从本地资源中获取证书的流
+                InputStream cerInputStream = context.getResources().openRawResource(certificateId);
+                String certificateAlias = Integer.toString(index++);
+                //certificate是java.security.cert.Certificate,而不是其他Certificate
+                Certificate certificate = certificateFactory.generateCertificate(cerInputStream);
+                //将证书certificate作为信任的证书放入到keyStore中
+                keyStore.setCertificateEntry(certificateAlias, certificate);
+                try {
+                    if (cerInputStream != null)
+                        cerInputStream.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            //TrustManagerFactory是用于生成TrustManager的,这里创建一个默认类型的TrustManagerFactory
+            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+            //用我们之前的keyStore实例初始化TrustManagerFactory,这样trustManagerFactory就会信任keyStore中的证书
+            trustManagerFactory.init(keyStore);
+            return trustManagerFactory.getTrustManagers();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    private static KeyManager[] prepareKeyManager(InputStream bksFile, String password) {
+        try {
+            if (bksFile == null || password == null) return null;
+
+            KeyStore clientKeyStore = KeyStore.getInstance("BKS");
+            clientKeyStore.load(bksFile, password.toCharArray());
+            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+            keyManagerFactory.init(clientKeyStore, password.toCharArray());
+            return keyManagerFactory.getKeyManagers();
+
+        } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    /**
+     * 客户端不对证书做任何检查;
+     * 客户端不对证书做任何验证的做法有很大的安全漏洞。
+     */
+    private static class UnSafeTrustManager implements X509TrustManager {
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType)
+                throws CertificateException {
+        }
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType)
+                throws CertificateException {
+        }
+
+        @Override
+        public X509Certificate[] getAcceptedIssuers() {
+            return new X509Certificate[]{};
+        }
+    }
+
+
+    private static class MyTrustManager implements X509TrustManager {
+        private X509TrustManager defaultTrustManager;
+        private X509TrustManager localTrustManager;
+
+        private MyTrustManager(X509TrustManager localTrustManager) throws NoSuchAlgorithmException, KeyStoreException {
+            //TrustManagerFactory是用于生成TrustManager的,创建一个默认类型的TrustManagerFactory
+            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+            trustManagerFactory.init((KeyStore) null);
+            defaultTrustManager = chooseTrustManager(trustManagerFactory.getTrustManagers());
+            this.localTrustManager = localTrustManager;
+        }
+
+
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+
+        }
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+            try {
+                defaultTrustManager.checkServerTrusted(chain, authType);
+            } catch (CertificateException ce) {
+                localTrustManager.checkServerTrusted(chain, authType);
+            }
+        }
+
+
+        @Override
+        public X509Certificate[] getAcceptedIssuers() {
+            return new X509Certificate[0];
+        }
+    }
+
+
+    private static X509TrustManager chooseTrustManager(TrustManager[] trustManagers) {
+        for (TrustManager trustManager : trustManagers) {
+            if (trustManager instanceof X509TrustManager) {
+                return (X509TrustManager) trustManager;
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * 主机名校验方法
+     */
+    public static HostnameVerifier getHostnameVerifier() {
+        return new HostnameVerifier() {
+            @Override
+            public boolean verify(String hostname, SSLSession session) {
+                return hostname.equalsIgnoreCase(session.getPeerHost());
+            }
+        };
+    }
+
+}

+ 141 - 0
common/src/main/java/com/guiying/common/http/LoggerInterceptor.java

@@ -0,0 +1,141 @@
+package com.guiying.common.http;
+
+import android.text.TextUtils;
+
+import com.orhanobut.logger.Logger;
+
+import java.io.IOException;
+
+import okhttp3.Headers;
+import okhttp3.Interceptor;
+import okhttp3.MediaType;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import okio.Buffer;
+
+public class LoggerInterceptor implements Interceptor {
+
+    public static final String TAG = "HttpClient";
+    private String tag;
+    private boolean showResponse;
+
+    public LoggerInterceptor(String tag, boolean showResponse) {
+        if (TextUtils.isEmpty(tag)) {
+            tag = TAG;
+        }
+        this.showResponse = showResponse;
+        this.tag = tag;
+    }
+
+    public LoggerInterceptor(String tag) {
+        this(tag, false);
+    }
+
+    @Override
+    public Response intercept(Chain chain) throws IOException {
+
+        Request request = chain.request();
+        logForRequest(request);
+        Response response = chain.proceed(request);
+        return logForResponse(response);
+    }
+
+    private void logForRequest(Request request) {
+        try {
+            String url = request.url().toString();
+            Headers headers = request.headers();
+
+            Logger.d("method : " + request.method() + "  ║  url : " + url);
+            if (headers != null && headers.size() > 0) {
+                //Logger.d("headers : " + headers.toString());
+            }
+
+            RequestBody requestBody = request.body();
+            if (requestBody != null) {
+                MediaType mediaType = requestBody.contentType();
+                if (mediaType != null) {
+                    //Logger.d("requestBody's contentType : " + mediaType.toString());
+                    if (isText(mediaType)) {
+                        Logger.d("requestBody's content : " + bodyToString(request));
+                    } else {
+                        //Logger.e("requestBody's content : " + " maybe [file part] , too large too print , ignored!");
+                    }
+                }
+            }
+        } catch (Exception e) {
+//            e.printStackTrace();
+        }
+    }
+
+    private Response logForResponse(Response response) {
+        try {
+            Response.Builder builder = response.newBuilder();
+            Response clone = builder.build();
+            Logger.d("url : " + clone.request().url() + "  ║  code : " + clone.code() + "  ║  protocol : " + clone.protocol());
+            if (!TextUtils.isEmpty(clone.message()))
+                //Logger.d("message : " + clone.message());
+
+                if (showResponse) {
+                    ResponseBody body = clone.body();
+                    if (body != null) {
+                        MediaType mediaType = body.contentType();
+                        if (mediaType != null) {
+                            //Logger.d("responseBody's contentType : " + mediaType.toString());
+                            if (isText(mediaType)) {
+                                String resp = body.string();
+                                //打印json格式或者xml格式日志
+                                switch (mediaType.subtype()) {
+                                    case "xml":
+                                        Logger.xml(resp);
+                                        break;
+                                    case "json":
+                                        Logger.json(resp);
+                                        break;
+                                    default:
+                                        Logger.d(resp);
+                                        break;
+                                }
+                                body = ResponseBody.create(mediaType, resp);
+                                return response.newBuilder().body(body).build();
+                            } else {
+                                Logger.e("responseBody's content : " + " maybe [file part] , too large too print , ignored!");
+                            }
+                        }
+                    }
+                }
+        } catch (Exception e) {
+//            e.printStackTrace();
+        }
+
+        return response;
+    }
+
+
+    private boolean isText(MediaType mediaType) {
+        if (mediaType.type() != null && mediaType.type().equals("text")) {
+            return true;
+        }
+        if (mediaType.subtype() != null) {
+            if (mediaType.subtype().equals("json") ||
+                    mediaType.subtype().equals("xml") ||
+                    mediaType.subtype().equals("html") ||
+                    mediaType.subtype().equals("webviewhtml")
+                    )
+                return true;
+        }
+        return false;
+    }
+
+    private String bodyToString(final Request request) {
+        try {
+            final Request copy = request.newBuilder().build();
+            final Buffer buffer = new Buffer();
+            copy.body().writeTo(buffer);
+            return buffer.readUtf8();
+        } catch (final IOException e) {
+            return "something error when show requestBody.";
+        }
+    }
+}

+ 37 - 0
common/src/main/java/com/guiying/common/http/OnResultListener.java

@@ -0,0 +1,37 @@
+package com.guiying.common.http;
+
+/**
+ * <p>在Retrofit中接口会导致泛型擦除,所以这里回调使用Class</p>
+ *
+ * @author 张华洋 2016/12/15 10:27
+ * @version V1.0.0
+ * @name OnResultListener
+ */
+public class OnResultListener<T> {
+
+    /**
+     * 请求成功的情况
+     *
+     * @param result 需要解析的解析类
+     */
+    public void onSuccess(T result) {
+    }
+
+    /**
+     * 响应成功,但是出错的情况
+     *
+     * @param code    错误码
+     * @param message 错误信息
+     */
+    public void onError(int code, String message) {
+    }
+
+    /**
+     * 请求失败的情况
+     *
+     * @param message 失败信息
+     */
+    public void onFailure(String message) {
+    }
+
+}

+ 51 - 0
common/src/main/java/com/guiying/common/loader/OkHttpGlideModule.java

@@ -0,0 +1,51 @@
+package com.guiying.common.loader;
+
+import android.content.Context;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.GlideBuilder;
+import com.bumptech.glide.load.model.GlideUrl;
+import com.bumptech.glide.module.GlideModule;
+import com.common.base.BaseApplication;
+import com.common.http.HttpsUtil;
+import com.hikvision.isms.petrel.common.R;
+
+import java.io.InputStream;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLSession;
+
+import okhttp3.OkHttpClient;
+
+/**
+ * A {@link GlideModule} implementation to replace Glide's default
+ * {@link java.net.HttpURLConnection} based {@link com.bumptech.glide.load.model.ModelLoader} with an OkHttp based
+ * {@link com.bumptech.glide.load.model.ModelLoader}.
+ * <p>
+ * <p>
+ * If you're using gradle, you can include this module simply by depending on the aar, the module will be merged
+ * in by manifest merger. For other build systems or for more more information, see
+ * {@link GlideModule}.
+ * </p>
+ */
+public class OkHttpGlideModule implements GlideModule {
+    @Override
+    public void applyOptions(Context context, GlideBuilder builder) {
+        // Do nothing.
+    }
+
+    @Override
+    public void registerComponents(Context context, Glide glide) {
+        HttpsUtil.SSLParams sslParams = HttpsUtil.getSslSocketFactory(BaseApplication.context, null, R.raw.ivms8700, "");
+        OkHttpClient okHttpClient = new OkHttpClient.Builder()
+                .sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager)
+                .hostnameVerifier(new HostnameVerifier() {
+                    @Override
+                    public boolean verify(String hostname, SSLSession session) {
+                        return true;
+                    }
+                })
+                .build();
+        glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(okHttpClient));
+    }
+}

+ 78 - 0
common/src/main/java/com/guiying/common/loader/OkHttpStreamFetcher.java

@@ -0,0 +1,78 @@
+package com.guiying.common.loader;
+
+import com.bumptech.glide.Priority;
+import com.bumptech.glide.load.data.DataFetcher;
+import com.bumptech.glide.load.model.GlideUrl;
+import com.bumptech.glide.util.ContentLengthInputStream;
+import com.common.util.CloseUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+
+/**
+ * Fetches an {@link InputStream} using the okhttp library.
+ */
+public class OkHttpStreamFetcher implements DataFetcher<InputStream> {
+    private final OkHttpClient client;
+    private final GlideUrl url;
+    private InputStream stream;
+    private ResponseBody responseBody;
+
+    public OkHttpStreamFetcher(OkHttpClient client, GlideUrl url) {
+        this.client = client;
+        this.url = url;
+    }
+
+    @Override
+    public InputStream loadData(Priority priority) throws Exception {
+        Request.Builder requestBuilder = new Request.Builder()
+                .url(url.toStringUrl());
+
+        for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
+            String key = headerEntry.getKey();
+            requestBuilder.addHeader(key, headerEntry.getValue());
+        }
+
+        Request request = requestBuilder.build();
+
+        Response response = client.newCall(request).execute();
+        responseBody = response.body();
+        if (!response.isSuccessful()) {
+            throw new IOException("Request failed with code: " + response.code());
+        }
+
+        long contentLength = responseBody.contentLength();
+        stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
+        return stream;
+    }
+
+    @Override
+    public void cleanup() {
+        if (stream != null) {
+            try {
+                stream.close();
+            } catch (IOException e) {
+                // Ignored
+            }
+        }
+        if (responseBody != null) {
+            CloseUtils.closeIO(responseBody);
+        }
+    }
+
+    @Override
+    public String getId() {
+        return url.getCacheKey();
+    }
+
+    @Override
+    public void cancel() {
+
+    }
+}

+ 73 - 0
common/src/main/java/com/guiying/common/loader/OkHttpUrlLoader.java

@@ -0,0 +1,73 @@
+package com.guiying.common.loader;
+
+import android.content.Context;
+
+import com.bumptech.glide.load.data.DataFetcher;
+import com.bumptech.glide.load.model.GenericLoaderFactory;
+import com.bumptech.glide.load.model.GlideUrl;
+import com.bumptech.glide.load.model.ModelLoader;
+import com.bumptech.glide.load.model.ModelLoaderFactory;
+
+import java.io.InputStream;
+
+import okhttp3.OkHttpClient;
+
+/**
+ * A simple model loader for fetching media over http/https using OkHttp.
+ */
+public class OkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
+
+    /**
+     * The default factory for {@link OkHttpUrlLoader}s.
+     */
+    public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
+        private static volatile OkHttpClient internalClient;
+        private OkHttpClient client;
+
+        private static OkHttpClient getInternalClient() {
+            if (internalClient == null) {
+                synchronized (Factory.class) {
+                    if (internalClient == null) {
+                        internalClient = new OkHttpClient();
+                    }
+                }
+            }
+            return internalClient;
+        }
+
+        /**
+         * Constructor for a new Factory that runs requests using a static singleton client.
+         */
+        public Factory() {
+            this(getInternalClient());
+        }
+
+        /**
+         * Constructor for a new Factory that runs requests using given client.
+         */
+        public Factory(OkHttpClient client) {
+            this.client = client;
+        }
+
+        @Override
+        public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
+            return new OkHttpUrlLoader(client);
+        }
+
+        @Override
+        public void teardown() {
+            // Do nothing, this instance doesn't own the client.
+        }
+    }
+
+    private final OkHttpClient client;
+
+    public OkHttpUrlLoader(OkHttpClient client) {
+        this.client = client;
+    }
+
+    @Override
+    public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
+        return new OkHttpStreamFetcher(client, model);
+    }
+}

+ 14 - 0
common/src/main/res/values-v21/styles.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <style name="AppTheme" parent="Base.AppTheme">
+        <item name="android:navigationBarColor">@color/gray_E6</item><!--虚拟导航栏颜色-->
+        <!--<item name="android:colorControlHighlight">@color/gray_AD</item>&lt;!&ndash;波纹颜色&ndash;&gt;-->
+    </style>
+
+    <style name="TransparentStatusBarTheme" parent="AppTheme.NoActionBar">
+        <item name="android:statusBarColor">@color/gray_D5</item>
+    </style>
+
+
+</resources>

+ 70 - 0
common/src/main/res/values/colors.xml

@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!--APP的主题色-->
+    <color name="colorPrimary">#3f85e4</color>
+    <color name="colorPrimaryDark">#3f85e4</color>
+    <color name="colorAccent">#3f85e4</color>
+
+    <!--APP公用的一些颜色-->
+    <color name="common_bg">#fff7f7f7</color>
+    <color name="common_text">@android:color/primary_text_light</color>
+    <color name="common_hint_text">@color/gray_AD</color>
+    <color name="divider_color">@color/gray_cc</color>
+    <color name="black_overlay">@color/black_alpha40</color>
+    <color name="common_title_bg">#CA2C32</color>
+    <color name="transparent">@android:color/transparent</color>
+    <color name="transparent_color">#00ffffff</color>
+    <color name="text_black_4d">#4d4d4d</color>
+    <color name="text_908f94">#908f94</color>
+    <color name="bg_999999">#999999</color>
+    <color name="bg_gray">#dedede</color>
+    <color name="text_color_404040">#404040</color>
+    <color name="color_map_grid_selected">#F9686D</color>
+
+    <color name="white">#ffffffff</color>
+    <color name="white_10">#1affffff</color>
+    <color name="white_20">#33ffffff</color>
+    <color name="white_30">#4dffffff</color>
+
+    <!--黑色-各种等级透明度-->
+    <color name="black">#ff000000</color>
+    <color name="black_alpha10">#1a000000</color>
+    <color name="black_alpha15">#26000000</color>
+    <color name="black_alpha20">#33000000</color>
+    <color name="black_alpha30">#4d000000</color>
+    <color name="black_alpha32">#52000000</color>
+    <color name="black_alpha40">#66000000</color>
+    <color name="black_alpha5">#0d000000</color>
+    <color name="black_alpha50">#80000000</color>
+    <color name="black_alpha54">#89000000</color>
+    <color name="black_alpha60">#99000000</color>
+    <color name="black_alpha80">#cc000000</color>
+
+    <!--灰色-各种等级透明度-->
+    <color name="gray_32">#ff323232</color>
+    <color name="gray_33">#ff333333</color>
+    <color name="gray_35">#ff353535</color>
+    <color name="gray_66">#ff666666</color>
+    <color name="gray_7f">#ff7f7f7f</color>
+    <color name="gray_80">#ff808080</color>
+    <color name="gray_88">#ff888888</color>
+    <color name="gray_8832">#88323232</color>
+    <color name="gray_AD">#ffadadad</color>
+    <color name="gray_C8">#ffc8c8c8</color>
+    <color name="gray_D5">#ffd5d5d5</color>
+    <color name="gray_E6">#ffe6e6e6</color>
+    <color name="gray_cc">#ffcccccc</color>
+    <color name="gray_d9">#ffd9d9d9</color>
+    <color name="gray_f0">#fff0f0f0</color>
+    <color name="gray_f2">#fff2f2f2</color>
+    <color name="gray_f3">#fff3f3f3</color>
+    <color name="gray_f4">#fff4f4f4</color>
+    <color name="gray_f6">#fff6f6f6</color>
+    <color name="gray_fc">#fffcfcfc</color>
+    <color name="gray_e0">#e0e0e0</color>
+
+    <!--消息中心-->
+    <color name="red_common">#dfca2c32</color>
+    <color name="message_detail_rb_red">#f74248</color>
+    <color name="message_detail_divider">#e1e3e6</color>
+</resources>

+ 240 - 0
common/src/main/res/values/dimens.xml

@@ -0,0 +1,240 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <!--字体大小-->
+    <dimen name="text_size_9sp">9.0sp</dimen>
+    <dimen name="text_size_10sp">10.0sp</dimen>
+    <dimen name="text_size_11sp">11.0sp</dimen>
+    <dimen name="text_size_12sp">12.0sp</dimen>
+    <dimen name="text_size_13sp">13.0sp</dimen>
+    <dimen name="text_size_14sp">14.0sp</dimen>
+    <dimen name="text_size_15sp">15.0sp</dimen>
+    <dimen name="text_size_16sp">16.0sp</dimen>
+    <dimen name="text_size_17sp">17.0sp</dimen>
+    <dimen name="text_size_18sp">18.0sp</dimen>
+    <dimen name="text_size_19sp">19.0sp</dimen>
+    <dimen name="text_size_20sp">20.0sp</dimen>
+    <dimen name="text_size_21sp">21.0sp</dimen>
+    <dimen name="text_size_22sp">22.0sp</dimen>
+    <dimen name="text_size_23sp">23.0sp</dimen>
+    <dimen name="text_size_24sp">24.0sp</dimen>
+    <dimen name="text_size_28sp">28.0sp</dimen>
+    <dimen name="text_size_31sp">31.0sp</dimen>
+    <dimen name="text_size_37sp">37.0sp</dimen>
+
+    <!--margin大小 0-100之间-->
+    <dimen name="size_0.5dp">0.5dip</dimen>
+    <dimen name="size_1dp">1.0dip</dimen>
+    <dimen name="size_1.5dp">1.5dip</dimen>
+    <dimen name="size_2dp">2.0dip</dimen>
+    <dimen name="size_2.5dp">2.5dip</dimen>
+    <dimen name="size_3dp">3.0dip</dimen>
+    <dimen name="size_3.5dp">3.5dip</dimen>
+    <dimen name="size_4dp">4.0dip</dimen>
+    <dimen name="size_5dp">5.0dip</dimen>
+    <dimen name="size_6dp">6.0dip</dimen>
+    <dimen name="size_7dp">7.0dip</dimen>
+    <dimen name="size_7.5dp">7.5dip</dimen>
+    <dimen name="size_8dp">8.0dip</dimen>
+    <dimen name="size_9dp">9.0dip</dimen>
+    <dimen name="size_10dp">10.0dip</dimen>
+    <dimen name="size_11dp">11.0dip</dimen>
+    <dimen name="size_12dp">12.0dip</dimen>
+    <dimen name="size_12.5dp">12.5dip</dimen>
+    <dimen name="size_13dp">13.0dip</dimen>
+    <dimen name="size_14dp">14.0dip</dimen>
+    <dimen name="size_14.5dp">14.5dip</dimen>
+    <dimen name="size_15dp">15.0dip</dimen>
+    <dimen name="size_16dp">16.0dip</dimen>
+    <dimen name="size_17dp">17.0dip</dimen>
+    <dimen name="size_18dp">18.0dip</dimen>
+    <dimen name="size_19dp">19.0dip</dimen>
+    <dimen name="size_20.5dp">20.5dip</dimen>
+    <dimen name="size_20dp">20.0dip</dimen>
+    <dimen name="size_21dp">21.0dip</dimen>
+    <dimen name="size_22dp">22.0dip</dimen>
+    <dimen name="size_23dp">23.0dip</dimen>
+    <dimen name="size_24dp">24.0dip</dimen>
+    <dimen name="size_25dp">25.0dip</dimen>
+    <dimen name="size_26dp">26.0dip</dimen>
+    <dimen name="size_27dp">27.0dip</dimen>
+    <dimen name="size_28dp">28.0dip</dimen>
+    <dimen name="size_29dp">29.0dip</dimen>
+    <dimen name="size_30dp">30.0dip</dimen>
+    <dimen name="size_31dp">31.0dip</dimen>
+    <dimen name="size_32dp">32.0dip</dimen>
+    <dimen name="size_33dp">33.0dip</dimen>
+    <dimen name="size_34dp">34.0dip</dimen>
+    <dimen name="size_35dp">35.0dip</dimen>
+    <dimen name="size_36dp">36.0dip</dimen>
+    <dimen name="size_37dp">37.0dip</dimen>
+    <dimen name="size_38dp">38.0dip</dimen>
+    <dimen name="size_39dp">39.0dip</dimen>
+    <dimen name="size_40dp">40.0dip</dimen>
+    <dimen name="size_41dp">41.0dip</dimen>
+    <dimen name="size_42dp">42.0dip</dimen>
+    <dimen name="size_43dp">43.0dip</dimen>
+    <dimen name="size_44dp">44.0dip</dimen>
+    <dimen name="size_45dp">45.0dip</dimen>
+    <dimen name="size_46dp">46.0dip</dimen>
+    <dimen name="size_47dp">47.0dip</dimen>
+    <dimen name="size_48dp">48.0dip</dimen>
+    <dimen name="size_49dp">49.0dip</dimen>
+    <dimen name="size_50dp">50.0dip</dimen>
+    <dimen name="size_51dp">51.0dip</dimen>
+    <dimen name="size_52dp">52.0dip</dimen>
+    <dimen name="size_53dp">53.0dip</dimen>
+    <dimen name="size_54dp">54.0dip</dimen>
+    <dimen name="size_55dp">55.0dip</dimen>
+    <dimen name="size_56dp">56.0dip</dimen>
+    <dimen name="size_57dp">57.0dip</dimen>
+    <dimen name="size_58dp">58.0dip</dimen>
+    <dimen name="size_59dp">59.0dip</dimen>
+    <dimen name="size_60dp">60.0dip</dimen>
+    <dimen name="size_61dp">61.0dip</dimen>
+    <dimen name="size_62dp">62.0dip</dimen>
+    <dimen name="size_63dp">63.0dip</dimen>
+    <dimen name="size_64dp">64.0dip</dimen>
+    <dimen name="size_65dp">65.0dip</dimen>
+    <dimen name="size_66dp">66.0dip</dimen>
+    <dimen name="size_67dp">67.0dip</dimen>
+    <dimen name="size_68dp">68.0dip</dimen>
+    <dimen name="size_69dp">69.0dip</dimen>
+    <dimen name="size_70dp">70.0dip</dimen>
+    <dimen name="size_71dp">71.0dip</dimen>
+    <dimen name="size_72dp">72.0dip</dimen>
+    <dimen name="size_73dp">73.0dip</dimen>
+    <dimen name="size_74dp">74.0dip</dimen>
+    <dimen name="size_75dp">75.0dip</dimen>
+    <dimen name="size_76dp">76.0dip</dimen>
+    <dimen name="size_77dp">77.0dip</dimen>
+    <dimen name="size_78dp">78.0dip</dimen>
+    <dimen name="size_79dp">79.0dip</dimen>
+    <dimen name="size_80dp">80.0dip</dimen>
+    <dimen name="size_81dp">81.0dip</dimen>
+    <dimen name="size_82dp">82.0dip</dimen>
+    <dimen name="size_83dp">83.0dip</dimen>
+    <dimen name="size_84dp">84.0dip</dimen>
+    <dimen name="size_85dp">85.0dip</dimen>
+    <dimen name="size_86dp">86.0dip</dimen>
+    <dimen name="size_87dp">87.0dip</dimen>
+    <dimen name="size_88dp">88.0dip</dimen>
+    <dimen name="size_89dp">89.0dip</dimen>
+    <dimen name="size_90dp">90.0dip</dimen>
+    <dimen name="size_91dp">91.0dip</dimen>
+    <dimen name="size_92dp">92.0dip</dimen>
+    <dimen name="size_93dp">93.0dip</dimen>
+    <dimen name="size_94dp">94.0dip</dimen>
+    <dimen name="size_95dp">95.0dip</dimen>
+    <dimen name="size_96dp">96.0dip</dimen>
+    <dimen name="size_97dp">97.0dip</dimen>
+    <dimen name="size_98dp">98.0dip</dimen>
+    <dimen name="size_99dp">99.0dip</dimen>
+
+    <!--margin大小 100以上-->
+    <dimen name="size_100dp">100.0dip</dimen>
+    <dimen name="size_101dp">101.0dip</dimen>
+    <dimen name="size_102dp">102.0dip</dimen>
+    <dimen name="size_103dp">103.0dip</dimen>
+    <dimen name="size_104dp">104.0dip</dimen>
+    <dimen name="size_105dp">105.0dip</dimen>
+    <dimen name="size_106dp">106.0dip</dimen>
+    <dimen name="size_107dp">107.0dip</dimen>
+    <dimen name="size_108dp">108.0dip</dimen>
+    <dimen name="size_109dp">109.0dip</dimen>
+    <dimen name="size_110dp">110.0dip</dimen>
+    <dimen name="size_111dp">111.0dip</dimen>
+    <dimen name="size_112dp">112.0dip</dimen>
+    <dimen name="size_113dp">113.0dip</dimen>
+    <dimen name="size_114dp">114.0dip</dimen>
+    <dimen name="size_115dp">115.0dip</dimen>
+    <dimen name="size_116dp">116.0dip</dimen>
+    <dimen name="size_117dp">117.0dip</dimen>
+    <dimen name="size_118dp">118.0dip</dimen>
+    <dimen name="size_119dp">119.0dip</dimen>
+    <dimen name="size_120dp">120.0dip</dimen>
+    <dimen name="size_121dp">121.0dip</dimen>
+    <dimen name="size_122dp">122.0dip</dimen>
+    <dimen name="size_123dp">123.0dip</dimen>
+    <dimen name="size_124dp">124.0dip</dimen>
+    <dimen name="size_125dp">125.0dip</dimen>
+    <dimen name="size_126dp">126.0dip</dimen>
+    <dimen name="size_127dp">127.0dip</dimen>
+    <dimen name="size_128dp">128.0dip</dimen>
+    <dimen name="size_129dp">129.0dip</dimen>
+    <dimen name="size_130dp">130.0dip</dimen>
+    <dimen name="size_131dp">131.0dip</dimen>
+    <dimen name="size_132dp">132.0dip</dimen>
+    <dimen name="size_133dp">133.0dip</dimen>
+    <dimen name="size_134dp">134.0dip</dimen>
+    <dimen name="size_135dp">135.0dip</dimen>
+    <dimen name="size_136dp">136.0dip</dimen>
+    <dimen name="size_137dp">137.0dip</dimen>
+    <dimen name="size_138dp">138.0dip</dimen>
+    <dimen name="size_139dp">139.0dip</dimen>
+    <dimen name="size_140dp">140.0dip</dimen>
+    <dimen name="size_141dp">141.0dip</dimen>
+    <dimen name="size_142dp">142.0dip</dimen>
+    <dimen name="size_143dp">143.0dip</dimen>
+    <dimen name="size_144dp">144.0dip</dimen>
+    <dimen name="size_145dp">145.0dip</dimen>
+    <dimen name="size_146dp">146.0dip</dimen>
+    <dimen name="size_147dp">147.0dip</dimen>
+    <dimen name="size_148dp">148.0dip</dimen>
+    <dimen name="size_149dp">149.0dip</dimen>
+    <dimen name="size_150dp">150.0dip</dimen>
+    <dimen name="size_151dp">151.0dip</dimen>
+    <dimen name="size_152dp">152.0dip</dimen>
+    <dimen name="size_153dp">153.0dip</dimen>
+    <dimen name="size_154dp">154.0dip</dimen>
+    <dimen name="size_155dp">155.0dip</dimen>
+    <dimen name="size_156dp">156.0dip</dimen>
+    <dimen name="size_157dp">157.0dip</dimen>
+    <dimen name="size_158dp">158.0dip</dimen>
+    <dimen name="size_159dp">159.0dip</dimen>
+    <dimen name="size_160dp">160.0dip</dimen>
+    <dimen name="size_161dp">161.0dip</dimen>
+    <dimen name="size_162dp">162.0dip</dimen>
+    <dimen name="size_163dp">163.0dip</dimen>
+    <dimen name="size_164dp">164.0dip</dimen>
+    <dimen name="size_165dp">165.0dip</dimen>
+    <dimen name="size_166dp">166.0dip</dimen>
+    <dimen name="size_167dp">167.0dip</dimen>
+    <dimen name="size_168dp">168.0dip</dimen>
+    <dimen name="size_169dp">169.0dip</dimen>
+    <dimen name="size_170dp">170.0dip</dimen>
+    <dimen name="size_171dp">171.0dip</dimen>
+    <dimen name="size_172dp">172.0dip</dimen>
+    <dimen name="size_173dp">173.0dip</dimen>
+    <dimen name="size_174dp">174.0dip</dimen>
+    <dimen name="size_175dp">175.0dip</dimen>
+    <dimen name="size_176dp">176.0dip</dimen>
+    <dimen name="size_177dp">177.0dip</dimen>
+    <dimen name="size_178dp">178.0dip</dimen>
+    <dimen name="size_179dp">179.0dip</dimen>
+    <dimen name="size_180dp">180.0dip</dimen>
+    <dimen name="size_181dp">181.0dip</dimen>
+    <dimen name="size_182dp">182.0dip</dimen>
+    <dimen name="size_183dp">183.0dip</dimen>
+    <dimen name="size_184dp">184.0dip</dimen>
+    <dimen name="size_185dp">185.0dip</dimen>
+    <dimen name="size_186dp">186.0dip</dimen>
+    <dimen name="size_187dp">187.0dip</dimen>
+    <dimen name="size_188dp">188.0dip</dimen>
+    <dimen name="size_189dp">189.0dip</dimen>
+    <dimen name="size_190dp">190.0dip</dimen>
+    <dimen name="size_191dp">191.0dip</dimen>
+    <dimen name="size_192dp">192.0dip</dimen>
+    <dimen name="size_193dp">193.0dip</dimen>
+    <dimen name="size_194dp">194.0dip</dimen>
+    <dimen name="size_195dp">195.0dip</dimen>
+    <dimen name="size_196dp">196.0dip</dimen>
+    <dimen name="size_197dp">197.0dip</dimen>
+    <dimen name="size_198dp">198.0dip</dimen>
+    <dimen name="size_199dp">199.0dip</dimen>
+    <dimen name="size_200dp">200.0dip</dimen>
+    <dimen name="size_240dp">240.0dip</dimen>
+    <dimen name="size_260dp">260.0dip</dimen>
+
+
+</resources>

+ 51 - 0
common/src/main/res/values/styles.xml

@@ -0,0 +1,51 @@
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="Base.AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimary</item>
+        <item name="colorAccent">@color/colorPrimary</item>
+
+    </style>
+
+    <style name="AppTheme" parent="Base.AppTheme" />
+
+    <!--无ActionBar的AppTheme-->
+    <style name="AppTheme.NoActionBar">
+        <item name="windowActionBar">false</item>
+        <item name="windowNoTitle">true</item>
+    </style>
+
+    <!--启动页样式及窗口背景-->
+    <!--<style name="SplashTheme" parent="TransparentStatusBarTheme">-->
+    <!--<item name="android:windowBackground">@drawable/bg_launch</item>-->
+    <!--<item name="android:windowFullscreen">false</item>-->
+    <!--</style>-->
+
+
+    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
+
+    <style name="Toolbar" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+    <!--全屏-->
+    <style name="FullscreenTheme" parent="AppTheme.NoActionBar">
+        <item name="android:windowActionBarOverlay">true</item>
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowBackground">@null</item>
+    </style>
+
+    <!-- 自定义loading dialog -->
+    <style name="ProgressDialogTheme" parent="Theme.AppCompat.Light.Dialog">
+        <item name="android:windowFrame">@null</item><!--边框-->
+        <item name="android:windowNoTitle">true</item><!--无标题-->
+        <item name="android:windowBackground">@android:color/transparent</item><!--背景透明-->
+        <item name="android:windowIsTranslucent">false</item><!--半透明-->
+        <item name="android:windowIsFloating">true</item><!--是否浮现在activity之上-->
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:backgroundDimEnabled">false</item><!--模糊-->
+    </style>
+
+</resources>

+ 8 - 3
Girls/src/main/AndroidManifest.xml → girls/src/main/debug/AndroidManifest.xml

@@ -1,8 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.guiying.girls">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.guiying.girls">
 
-    <application android:allowBackup="true" android:icon="@mipmap/ic_launcher"
-        android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme">
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme">
         <activity android:name=".GirlsActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />

+ 12 - 0
girls/src/main/java/com/guiying/girls/Girls.java

@@ -0,0 +1,12 @@
+package com.guiying.girls;
+
+/**
+ * <p>类说明</p>
+ *
+ * @author 张华洋 2017/2/15 16:34
+ * @version V1.2.0
+ * @name Girls
+ */
+@Module("girls")
+public class Girls {
+}

+ 8 - 0
girls/src/main/release/AndroidManifest.xml

@@ -0,0 +1,8 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.guiying.girls">
+
+    <application android:theme="@style/AppTheme">
+
+    </application>
+
+</manifest>

+ 8 - 4
News/src/main/AndroidManifest.xml → news/src/main/debug/AndroidManifest.xml

@@ -1,8 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.guiying.news">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.guiying.news">
 
-    <application android:allowBackup="true" android:icon="@mipmap/ic_launcher"
-        android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme">
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme">
         <activity android:name=".NewsActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -11,5 +16,4 @@
             </intent-filter>
         </activity>
     </application>
-
 </manifest>

+ 13 - 0
news/src/main/java/com/guiying/news/news.java

@@ -0,0 +1,13 @@
+package com.guiying.news;
+
+/**
+ * <p>类说明</p>
+ *
+ * @author 张华洋 2017/2/15 16:31
+ * @version V1.2.0
+ * @name news
+ */
+
+@Module("news")
+public class News {
+}

+ 8 - 0
news/src/main/release/AndroidManifest.xml

@@ -0,0 +1,8 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.guiying.news">
+
+    <application android:theme="@style/AppTheme">
+
+    </application>
+
+</manifest>