diff --git a/.gitignore b/.gitignore
index e17edd6..e3c427a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
 *.iml
 .gradle
+/.idea
 /local.properties
 /.idea/workspace.xml
 /.idea/libraries
diff --git a/.idea/dictionaries/Rae.xml b/.idea/dictionaries/Rae.xml
deleted file mode 100644
index f36007d..0000000
--- a/.idea/dictionaries/Rae.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<component name="ProjectDictionaryState">
-  <dictionary name="Rae">
-    <words>
-      <w>bugly</w>
-    </words>
-  </dictionary>
-</component>
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 77ef51e..93124d6 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -10,7 +10,11 @@
           <set>
             <option value="$PROJECT_DIR$" />
             <option value="$PROJECT_DIR$/app" />
+            <option value="$PROJECT_DIR$/module-basic" />
+            <option value="$PROJECT_DIR$/module-blog" />
+            <option value="$PROJECT_DIR$/module-resource" />
             <option value="$PROJECT_DIR$/sdk" />
+            <option value="$PROJECT_DIR$/widget" />
           </set>
         </option>
         <option name="resolveModulePerSourceSet" value="false" />
diff --git a/.idea/modules.xml b/.idea/modules.xml
index eda1816..1583409 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -4,7 +4,11 @@
     <modules>
       <module fileurl="file://$PROJECT_DIR$/android-cnblogs.iml" filepath="$PROJECT_DIR$/android-cnblogs.iml" />
       <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
+      <module fileurl="file://$PROJECT_DIR$/module-basic/module-basic.iml" filepath="$PROJECT_DIR$/module-basic/module-basic.iml" />
+      <module fileurl="file://$PROJECT_DIR$/module-blog/module-blog.iml" filepath="$PROJECT_DIR$/module-blog/module-blog.iml" />
+      <module fileurl="file://$PROJECT_DIR$/module-resource/module-resource.iml" filepath="$PROJECT_DIR$/module-resource/module-resource.iml" />
       <module fileurl="file://$PROJECT_DIR$/sdk/sdk.iml" filepath="$PROJECT_DIR$/sdk/sdk.iml" />
+      <module fileurl="file://$PROJECT_DIR$/widget/widget.iml" filepath="$PROJECT_DIR$/widget/widget.iml" />
     </modules>
   </component>
 </project>
\ No newline at end of file
diff --git a/CHANGES.md b/CHANGES.md
index 7d234ad..d192d46 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,8 +1,11 @@
 # 版本更新记录
 
-## V1.1.3
+## V1.1.5
+
+- 重构代码模块
 
-*开发中*
+
+## V1.1.3
 
 > **2018-01-25**
 
diff --git a/README.md b/README.md
index 902fb08..6b40eba 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,6 @@
 
 *请欣赏下面大图，图片较大，客官请稍等一下哦！*
 
-<img src="https://github.com/raee/android-cnblogs/blob/master/guide.jpg" alt="博客园APP介绍" />
+<img src="https://github.com/raee/android-cnblogs/blob/master/doc/guide.jpg" alt="博客园APP介绍" />
 
 
diff --git a/app/build.gradle b/app/build.gradle
index 7c3417b..566509b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -26,11 +26,6 @@ def qqAppSecret = properties.getProperty("qqAppSecret", "replace_your_qqAppSecre
 
 android {
     compileSdkVersion rootProject.ext.compileSdkVersion
-    buildToolsVersion rootProject.ext.buildToolsVersion
-
-//    dexOptions {
-//        javaMaxHeapSize '2g'
-//    }
 
     signingConfigs {
         release {
@@ -45,10 +40,17 @@ android {
         applicationId "com.rae.cnblogs"
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
-        versionCode 7
-        versionName "1.1.4"
+        versionCode 8
+        versionName "1.1.5"
         multiDexEnabled true
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        javaCompileOptions {
+            annotationProcessorOptions {
+                arguments = [moduleName: project.getName()]
+            }
+        }
+
+
         buildConfigField 'String', 'BUGLY_APP_ID', '"' + buglyAppId + '"'
         buildConfigField 'String', 'LEAN_CLOUD_APP_ID', '"' + leancloudAppId + '"'
         buildConfigField 'String', 'LEAN_CLOUD_APP_KEY', '"' + leancloudAppKey + '"'
@@ -80,21 +82,26 @@ android {
             signingConfig signingConfigs.release
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
-        xiaomi {
-            initWith release
-            tasks.whenTaskAdded { task ->
-                if (task.name.contains('assembleXiaomiChannels')) {
-                    task.doFirst {
-                        printf('渠道包任务：%s\n', task.name)
-                        walle.channelFile = new File("${project.getProjectDir()}/xiaomiChannel")
-                    }
-                }
-            }
-        }
+//        xiaomi {
+//            initWith release
+//            tasks.whenTaskAdded { task ->
+//                if (task.name.contains('assembleXiaomiChannels')) {
+//                    task.doFirst {
+//                        printf('渠道包任务：%s\n', task.name)
+//                        walle.channelFile = new File("${project.getProjectDir()}/xiaomiChannel")
+//                    }
+//                }
+//            }
+//        }
     }
     sourceSets { main { assets.srcDirs = ['src/main/assets', 'src/main/assets/'] } }
+    dexOptions{
+        javaMaxHeapSize "2g"
+        preDexLibraries = false
+    }
 }
 
+// 多渠道打包
 walle {
     // 指定渠道包的输出路径
     apkOutputFolder = new File("${project.buildDir}/outputs/channels")
@@ -102,7 +109,6 @@ walle {
     apkFileNameFormat = '${appName}-${buildType}-v${versionName}-${channel}.apk'
     // 渠道配置文件
     channelFile = new File("${project.getProjectDir()}/channel")
-//    configFile = new File("${project.getProjectDir()}/config.json")
 }
 
 dependencies {
@@ -111,27 +117,32 @@ dependencies {
         exclude group: 'com.android.support', module: 'support-annotations'
     })
     testImplementation 'junit:junit:4.12'
-    implementation 'com.android.support:multidex:1.0.2'
+    implementation 'com.android.support:multidex:1.0.3'
+
+    implementation project(':module-basic')
+    implementation project(':module-resource')
+    implementation project(':widget')
 
-    implementation project(':sdk')
+    // 模块化
+    if(!isModule.toBoolean()){
+        implementation project(':module-blog')
+    }
 
     annotationProcessor rootProject.ext.butterknifeCompiler
+    // 阿里路由注解
+    annotationProcessor rootProject.ext.arouterCompiler
     implementation rootProject.ext.appcompat
     implementation rootProject.ext.design
     implementation rootProject.ext.cardview
     implementation rootProject.ext.butterknife
     implementation rootProject.ext.rxjava
+    implementation rootProject.ext.eventBus
 
     implementation 'com.michaelpardo:activeandroid:3.1.0-SNAPSHOT'
-    implementation 'in.srain.cube:ultra-ptr:1.0.11'
-    implementation 'com.github.raee:XRecyclerView:1.3.3'
     implementation 'com.makeramen:roundedimageview:2.3.0'
     implementation 'com.github.bumptech.glide:glide:4.0.0'
     annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0'
-    implementation 'com.umeng.sdk:common:1.4.1'
-    implementation 'com.umeng.sdk:analytics:7.4.1'
-    implementation 'org.greenrobot:eventbus:3.1.1'
-    implementation 'me.imid.swipebacklayout.lib:library:1.1.0'
+
     implementation 'com.github.chrisbanes:PhotoView:2.1.2'
     debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.1'
     releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
@@ -141,15 +152,13 @@ dependencies {
     implementation 'cn.leancloud.android:avoscloud-sdk:v4.4.3'
     // LeanCloud 用户反馈包
     implementation 'cn.leancloud.android:avoscloud-feedback:v4.4.3@aar'
-    // 主题切换
-    implementation 'skin.support:skin-support:2.1.2'
-    implementation 'skin.support:skin-support-design:1.2.5'
+
     implementation 'com.kyleduo.switchbutton:library:1.4.6'
     implementation 'com.google.code.gson:gson:2.8.0'
-    implementation 'com.meituan.android.walle:library:1.1.6'
     implementation 'jp.wasabeef:glide-transformations:3.0.1'
     // If you want to use the GPU Filters
     implementation 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.4.1'
+
 }
 
 // 热更新
diff --git a/app/src/debug/AndroidManifest.xml b/app/src/debug/AndroidManifest.xml
index 5ea16e2..01521d2 100644
--- a/app/src/debug/AndroidManifest.xml
+++ b/app/src/debug/AndroidManifest.xml
@@ -9,10 +9,8 @@
     <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
-    <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
     <uses-permission android:name="android.permission.CAMERA"/>
     <uses-permission android:name="android.permission.INTERNET"/>
-    <uses-permission android:name="android.permission.READ_LOGS"/>
 
     <application
         android:name=".CnblogsApplication"
@@ -22,13 +20,13 @@
         android:supportsRtl="true"
         android:theme="@style/AppTheme">
 
-   <!--     &lt;!&ndash;启动页&ndash;&gt;
+        <!--启动页-->
         <activity
             android:name=".activity.LauncherActivity"
             android:theme="@style/AppTheme.NoActionBar.FullScreen"
             tools:node="replace"/>
 
-        &lt;!&ndash;主界面&ndash;&gt;
+        <!--主界面-->
         <activity
             android:name=".activity.MainActivity"
             android:theme="@style/AppTheme.Dark"
@@ -38,7 +36,7 @@
 
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
-        </activity>-->
+        </activity>
 
         <!--开始-快速开发需要移除下面的多进程-->
         <service
diff --git a/app/src/main/java/android/support/design/widget/DesignTabLayout.java b/app/src/main/java/android/support/design/widget/DesignTabLayout.java
deleted file mode 100644
index 3baf609..0000000
--- a/app/src/main/java/android/support/design/widget/DesignTabLayout.java
+++ /dev/null
@@ -1,2268 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.design.widget;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.annotation.ColorInt;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.IntDef;
-import android.support.annotation.LayoutRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.StringRes;
-import android.support.design.R;
-import android.support.v4.util.Pools;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.PagerAdapter;
-import android.support.v4.view.PointerIconCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPager;
-import android.support.v4.widget.TextViewCompat;
-import android.support.v7.app.ActionBar;
-import android.support.v7.content.res.AppCompatResources;
-import android.text.Layout;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.SoundEffectConstants;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.HorizontalScrollView;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Iterator;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static android.support.v4.view.ViewPager.SCROLL_STATE_DRAGGING;
-import static android.support.v4.view.ViewPager.SCROLL_STATE_IDLE;
-import static android.support.v4.view.ViewPager.SCROLL_STATE_SETTLING;
-
-/**
- * RaeTabLayout provides a horizontal layout to display tabs.
- * <p>
- * <p>Population of the tabs to display is
- * done through {@link Tab} instances. You create tabs via {@link #newTab()}. From there you can
- * change the tab's label or icon via {@link Tab#setText(int)} and {@link Tab#setIcon(int)}
- * respectively. To display the tab, you need to add it to the layout via one of the
- * {@link #addTab(Tab)} methods. For example:
- * <pre>
- * RaeTabLayout tabLayout = ...;
- * tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
- * tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
- * tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));
- * </pre>
- * You should set a listener via {@link #setOnTabSelectedListener(OnTabSelectedListener)} to be
- * notified when any tab's selection state has been changed.
- * <p>
- * <p>You can also add items to RaeTabLayout in your layout through the use of {@link TabItem}.
- * An example usage is like so:</p>
- * <p>
- * <pre>
- * &lt;android.support.design.widget.RaeTabLayout
- *         android:layout_height=&quot;wrap_content&quot;
- *         android:layout_width=&quot;match_parent&quot;&gt;
- *
- *     &lt;android.support.design.widget.TabItem
- *             android:text=&quot;@string/tab_text&quot;/&gt;
- *
- *     &lt;android.support.design.widget.TabItem
- *             android:icon=&quot;@drawable/ic_android&quot;/&gt;
- *
- * &lt;/android.support.design.widget.RaeTabLayout&gt;
- * </pre>
- * <p>
- * <h3>ViewPager integration</h3>
- * <p>
- * If you're using a {@link ViewPager} together
- * with this layout, you can call {@link #setupWithViewPager(ViewPager)} to link the two together.
- * This layout will be automatically populated from the {@link PagerAdapter}'s page titles.</p>
- * <p>
- * <p>
- * This view also supports being used as part of a ViewPager's decor, and can be added
- * directly to the ViewPager in a layout resource file like so:</p>
- * <p>
- * <pre>
- * &lt;android.support.v4.view.ViewPager
- *     android:layout_width=&quot;match_parent&quot;
- *     android:layout_height=&quot;match_parent&quot;&gt;
- *
- *     &lt;android.support.design.widget.RaeTabLayout
- *         android:layout_width=&quot;match_parent&quot;
- *         android:layout_height=&quot;wrap_content&quot;
- *         android:layout_gravity=&quot;top&quot; /&gt;
- *
- * &lt;/android.support.v4.view.ViewPager&gt;
- * </pre>
- *
- * @attr ref android.support.design.R.styleable#TabLayout_tabPadding
- * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingStart
- * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingTop
- * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingEnd
- * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingBottom
- * @attr ref android.support.design.R.styleable#TabLayout_tabContentStart
- * @attr ref android.support.design.R.styleable#TabLayout_tabBackground
- * @attr ref android.support.design.R.styleable#TabLayout_tabMinWidth
- * @attr ref android.support.design.R.styleable#TabLayout_tabMaxWidth
- * @attr ref android.support.design.R.styleable#TabLayout_tabTextAppearance
- * @see <a href="http://www.google.com/design/spec/components/tabs.html">Tabs</a>
- */
-@ViewPager.DecorView
-public class DesignTabLayout extends HorizontalScrollView {
-
-    private static final int DEFAULT_HEIGHT_WITH_TEXT_ICON = 72; // dps
-    static final int DEFAULT_GAP_TEXT_ICON = 8; // dps
-    private static final int INVALID_WIDTH = -1;
-    private static final int DEFAULT_HEIGHT = 48; // dps
-    private static final int TAB_MIN_WIDTH_MARGIN = 56; //dps
-    static final int FIXED_WRAP_GUTTER_MIN = 16; //dps
-    static final int MOTION_NON_ADJACENT_OFFSET = 24;
-
-    private static final int ANIMATION_DURATION = 300;
-
-    private static final Pools.Pool<Tab> sTabPool = new Pools.SynchronizedPool<>(16);
-
-    /**
-     * Scrollable tabs display a subset of tabs at any given moment, and can contain longer tab
-     * labels and a larger number of tabs. They are best used for browsing contexts in touch
-     * interfaces when users don’t need to directly compare the tab labels.
-     *
-     * @see #setTabMode(int)
-     * @see #getTabMode()
-     */
-    public static final int MODE_SCROLLABLE = 0;
-
-    /**
-     * Fixed tabs display all tabs concurrently and are best used with content that benefits from
-     * quick pivots between tabs. The maximum number of tabs is limited by the view’s width.
-     * Fixed tabs have equal width, based on the widest tab label.
-     *
-     * @see #setTabMode(int)
-     * @see #getTabMode()
-     */
-    public static final int MODE_FIXED = 1;
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef(value = {MODE_SCROLLABLE, MODE_FIXED})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Mode {
-    }
-
-    /**
-     * Gravity used to fill the {@link DesignTabLayout} as much as possible. This option only takes effect
-     * when used with {@link #MODE_FIXED}.
-     *
-     * @see #setTabGravity(int)
-     * @see #getTabGravity()
-     */
-    public static final int GRAVITY_FILL = 0;
-
-    /**
-     * Gravity used to lay out the tabs in the center of the {@link DesignTabLayout}.
-     *
-     * @see #setTabGravity(int)
-     * @see #getTabGravity()
-     */
-    public static final int GRAVITY_CENTER = 1;
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef(flag = true, value = {GRAVITY_FILL, GRAVITY_CENTER})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TabGravity {
-    }
-
-    /**
-     * Callback interface invoked when a tab's selection state changes.
-     */
-    public interface OnTabSelectedListener {
-
-        /**
-         * Called when a tab enters the selected state.
-         *
-         * @param tab The tab that was selected
-         */
-        public void onTabSelected(Tab tab);
-
-        /**
-         * Called when a tab exits the selected state.
-         *
-         * @param tab The tab that was unselected
-         */
-        public void onTabUnselected(Tab tab);
-
-        /**
-         * Called when a tab that is already selected is chosen again by the user. Some applications
-         * may use this action to return to the top level of a category.
-         *
-         * @param tab The tab that was reselected.
-         */
-        public void onTabReselected(Tab tab);
-    }
-
-    private final ArrayList<Tab> mTabs = new ArrayList<>();
-    private Tab mSelectedTab;
-
-    private final SlidingTabStrip mTabStrip;
-
-    int mTabPaddingStart;
-    int mTabPaddingTop;
-    int mTabPaddingEnd;
-    int mTabPaddingBottom;
-
-    int mTabTextAppearance;
-    ColorStateList mTabTextColors;
-    float mTabTextSize;
-    float mTabTextMultiLineSize;
-
-    final int mTabBackgroundResId;
-
-    int mTabMaxWidth = Integer.MAX_VALUE;
-    private final int mRequestedTabMinWidth;
-    private final int mRequestedTabMaxWidth;
-    private final int mScrollableTabMinWidth;
-
-    private int mContentInsetStart;
-
-    int mTabGravity;
-    int mMode;
-
-    private OnTabSelectedListener mSelectedListener;
-    private final ArrayList<OnTabSelectedListener> mSelectedListeners = new ArrayList<>();
-    private OnTabSelectedListener mCurrentVpSelectedListener;
-
-    private ValueAnimator mScrollAnimator;
-
-    ViewPager mViewPager;
-    private PagerAdapter mPagerAdapter;
-    private DataSetObserver mPagerAdapterObserver;
-    private TabLayoutOnPageChangeListener mPageChangeListener;
-    private AdapterChangeListener mAdapterChangeListener;
-    private boolean mSetupViewPagerImplicitly;
-
-    // Pool we use as a simple RecyclerBin
-    private final Pools.Pool<TabView> mTabViewPool = new Pools.SimplePool<>(12);
-
-    public DesignTabLayout(Context context) {
-        this(context, null);
-    }
-
-    public DesignTabLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public DesignTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        ThemeUtils.checkAppCompatTheme(context);
-
-        // Disable the Scroll Bar
-        setHorizontalScrollBarEnabled(false);
-
-        // Add the TabStrip
-        mTabStrip = new SlidingTabStrip(context);
-        super.addView(mTabStrip, 0, new LayoutParams(
-                LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
-
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabLayout,
-                defStyleAttr, R.style.Widget_Design_TabLayout);
-
-        mTabStrip.setSelectedIndicatorHeight(
-                a.getDimensionPixelSize(R.styleable.TabLayout_tabIndicatorHeight, 0));
-        mTabStrip.setSelectedIndicatorColor(a.getColor(R.styleable.TabLayout_tabIndicatorColor, 0));
-
-        mTabPaddingStart = mTabPaddingTop = mTabPaddingEnd = mTabPaddingBottom = a
-                .getDimensionPixelSize(R.styleable.TabLayout_tabPadding, 0);
-        mTabPaddingStart = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingStart,
-                mTabPaddingStart);
-        mTabPaddingTop = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingTop,
-                mTabPaddingTop);
-        mTabPaddingEnd = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingEnd,
-                mTabPaddingEnd);
-        mTabPaddingBottom = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingBottom,
-                mTabPaddingBottom);
-
-        mTabTextAppearance = a.getResourceId(R.styleable.TabLayout_tabTextAppearance,
-                R.style.TextAppearance_Design_Tab);
-
-        // rae attrs
-        TypedArray array = context.obtainStyledAttributes(attrs, com.rae.cnblogs.R.styleable.DesignTabLayout);
-        mTabStrip.setIndicatorWidth(array.getDimensionPixelSize(com.rae.cnblogs.R.styleable.DesignTabLayout_tabIndicatorWidth, 0));
-        array.recycle();
-
-
-        // Text colors/sizes come from the text appearance first
-        final TypedArray ta = context.obtainStyledAttributes(mTabTextAppearance,
-                android.support.v7.appcompat.R.styleable.TextAppearance);
-        try {
-            mTabTextSize = ta.getDimensionPixelSize(
-                    android.support.v7.appcompat.R.styleable.TextAppearance_android_textSize, 0);
-            mTabTextColors = ta.getColorStateList(
-                    android.support.v7.appcompat.R.styleable.TextAppearance_android_textColor);
-        } finally {
-            ta.recycle();
-        }
-
-        if (a.hasValue(R.styleable.TabLayout_tabTextColor)) {
-            // If we have an explicit text color set, use it instead
-            mTabTextColors = a.getColorStateList(R.styleable.TabLayout_tabTextColor);
-        }
-
-        if (a.hasValue(R.styleable.TabLayout_tabSelectedTextColor)) {
-            // We have an explicit selected text color set, so we need to make merge it with the
-            // current colors. This is exposed so that developers can use theme attributes to set
-            // this (theme attrs in ColorStateLists are Lollipop+)
-            final int selected = a.getColor(R.styleable.TabLayout_tabSelectedTextColor, 0);
-            mTabTextColors = createColorStateList(mTabTextColors.getDefaultColor(), selected);
-        }
-
-        mRequestedTabMinWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMinWidth,
-                INVALID_WIDTH);
-        mRequestedTabMaxWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMaxWidth,
-                INVALID_WIDTH);
-        mTabBackgroundResId = a.getResourceId(R.styleable.TabLayout_tabBackground, 0);
-        mContentInsetStart = a.getDimensionPixelSize(R.styleable.TabLayout_tabContentStart, 0);
-        mMode = a.getInt(R.styleable.TabLayout_tabMode, MODE_FIXED);
-        mTabGravity = a.getInt(R.styleable.TabLayout_tabGravity, GRAVITY_FILL);
-        a.recycle();
-
-        final Resources res = getResources();
-        mTabTextMultiLineSize = res.getDimensionPixelSize(R.dimen.design_tab_text_size_2line);
-        mScrollableTabMinWidth = res.getDimensionPixelSize(R.dimen.design_tab_scrollable_min_width);
-
-        // Now apply the tab mode and gravity
-        applyModeAndGravity();
-    }
-
-    /**
-     * Sets the tab indicator's color for the currently selected tab.
-     *
-     * @param color color to use for the indicator
-     * @attr ref android.support.design.R.styleable#TabLayout_tabIndicatorColor
-     */
-    public void setSelectedTabIndicatorColor(@ColorInt int color) {
-        mTabStrip.setSelectedIndicatorColor(color);
-    }
-
-    /**
-     * 设置指示器宽度
-     */
-    public void setIndicatorWidth(int indicatorWidth) {
-        mTabStrip.setIndicatorWidth(indicatorWidth);
-    }
-
-    /**
-     * Sets the tab indicator's height for the currently selected tab.
-     *
-     * @param height height to use for the indicator in pixels
-     * @attr ref android.support.design.R.styleable#TabLayout_tabIndicatorHeight
-     */
-    public void setSelectedTabIndicatorHeight(int height) {
-        mTabStrip.setSelectedIndicatorHeight(height);
-    }
-
-    /**
-     * Set the scroll position of the tabs. This is useful for when the tabs are being displayed as
-     * part of a scrolling container such as {@link ViewPager}.
-     * <p>
-     * Calling this method does not update the selected tab, it is only used for drawing purposes.
-     *
-     * @param position           current scroll position
-     * @param positionOffset     Value from [0, 1) indicating the offset from {@code position}.
-     * @param updateSelectedText Whether to update the text's selected state.
-     */
-    public void setScrollPosition(int position, float positionOffset, boolean updateSelectedText) {
-        setScrollPosition(position, positionOffset, updateSelectedText, true);
-    }
-
-    void setScrollPosition(int position, float positionOffset, boolean updateSelectedText,
-                           boolean updateIndicatorPosition) {
-        final int roundedPosition = Math.round(position + positionOffset);
-        if (roundedPosition < 0 || roundedPosition >= mTabStrip.getChildCount()) {
-            return;
-        }
-
-        // Set the indicator position, if enabled
-        if (updateIndicatorPosition) {
-            mTabStrip.setIndicatorPositionFromTabPosition(position, positionOffset);
-        }
-
-        // Now update the scroll position, canceling any running animation
-        if (mScrollAnimator != null && mScrollAnimator.isRunning()) {
-            mScrollAnimator.cancel();
-        }
-        scrollTo(calculateScrollXForTab(position, positionOffset), 0);
-
-        // Update the 'selected state' view as we scroll, if enabled
-        if (updateSelectedText) {
-            setSelectedTabView(roundedPosition);
-        }
-    }
-
-    private float getScrollPosition() {
-        return mTabStrip.getIndicatorPosition();
-    }
-
-    /**
-     * Add a tab to this layout. The tab will be added at the end of the list.
-     * If this is the first tab to be added it will become the selected tab.
-     *
-     * @param tab Tab to add
-     */
-    public void addTab(@NonNull Tab tab) {
-        addTab(tab, mTabs.isEmpty());
-    }
-
-    /**
-     * Add a tab to this layout. The tab will be inserted at <code>position</code>.
-     * If this is the first tab to be added it will become the selected tab.
-     *
-     * @param tab      The tab to add
-     * @param position The new position of the tab
-     */
-    public void addTab(@NonNull Tab tab, int position) {
-        addTab(tab, position, mTabs.isEmpty());
-    }
-
-    /**
-     * Add a tab to this layout. The tab will be added at the end of the list.
-     *
-     * @param tab         Tab to add
-     * @param setSelected True if the added tab should become the selected tab.
-     */
-    public void addTab(@NonNull Tab tab, boolean setSelected) {
-        addTab(tab, mTabs.size(), setSelected);
-    }
-
-    /**
-     * Add a tab to this layout. The tab will be inserted at <code>position</code>.
-     *
-     * @param tab         The tab to add
-     * @param position    The new position of the tab
-     * @param setSelected True if the added tab should become the selected tab.
-     */
-    public void addTab(@NonNull Tab tab, int position, boolean setSelected) {
-        if (tab.mParent != this) {
-            throw new IllegalArgumentException("Tab belongs to a different RaeTabLayout.");
-        }
-        configureTab(tab, position);
-        addTabView(tab);
-
-        if (setSelected) {
-            tab.select();
-        }
-    }
-
-    private void addTabFromItemView(@NonNull TabItem item) {
-        final Tab tab = newTab();
-        if (item.mText != null) {
-            tab.setText(item.mText);
-        }
-        if (item.mIcon != null) {
-            tab.setIcon(item.mIcon);
-        }
-        if (item.mCustomLayout != 0) {
-            tab.setCustomView(item.mCustomLayout);
-        }
-        if (!TextUtils.isEmpty(item.getContentDescription())) {
-            tab.setContentDescription(item.getContentDescription());
-        }
-        addTab(tab);
-    }
-
-    /**
-     * @deprecated Use {@link #addOnTabSelectedListener(OnTabSelectedListener)} and
-     * {@link #removeOnTabSelectedListener(OnTabSelectedListener)}.
-     */
-    @Deprecated
-    public void setOnTabSelectedListener(@Nullable OnTabSelectedListener listener) {
-        // The logic in this method emulates what we had before support for multiple
-        // registered listeners.
-        if (mSelectedListener != null) {
-            removeOnTabSelectedListener(mSelectedListener);
-        }
-        // Update the deprecated field so that we can remove the passed listener the next
-        // time we're called
-        mSelectedListener = listener;
-        if (listener != null) {
-            addOnTabSelectedListener(listener);
-        }
-    }
-
-    /**
-     * Add a {@link OnTabSelectedListener} that will be invoked when tab selection
-     * changes.
-     * <p>
-     * <p>Components that add a listener should take care to remove it when finished via
-     * {@link #removeOnTabSelectedListener(OnTabSelectedListener)}.</p>
-     *
-     * @param listener listener to add
-     */
-    public void addOnTabSelectedListener(@NonNull OnTabSelectedListener listener) {
-        if (!mSelectedListeners.contains(listener)) {
-            mSelectedListeners.add(listener);
-        }
-    }
-
-    /**
-     * Remove the given {@link OnTabSelectedListener} that was previously added via
-     * {@link #addOnTabSelectedListener(OnTabSelectedListener)}.
-     *
-     * @param listener listener to remove
-     */
-    public void removeOnTabSelectedListener(@NonNull OnTabSelectedListener listener) {
-        mSelectedListeners.remove(listener);
-    }
-
-    /**
-     * Remove all previously added {@link OnTabSelectedListener}s.
-     */
-    public void clearOnTabSelectedListeners() {
-        mSelectedListeners.clear();
-    }
-
-    /**
-     * Create and return a new {@link Tab}. You need to manually add this using
-     * {@link #addTab(Tab)} or a related method.
-     *
-     * @return A new Tab
-     * @see #addTab(Tab)
-     */
-    @NonNull
-    public Tab newTab() {
-        Tab tab = sTabPool.acquire();
-        if (tab == null) {
-            tab = new Tab();
-        }
-        tab.mParent = this;
-        tab.mView = createTabView(tab);
-        return tab;
-    }
-
-    /**
-     * Returns the number of tabs currently registered with the action bar.
-     *
-     * @return Tab count
-     */
-    public int getTabCount() {
-        return mTabs.size();
-    }
-
-    /**
-     * Returns the tab at the specified index.
-     */
-    @Nullable
-    public Tab getTabAt(int index) {
-        return (index < 0 || index >= getTabCount()) ? null : mTabs.get(index);
-    }
-
-    /**
-     * Returns the position of the current selected tab.
-     *
-     * @return selected tab position, or {@code -1} if there isn't a selected tab.
-     */
-    public int getSelectedTabPosition() {
-        return mSelectedTab != null ? mSelectedTab.getPosition() : -1;
-    }
-
-    /**
-     * Remove a tab from the layout. If the removed tab was selected it will be deselected
-     * and another tab will be selected if present.
-     *
-     * @param tab The tab to remove
-     */
-    public void removeTab(Tab tab) {
-        if (tab.mParent != this) {
-            throw new IllegalArgumentException("Tab does not belong to this RaeTabLayout.");
-        }
-
-        removeTabAt(tab.getPosition());
-    }
-
-    /**
-     * Remove a tab from the layout. If the removed tab was selected it will be deselected
-     * and another tab will be selected if present.
-     *
-     * @param position Position of the tab to remove
-     */
-    public void removeTabAt(int position) {
-        final int selectedTabPosition = mSelectedTab != null ? mSelectedTab.getPosition() : 0;
-        removeTabViewAt(position);
-
-        final Tab removedTab = mTabs.remove(position);
-        if (removedTab != null) {
-            removedTab.reset();
-            sTabPool.release(removedTab);
-        }
-
-        final int newTabCount = mTabs.size();
-        for (int i = position; i < newTabCount; i++) {
-            mTabs.get(i).setPosition(i);
-        }
-
-        if (selectedTabPosition == position) {
-            selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
-        }
-    }
-
-    /**
-     * Remove all tabs from the action bar and deselect the current tab.
-     */
-    public void removeAllTabs() {
-        // Remove all the views
-        for (int i = mTabStrip.getChildCount() - 1; i >= 0; i--) {
-            removeTabViewAt(i);
-        }
-
-        for (final Iterator<Tab> i = mTabs.iterator(); i.hasNext(); ) {
-            final Tab tab = i.next();
-            i.remove();
-            tab.reset();
-            sTabPool.release(tab);
-        }
-
-        mSelectedTab = null;
-    }
-
-    /**
-     * Set the behavior mode for the Tabs in this layout. The valid input options are:
-     * <ul>
-     * <li>{@link #MODE_FIXED}: Fixed tabs display all tabs concurrently and are best used
-     * with content that benefits from quick pivots between tabs.</li>
-     * <li>{@link #MODE_SCROLLABLE}: Scrollable tabs display a subset of tabs at any given moment,
-     * and can contain longer tab labels and a larger number of tabs. They are best used for
-     * browsing contexts in touch interfaces when users don’t need to directly compare the tab
-     * labels. This mode is commonly used with a {@link ViewPager}.</li>
-     * </ul>
-     *
-     * @param mode one of {@link #MODE_FIXED} or {@link #MODE_SCROLLABLE}.
-     * @attr ref android.support.design.R.styleable#TabLayout_tabMode
-     */
-    public void setTabMode(@Mode int mode) {
-        if (mode != mMode) {
-            mMode = mode;
-            applyModeAndGravity();
-        }
-    }
-
-    /**
-     * Returns the current mode used by this {@link DesignTabLayout}.
-     *
-     * @see #setTabMode(int)
-     */
-    @Mode
-    public int getTabMode() {
-        return mMode;
-    }
-
-    /**
-     * Set the gravity to use when laying out the tabs.
-     *
-     * @param gravity one of {@link #GRAVITY_CENTER} or {@link #GRAVITY_FILL}.
-     * @attr ref android.support.design.R.styleable#TabLayout_tabGravity
-     */
-    public void setTabGravity(@TabGravity int gravity) {
-        if (mTabGravity != gravity) {
-            mTabGravity = gravity;
-            applyModeAndGravity();
-        }
-    }
-
-    /**
-     * The current gravity used for laying out tabs.
-     *
-     * @return one of {@link #GRAVITY_CENTER} or {@link #GRAVITY_FILL}.
-     */
-    @TabGravity
-    public int getTabGravity() {
-        return mTabGravity;
-    }
-
-    /**
-     * Sets the text colors for the different states (normal, selected) used for the tabs.
-     *
-     * @see #getTabTextColors()
-     */
-    public void setTabTextColors(@Nullable ColorStateList textColor) {
-        if (mTabTextColors != textColor) {
-            mTabTextColors = textColor;
-            updateAllTabs();
-        }
-    }
-
-    /**
-     * Gets the text colors for the different states (normal, selected) used for the tabs.
-     */
-    @Nullable
-    public ColorStateList getTabTextColors() {
-        return mTabTextColors;
-    }
-
-    /**
-     * Sets the text colors for the different states (normal, selected) used for the tabs.
-     *
-     * @attr ref android.support.design.R.styleable#TabLayout_tabTextColor
-     * @attr ref android.support.design.R.styleable#TabLayout_tabSelectedTextColor
-     */
-    public void setTabTextColors(int normalColor, int selectedColor) {
-        setTabTextColors(createColorStateList(normalColor, selectedColor));
-    }
-
-    /**
-     * The one-cancel shop for setting up this {@link DesignTabLayout} with a {@link ViewPager}.
-     * <p>
-     * <p>This is the same as calling {@link #setupWithViewPager(ViewPager, boolean)} with
-     * auto-refresh enabled.</p>
-     *
-     * @param viewPager the ViewPager to link to, or {@code null} to clear any previous link
-     */
-    public void setupWithViewPager(@Nullable ViewPager viewPager) {
-        setupWithViewPager(viewPager, true);
-    }
-
-    /**
-     * The one-cancel shop for setting up this {@link DesignTabLayout} with a {@link ViewPager}.
-     * <p>
-     * <p>This method will link the given ViewPager and this RaeTabLayout together so that
-     * changes in one are automatically reflected in the other. This includes scroll state changes
-     * and clicks. The tabs displayed in this layout will be populated
-     * from the ViewPager adapter's page titles.</p>
-     * <p>
-     * <p>If {@code autoRefresh} is {@code true}, any changes in the {@link PagerAdapter} will
-     * trigger this layout to re-populate itself from the adapter's titles.</p>
-     * <p>
-     * <p>If the given ViewPager is non-null, it needs to already have a
-     * {@link PagerAdapter} set.</p>
-     *
-     * @param viewPager   the ViewPager to link to, or {@code null} to clear any previous link
-     * @param autoRefresh whether this layout should refresh its contents if the given ViewPager's
-     *                    content changes
-     */
-    public void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh) {
-        setupWithViewPager(viewPager, autoRefresh, false);
-    }
-
-    private void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh,
-                                    boolean implicitSetup) {
-        if (mViewPager != null) {
-            // If we've already been setup with a ViewPager, remove us from it
-            if (mPageChangeListener != null) {
-                mViewPager.removeOnPageChangeListener(mPageChangeListener);
-            }
-            if (mAdapterChangeListener != null) {
-                mViewPager.removeOnAdapterChangeListener(mAdapterChangeListener);
-            }
-        }
-
-        if (mCurrentVpSelectedListener != null) {
-            // If we already have a tab selected listener for the ViewPager, remove it
-            removeOnTabSelectedListener(mCurrentVpSelectedListener);
-            mCurrentVpSelectedListener = null;
-        }
-
-        if (viewPager != null) {
-            mViewPager = viewPager;
-
-            // Add our custom OnPageChangeListener to the ViewPager
-            if (mPageChangeListener == null) {
-                mPageChangeListener = new TabLayoutOnPageChangeListener(this);
-            }
-            mPageChangeListener.reset();
-            viewPager.addOnPageChangeListener(mPageChangeListener);
-
-            // Now we'll add a tab selected listener to set ViewPager's current item
-            mCurrentVpSelectedListener = new ViewPagerOnTabSelectedListener(viewPager);
-            addOnTabSelectedListener(mCurrentVpSelectedListener);
-
-            final PagerAdapter adapter = viewPager.getAdapter();
-            if (adapter != null) {
-                // Now we'll populate ourselves from the pager adapter, adding an observer if
-                // autoRefresh is enabled
-                setPagerAdapter(adapter, autoRefresh);
-            }
-
-            // Add a listener so that we're notified of any adapter changes
-            if (mAdapterChangeListener == null) {
-                mAdapterChangeListener = new AdapterChangeListener();
-            }
-            mAdapterChangeListener.setAutoRefresh(autoRefresh);
-            viewPager.addOnAdapterChangeListener(mAdapterChangeListener);
-
-            // Now update the scroll position to match the ViewPager's current item
-            setScrollPosition(viewPager.getCurrentItem(), 0f, true);
-        } else {
-            // We've been given a null ViewPager so we need to clear out the internal state,
-            // listeners and observers
-            mViewPager = null;
-            setPagerAdapter(null, false);
-        }
-
-        mSetupViewPagerImplicitly = implicitSetup;
-    }
-
-    /**
-     * @deprecated Use {@link #setupWithViewPager(ViewPager)} to link a RaeTabLayout with a ViewPager
-     * together. When that method is used, the RaeTabLayout will be automatically updated
-     * when the {@link PagerAdapter} is changed.
-     */
-    @Deprecated
-    public void setTabsFromPagerAdapter(@Nullable final PagerAdapter adapter) {
-        setPagerAdapter(adapter, false);
-    }
-
-    @Override
-    public boolean shouldDelayChildPressedState() {
-        // Only delay the pressed state if the tabs can scroll
-        return getTabScrollRange() > 0;
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        if (mViewPager == null) {
-            // If we don't have a ViewPager already, check if our parent is a ViewPager to
-            // setup with it automatically
-            final ViewParent vp = getParent();
-            if (vp instanceof ViewPager) {
-                // If we have a ViewPager parent and we've been added as part of its decor, let's
-                // assume that we should automatically setup to display any titles
-                setupWithViewPager((ViewPager) vp, true, true);
-            }
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-
-        if (mSetupViewPagerImplicitly) {
-            // If we've been setup with a ViewPager implicitly, let's clear out any listeners, etc
-            setupWithViewPager(null);
-            mSetupViewPagerImplicitly = false;
-        }
-    }
-
-    private int getTabScrollRange() {
-        return Math.max(0, mTabStrip.getWidth() - getWidth() - getPaddingLeft()
-                - getPaddingRight());
-    }
-
-    void setPagerAdapter(@Nullable final PagerAdapter adapter, final boolean addObserver) {
-        if (mPagerAdapter != null && mPagerAdapterObserver != null) {
-            // If we already have a PagerAdapter, unregister our observer
-            mPagerAdapter.unregisterDataSetObserver(mPagerAdapterObserver);
-        }
-
-        mPagerAdapter = adapter;
-
-        if (addObserver && adapter != null) {
-            // Register our observer on the new adapter
-            if (mPagerAdapterObserver == null) {
-                mPagerAdapterObserver = new PagerAdapterObserver();
-            }
-            adapter.registerDataSetObserver(mPagerAdapterObserver);
-        }
-
-        // Finally make sure we reflect the new adapter
-        populateFromPagerAdapter();
-    }
-
-    void populateFromPagerAdapter() {
-        removeAllTabs();
-
-        if (mPagerAdapter != null) {
-            final int adapterCount = mPagerAdapter.getCount();
-            for (int i = 0; i < adapterCount; i++) {
-                addTab(newTab().setText(mPagerAdapter.getPageTitle(i)), false);
-            }
-
-            // Make sure we reflect the currently set ViewPager item
-            if (mViewPager != null && adapterCount > 0) {
-                final int curItem = mViewPager.getCurrentItem();
-                if (curItem != getSelectedTabPosition() && curItem < getTabCount()) {
-                    selectTab(getTabAt(curItem));
-                }
-            }
-        }
-    }
-
-    private void updateAllTabs() {
-        for (int i = 0, z = mTabs.size(); i < z; i++) {
-            mTabs.get(i).updateView();
-        }
-    }
-
-    private TabView createTabView(@NonNull final Tab tab) {
-        TabView tabView = mTabViewPool != null ? mTabViewPool.acquire() : null;
-        if (tabView == null) {
-            tabView = new TabView(getContext());
-        }
-        tabView.setTab(tab);
-        tabView.setFocusable(true);
-        tabView.setMinimumWidth(getTabMinWidth());
-        return tabView;
-    }
-
-    private void configureTab(Tab tab, int position) {
-        tab.setPosition(position);
-        mTabs.add(position, tab);
-
-        final int count = mTabs.size();
-        for (int i = position + 1; i < count; i++) {
-            mTabs.get(i).setPosition(i);
-        }
-    }
-
-    private void addTabView(Tab tab) {
-        final TabView tabView = tab.mView;
-        mTabStrip.addView(tabView, tab.getPosition(), createLayoutParamsForTabs());
-    }
-
-    @Override
-    public void addView(View child) {
-        addViewInternal(child);
-    }
-
-    @Override
-    public void addView(View child, int index) {
-        addViewInternal(child);
-    }
-
-    @Override
-    public void addView(View child, ViewGroup.LayoutParams params) {
-        addViewInternal(child);
-    }
-
-    @Override
-    public void addView(View child, int index, ViewGroup.LayoutParams params) {
-        addViewInternal(child);
-    }
-
-    private void addViewInternal(final View child) {
-        if (child instanceof TabItem) {
-            addTabFromItemView((TabItem) child);
-        } else {
-            throw new IllegalArgumentException("Only TabItem instances can be added to RaeTabLayout");
-        }
-    }
-
-    private LinearLayout.LayoutParams createLayoutParamsForTabs() {
-        final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
-                LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
-        updateTabViewLayoutParams(lp);
-        return lp;
-    }
-
-    private void updateTabViewLayoutParams(LinearLayout.LayoutParams lp) {
-        if (mMode == MODE_FIXED && mTabGravity == GRAVITY_FILL) {
-            lp.width = 0;
-            lp.weight = 1;
-        } else {
-            lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
-            lp.weight = 0;
-        }
-    }
-
-    int dpToPx(int dps) {
-        return Math.round(getResources().getDisplayMetrics().density * dps);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // If we have a MeasureSpec which allows us to decide our height, try and use the default
-        // height
-        final int idealHeight = dpToPx(getDefaultHeight()) + getPaddingTop() + getPaddingBottom();
-        switch (MeasureSpec.getMode(heightMeasureSpec)) {
-            case MeasureSpec.AT_MOST:
-                heightMeasureSpec = MeasureSpec.makeMeasureSpec(
-                        Math.min(idealHeight, MeasureSpec.getSize(heightMeasureSpec)),
-                        MeasureSpec.EXACTLY);
-                break;
-            case MeasureSpec.UNSPECIFIED:
-                heightMeasureSpec = MeasureSpec.makeMeasureSpec(idealHeight, MeasureSpec.EXACTLY);
-                break;
-        }
-
-        final int specWidth = MeasureSpec.getSize(widthMeasureSpec);
-        if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
-            // If we don't have an unspecified width spec, use the given size to calculate
-            // the max tab width
-            mTabMaxWidth = mRequestedTabMaxWidth > 0
-                    ? mRequestedTabMaxWidth
-                    : specWidth - dpToPx(TAB_MIN_WIDTH_MARGIN);
-        }
-
-        // Now super measure itself using the (possibly) modified height spec
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        if (getChildCount() == 1) {
-            // If we're in fixed mode then we need to make the tab strip is the same width as us
-            // so we don't scroll
-            final View child = getChildAt(0);
-            boolean remeasure = false;
-
-            switch (mMode) {
-                case MODE_SCROLLABLE:
-                    // We only need to resize the child if it's smaller than us. This is similar
-                    // to fillViewport
-                    remeasure = child.getMeasuredWidth() < getMeasuredWidth();
-                    break;
-                case MODE_FIXED:
-                    // Resize the child so that it doesn't scroll
-                    remeasure = child.getMeasuredWidth() != getMeasuredWidth();
-                    break;
-            }
-
-            if (remeasure) {
-                // Re-measure the child with a widthSpec set to be exactly our measure width
-                int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop()
-                        + getPaddingBottom(), child.getLayoutParams().height);
-                int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                        getMeasuredWidth(), MeasureSpec.EXACTLY);
-                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
-            }
-        }
-    }
-
-    private void removeTabViewAt(int position) {
-        final TabView view = (TabView) mTabStrip.getChildAt(position);
-        mTabStrip.removeViewAt(position);
-        if (view != null) {
-            view.reset();
-            mTabViewPool.release(view);
-        }
-        requestLayout();
-    }
-
-    private void animateToTab(int newPosition) {
-        if (newPosition == Tab.INVALID_POSITION) {
-            return;
-        }
-
-        if (getWindowToken() == null || !ViewCompat.isLaidOut(this)
-                || mTabStrip.childrenNeedLayout()) {
-            // If we don't have a window token, or we haven't been laid out yet just draw the new
-            // position now
-            setScrollPosition(newPosition, 0f, true);
-            return;
-        }
-
-        final int startScrollX = getScrollX();
-        final int targetScrollX = calculateScrollXForTab(newPosition, 0);
-
-        if (startScrollX != targetScrollX) {
-            if(this.mScrollAnimator == null) {
-                this.mScrollAnimator = new ValueAnimator();
-                this.mScrollAnimator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
-                this.mScrollAnimator.setDuration(300L);
-                this.mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                    public void onAnimationUpdate(ValueAnimator animator) {
-                        scrollTo(((Integer)animator.getAnimatedValue()).intValue(), 0);
-                    }
-                });
-            }
-
-            mScrollAnimator.setIntValues(startScrollX, targetScrollX);
-            mScrollAnimator.start();
-        }
-
-        // Now animate the indicator
-        mTabStrip.animateIndicatorToPosition(newPosition, ANIMATION_DURATION);
-    }
-
-    private void setSelectedTabView(int position) {
-        final int tabCount = mTabStrip.getChildCount();
-        if (position < tabCount) {
-            for (int i = 0; i < tabCount; i++) {
-                final View child = mTabStrip.getChildAt(i);
-                child.setSelected(i == position);
-            }
-        }
-    }
-
-    public void setEnableIndicatorAnimate(boolean value) {
-        mTabStrip.setEnableIndicatorAnimate(value);
-    }
-
-    void selectTab(Tab tab) {
-        selectTab(tab, true);
-    }
-
-    void selectTab(final Tab tab, boolean updateIndicator) {
-        final Tab currentTab = mSelectedTab;
-
-        if (currentTab == tab) {
-            if (currentTab != null) {
-                dispatchTabReselected(tab);
-                animateToTab(tab.getPosition());
-            }
-        } else {
-            final int newPosition = tab != null ? tab.getPosition() : Tab.INVALID_POSITION;
-            if (updateIndicator) {
-                if ((currentTab == null || currentTab.getPosition() == Tab.INVALID_POSITION)
-                        && newPosition != Tab.INVALID_POSITION) {
-                    // If we don't currently have a tab, just draw the indicator
-                    setScrollPosition(newPosition, 0f, true);
-                } else {
-                    animateToTab(newPosition);
-                }
-                if (newPosition != Tab.INVALID_POSITION) {
-                    setSelectedTabView(newPosition);
-                }
-            }
-            if (currentTab != null) {
-                dispatchTabUnselected(currentTab);
-            }
-            mSelectedTab = tab;
-            if (tab != null) {
-                dispatchTabSelected(tab);
-            }
-        }
-    }
-
-    private void dispatchTabSelected(@NonNull final Tab tab) {
-        for (int i = mSelectedListeners.size() - 1; i >= 0; i--) {
-            mSelectedListeners.get(i).onTabSelected(tab);
-        }
-    }
-
-    private void dispatchTabUnselected(@NonNull final Tab tab) {
-        for (int i = mSelectedListeners.size() - 1; i >= 0; i--) {
-            mSelectedListeners.get(i).onTabUnselected(tab);
-        }
-    }
-
-    private void dispatchTabReselected(@NonNull final Tab tab) {
-        for (int i = mSelectedListeners.size() - 1; i >= 0; i--) {
-            mSelectedListeners.get(i).onTabReselected(tab);
-        }
-    }
-
-    private int calculateScrollXForTab(int position, float positionOffset) {
-        if (mMode == MODE_SCROLLABLE) {
-            final View selectedChild = mTabStrip.getChildAt(position);
-            final View nextChild = position + 1 < mTabStrip.getChildCount()
-                    ? mTabStrip.getChildAt(position + 1)
-                    : null;
-            final int selectedWidth = selectedChild != null ? selectedChild.getWidth() : 0;
-            final int nextWidth = nextChild != null ? nextChild.getWidth() : 0;
-
-            return selectedChild.getLeft()
-                    + ((int) ((selectedWidth + nextWidth) * positionOffset * 0.5f))
-                    + (selectedChild.getWidth() / 2)
-                    - (getWidth() / 2);
-        }
-        return 0;
-    }
-
-    private void applyModeAndGravity() {
-        int paddingStart = 0;
-        if (mMode == MODE_SCROLLABLE) {
-            // If we're scrollable, or fixed at start, inset using padding
-            paddingStart = Math.max(0, mContentInsetStart - mTabPaddingStart);
-        }
-        ViewCompat.setPaddingRelative(mTabStrip, paddingStart, 0, 0, 0);
-
-        switch (mMode) {
-            case MODE_FIXED:
-                mTabStrip.setGravity(Gravity.CENTER_HORIZONTAL);
-                break;
-            case MODE_SCROLLABLE:
-                mTabStrip.setGravity(GravityCompat.START);
-                break;
-        }
-
-        updateTabViews(true);
-    }
-
-    void updateTabViews(final boolean requestLayout) {
-        for (int i = 0; i < mTabStrip.getChildCount(); i++) {
-            View child = mTabStrip.getChildAt(i);
-            child.setMinimumWidth(getTabMinWidth());
-            updateTabViewLayoutParams((LinearLayout.LayoutParams) child.getLayoutParams());
-            if (requestLayout) {
-                child.requestLayout();
-            }
-        }
-    }
-
-    /**
-     * A tab in this layout. Instances can be created via {@link #newTab()}.
-     */
-    public static final class Tab {
-
-        /**
-         * An invalid position for a tab.
-         *
-         * @see #getPosition()
-         */
-        public static final int INVALID_POSITION = -1;
-
-        private Object mTag;
-        private Drawable mIcon;
-        private CharSequence mText;
-        private CharSequence mContentDesc;
-        private int mPosition = INVALID_POSITION;
-        private View mCustomView;
-
-        DesignTabLayout mParent;
-        TabView mView;
-
-        Tab() {
-            // Private constructor
-        }
-
-        public void setTextStyle(int flag) {
-            if (mView == null) return;
-            TextView textView = mView.mTextView;
-            if (flag == 1) {
-                textView.setTypeface(textView.getTypeface(), Typeface.BOLD);
-            } else {
-                textView.setTypeface(null, Typeface.NORMAL);
-            }
-        }
-
-        /**
-         * @return This Tab's tag object.
-         */
-        @Nullable
-        public Object getTag() {
-            return mTag;
-        }
-
-        /**
-         * Give this Tab an arbitrary object to hold for later use.
-         *
-         * @param tag Object to store
-         * @return The current instance for call chaining
-         */
-        @NonNull
-        public Tab setTag(@Nullable Object tag) {
-            mTag = tag;
-            return this;
-        }
-
-
-        /**
-         * Returns the custom view used for this tab.
-         *
-         * @see #setCustomView(View)
-         * @see #setCustomView(int)
-         */
-        @Nullable
-        public View getCustomView() {
-            return mCustomView;
-        }
-
-        /**
-         * Set a custom view to be used for this tab.
-         * <p>
-         * If the provided view contains a {@link TextView} with an ID of
-         * {@link android.R.id#text1} then that will be updated with the value given
-         * to {@link #setText(CharSequence)}. Similarly, if this layout contains an
-         * {@link ImageView} with ID {@link android.R.id#icon} then it will be updated with
-         * the value given to {@link #setIcon(Drawable)}.
-         * </p>
-         *
-         * @param view Custom view to be used as a tab.
-         * @return The current instance for call chaining
-         */
-        @NonNull
-        public Tab setCustomView(@Nullable View view) {
-            mCustomView = view;
-            updateView();
-            return this;
-        }
-
-        /**
-         * Set a custom view to be used for this tab.
-         * <p>
-         * If the inflated layout contains a {@link TextView} with an ID of
-         * {@link android.R.id#text1} then that will be updated with the value given
-         * to {@link #setText(CharSequence)}. Similarly, if this layout contains an
-         * {@link ImageView} with ID {@link android.R.id#icon} then it will be updated with
-         * the value given to {@link #setIcon(Drawable)}.
-         * </p>
-         *
-         * @param resId A layout resource to inflate and use as a custom tab view
-         * @return The current instance for call chaining
-         */
-        @NonNull
-        public Tab setCustomView(@LayoutRes int resId) {
-            final LayoutInflater inflater = LayoutInflater.from(mView.getContext());
-            return setCustomView(inflater.inflate(resId, mView, false));
-        }
-
-        /**
-         * Return the icon associated with this tab.
-         *
-         * @return The tab's icon
-         */
-        @Nullable
-        public Drawable getIcon() {
-            return mIcon;
-        }
-
-        /**
-         * Return the current position of this tab in the action bar.
-         *
-         * @return Current position, or {@link #INVALID_POSITION} if this tab is not currently in
-         * the action bar.
-         */
-        public int getPosition() {
-            return mPosition;
-        }
-
-        void setPosition(int position) {
-            mPosition = position;
-        }
-
-        /**
-         * Return the text of this tab.
-         *
-         * @return The tab's text
-         */
-        @Nullable
-        public CharSequence getText() {
-            return mText;
-        }
-
-        /**
-         * Set the icon displayed on this tab.
-         *
-         * @param icon The drawable to use as an icon
-         * @return The current instance for call chaining
-         */
-        @NonNull
-        public Tab setIcon(@Nullable Drawable icon) {
-            mIcon = icon;
-            updateView();
-            return this;
-        }
-
-        /**
-         * Set the icon displayed on this tab.
-         *
-         * @param resId A resource ID referring to the icon that should be displayed
-         * @return The current instance for call chaining
-         */
-        @NonNull
-        public Tab setIcon(@DrawableRes int resId) {
-            if (mParent == null) {
-                throw new IllegalArgumentException("Tab not attached to a RaeTabLayout");
-            }
-            return setIcon(AppCompatResources.getDrawable(mParent.getContext(), resId));
-        }
-
-        /**
-         * Set the text displayed on this tab. Text may be truncated if there is not room to display
-         * the entire string.
-         *
-         * @param text The text to display
-         * @return The current instance for call chaining
-         */
-        @NonNull
-        public Tab setText(@Nullable CharSequence text) {
-            mText = text;
-            updateView();
-            return this;
-        }
-
-        /**
-         * Set the text displayed on this tab. Text may be truncated if there is not room to display
-         * the entire string.
-         *
-         * @param resId A resource ID referring to the text that should be displayed
-         * @return The current instance for call chaining
-         */
-        @NonNull
-        public Tab setText(@StringRes int resId) {
-            if (mParent == null) {
-                throw new IllegalArgumentException("Tab not attached to a RaeTabLayout");
-            }
-            return setText(mParent.getResources().getText(resId));
-        }
-
-        /**
-         * Select this tab. Only valid if the tab has been added to the action bar.
-         */
-        public void select() {
-            if (mParent == null) {
-                throw new IllegalArgumentException("Tab not attached to a RaeTabLayout");
-            }
-            mParent.selectTab(this);
-        }
-
-        /**
-         * Returns true if this tab is currently selected.
-         */
-        public boolean isSelected() {
-            if (mParent == null) {
-                throw new IllegalArgumentException("Tab not attached to a RaeTabLayout");
-            }
-            return mParent.getSelectedTabPosition() == mPosition;
-        }
-
-        /**
-         * Set a description of this tab's content for use in accessibility support. If no content
-         * description is provided the title will be used.
-         *
-         * @param resId A resource ID referring to the description text
-         * @return The current instance for call chaining
-         * @see #setContentDescription(CharSequence)
-         * @see #getContentDescription()
-         */
-        @NonNull
-        public Tab setContentDescription(@StringRes int resId) {
-            if (mParent == null) {
-                throw new IllegalArgumentException("Tab not attached to a RaeTabLayout");
-            }
-            return setContentDescription(mParent.getResources().getText(resId));
-        }
-
-        /**
-         * Set a description of this tab's content for use in accessibility support. If no content
-         * description is provided the title will be used.
-         *
-         * @param contentDesc Description of this tab's content
-         * @return The current instance for call chaining
-         * @see #setContentDescription(int)
-         * @see #getContentDescription()
-         */
-        @NonNull
-        public Tab setContentDescription(@Nullable CharSequence contentDesc) {
-            mContentDesc = contentDesc;
-            updateView();
-            return this;
-        }
-
-        /**
-         * Gets a brief description of this tab's content for use in accessibility support.
-         *
-         * @return Description of this tab's content
-         * @see #setContentDescription(CharSequence)
-         * @see #setContentDescription(int)
-         */
-        @Nullable
-        public CharSequence getContentDescription() {
-            return mContentDesc;
-        }
-
-        void updateView() {
-            if (mView != null) {
-                mView.update();
-            }
-        }
-
-        void reset() {
-            mParent = null;
-            mView = null;
-            mTag = null;
-            mIcon = null;
-            mText = null;
-            mContentDesc = null;
-            mPosition = INVALID_POSITION;
-            mCustomView = null;
-        }
-    }
-
-    class TabView extends LinearLayout implements OnLongClickListener {
-        private Tab mTab;
-        private TextView mTextView;
-        private ImageView mIconView;
-
-        private View mCustomView;
-        private TextView mCustomTextView;
-        private ImageView mCustomIconView;
-
-        private int mDefaultMaxLines = 2;
-
-        public TabView(Context context) {
-            super(context);
-            if (mTabBackgroundResId != 0) {
-                ViewCompat.setBackground(
-                        this, AppCompatResources.getDrawable(context, mTabBackgroundResId));
-            }
-            ViewCompat.setPaddingRelative(this, mTabPaddingStart, mTabPaddingTop,
-                    mTabPaddingEnd, mTabPaddingBottom);
-            setGravity(Gravity.CENTER);
-            setOrientation(VERTICAL);
-            setClickable(true);
-            ViewCompat.setPointerIcon(this,
-                    PointerIconCompat.getSystemIcon(getContext(), PointerIconCompat.TYPE_HAND));
-        }
-
-        @Override
-        public boolean performClick() {
-            final boolean handled = super.performClick();
-
-            if (mTab != null) {
-                if (!handled) {
-                    playSoundEffect(SoundEffectConstants.CLICK);
-                }
-                mTab.select();
-                return true;
-            } else {
-                return handled;
-            }
-        }
-
-        @Override
-        public void setSelected(final boolean selected) {
-            final boolean changed = isSelected() != selected;
-
-            super.setSelected(selected);
-
-            if (changed && selected && Build.VERSION.SDK_INT < 16) {
-                // Pre-JB we need to manually send the TYPE_VIEW_SELECTED event
-                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
-            }
-
-            // Always dispatch this to the child views, regardless of whether the value has
-            // changed
-            if (mTextView != null) {
-                mTextView.setSelected(selected);
-            }
-            if (mIconView != null) {
-                mIconView.setSelected(selected);
-            }
-            if (mCustomView != null) {
-                mCustomView.setSelected(selected);
-            }
-        }
-
-        @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
-        @Override
-        public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-            super.onInitializeAccessibilityEvent(event);
-            // This view masquerades as an action bar tab.
-            event.setClassName(ActionBar.Tab.class.getName());
-        }
-
-        @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
-        @Override
-        public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-            super.onInitializeAccessibilityNodeInfo(info);
-            // This view masquerades as an action bar tab.
-            info.setClassName(ActionBar.Tab.class.getName());
-        }
-
-        @Override
-        public void onMeasure(final int origWidthMeasureSpec, final int origHeightMeasureSpec) {
-            final int specWidthSize = MeasureSpec.getSize(origWidthMeasureSpec);
-            final int specWidthMode = MeasureSpec.getMode(origWidthMeasureSpec);
-            final int maxWidth = getTabMaxWidth();
-
-            final int widthMeasureSpec;
-            final int heightMeasureSpec = origHeightMeasureSpec;
-
-            if (maxWidth > 0 && (specWidthMode == MeasureSpec.UNSPECIFIED
-                    || specWidthSize > maxWidth)) {
-                // If we have a max width and a given spec which is either unspecified or
-                // larger than the max width, update the width spec using the same mode
-                widthMeasureSpec = MeasureSpec.makeMeasureSpec(mTabMaxWidth, MeasureSpec.AT_MOST);
-            } else {
-                // Else, use the original width spec
-                widthMeasureSpec = origWidthMeasureSpec;
-            }
-
-            // Now lets measure
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-            // We need to switch the text size based on whether the text is spanning 2 lines or not
-            if (mTextView != null) {
-                final Resources res = getResources();
-                float textSize = mTabTextSize;
-                int maxLines = mDefaultMaxLines;
-
-                if (mIconView != null && mIconView.getVisibility() == VISIBLE) {
-                    // If the icon view is being displayed, we limit the text to 1 line
-                    maxLines = 1;
-                } else if (mTextView != null && mTextView.getLineCount() > 1) {
-                    // Otherwise when we have text which wraps we reduce the text size
-                    textSize = mTabTextMultiLineSize;
-                }
-
-                final float curTextSize = mTextView.getTextSize();
-                final int curLineCount = mTextView.getLineCount();
-                final int curMaxLines = TextViewCompat.getMaxLines(mTextView);
-
-                if (textSize != curTextSize || (curMaxLines >= 0 && maxLines != curMaxLines)) {
-                    // We've got a new text size and/or max lines...
-                    boolean updateTextView = true;
-
-                    if (mMode == MODE_FIXED && textSize > curTextSize && curLineCount == 1) {
-                        // If we're in fixed mode, going up in text size and currently have 1 line
-                        // then it's very easy to get into an infinite recursion.
-                        // To combat that we check to see if the change in text size
-                        // will cause a line count change. If so, abort the size change and stick
-                        // to the smaller size.
-                        final Layout layout = mTextView.getLayout();
-                        if (layout == null || approximateLineWidth(layout, 0, textSize)
-                                > getMeasuredWidth() - getPaddingLeft() - getPaddingRight()) {
-                            updateTextView = false;
-                        }
-                    }
-
-                    if (updateTextView) {
-                        mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-                        mTextView.setMaxLines(maxLines);
-                        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-                    }
-                }
-            }
-        }
-
-        void setTab(@Nullable final Tab tab) {
-            if (tab != mTab) {
-                mTab = tab;
-                update();
-            }
-        }
-
-        void reset() {
-            setTab(null);
-            setSelected(false);
-        }
-
-        final void update() {
-            final Tab tab = mTab;
-            final View custom = tab != null ? tab.getCustomView() : null;
-            if (custom != null) {
-                final ViewParent customParent = custom.getParent();
-                if (customParent != this) {
-                    if (customParent != null) {
-                        ((ViewGroup) customParent).removeView(custom);
-                    }
-                    addView(custom);
-                }
-                mCustomView = custom;
-                if (mTextView != null) {
-                    mTextView.setVisibility(GONE);
-                }
-                if (mIconView != null) {
-                    mIconView.setVisibility(GONE);
-                    mIconView.setImageDrawable(null);
-                }
-
-                mCustomTextView = (TextView) custom.findViewById(android.R.id.text1);
-                if (mCustomTextView != null) {
-                    mDefaultMaxLines = TextViewCompat.getMaxLines(mCustomTextView);
-                }
-                mCustomIconView = (ImageView) custom.findViewById(android.R.id.icon);
-            } else {
-                // We do not have a custom view. Remove one if it already exists
-                if (mCustomView != null) {
-                    removeView(mCustomView);
-                    mCustomView = null;
-                }
-                mCustomTextView = null;
-                mCustomIconView = null;
-            }
-
-            if (mCustomView == null) {
-                // If there isn't a custom view, we'll us our own in-built layouts
-                if (mIconView == null) {
-                    ImageView iconView = (ImageView) LayoutInflater.from(getContext())
-                            .inflate(R.layout.design_layout_tab_icon, this, false);
-                    addView(iconView, 0);
-                    mIconView = iconView;
-                }
-                if (mTextView == null) {
-                    TextView textView = (TextView) LayoutInflater.from(getContext())
-                            .inflate(R.layout.design_layout_tab_text, this, false);
-                    addView(textView);
-                    mTextView = textView;
-                    mDefaultMaxLines = TextViewCompat.getMaxLines(mTextView);
-                }
-                TextViewCompat.setTextAppearance(mTextView, mTabTextAppearance);
-                if (mTabTextColors != null) {
-                    mTextView.setTextColor(mTabTextColors);
-                }
-                updateTextAndIcon(mTextView, mIconView);
-            } else {
-                // Else, we'll see if there is a TextView or ImageView present and update them
-                if (mCustomTextView != null || mCustomIconView != null) {
-                    updateTextAndIcon(mCustomTextView, mCustomIconView);
-                }
-            }
-
-            // Finally update our selected state
-            setSelected(tab != null && tab.isSelected());
-        }
-
-        private void updateTextAndIcon(@Nullable final TextView textView,
-                                       @Nullable final ImageView iconView) {
-            final Drawable icon = mTab != null ? mTab.getIcon() : null;
-            final CharSequence text = mTab != null ? mTab.getText() : null;
-            final CharSequence contentDesc = mTab != null ? mTab.getContentDescription() : null;
-
-            if (iconView != null) {
-                if (icon != null) {
-                    iconView.setImageDrawable(icon);
-                    iconView.setVisibility(VISIBLE);
-                    setVisibility(VISIBLE);
-                } else {
-                    iconView.setVisibility(GONE);
-                    iconView.setImageDrawable(null);
-                }
-                iconView.setContentDescription(contentDesc);
-            }
-
-            final boolean hasText = !TextUtils.isEmpty(text);
-            if (textView != null) {
-                if (hasText) {
-                    textView.setText(text);
-                    textView.setVisibility(VISIBLE);
-                    setVisibility(VISIBLE);
-                } else {
-                    textView.setVisibility(GONE);
-                    textView.setText(null);
-                }
-                textView.setContentDescription(contentDesc);
-            }
-
-            if (iconView != null) {
-                MarginLayoutParams lp = ((MarginLayoutParams) iconView.getLayoutParams());
-                int bottomMargin = 0;
-                if (hasText && iconView.getVisibility() == VISIBLE) {
-                    // If we're showing both text and icon, add some margin bottom to the icon
-                    bottomMargin = dpToPx(DEFAULT_GAP_TEXT_ICON);
-                }
-                if (bottomMargin != lp.bottomMargin) {
-                    lp.bottomMargin = bottomMargin;
-                    iconView.requestLayout();
-                }
-            }
-
-            if (!hasText && !TextUtils.isEmpty(contentDesc)) {
-                setOnLongClickListener(this);
-            } else {
-                setOnLongClickListener(null);
-                setLongClickable(false);
-            }
-        }
-
-        @Override
-        public boolean onLongClick(final View v) {
-            final int[] screenPos = new int[2];
-            final Rect displayFrame = new Rect();
-            getLocationOnScreen(screenPos);
-            getWindowVisibleDisplayFrame(displayFrame);
-
-            final Context context = getContext();
-            final int width = getWidth();
-            final int height = getHeight();
-            final int midy = screenPos[1] + height / 2;
-            int referenceX = screenPos[0] + width / 2;
-            if (ViewCompat.getLayoutDirection(v) == ViewCompat.LAYOUT_DIRECTION_LTR) {
-                final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
-                referenceX = screenWidth - referenceX; // mirror
-            }
-
-            Toast cheatSheet = Toast.makeText(context, mTab.getContentDescription(),
-                    Toast.LENGTH_SHORT);
-            if (midy < displayFrame.height()) {
-                // Show below the tab view
-                cheatSheet.setGravity(Gravity.TOP | GravityCompat.END, referenceX,
-                        screenPos[1] + height - displayFrame.top);
-            } else {
-                // Show along the bottom center
-                cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
-            }
-            cheatSheet.show();
-            return true;
-        }
-
-        public Tab getTab() {
-            return mTab;
-        }
-
-        /**
-         * Approximates a given lines width with the new provided text size.
-         */
-        private float approximateLineWidth(Layout layout, int line, float textSize) {
-            return layout.getLineWidth(line) * (textSize / layout.getPaint().getTextSize());
-        }
-    }
-
-    private class SlidingTabStrip extends LinearLayout {
-        private int mSelectedIndicatorHeight;
-        private final Paint mSelectedIndicatorPaint;
-
-        int mSelectedPosition = -1;
-        float mSelectionOffset;
-
-        private int mIndicatorLeft = -1;
-        private int mIndicatorRight = -1;
-
-        private ValueAnimator mIndicatorAnimator;
-        private int mIndicatorWidth;
-        private boolean mEnableIndicatorAnimate = true;
-
-        SlidingTabStrip(Context context) {
-            super(context);
-            setWillNotDraw(false);
-            mSelectedIndicatorPaint = new Paint();
-        }
-
-        public void setIndicatorWidth(int indicatorWidth) {
-            mIndicatorWidth = indicatorWidth;
-        }
-
-        void setSelectedIndicatorColor(int color) {
-            if (mSelectedIndicatorPaint.getColor() != color) {
-                mSelectedIndicatorPaint.setColor(color);
-                ViewCompat.postInvalidateOnAnimation(this);
-            }
-        }
-
-        void setSelectedIndicatorHeight(int height) {
-            if (mSelectedIndicatorHeight != height) {
-                mSelectedIndicatorHeight = height;
-                ViewCompat.postInvalidateOnAnimation(this);
-            }
-        }
-
-        boolean childrenNeedLayout() {
-            for (int i = 0, z = getChildCount(); i < z; i++) {
-                final View child = getChildAt(i);
-                if (child.getWidth() <= 0) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        void setIndicatorPositionFromTabPosition(int position, float positionOffset) {
-            if (mIndicatorAnimator != null && mIndicatorAnimator.isRunning()) {
-                mIndicatorAnimator.cancel();
-            }
-
-            mSelectedPosition = position;
-            mSelectionOffset = positionOffset;
-            updateIndicatorPosition();
-        }
-
-        float getIndicatorPosition() {
-            return mSelectedPosition + mSelectionOffset;
-        }
-
-        @Override
-        protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-            if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY) {
-                // HorizontalScrollView will first measure use with UNSPECIFIED, and then with
-                // EXACTLY. Ignore the first call since anything we do will be overwritten anyway
-                return;
-            }
-
-            if (mMode == MODE_FIXED && mTabGravity == GRAVITY_CENTER) {
-                final int count = getChildCount();
-
-                // First we'll find the widest tab
-                int largestTabWidth = 0;
-                for (int i = 0, z = count; i < z; i++) {
-                    View child = getChildAt(i);
-                    if (child.getVisibility() == VISIBLE) {
-                        largestTabWidth = Math.max(largestTabWidth, child.getMeasuredWidth());
-                    }
-                }
-
-                if (largestTabWidth <= 0) {
-                    // If we don't have a largest child yet, skip until the next measure pass
-                    return;
-                }
-
-                final int gutter = dpToPx(FIXED_WRAP_GUTTER_MIN);
-                boolean remeasure = false;
-
-                if (largestTabWidth * count <= getMeasuredWidth() - gutter * 2) {
-                    // If the tabs fit within our width minus gutters, we will set all tabs to have
-                    // the same width
-                    for (int i = 0; i < count; i++) {
-                        final LayoutParams lp =
-                                (LayoutParams) getChildAt(i).getLayoutParams();
-                        if (lp.width != largestTabWidth || lp.weight != 0) {
-                            lp.width = largestTabWidth;
-                            lp.weight = 0;
-                            remeasure = true;
-                        }
-                    }
-                } else {
-                    // If the tabs will wrap to be larger than the width minus gutters, we need
-                    // to switch to GRAVITY_FILL
-                    mTabGravity = GRAVITY_FILL;
-                    updateTabViews(false);
-                    remeasure = true;
-                }
-
-                if (remeasure) {
-                    // Now re-measure after our changes
-                    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-                }
-            }
-        }
-
-        @Override
-        protected void onLayout(boolean changed, int l, int t, int r, int b) {
-            super.onLayout(changed, l, t, r, b);
-
-            if (mIndicatorAnimator != null && mIndicatorAnimator.isRunning()) {
-                // If we're currently running an animation, lets cancel it and start a
-                // new animation with the remaining duration
-                mIndicatorAnimator.cancel();
-                final long duration = mIndicatorAnimator.getDuration();
-                animateIndicatorToPosition(mSelectedPosition,
-                        Math.round((1f - mIndicatorAnimator.getAnimatedFraction()) * duration));
-            } else {
-                // If we've been layed out, update the indicator position
-                updateIndicatorPosition();
-            }
-        }
-
-        private void updateIndicatorPosition() {
-            final View selectedTitle = getChildAt(mSelectedPosition);
-            int left, right;
-
-            if (selectedTitle != null && selectedTitle.getWidth() > 0) {
-                left = selectedTitle.getLeft();
-                right = selectedTitle.getRight();
-
-                if (mSelectionOffset > 0f && mSelectedPosition < getChildCount() - 1) {
-                    // Draw the selection partway between the tabs
-                    View nextTitle = getChildAt(mSelectedPosition + 1);
-                    left = (int) (mSelectionOffset * nextTitle.getLeft() +
-                            (1.0f - mSelectionOffset) * left);
-                    right = (int) (mSelectionOffset * nextTitle.getRight() +
-                            (1.0f - mSelectionOffset) * right);
-                }
-            } else {
-                left = right = -1;
-            }
-
-            setIndicatorPosition(left, right);
-        }
-
-        void setIndicatorPosition(int left, int right) {
-            if (left != mIndicatorLeft || right != mIndicatorRight) {
-                // If the indicator's left/right has changed, invalidate
-                mIndicatorLeft = left;
-                mIndicatorRight = right;
-                ViewCompat.postInvalidateOnAnimation(this);
-            }
-        }
-
-        void setEnableIndicatorAnimate(boolean value) {
-            mEnableIndicatorAnimate = value;
-        }
-
-        void animateIndicatorToPosition(final int position, int duration) {
-            if (mIndicatorAnimator != null && mIndicatorAnimator.isRunning()) {
-                mIndicatorAnimator.cancel();
-            }
-
-            if (!mEnableIndicatorAnimate) return;
-
-            final boolean isRtl = ViewCompat.getLayoutDirection(this)
-                    == ViewCompat.LAYOUT_DIRECTION_RTL;
-
-            final View targetView = getChildAt(position);
-            if (targetView == null) {
-                // If we don't have a view, just update the position now and return
-                updateIndicatorPosition();
-                return;
-            }
-
-            final int targetLeft = targetView.getLeft();
-            final int targetRight = targetView.getRight();
-            final int startLeft;
-            final int startRight;
-
-            if (Math.abs(position - mSelectedPosition) <= 1) {
-                // If the views are adjacent, we'll animate from edge-to-edge
-                startLeft = mIndicatorLeft;
-                startRight = mIndicatorRight;
-            } else {
-                // Else, we'll just grow from the nearest edge
-                final int offset = dpToPx(MOTION_NON_ADJACENT_OFFSET);
-                if (position < mSelectedPosition) {
-                    // We're going end-to-start
-                    if (isRtl) {
-                        startLeft = startRight = targetLeft - offset;
-                    } else {
-                        startLeft = startRight = targetRight + offset;
-                    }
-                } else {
-                    // We're going start-to-end
-                    if (isRtl) {
-                        startLeft = startRight = targetRight + offset;
-                    } else {
-                        startLeft = startRight = targetLeft - offset;
-                    }
-                }
-            }
-
-            if (startLeft != targetLeft || startRight != targetRight) {
-                ValueAnimator animator = this.mIndicatorAnimator = new ValueAnimator();
-                animator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
-                animator.setDuration((long)duration);
-                animator.setFloatValues(new float[]{0.0F, 1.0F});
-                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                    public void onAnimationUpdate(ValueAnimator animator) {
-                        float fraction = animator.getAnimatedFraction();
-                        SlidingTabStrip.this.setIndicatorPosition(AnimationUtils.lerp(startLeft, targetLeft, fraction), AnimationUtils.lerp(startRight, targetRight, fraction));
-                    }
-                });
-                animator.addListener(new AnimatorListenerAdapter() {
-                    public void onAnimationEnd(Animator animator) {
-                        SlidingTabStrip.this.mSelectedPosition = position;
-                        SlidingTabStrip.this.mSelectionOffset = 0.0F;
-                    }
-                });
-                animator.start();
-            }
-        }
-
-        @Override
-        public void draw(Canvas canvas) {
-            super.draw(canvas);
-
-            // 1、计算指示器的宽度
-            int w = mIndicatorRight - mIndicatorLeft;
-            // 2、计算两边的减去的间距
-            int span = (w - mIndicatorWidth) / 2;
-            if (span > 0 && mIndicatorWidth > 0) {
-                mIndicatorLeft = Math.max(0, mIndicatorLeft + span);
-                mIndicatorRight = Math.max(0, mIndicatorRight - span);
-            }
-
-            // Thick colored underline below the current selection
-            if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) {
-                canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,
-                        mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
-            }
-        }
-    }
-
-    private static ColorStateList createColorStateList(int defaultColor, int selectedColor) {
-        final int[][] states = new int[2][];
-        final int[] colors = new int[2];
-        int i = 0;
-
-        states[i] = SELECTED_STATE_SET;
-        colors[i] = selectedColor;
-        i++;
-
-        // Default enabled state
-        states[i] = EMPTY_STATE_SET;
-        colors[i] = defaultColor;
-        i++;
-
-        return new ColorStateList(states, colors);
-    }
-
-    private int getDefaultHeight() {
-        boolean hasIconAndText = false;
-        for (int i = 0, count = mTabs.size(); i < count; i++) {
-            Tab tab = mTabs.get(i);
-            if (tab != null && tab.getIcon() != null && !TextUtils.isEmpty(tab.getText())) {
-                hasIconAndText = true;
-                break;
-            }
-        }
-        return hasIconAndText ? DEFAULT_HEIGHT_WITH_TEXT_ICON : DEFAULT_HEIGHT;
-    }
-
-    private int getTabMinWidth() {
-        if (mRequestedTabMinWidth != INVALID_WIDTH) {
-            // If we have been given a min width, use it
-            return mRequestedTabMinWidth;
-        }
-        // Else, we'll use the default value
-        return mMode == MODE_SCROLLABLE ? mScrollableTabMinWidth : 0;
-    }
-
-    @Override
-    public LayoutParams generateLayoutParams(AttributeSet attrs) {
-        // We don't care about the layout params of any views added to us, since we don't actually
-        // add them. The only view we add is the SlidingTabStrip, which is done manually.
-        // We return the default layout params so that we don't blow up if we're given a TabItem
-        // without android:layout_* values.
-        return generateDefaultLayoutParams();
-    }
-
-    int getTabMaxWidth() {
-        return mTabMaxWidth;
-    }
-
-    /**
-     * A {@link ViewPager.OnPageChangeListener} class which contains the
-     * necessary calls back to the provided {@link DesignTabLayout} so that the tab position is
-     * kept in sync.
-     * <p>
-     * <p>This class stores the provided RaeTabLayout weakly, meaning that you can use
-     * {@link ViewPager#addOnPageChangeListener(ViewPager.OnPageChangeListener)
-     * addOnPageChangeListener(OnPageChangeListener)} without removing the listener and
-     * not cause a leak.
-     */
-    public static class TabLayoutOnPageChangeListener implements ViewPager.OnPageChangeListener {
-        private final WeakReference<DesignTabLayout> mTabLayoutRef;
-        private int mPreviousScrollState;
-        private int mScrollState;
-
-        public TabLayoutOnPageChangeListener(DesignTabLayout tabLayout) {
-            mTabLayoutRef = new WeakReference<>(tabLayout);
-        }
-
-        @Override
-        public void onPageScrollStateChanged(final int state) {
-            mPreviousScrollState = mScrollState;
-            mScrollState = state;
-        }
-
-        @Override
-        public void onPageScrolled(final int position, final float positionOffset,
-                                   final int positionOffsetPixels) {
-            final DesignTabLayout tabLayout = mTabLayoutRef.get();
-            if (tabLayout != null) {
-                // Only update the text selection if we're not settling, or we are settling after
-                // being dragged
-                final boolean updateText = mScrollState != SCROLL_STATE_SETTLING ||
-                        mPreviousScrollState == SCROLL_STATE_DRAGGING;
-                // Update the indicator if we're not settling after being idle. This is caused
-                // from a setCurrentItem() call and will be handled by an animation from
-                // onPageSelected() instead.
-                final boolean updateIndicator = !(mScrollState == SCROLL_STATE_SETTLING
-                        && mPreviousScrollState == SCROLL_STATE_IDLE);
-                tabLayout.setScrollPosition(position, positionOffset, updateText, updateIndicator);
-            }
-        }
-
-        @Override
-        public void onPageSelected(final int position) {
-            final DesignTabLayout tabLayout = mTabLayoutRef.get();
-            if (tabLayout != null && tabLayout.getSelectedTabPosition() != position
-                    && position < tabLayout.getTabCount()) {
-                // Select the tab, only updating the indicator if we're not being dragged/settled
-                // (since onPageScrolled will handle that).
-                final boolean updateIndicator = mScrollState == SCROLL_STATE_IDLE
-                        || (mScrollState == SCROLL_STATE_SETTLING
-                        && mPreviousScrollState == SCROLL_STATE_IDLE);
-                tabLayout.selectTab(tabLayout.getTabAt(position), updateIndicator);
-            }
-        }
-
-        void reset() {
-            mPreviousScrollState = mScrollState = SCROLL_STATE_IDLE;
-        }
-    }
-
-    /**
-     * A {@link OnTabSelectedListener} class which contains the necessary calls back
-     * to the provided {@link ViewPager} so that the tab position is kept in sync.
-     */
-    public static class ViewPagerOnTabSelectedListener implements OnTabSelectedListener {
-        private final ViewPager mViewPager;
-
-        public ViewPagerOnTabSelectedListener(ViewPager viewPager) {
-            mViewPager = viewPager;
-        }
-
-        @Override
-        public void onTabSelected(Tab tab) {
-            mViewPager.setCurrentItem(tab.getPosition());
-        }
-
-        @Override
-        public void onTabUnselected(Tab tab) {
-            // No-op
-        }
-
-        @Override
-        public void onTabReselected(Tab tab) {
-            // No-op
-        }
-    }
-
-    private class PagerAdapterObserver extends DataSetObserver {
-        PagerAdapterObserver() {
-        }
-
-        @Override
-        public void onChanged() {
-            populateFromPagerAdapter();
-        }
-
-        @Override
-        public void onInvalidated() {
-            populateFromPagerAdapter();
-        }
-    }
-
-    private class AdapterChangeListener implements ViewPager.OnAdapterChangeListener {
-        private boolean mAutoRefresh;
-
-        AdapterChangeListener() {
-        }
-
-        @Override
-        public void onAdapterChanged(@NonNull ViewPager viewPager,
-                                     @Nullable PagerAdapter oldAdapter, @Nullable PagerAdapter newAdapter) {
-            if (mViewPager == viewPager) {
-                setPagerAdapter(newAdapter, mAutoRefresh);
-            }
-        }
-
-        void setAutoRefresh(boolean autoRefresh) {
-            mAutoRefresh = autoRefresh;
-        }
-    }
-}
diff --git a/app/src/main/java/com/rae/cnblogs/AppRoute.java b/app/src/main/java/com/rae/cnblogs/AppRoute.java
deleted file mode 100644
index 4aba925..0000000
--- a/app/src/main/java/com/rae/cnblogs/AppRoute.java
+++ /dev/null
@@ -1,457 +0,0 @@
-package com.rae.cnblogs;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.support.annotation.NonNull;
-import android.text.TextUtils;
-
-import com.rae.cnblogs.activity.AboutMeActivity;
-import com.rae.cnblogs.activity.BlogContentActivity;
-import com.rae.cnblogs.activity.BloggerActivity;
-import com.rae.cnblogs.activity.CategoryActivity;
-import com.rae.cnblogs.activity.CommentActivity;
-import com.rae.cnblogs.activity.FavoritesActivity;
-import com.rae.cnblogs.activity.FeedbackActivity;
-import com.rae.cnblogs.activity.FontSettingActivity;
-import com.rae.cnblogs.activity.FriendsActivity;
-import com.rae.cnblogs.activity.ImageSelectionActivity;
-import com.rae.cnblogs.activity.LoginActivity;
-import com.rae.cnblogs.activity.MainActivity;
-import com.rae.cnblogs.activity.MomentAtMeActivity;
-import com.rae.cnblogs.activity.MomentDetailActivity;
-import com.rae.cnblogs.activity.MomentMessageActivity;
-import com.rae.cnblogs.activity.PostMomentActivity;
-import com.rae.cnblogs.activity.SearchActivity;
-import com.rae.cnblogs.activity.SettingActivity;
-import com.rae.cnblogs.activity.SystemMessageActivity;
-import com.rae.cnblogs.activity.WebActivity;
-import com.rae.cnblogs.activity.WebLoginActivity;
-import com.rae.cnblogs.image.ImagePreviewActivity;
-import com.rae.cnblogs.sdk.bean.BlogBean;
-import com.rae.cnblogs.sdk.bean.BlogType;
-import com.rae.cnblogs.sdk.bean.MomentBean;
-import com.rae.cnblogs.sdk.model.MomentMetaData;
-
-import java.util.ArrayList;
-
-/**
- * 路由
- * Created by ChenRui on 2016/12/6 23:49.
- */
-public final class AppRoute {
-
-    // WEB 登录
-    public static final int REQ_CODE_WEB_LOGIN = 100;
-    /*朋友界面 - 来自粉丝*/
-    public static final int ACTIVITY_FRIENDS_TYPE_FANS = 1;
-    /*朋友界面 - 来自关注*/
-    private static final int ACTIVITY_FRIENDS_TYPE_FOLLOW = 2;
-    // 分类
-    public static final int REQ_CODE_CATEGORY = 102;
-    // 收藏
-    public static final int REQ_CODE_FAVORITES = 103;
-    // 博主
-    public static final int REQ_CODE_BLOGGER = 104;
-    // 发布闪存
-    public static final int REQ_POST_MOMENT = 105;
-    // 图片选择
-    public static final int REQ_IMAGE_SELECTION = 106;
-    // 图片选择
-    public static final int REQ_CODE_IMAGE_SELECTED = 107;
-
-    private static void startActivity(Context context, Intent intent) {
-        context.startActivity(intent);
-    }
-
-    private static void startActivity(Context context, Class<?> cls) {
-        startActivity(context, new Intent(context, cls));
-    }
-
-    private static void startActivityForResult(Activity context, Intent intent, int requestCode) {
-        context.startActivityForResult(intent, requestCode);
-    }
-
-    private static void startActivityForResult(Activity context, Class<?> cls, int requestCode) {
-        startActivityForResult(context, new Intent(context, cls), requestCode);
-    }
-
-    /**
-     * 博客正文界面
-     *
-     * @param blogId 博客ID
-     * @param type   博客类型
-     */
-    public static void jumpToBlogContent(Context context, String blogId, BlogType type) {
-        Intent intent = new Intent(context, BlogContentActivity.class);
-        intent.putExtra("blogId", blogId);
-        intent.putExtra("type", type.getTypeName());
-        startActivity(context, intent);
-    }
-
-    /**
-     * 博客正文界面
-     *
-     * @param blog 博客实体
-     * @param type 博客类型
-     */
-    public static void jumpToBlogContent(Context context, BlogBean blog, BlogType type) {
-        if (blog == null) return;
-        Intent intent = new Intent(context, BlogContentActivity.class);
-        // 不传递摘要和正文这些过大的数据。进去博文正文之后再从数据库拉取。已经在BlogBean里面处理大数据问题
-        intent.putExtra("blog", blog);
-        intent.putExtra("blogId", blog.getBlogId());
-        intent.putExtra("type", type.getTypeName());
-        startActivity(context, intent);
-    }
-
-    /**
-     * 网页
-     *
-     * @param url 路径
-     */
-    public static void jumpToWeb(Context context, String url) {
-        Intent intent = new Intent(context, WebActivity.class);
-        intent.setData(Uri.parse(url));
-        startActivity(context, intent);
-    }
-
-    /**
-     * 用户反馈
-     */
-    public static void jumpToFeedback(Context context) {
-        Intent intent = new Intent(context, FeedbackActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        startActivity(context, intent);
-    }
-
-
-    /**
-     * 网页，新线程
-     *
-     * @param url 路径
-     */
-    public static void jumpToWebNewTask(Context context, String url) {
-        Intent intent = new Intent(context, WebActivity.class);
-        intent.setData(Uri.parse(url));
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        startActivity(context, intent);
-    }
-
-    /**
-     * 主界面
-     */
-    public static void jumpToMain(Context context) {
-        Intent intent = new Intent(context, MainActivity.class);
-        intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
-        intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
-        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        context.startActivity(intent);
-    }
-
-
-    /**
-     * 登录
-     */
-    public static void jumpToLogin(Context context) {
-        startActivity(context, LoginActivity.class);
-    }
-
-    /**
-     * 登录
-     */
-    public static void jumpToLogin(Activity context, int req) {
-        startActivityForResult(context, LoginActivity.class, req);
-    }
-
-    /**
-     * 登录，有回调结果
-     */
-    public static void jumpToWebLogin(Activity context) {
-        startActivityForResult(context, WebLoginActivity.class, REQ_CODE_WEB_LOGIN);
-    }
-
-    /**
-     * 粉丝
-     *
-     * @param bloggerName 博主昵称
-     * @param blogApp     博主ID
-     */
-    public static void jumpToFans(Context context, String bloggerName, String blogApp) {
-        jumpToFriends(context, ACTIVITY_FRIENDS_TYPE_FANS, bloggerName, blogApp);
-    }
-
-    /**
-     * 关注
-     *
-     * @param bloggerName 博主昵称
-     * @param blogApp     博主ID
-     */
-    public static void jumpToFollow(Context context, String bloggerName, String blogApp) {
-        jumpToFriends(context, ACTIVITY_FRIENDS_TYPE_FOLLOW, bloggerName, blogApp);
-    }
-
-    /**
-     * 跳转到朋友界面
-     *
-     * @param type        来源类型，参考该类{@link #ACTIVITY_FRIENDS_TYPE_FANS}
-     * @param bloggerName 博主昵称
-     * @param blogApp     博主ID
-     */
-    private static void jumpToFriends(Context context, int type, String bloggerName, String blogApp) {
-        Intent intent = new Intent(context, FriendsActivity.class);
-        intent.putExtra("blogApp", blogApp);
-        intent.putExtra("bloggerName", bloggerName);
-        intent.putExtra("fromType", type);
-        startActivity(context, intent);
-    }
-
-    /**
-     * 图片大图预览
-     *
-     * @param images   图片数组
-     * @param position 跳转到低几张图片，默认传0
-     */
-    public static void jumpToImagePreview(Activity context, @NonNull ArrayList<String> images, int position, ArrayList<String> selectedImages, int maxCount) {
-        Intent intent = new Intent(context, ImagePreviewActivity.class);
-        intent.putStringArrayListExtra("images", images);
-        if (selectedImages != null) {
-            intent.putStringArrayListExtra("selectedImages", selectedImages);
-        }
-        intent.putExtra("position", position);
-        intent.putExtra("maxCount", maxCount);
-        startActivityForResult(context, intent, REQ_CODE_IMAGE_SELECTED);
-    }
-
-    /**
-     * 图片大图预览
-     *
-     * @param images   图片数组
-     * @param position 跳转到低几张图片，默认传0
-     */
-    public static void jumpToImagePreview(Activity context, @NonNull ArrayList<String> images, int position) {
-        jumpToImagePreview(context, images, position, null, 0);
-    }
-
-    /**
-     * 图片大图预览
-     *
-     * @param images   图片数组
-     * @param position 跳转到低几张图片，默认传0
-     */
-    public static void jumpToImagePreview(Context context, @NonNull ArrayList<String> images, int position) {
-        Intent intent = new Intent(context, ImagePreviewActivity.class);
-        intent.putStringArrayListExtra("images", images);
-        intent.putExtra("position", position);
-        startActivity(context, intent);
-    }
-
-
-    /**
-     * 图片查看
-     *
-     * @param imgUrl 图片路径
-     */
-    public static void jumpToImagePreview(Activity context, @NonNull String imgUrl) {
-        ArrayList<String> data = new ArrayList<>();
-        data.add(imgUrl);
-        jumpToImagePreview(context, data, 0);
-    }
-
-
-    /**
-     * 图片查看
-     *
-     * @param imgUrl 图片路径
-     */
-    public static void jumpToImagePreview(Context context, @NonNull String imgUrl) {
-        ArrayList<String> data = new ArrayList<>();
-        data.add(imgUrl);
-        jumpToImagePreview(context, data, 0);
-    }
-
-    /**
-     * 博主界面
-     *
-     * @param blogApp 博客APP
-     */
-    public static void jumpToBlogger(Context context, String blogApp) {
-        if (TextUtils.isEmpty(blogApp)) {
-            AppUI.toast(context, "博主信息为空！");
-            return;
-        }
-        Intent intent = new Intent(context, BloggerActivity.class);
-        intent.putExtra("blogApp", blogApp);
-        startActivity(context, intent);
-    }
-
-    /**
-     * 博主界面
-     *
-     * @param blogApp 博客APP
-     */
-    public static void jumpToBlogger(Activity context, String blogApp) {
-        if (TextUtils.isEmpty(blogApp)) {
-            AppUI.toast(context, "博主信息为空！");
-            return;
-        }
-        Intent intent = new Intent(context, BloggerActivity.class);
-        intent.putExtra("blogApp", blogApp);
-        startActivityForResult(context, intent, REQ_CODE_BLOGGER);
-    }
-
-
-    /**
-     * 分类管理
-     */
-    public static void jumpToCategoryForResult(Activity context) {
-        Intent intent = new Intent(context, CategoryActivity.class);
-        startActivityForResult(context, intent, REQ_CODE_CATEGORY);
-    }
-
-    /**
-     * 我的收藏
-     */
-    public static void jumpToFavorites(Activity context) {
-        startActivityForResult(context, FavoritesActivity.class, REQ_CODE_FAVORITES);
-    }
-
-    /**
-     * 设置
-     */
-    public static void jumpToSetting(Context context) {
-        startActivity(context, SettingActivity.class);
-    }
-
-    /**
-     * 搜索
-     */
-    public static void jumpToSearch(Context context) {
-        startActivity(context, SearchActivity.class);
-    }
-
-    /**
-     * 搜索-新闻
-     */
-    public static void jumpToSearchNews(Context context) {
-        Intent intent = new Intent(context, SearchActivity.class);
-        intent.putExtra("position", 2);
-        startActivity(context, intent);
-    }
-
-    /**
-     * 搜索-知识库
-     */
-    public static void jumpToSearchKb(Context context) {
-        Intent intent = new Intent(context, SearchActivity.class);
-        intent.putExtra("position", 3);
-        startActivity(context, intent);
-    }
-
-    /**
-     * 搜索-博主
-     */
-    public static void jumpToSearchBlogger(Context context, String blogApp, String nickName) {
-        Intent intent = new Intent(context, SearchActivity.class);
-        intent.putExtra("blogApp", blogApp);
-        intent.putExtra("nickName", nickName);
-        startActivity(context, intent);
-    }
-
-    /**
-     * 系统消息
-     */
-    public static void jumpToSystemMessage(Context context) {
-        startActivity(context, SystemMessageActivity.class);
-    }
-
-    /**
-     * 字体设置
-     */
-    public static void jumpToFontSetting(Context context) {
-        startActivity(context, FontSettingActivity.class);
-    }
-
-    /**
-     * 博客评论
-     */
-    public static void jumpToComment(Context context, BlogBean blog, BlogType type) {
-        Intent intent = new Intent(context, CommentActivity.class);
-        intent.putExtra("type", type.getTypeName());
-        intent.putExtra("blog", blog);
-        startActivity(context, intent);
-    }
-
-    /**
-     * 发布闪存
-     */
-    public static void jumpToPostMoment(Activity context) {
-        Intent intent = new Intent(context, PostMomentActivity.class);
-        startActivityForResult(context, intent, REQ_POST_MOMENT);
-    }
-
-    /**
-     * 发布闪存
-     */
-    public static void jumpToPostMoment(Activity context, MomentMetaData data) {
-        Intent intent = new Intent(context, PostMomentActivity.class);
-        intent.putExtra(Intent.EXTRA_TEXT, data);
-        startActivityForResult(context, intent, REQ_POST_MOMENT);
-    }
-
-    /**
-     * 闪存详情
-     */
-    public static void jumpToMomentDetail(Context context, MomentBean data) {
-        Intent intent = new Intent(context, MomentDetailActivity.class);
-        intent.putExtra("data", data);
-        startActivity(context, intent);
-    }
-
-    /**
-     * 闪存详情
-     */
-    public static void jumpToMomentDetail(Context context, String userAlias, String ingId) {
-        Intent intent = new Intent(context, MomentDetailActivity.class);
-        intent.putExtra("ingId", ingId);
-        intent.putExtra("userId", userAlias);
-        startActivity(context, intent);
-    }
-
-
-    /**
-     * 闪存消息
-     */
-    public static void jumpToMomentMessage(Context context) {
-        Intent intent = new Intent(context, MomentMessageActivity.class);
-        startActivity(context, intent);
-    }
-
-    /**
-     * 提到我的闪存
-     */
-    public static void jumpToMomentAtMe(Context context) {
-        Intent intent = new Intent(context, MomentAtMeActivity.class);
-        startActivity(context, intent);
-    }
-
-    /**
-     * 跳转到图片选择
-     */
-    public static void jumpToImageSelection(Activity context, ArrayList<String> selectedImages) {
-        Intent intent = new Intent(context, ImageSelectionActivity.class);
-        if (selectedImages != null)
-            intent.putStringArrayListExtra("selectedImages", selectedImages);
-        startActivityForResult(context, intent, REQ_IMAGE_SELECTION);
-    }
-
-    /**
-     * 关于我们
-     */
-    public static void jumpToAboutMe(Context context) {
-        startActivity(context, AboutMeActivity.class);
-    }
-
-}
diff --git a/app/src/main/java/com/rae/cnblogs/CnblogsApplication.java b/app/src/main/java/com/rae/cnblogs/CnblogsApplication.java
index 59bbb67..637e9be 100644
--- a/app/src/main/java/com/rae/cnblogs/CnblogsApplication.java
+++ b/app/src/main/java/com/rae/cnblogs/CnblogsApplication.java
@@ -1,80 +1,12 @@
 package com.rae.cnblogs;
 
-import android.app.Application;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.support.multidex.MultiDex;
-import android.text.TextUtils;
-
-import com.avos.avoscloud.AVOSCloud;
-import com.avos.avoscloud.feedback.FeedbackThread;
-import com.meituan.android.walle.WalleChannelReader;
-import com.rae.cnblogs.sdk.UserProvider;
-import com.rae.cnblogs.sdk.bean.UserInfoBean;
-import com.rae.cnblogs.sdk.config.CnblogSdkConfig;
 import com.rae.cnblogs.sdk.db.DbFactory;
-import com.rae.swift.session.SessionManager;
-import com.tencent.bugly.Bugly;
-import com.tencent.tinker.loader.app.TinkerApplication;
-import com.tencent.tinker.loader.shareutil.ShareConstants;
-import com.umeng.commonsdk.UMConfigure;
-import com.umeng.socialize.PlatformConfig;
-import com.umeng.socialize.UMShareAPI;
-
-import skin.support.SkinCompatManager;
-import skin.support.design.app.SkinMaterialViewInflater;
 
 /**
  * 集成热更新的应用程序
  * Created by ChenRui on 2017/7/25 0025 19:15.
  */
-public class CnblogsApplication extends TinkerApplication {
-
-    public CnblogsApplication() {
-        super(ShareConstants.TINKER_ENABLE_ALL, "com.rae.cnblogs.CnblogsApplicationProxy");
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-
-        // 级别较高的初始化操作
-        if (BuildConfig.DEBUG) {
-//            if (!LeakCanary.isInAnalyzerProcess(this)) {
-//                LeakCanary.install(this);
-//            }
-        } else {
-            // 正式环境
-            Bugly.init(getApplication(), BuildConfig.BUGLY_APP_ID, false);
-        }
-
-        DbFactory.init(this);
-        initUmengConfig();
-
-        // LeanCloud用户反馈初始化，要在主线程
-        AVOSCloud.initialize(getApplication(), BuildConfig.LEAN_CLOUD_APP_ID, BuildConfig.LEAN_CLOUD_APP_KEY);
-        FeedbackThread.getInstance();
-
-        // 加载皮肤
-        SkinCompatManager.init(getApplicationContext());
-        SkinActivityLifecycleCompat.init(this);
-        SkinCompatManager.getInstance()
-                .addHookInflater(new ThemeCompat.CnblogsThemeHookInflater())
-                .addInflater(new CnblogsLayoutInflater())
-                .addInflater(new SkinMaterialViewInflater());
-
-        SkinCompatManager.getInstance().loadSkin();
-
-        // 一些要求不高的初始化操作放到线程中去操作
-        new Thread(new Runnable() {
-            @Override
-            public void run() {
-                UserProvider.init(getApplication());
-                SessionManager.initWithConfig(new SessionManager.ConfigBuilder().context(getApplication()).userClass(UserInfoBean.class).build());
-
-            }
-        }).start();
-    }
+public class CnblogsApplication extends CnblogsTinkerApplication {
 
     /**
      * 清除应用
@@ -86,55 +18,4 @@ public void clearCache() {
         DbFactory.getInstance().clearCache();
         new AppDataManager(this).clearCache();
     }
-
-    /**
-     * 友盟
-     */
-    private void initUmengConfig() {
-        // 初始化友盟
-        UMConfigure.setLogEnabled(BuildConfig.DEBUG);
-        UMConfigure.init(this, BuildConfig.UMENG_APPKEY, getChannel(), UMConfigure.DEVICE_TYPE_PHONE, null);
-        UMShareAPI.get(getApplication());
-        PlatformConfig.setWeixin(AppConstant.WECHAT_APP_ID, AppConstant.WECHAT_APP_SECRET);
-        PlatformConfig.setSinaWeibo(AppConstant.WEIBO_APP_ID, AppConstant.WEIBO_APP_SECRET, "http://www.raeblog.com/cnblogs/index.php/share/weibo/redirect");
-        PlatformConfig.setQQZone(AppConstant.QQ_APP_ID, AppConstant.QQ_APP_SECRET);
-        CnblogSdkConfig.APP_CHANNEL = getChannel();
-    }
-
-    public Application getApplication() {
-        return this;
-    }
-
-
-    /**
-     * 获取渠道包
-     */
-    public String getChannel() {
-        String channel = WalleChannelReader.getChannel(this.getApplicationContext());
-        return TextUtils.isEmpty(channel) ? CnblogSdkConfig.APP_CHANNEL : channel;
-    }
-
-    @Override
-    protected void attachBaseContext(Context base) {
-        MultiDex.install(base); // 解决Tinker存在的BUG，一定要在这之前初始化
-        super.attachBaseContext(base);
-    }
-
-    public int getVersionCode() {
-        try {
-            return getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_META_DATA).versionCode;
-        } catch (PackageManager.NameNotFoundException e) {
-            e.printStackTrace();
-        }
-        return 1;
-    }
-
-    public String getVersionName() {
-        try {
-            return getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_META_DATA).versionName;
-        } catch (PackageManager.NameNotFoundException e) {
-            e.printStackTrace();
-        }
-        return "1.0.0";
-    }
 }
diff --git a/app/src/main/java/com/rae/cnblogs/CnblogsApplicationProxy.java b/app/src/main/java/com/rae/cnblogs/CnblogsApplicationProxy.java
index 93b6871..26eee61 100644
--- a/app/src/main/java/com/rae/cnblogs/CnblogsApplicationProxy.java
+++ b/app/src/main/java/com/rae/cnblogs/CnblogsApplicationProxy.java
@@ -6,13 +6,25 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
-import android.support.multidex.MultiDex;
 
+import com.avos.avoscloud.AVOSCloud;
+import com.avos.avoscloud.feedback.FeedbackThread;
+import com.rae.cnblogs.sdk.UserProvider;
+import com.rae.cnblogs.sdk.bean.UserInfoBean;
+import com.rae.cnblogs.sdk.config.CnblogAppConfig;
+import com.rae.cnblogs.sdk.db.DbFactory;
+import com.rae.swift.session.SessionManager;
+import com.tencent.bugly.Bugly;
 import com.tencent.bugly.beta.Beta;
 import com.tencent.bugly.beta.tinker.TinkerApplicationLike;
+import com.umeng.socialize.PlatformConfig;
+import com.umeng.socialize.UMShareAPI;
+
+import skin.support.SkinCompatManager;
+import skin.support.design.app.SkinMaterialViewInflater;
 
 /**
- * 应用程序
+ * 热更新应用程序代理类，实例化在：{@link CnblogsTinkerApplication} 中操作
  * Created by ChenRui on 2016/12/1 21:35.
  */
 public class CnblogsApplicationProxy extends TinkerApplicationLike {
@@ -25,6 +37,58 @@ public CnblogsApplicationProxy(Application application, int tinkerFlags, boolean
     @Override
     public void onCreate() {
         super.onCreate();
+        // 级别较高的初始化操作
+        if (!BuildConfig.DEBUG) {
+            // 正式环境
+            Bugly.init(getApplication(), BuildConfig.BUGLY_APP_ID, false);
+        }
+
+        // 初始化路由
+        AppRoute.init(getApplication(), BuildConfig.DEBUG);
+        DbFactory.init(getApplication());
+
+
+        // LeanCloud用户反馈初始化，要在主线程
+        AVOSCloud.initialize(getApplication(), BuildConfig.LEAN_CLOUD_APP_ID, BuildConfig.LEAN_CLOUD_APP_KEY);
+        FeedbackThread.getInstance();
+
+        // 加载皮肤
+        SkinCompatManager.init(getApplication());
+        SkinActivityLifecycleCompat.init(getApplication());
+        SkinCompatManager.getInstance()
+                .addHookInflater(new ThemeCompat.CnblogsThemeHookInflater())
+                .addInflater(new CnblogsLayoutInflater())
+                .addInflater(new SkinMaterialViewInflater());
+
+        SkinCompatManager.getInstance().loadSkin();
+
+        // 一些要求不高的初始化操作放到线程中去操作
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                initUmengConfig();
+                UserProvider.init(getApplication());
+                SessionManager.initWithConfig(new SessionManager.ConfigBuilder().context(getApplication()).userClass(UserInfoBean.class).build());
+
+            }
+        }).start();
+    }
+
+
+    /**
+     * 友盟
+     */
+    private void initUmengConfig() {
+        // 初始化友盟
+        String channel = ApplicationCompat.getChannel(getApplication());
+        CnblogAppConfig.APP_CHANNEL = channel;
+        AppMobclickAgent.init(getApplication(), BuildConfig.UMENG_APPKEY, channel);
+
+        // 初始化友盟分享
+        UMShareAPI.get(getApplication());
+        PlatformConfig.setWeixin(AppConstant.WECHAT_APP_ID, AppConstant.WECHAT_APP_SECRET);
+        PlatformConfig.setSinaWeibo(AppConstant.WEIBO_APP_ID, AppConstant.WEIBO_APP_SECRET, "https://raedev.io/cnblogs/share/weibo/redirect");
+        PlatformConfig.setQQZone(AppConstant.QQ_APP_ID, AppConstant.QQ_APP_SECRET);
     }
 
     @Override
@@ -39,9 +103,7 @@ public void onTrimMemory(int level) {
     @Override
     public void onBaseContextAttached(Context base) {
         super.onBaseContextAttached(base);
-
         // 安装tinker
-        // TinkerManager.installTinker(this); 替换成下面Bugly提供的方法
         Beta.installTinker(this);
     }
 }
diff --git a/app/src/main/java/com/rae/cnblogs/activity/AboutMeActivity.java b/app/src/main/java/com/rae/cnblogs/activity/AboutMeActivity.java
index a9ab15b..f1f9240 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/AboutMeActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/AboutMeActivity.java
@@ -3,13 +3,16 @@
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.R;
 
 /**
  * 关于我们
  * Created by ChenRui on 2018/2/9 0009 17:21.
  */
-public class AboutMeActivity extends BaseActivity {
+@Route(path = AppRoute.PATH_ABOUT_ME)
+public class AboutMeActivity extends BasicActivity {
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
diff --git a/app/src/main/java/com/rae/cnblogs/activity/BaseFragmentActivity.java b/app/src/main/java/com/rae/cnblogs/activity/BasicFragmentActivity.java
similarity index 90%
rename from app/src/main/java/com/rae/cnblogs/activity/BaseFragmentActivity.java
rename to app/src/main/java/com/rae/cnblogs/activity/BasicFragmentActivity.java
index 0f33a88..2c2b25b 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/BaseFragmentActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/BasicFragmentActivity.java
@@ -10,7 +10,7 @@
  * 单个Fragment页面
  * Created by ChenRui on 2017/8/29 0029 22:59.
  */
-public abstract class BaseFragmentActivity extends SwipeBackBaseActivity {
+public abstract class BasicFragmentActivity extends SwipeBackBasicActivity {
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
diff --git a/app/src/main/java/com/rae/cnblogs/activity/BlogContentActivity.java b/app/src/main/java/com/rae/cnblogs/activity/BlogContentActivity.java
index 81081e2..d27e6f3 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/BlogContentActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/BlogContentActivity.java
@@ -9,6 +9,7 @@
 import android.view.View;
 import android.widget.TextView;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
 import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.AppUI;
 import com.rae.cnblogs.R;
@@ -48,7 +49,8 @@
  * 博文查看
  * Created by ChenRui on 2016/12/6 21:38.
  */
-public class BlogContentActivity extends SwipeBackBaseActivity implements EditCommentDialog.OnEditCommentListener, IBlogCommentPresenter.IBlogCommentView {
+@Route(path = AppRoute.PATH_BLOG_CONTENT)
+public class BlogContentActivity extends SwipeBackBasicActivity implements EditCommentDialog.OnEditCommentListener, IBlogCommentPresenter.IBlogCommentView {
 
 //    @BindView(R.id.tool_bar)
 //    Toolbar mToolbar;
@@ -108,7 +110,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
         mShareDialog = new BlogShareDialog(this) {
             @Override
             protected void onViewSourceClick() {
-                AppRoute.jumpToWeb(getContext(), mBlog.getUrl());
+                AppRoute.routeToWeb(getContext(), mBlog.getUrl());
             }
         };
 
@@ -254,7 +256,7 @@ public void onActionMenuMoreClick() {
     // 查看评论
     @OnClick(R.id.layout_content_comment)
     public void onCommentClick() {
-        AppRoute.jumpToComment(this, mBlog, mBlogType);
+        AppRoute.routeToComment(this, mBlog, mBlogType);
 //        mCommentLayout.toggleSmoothScroll();
     }
 
@@ -274,7 +276,7 @@ public void onEditCommentClick() {
 //            return;
 //        }
 //
-//        AppRoute.jumpToBlogger(this, mBlog.getBlogApp());
+//        AppRoute.routeToBlogger(this, mBlog.getBlogApp());
 //    }
 
     @OnClick(R.id.tool_bar)
diff --git a/app/src/main/java/com/rae/cnblogs/activity/BloggerActivity.java b/app/src/main/java/com/rae/cnblogs/activity/BloggerActivity.java
index e38a9e7..a29043f 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/BloggerActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/BloggerActivity.java
@@ -4,7 +4,7 @@
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.design.widget.AppBarLayout;
-import android.support.design.widget.DesignTabLayout;
+import android.support.design.widget.RaeTabLayout;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.view.ViewPager;
 import android.support.v7.widget.Toolbar;
@@ -16,6 +16,7 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
 import com.bumptech.glide.load.DataSource;
 import com.bumptech.glide.load.MultiTransformation;
 import com.bumptech.glide.load.engine.GlideException;
@@ -55,7 +56,8 @@
  * blogger info
  * Created by ChenRui on 2017/2/9 0009 10:02.
  */
-public class BloggerActivity extends SwipeBackBaseActivity implements IBloggerPresenter.IBloggerView, DesignTabLayout.OnTabSelectedListener {
+@Route(path = AppRoute.PATH_BLOGGER)
+public class BloggerActivity extends SwipeBackBasicActivity implements IBloggerPresenter.IBloggerView, RaeTabLayout.OnTabSelectedListener {
 
     @BindView(R.id.img_background)
     ImageView mBackgroundView;
@@ -85,7 +87,7 @@ public class BloggerActivity extends SwipeBackBaseActivity implements IBloggerPr
     ViewPager mViewPager;
 
     @BindView(R.id.tab_category)
-    DesignTabLayout mTabLayout;
+    RaeTabLayout mTabLayout;
 
     @BindView(R.id.layout_account_fans)
     View mFansLayout;
@@ -333,7 +335,7 @@ public void onFollowSuccess() {
     @Override
     public void onNotLogin() {
 //        AppUI.toastInCenter(getContext(), getString(R.string.blogger_need_login));
-        AppRoute.jumpToLogin(this);
+        AppRoute.routeToLogin(this);
         finish();
     }
 
@@ -350,7 +352,7 @@ public void onSearchClick() {
         if (mUserInfo == null) {
             return;
         }
-        AppRoute.jumpToSearchBlogger(this, mBlogApp, mUserInfo.getDisplayName());
+        AppRoute.routeToSearchBlogger(this, mBlogApp, mUserInfo.getDisplayName());
     }
 
     /**
@@ -359,7 +361,7 @@ public void onSearchClick() {
     @OnClick(R.id.layout_account_fans)
     public void onFansClick() {
         if (mUserInfo == null) return;
-        AppRoute.jumpToFans(this.getContext(), mUserInfo.getDisplayName(), mUserInfo.getBlogApp());
+        AppRoute.routeToFans(this.getContext(), mUserInfo.getDisplayName(), mUserInfo.getBlogApp());
     }
 
 
@@ -369,7 +371,7 @@ public void onFansClick() {
     @OnClick(R.id.layout_account_follow)
     public void onFollowClick() {
         if (mUserInfo == null) return;
-        AppRoute.jumpToFollow(this.getContext(), mUserInfo.getDisplayName(), mUserInfo.getBlogApp());
+        AppRoute.routeToFollow(this.getContext(), mUserInfo.getDisplayName(), mUserInfo.getBlogApp());
     }
 
     @OnClick(R.id.btn_blogger_follow)
@@ -396,21 +398,21 @@ public void onAvatarClick(View view) {
         } else {
             images.add(mUserInfo.getAvatar());
         }
-        AppRoute.jumpToImagePreview(this, images, 0);
+        AppRoute.routeToImagePreview(this, images, 0);
     }
 
     @Override
-    public void onTabSelected(DesignTabLayout.Tab tab) {
+    public void onTabSelected(RaeTabLayout.Tab tab) {
 
     }
 
     @Override
-    public void onTabUnselected(DesignTabLayout.Tab tab) {
+    public void onTabUnselected(RaeTabLayout.Tab tab) {
 
     }
 
     @Override
-    public void onTabReselected(DesignTabLayout.Tab tab) {
+    public void onTabReselected(RaeTabLayout.Tab tab) {
         takeScrollToTop(tab.getPosition());
     }
 
diff --git a/app/src/main/java/com/rae/cnblogs/activity/BookmarkActivity.java b/app/src/main/java/com/rae/cnblogs/activity/BookmarkActivity.java
index 681537a..93350c8 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/BookmarkActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/BookmarkActivity.java
@@ -9,7 +9,7 @@
 // * 我的收藏
 // * Created by ChenRui on 2017/3/16 23:33.
 // */
-//public class BookmarkActivity extends BaseActivity {
+//public class BookmarkActivity extends BasicActivity {
 //
 //    @Override
 //    protected void onCreate(@Nullable Bundle savedInstanceState) {
diff --git a/app/src/main/java/com/rae/cnblogs/activity/CategoryActivity.java b/app/src/main/java/com/rae/cnblogs/activity/CategoryActivity.java
index 40b35ef..a016523 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/CategoryActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/CategoryActivity.java
@@ -3,6 +3,8 @@
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.R;
 import com.rae.cnblogs.fragment.CategoriesFragment;
 import com.rae.cnblogs.sdk.bean.CategoryBean;
@@ -13,7 +15,8 @@
  * 分类编辑
  * Created by ChenRui on 2017/7/19 0019 10:10.
  */
-public class CategoryActivity extends BaseActivity {
+@Route(path = AppRoute.PATH_CATEGORY)
+public class CategoryActivity extends BasicActivity {
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
diff --git a/app/src/main/java/com/rae/cnblogs/activity/CommentActivity.java b/app/src/main/java/com/rae/cnblogs/activity/CommentActivity.java
index 417150e..36fb590 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/CommentActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/CommentActivity.java
@@ -4,6 +4,8 @@
 import android.support.annotation.Nullable;
 import android.view.View;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.R;
 import com.rae.cnblogs.fragment.BlogCommentFragment;
 import com.rae.cnblogs.sdk.bean.BlogBean;
@@ -13,7 +15,8 @@
  * 评论
  * Created by ChenRui on 2017/10/21 0021 2:54.
  */
-public class CommentActivity extends SwipeBackBaseActivity {
+@Route(path = AppRoute.PATH_BLOG_COMMENT)
+public class CommentActivity extends SwipeBackBasicActivity {
 
     private BlogCommentFragment fragment;
 
diff --git a/app/src/main/java/com/rae/cnblogs/activity/FavoritesActivity.java b/app/src/main/java/com/rae/cnblogs/activity/FavoritesActivity.java
index 777497b..75ddd86 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/FavoritesActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/FavoritesActivity.java
@@ -3,6 +3,7 @@
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
 import com.jcodecraeer.xrecyclerview.XRecyclerView;
 import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.AppUI;
@@ -34,7 +35,8 @@
  * 我的收藏
  * Created by ChenRui on 2017/7/14 0014 14:58.
  */
-public class FavoritesActivity extends SwipeBackBaseActivity {
+@Route(path = AppRoute.PATH_FAVORITE)
+public class FavoritesActivity extends SwipeBackBasicActivity {
 
     @BindView(R.id.placeholder)
     PlaceholderView mPlaceholder;
@@ -85,7 +87,7 @@ public void onMenuItemClick(MenuDialog dialog, MenuDialogItem item) {
         mAdapter.setOnItemClickListener(new BaseItemAdapter.onItemClickListener<BookmarksBean>() {
             @Override
             public void onItemClick(BookmarksBean item) {
-                AppRoute.jumpToWeb(getContext(), item.getLinkUrl());
+                AppRoute.routeToWeb(getContext(), item.getLinkUrl());
             }
         });
 
diff --git a/app/src/main/java/com/rae/cnblogs/activity/FeedbackActivity.java b/app/src/main/java/com/rae/cnblogs/activity/FeedbackActivity.java
index dcad294..c6af8e3 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/FeedbackActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/FeedbackActivity.java
@@ -35,6 +35,7 @@
 import android.widget.ListView;
 import android.widget.TextView;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
 import com.avos.avoscloud.AVException;
 import com.avos.avoscloud.AVOSCloud;
 import com.avos.avoscloud.AVPersistenceUtils;
@@ -70,7 +71,8 @@
 import io.reactivex.schedulers.Schedulers;
 import skin.support.content.res.SkinCompatResources;
 
-public class FeedbackActivity extends SwipeBackBaseActivity {
+@Route(path = AppRoute.PATH_FEEDBACK)
+public class FeedbackActivity extends SwipeBackBasicActivity {
 
     FeedbackAgent agent;
     ListView feedbackListView;
@@ -603,7 +605,7 @@ public void onClick(View v) {
                             return;
                         }
 
-                        AppRoute.jumpToImagePreview(getContext(), file.getPath());
+                        AppRoute.routeToImagePreview(getContext(), file.getPath());
                     }
                 };
                 Bitmap attachmentCache = cache.getImage(comment.getAttachment().getUrl());
diff --git a/app/src/main/java/com/rae/cnblogs/activity/FontSettingActivity.java b/app/src/main/java/com/rae/cnblogs/activity/FontSettingActivity.java
index b084efc..db5c2cd 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/FontSettingActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/FontSettingActivity.java
@@ -6,6 +6,8 @@
 import android.widget.SeekBar;
 import android.widget.TextView;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.R;
 import com.rae.cnblogs.message.FontChangedEvent;
 import com.rae.cnblogs.widget.RaeSeekBar;
@@ -18,7 +20,8 @@
  * 字体设置
  * Created by ChenRui on 2017/10/12 0012 23:30.
  */
-public class FontSettingActivity extends SwipeBackBaseActivity {
+@Route(path = AppRoute.PATH_FONT_SETTING)
+public class FontSettingActivity extends SwipeBackBasicActivity {
     @BindView(R.id.tv_message)
     TextView mMessage;
     @BindView(R.id.seekBar)
diff --git a/app/src/main/java/com/rae/cnblogs/activity/FriendsActivity.java b/app/src/main/java/com/rae/cnblogs/activity/FriendsActivity.java
index dc72278..d6e92e8 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/FriendsActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/FriendsActivity.java
@@ -4,6 +4,7 @@
 import android.support.annotation.Nullable;
 import android.text.TextUtils;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
 import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.AppUI;
 import com.rae.cnblogs.R;
@@ -13,7 +14,8 @@
  * 粉丝和关注
  * Created by ChenRui on 2017/2/23 00:41.
  */
-public class FriendsActivity extends SwipeBackBaseActivity {
+@Route(path = AppRoute.PATH_FRIENDS)
+public class FriendsActivity extends SwipeBackBasicActivity {
 
     private int mFromType;
 
diff --git a/app/src/main/java/com/rae/cnblogs/activity/ImageSelectionActivity.java b/app/src/main/java/com/rae/cnblogs/activity/ImageSelectionActivity.java
index 3a2fd31..9a1f768 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/ImageSelectionActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/ImageSelectionActivity.java
@@ -25,6 +25,7 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
 import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.AppUI;
 import com.rae.cnblogs.GlideApp;
@@ -45,7 +46,8 @@
  * 图片选择
  * Created by ChenRui on 2017/10/27 0027 14:04.
  */
-public class ImageSelectionActivity extends BaseActivity {
+@Route(path = AppRoute.PATH_IMAGE_SELECTION)
+public class ImageSelectionActivity extends BasicActivity {
     @BindView(R.id.recycler_view)
     RecyclerView mRecyclerView;
     @BindView(R.id.recycler_view_selected)
@@ -119,7 +121,7 @@ public boolean onLongClick(View v) {
             public void onClick(View v) {
                 int position = (int) v.getTag();
                 ArrayList<String> images = (ArrayList<String>) mSelectedAdapter.getDataList();
-                AppRoute.jumpToImagePreview((Activity) v.getContext(), images, position, images, mMaxCount);
+                AppRoute.routeToImagePreview((Activity) v.getContext(), images, position, images, mMaxCount);
             }
         });
         mAdapter.setOnClickListener(new View.OnClickListener() {
@@ -128,7 +130,7 @@ public void onClick(View v) {
                 int position = (int) v.getTag();
                 ArrayList<String> images = (ArrayList<String>) mAdapter.getDataList();
                 ArrayList<String> selectedImages = (ArrayList<String>) mAdapter.getSelectedList();
-                AppRoute.jumpToImagePreview((Activity) v.getContext(), images, position, selectedImages, mMaxCount);
+                AppRoute.routeToImagePreview((Activity) v.getContext(), images, position, selectedImages, mMaxCount);
             }
         });
         start();
diff --git a/app/src/main/java/com/rae/cnblogs/activity/LauncherActivity.java b/app/src/main/java/com/rae/cnblogs/activity/LauncherActivity.java
index dcf0c11..d8bb4f1 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/LauncherActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/LauncherActivity.java
@@ -4,7 +4,6 @@
 import android.support.annotation.Nullable;
 import android.text.Html;
 import android.text.TextUtils;
-import android.util.Log;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -24,7 +23,7 @@
  * 启动页
  * Created by ChenRui on 2016/12/22 22:08.
  */
-public class LauncherActivity extends BaseActivity implements ILauncherPresenter.ILauncherView {
+public class LauncherActivity extends BasicActivity implements ILauncherPresenter.ILauncherView {
 
     @BindView(R.id.img_launcher_display)
     ImageView mDisplayView;
@@ -41,9 +40,13 @@ public class LauncherActivity extends BaseActivity implements ILauncherPresenter
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        // 非栈顶的时候，点击首页图标不跳转到这里
+        if (!isTaskRoot()) {
+            finish();
+            return;
+        }
         setContentView(R.layout.activity_launcher);
         mLauncherPresenter = CnblogsPresenterFactory.getLauncherPresenter(this, this);
-        Log.w("rae", "渠道为：" + getChannel());
     }
 
     @Override
@@ -87,15 +90,15 @@ public void onJumpToWeb(String url) {
         if (TextUtils.isEmpty(url)) {
             return;
         }
-        AppRoute.jumpToMain(this);
-        AppRoute.jumpToWeb(this, url);
+        AppRoute.routeToMain(this);
+        AppRoute.routeToWeb(this, url);
         finish();
     }
 
     @Override
     public void onJumpToBlog(String id) {
-        AppRoute.jumpToMain(this);
-        AppRoute.jumpToBlogContent(this, id, BlogType.BLOG);
+        AppRoute.routeToMain(this);
+        AppRoute.routeToBlogContent(this, id, BlogType.BLOG);
         finish();
     }
 
@@ -114,7 +117,7 @@ public void onAdClick() {
 
     @Override
     public void onJumpToMain() {
-        AppRoute.jumpToMain(this);
+        AppRoute.routeToMain(this);
         finish();
     }
 
diff --git a/app/src/main/java/com/rae/cnblogs/activity/LoginActivity.java b/app/src/main/java/com/rae/cnblogs/activity/LoginActivity.java
index c65275b..cf8a3db 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/LoginActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/LoginActivity.java
@@ -10,6 +10,7 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
 import com.rae.cnblogs.AppMobclickAgent;
 import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.AppUI;
@@ -34,7 +35,8 @@
  * 登录
  * Created by ChenRui on 2017/1/19 0019 9:59.
  */
-public class LoginActivity extends BaseActivity
+@Route(path = AppRoute.PATH_LOGIN)
+public class LoginActivity extends BasicActivity
         implements ILoginPresenter.ILoginView {
 
     @BindView(com.rae.cnblogs.R.id.ll_login_container)
@@ -191,7 +193,7 @@ public void finish() {
     @OnClick(R.id.tv_forget_password)
     public void onForgetPasswordClick() {
         AppMobclickAgent.onClickEvent(this, "ForgetPassword");
-        AppRoute.jumpToWeb(this, getString(R.string.forget_password_url));
+        AppRoute.routeToWeb(this, getString(R.string.forget_password_url));
     }
 
     /**
@@ -200,7 +202,7 @@ public void onForgetPasswordClick() {
     @OnClick(R.id.btn_reg)
     public void onRegClick() {
         AppMobclickAgent.onClickEvent(this, "Reg");
-        AppRoute.jumpToWeb(this, getString(R.string.reg_url));
+        AppRoute.routeToWeb(this, getString(R.string.reg_url));
     }
 
 
@@ -231,7 +233,7 @@ public void onDismiss(DialogInterface dialog) {
 
     private void preformLogin() {
 //        performUserInfo();
-        AppRoute.jumpToWebLogin(this);
+        AppRoute.routeToWebLogin(this);
 //        mLoginPresenter.login();
 //        removeAccountTextListener(mAccountTextWatcher);
 //        mLoginButton.setEnabled(false);
@@ -261,7 +263,7 @@ public void onLoginContractClick() {
             @Override
             public void onClick(IAppDialog dialog, int buttonType) {
                 dialog.dismiss();
-                AppRoute.jumpToWeb(getContext(), getContext().getString(R.string.url_login_help));
+                AppRoute.routeToWeb(getContext(), getContext().getString(R.string.url_login_help));
             }
         });
         dialog.showCloseButton();
diff --git a/app/src/main/java/com/rae/cnblogs/activity/MainActivity.java b/app/src/main/java/com/rae/cnblogs/activity/MainActivity.java
index 50bf7f8..f25d0e6 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/MainActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/MainActivity.java
@@ -8,7 +8,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.support.annotation.NonNull;
-import android.support.design.widget.TabLayout;
+import android.support.design.widget.RaeTabLayout;
 import android.support.v4.app.ActivityCompat;
 import android.support.v4.app.Fragment;
 import android.support.v4.view.ViewPager;
@@ -17,12 +17,15 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
 import com.rae.cnblogs.AppMobclickAgent;
+import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.AppUI;
 import com.rae.cnblogs.BuildConfig;
 import com.rae.cnblogs.R;
 import com.rae.cnblogs.RaeImageLoader;
 import com.rae.cnblogs.RxObservable;
+import com.rae.cnblogs.ThemeChangedEvent;
 import com.rae.cnblogs.ThemeCompat;
 import com.rae.cnblogs.dialog.IAppDialog;
 import com.rae.cnblogs.dialog.IAppDialogClickListener;
@@ -33,7 +36,6 @@
 import com.rae.cnblogs.fragment.MineFragment;
 import com.rae.cnblogs.fragment.SNSFragment;
 import com.rae.cnblogs.message.TabEvent;
-import com.rae.cnblogs.message.ThemeChangedEvent;
 import com.rae.cnblogs.sdk.ApiDefaultObserver;
 import com.rae.cnblogs.sdk.CnblogsApiFactory;
 import com.rae.cnblogs.sdk.bean.VersionInfo;
@@ -47,13 +49,14 @@
 
 import butterknife.BindView;
 
-public class MainActivity extends BaseActivity {
+@Route(path = AppRoute.PATH_APP_HOME)
+public class MainActivity extends BasicActivity {
 
     @BindView(R.id.vp_main)
     ViewPager mViewPager;
 
     @BindView(R.id.tab_main)
-    TabLayout mTabLayout;
+    RaeTabLayout mTabLayout;
 
     private RaeFragmentAdapter mFragmentAdapter;
 
@@ -95,24 +98,26 @@ public void onServiceDisconnected(ComponentName name) {
         mViewPager.setAdapter(mFragmentAdapter);
 
         // 联动
-        mTabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));
-        mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
+        mTabLayout.addOnTabSelectedListener(new RaeTabLayout.ViewPagerOnTabSelectedListener(mViewPager));
+        mViewPager.addOnPageChangeListener(new RaeTabLayout.TabLayoutOnPageChangeListener(mTabLayout));
 
-        mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+        mTabLayout.addOnTabSelectedListener(new RaeTabLayout.OnTabSelectedListener() {
             @Override
-            public void onTabSelected(TabLayout.Tab tab) {
+            public void onTabSelected(RaeTabLayout.Tab tab) {
                 // 统计分类点击
                 int position = tab.getPosition();
                 CharSequence title = mFragmentAdapter.getPageTitle(position);
-                AppMobclickAgent.onCategoryEvent(getContext(), title.toString());
+                if (title != null) {
+                    AppMobclickAgent.onCategoryEvent(getContext(), title.toString());
+                }
             }
 
             @Override
-            public void onTabUnselected(TabLayout.Tab tab) {
+            public void onTabUnselected(RaeTabLayout.Tab tab) {
             }
 
             @Override
-            public void onTabReselected(TabLayout.Tab tab) {
+            public void onTabReselected(RaeTabLayout.Tab tab) {
                 EventBus.getDefault().post(new TabEvent(tab.getPosition()));
             }
         });
@@ -189,10 +194,10 @@ public void onClick(IAppDialog dialog, int buttonType) {
     }
 
     private void addTab(int resId, int iconId, Fragment fragment) {
-        TabLayout.Tab tab = mTabLayout.newTab();
-        View tabView = getLayoutInflater().inflate(R.layout.tab_view, null);
-        TextView v = (TextView) tabView.findViewById(R.id.tv_tab_view);
-        ImageView iconView = (ImageView) tabView.findViewById(R.id.img_tab_icon);
+        RaeTabLayout.Tab tab = mTabLayout.newTab();
+        View tabView = View.inflate(this, R.layout.tab_view, null);
+        TextView v = tabView.findViewById(R.id.tv_tab_view);
+        ImageView iconView = tabView.findViewById(R.id.img_tab_icon);
         v.setText(resId);
         iconView.setImageResource(iconId);
 //        v.setCompoundDrawablesWithIntrinsicBounds(0, iconId, 0, 0);
diff --git a/app/src/main/java/com/rae/cnblogs/activity/MomentAtMeActivity.java b/app/src/main/java/com/rae/cnblogs/activity/MomentAtMeActivity.java
index 0bc1028..5428770 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/MomentAtMeActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/MomentAtMeActivity.java
@@ -3,6 +3,8 @@
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.R;
 import com.rae.cnblogs.fragment.MomentFragment;
 import com.rae.cnblogs.sdk.ApiDefaultObserver;
@@ -17,7 +19,8 @@
  * 提到我的闪存
  * Created by ChenRui on 2017/11/8 0008 10:19.
  */
-public class MomentAtMeActivity extends SwipeBackBaseActivity {
+@Route(path = AppRoute.PATH_MOMENT_MENTION)
+public class MomentAtMeActivity extends SwipeBackBasicActivity {
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
diff --git a/app/src/main/java/com/rae/cnblogs/activity/MomentDetailActivity.java b/app/src/main/java/com/rae/cnblogs/activity/MomentDetailActivity.java
index 505cf0e..31093fa 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/MomentDetailActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/MomentDetailActivity.java
@@ -5,6 +5,7 @@
 import android.text.TextUtils;
 import android.view.View;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
 import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.R;
 import com.rae.cnblogs.RxObservable;
@@ -24,7 +25,8 @@
  * 详情
  * Created by ChenRui on 2017/11/2 0002 15:01.
  */
-public class MomentDetailActivity extends SwipeBackBaseActivity {
+@Route(path = AppRoute.PATH_MOMENT_DETAIL)
+public class MomentDetailActivity extends SwipeBackBasicActivity {
 
     private MomentBean mMomentBean;
 
@@ -56,7 +58,7 @@ public void onClick(View v) {
                 if (UserProvider.getInstance().isLogin()) {
                     loadMomentDetail();
                 } else {
-                    AppRoute.jumpToLogin(v.getContext());
+                    AppRoute.routeToLogin(v.getContext());
                 }
             }
         });
diff --git a/app/src/main/java/com/rae/cnblogs/activity/MomentMessageActivity.java b/app/src/main/java/com/rae/cnblogs/activity/MomentMessageActivity.java
index cc30fdb..42779ff 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/MomentMessageActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/MomentMessageActivity.java
@@ -3,6 +3,7 @@
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
 import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.R;
 import com.rae.cnblogs.fragment.MomentMessageFragment;
@@ -13,7 +14,8 @@
  * 闪存消息
  * Created by ChenRui on 2017/11/6 0006 14:21.
  */
-public class MomentMessageActivity extends SwipeBackBaseActivity {
+@Route(path = AppRoute.PATH_MOMENT_MESSAGE)
+public class MomentMessageActivity extends SwipeBackBasicActivity {
     private MomentMessageFragment mFragment;
 
     @Override
diff --git a/app/src/main/java/com/rae/cnblogs/activity/PostMomentActivity.java b/app/src/main/java/com/rae/cnblogs/activity/PostMomentActivity.java
index ea32402..74422ac 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/PostMomentActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/PostMomentActivity.java
@@ -16,6 +16,7 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
 import com.rae.cnblogs.AppMobclickAgent;
 import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.AppUI;
@@ -44,7 +45,8 @@
  * 发布闪存
  * Created by ChenRui on 2017/10/27 0027 14:04.
  */
-public class PostMomentActivity extends BaseActivity implements IPostMomentContract.View {
+@Route(path = AppRoute.PATH_MOMENT_POST)
+public class PostMomentActivity extends BasicActivity implements IPostMomentContract.View {
     @BindView(R.id.et_content)
     EditText mContentView;
     @BindView(R.id.tv_post)
@@ -98,7 +100,7 @@ public void onClick(View v) {
         mAdapter.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                AppRoute.jumpToImagePreview(PostMomentActivity.this, mAdapter.getImageSelectedList(), (Integer) v.getTag(), mAdapter.getImageSelectedList(), mAdapter.getMaxCount());
+                AppRoute.routeToImagePreview(PostMomentActivity.this, mAdapter.getImageSelectedList(), (Integer) v.getTag(), mAdapter.getImageSelectedList(), mAdapter.getMaxCount());
             }
         });
 
@@ -208,7 +210,7 @@ public void onLoadBlogOpenStatus(Boolean value) {
 
     @OnClick(R.id.tv_blog_apply)
     public void onBlogApplyClick() {
-        AppRoute.jumpToWeb(this, getString(R.string.url_blog_apply));
+        AppRoute.routeToWeb(this, getString(R.string.url_blog_apply));
     }
 
     @OnClick(R.id.tv_post)
diff --git a/app/src/main/java/com/rae/cnblogs/activity/SearchActivity.java b/app/src/main/java/com/rae/cnblogs/activity/SearchActivity.java
index cb26d2f..1a6ba08 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/SearchActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/SearchActivity.java
@@ -8,7 +8,9 @@
 import android.support.v4.content.ContextCompat;
 import android.view.View;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
 import com.rae.cnblogs.AppMobclickAgent;
+import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.R;
 import com.rae.cnblogs.ThemeCompat;
 import com.rae.cnblogs.fragment.SearchFragment;
@@ -17,7 +19,8 @@
  * 搜索界面
  * Created by ChenRui on 2017/8/29 0029 22:59.
  */
-public class SearchActivity extends BaseFragmentActivity {
+@Route(path = AppRoute.PATH_SEARCH)
+public class SearchActivity extends BasicFragmentActivity {
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
diff --git a/app/src/main/java/com/rae/cnblogs/activity/SettingActivity.java b/app/src/main/java/com/rae/cnblogs/activity/SettingActivity.java
index eaa7dcb..0ee7ac4 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/SettingActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/SettingActivity.java
@@ -12,6 +12,7 @@
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
 import com.rae.cnblogs.AppMobclickAgent;
 import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.AppUI;
@@ -33,7 +34,6 @@
 import com.rae.cnblogs.sdk.bean.VersionInfo;
 import com.rae.cnblogs.widget.ImageLoadingView;
 import com.tencent.bugly.beta.tinker.TinkerManager;
-import com.umeng.analytics.MobclickAgent;
 
 import org.greenrobot.eventbus.EventBus;
 
@@ -46,7 +46,8 @@
  * 设置
  * Created by ChenRui on 2017/7/24 0024 1:18.
  */
-public class SettingActivity extends SwipeBackBaseActivity {
+@Route(path = AppRoute.PATH_SETTING)
+public class SettingActivity extends SwipeBackBasicActivity {
 
     @BindView(R.id.img_clear_cache)
     ImageLoadingView mClearCacheView;
@@ -141,7 +142,7 @@ public void onLogoutClick() {
             @Override
             public void onClick(IAppDialog dialog, int buttonType) {
                 dialog.dismiss();
-                MobclickAgent.onProfileSignOff();
+                AppMobclickAgent.onProfileSignOff();
                 UserProvider.getInstance().logout();
                 EventBus.getDefault().post(new UserInfoEvent());
                 finish();
@@ -173,7 +174,7 @@ public void onShareClick() {
     @OnClick(R.id.ll_github)
     public void onOpenSourceClick() {
         AppMobclickAgent.onClickEvent(getContext(), "OpenSource");
-        AppRoute.jumpToWeb(this.getContext(), getString(R.string.github_url));
+        AppRoute.routeToWeb(this.getContext(), getString(R.string.github_url));
     }
 
     /**
@@ -182,7 +183,7 @@ public void onOpenSourceClick() {
     @OnClick(R.id.ll_open_source)
     public void onOpenSourceLicenseClick() {
         AppMobclickAgent.onClickEvent(getContext(), "OpenSourceLicense");
-        AppRoute.jumpToWeb(this.getContext(), getString(R.string.url_license));
+        AppRoute.routeToWeb(this.getContext(), getString(R.string.url_license));
     }
 
     /**
@@ -191,7 +192,7 @@ public void onOpenSourceLicenseClick() {
     @OnClick(R.id.ll_beta_version)
     public void onBetaVersionLicenseClick() {
         AppMobclickAgent.onClickEvent(getContext(), "BetaVersion");
-        AppRoute.jumpToWeb(this.getContext(), getString(R.string.url_beta_version));
+        AppRoute.routeToWeb(this.getContext(), getString(R.string.url_beta_version));
     }
 
     /**
@@ -200,7 +201,7 @@ public void onBetaVersionLicenseClick() {
     @OnClick(R.id.ll_help_center)
     public void onHelpCenterClick() {
         AppMobclickAgent.onClickEvent(getContext(), "HelpCenter");
-        AppRoute.jumpToWeb(this.getContext(), getString(R.string.url_help_center));
+        AppRoute.routeToWeb(this.getContext(), getString(R.string.url_help_center));
     }
 
     /**
@@ -249,8 +250,9 @@ public void onPraisesClick() {
 
     @OnClick(R.id.ll_font_setting)
     public void onFontSettingClick() {
-        AppRoute.jumpToFontSetting(this);
+        AppRoute.routeToFontSetting(this);
     }
+
     @OnClick(R.id.ll_about_me)
     public void onAboutMeClick() {
         AppRoute.jumpToAboutMe(this);
diff --git a/app/src/main/java/com/rae/cnblogs/activity/SystemMessageActivity.java b/app/src/main/java/com/rae/cnblogs/activity/SystemMessageActivity.java
index e6fe99d..c8a3004 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/SystemMessageActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/SystemMessageActivity.java
@@ -3,6 +3,7 @@
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
 import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.R;
 import com.rae.cnblogs.RxObservable;
@@ -24,7 +25,8 @@
  * 系统消息
  * Created by ChenRui on 2017/9/5 0005 15:39.
  */
-public class SystemMessageActivity extends SwipeBackBaseActivity {
+@Route(path = AppRoute.PATH_SYSTEM_MESSAGE)
+public class SystemMessageActivity extends SwipeBackBasicActivity {
 
     @BindView(R.id.recycler_view)
     RaeRecyclerView mRecyclerView;
@@ -48,7 +50,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
         mAdapter.setOnItemClickListener(new BaseItemAdapter.onItemClickListener<SystemMessageBean>() {
             @Override
             public void onItemClick(SystemMessageBean item) {
-                AppRoute.jumpToWeb(getContext(), item.getUrl());
+                AppRoute.routeToWeb(getContext(), item.getUrl());
             }
         });
 
diff --git a/app/src/main/java/com/rae/cnblogs/activity/TestActivity.java b/app/src/main/java/com/rae/cnblogs/activity/TestActivity.java
index 86b8832..66ed791 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/TestActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/TestActivity.java
@@ -23,7 +23,7 @@
  * TEST ACTIVITY
  * Created by ChenRui on 2016/12/3 18:06.
  */
-public class TestActivity extends BaseActivity {
+public class TestActivity extends BasicActivity {
 
     @BindView(R.id.tv_message)
     TextView mMsgView;
diff --git a/app/src/main/java/com/rae/cnblogs/activity/WebActivity.java b/app/src/main/java/com/rae/cnblogs/activity/WebActivity.java
index 5b55b68..fded920 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/WebActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/WebActivity.java
@@ -8,6 +8,8 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.AppUI;
 import com.rae.cnblogs.R;
 import com.rae.cnblogs.ThemeCompat;
@@ -21,7 +23,8 @@
  * 网页
  * Created by ChenRui on 2017/1/25 0025 9:32.
  */
-public class WebActivity extends BaseActivity {
+@Route(path = AppRoute.PATH_WEB)
+public class WebActivity extends BasicActivity {
 
     @BindView(R.id.tool_bar)
     Toolbar mToolbar;
@@ -88,7 +91,8 @@ protected WebViewFragment getWebViewFragment(String url) {
 
     @Nullable
     protected String getUrl() {
-        if (getIntent().getData() == null) return null;
+        // data 为空从intent 获取
+        if (getIntent().getData() == null) return getIntent().getStringExtra("url");
         return getIntent().getData().toString();
     }
 
diff --git a/app/src/main/java/com/rae/cnblogs/activity/WebLoginActivity.java b/app/src/main/java/com/rae/cnblogs/activity/WebLoginActivity.java
index 8f112c4..38623c8 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/WebLoginActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/activity/WebLoginActivity.java
@@ -5,6 +5,8 @@
 import android.support.annotation.Nullable;
 import android.view.View;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.fragment.WebLoginFragment;
 import com.rae.cnblogs.fragment.WebViewFragment;
 
@@ -12,6 +14,7 @@
  * 网页版登录
  * Created by ChenRui on 2017/2/3 0003 10:47.
  */
+@Route(path = AppRoute.PATH_WEB_LOGIN)
 public class WebLoginActivity extends WebActivity {
 
 
diff --git a/app/src/main/java/com/rae/cnblogs/adapter/BlogListItemAdapter.java b/app/src/main/java/com/rae/cnblogs/adapter/BlogListItemAdapter.java
index 85dce2a..1e672c3 100644
--- a/app/src/main/java/com/rae/cnblogs/adapter/BlogListItemAdapter.java
+++ b/app/src/main/java/com/rae/cnblogs/adapter/BlogListItemAdapter.java
@@ -114,9 +114,9 @@ public void onBindViewHolder(RecyclerView.ViewHolder vh, int position, final Blo
                     public void onClick(View v) {
                         // 判断是否已经登录
                         if (UserProvider.getInstance().isLogin()) {
-                            AppRoute.jumpToBlogger(v.getContext(), m.getBlogApp());
+                            AppRoute.routeToBlogger(v.getContext(), m.getBlogApp());
                         } else {
-                            AppRoute.jumpToLogin(v.getContext());
+                            AppRoute.routeToLogin(v.getContext());
                         }
                     }
                 });
@@ -222,7 +222,7 @@ public void run() {
                 notifyDataSetChanged();
             }
         }, 1000);
-        AppRoute.jumpToBlogContent(view.getContext(), blog, mBlogType);
+        AppRoute.routeToBlogContent(view.getContext(), blog, mBlogType);
     }
 
 }
diff --git a/app/src/main/java/com/rae/cnblogs/adapter/MomentAdapter.java b/app/src/main/java/com/rae/cnblogs/adapter/MomentAdapter.java
index 7970612..0f8f119 100644
--- a/app/src/main/java/com/rae/cnblogs/adapter/MomentAdapter.java
+++ b/app/src/main/java/com/rae/cnblogs/adapter/MomentAdapter.java
@@ -148,7 +148,7 @@ public ItemImageClickListener(String url) {
 
         @Override
         public void onClick(View v) {
-            AppRoute.jumpToImagePreview(v.getContext(), mUrl);
+            AppRoute.routeToImagePreview(v.getContext(), mUrl);
         }
     }
 
diff --git a/app/src/main/java/com/rae/cnblogs/adapter/MomentImageAdapter.java b/app/src/main/java/com/rae/cnblogs/adapter/MomentImageAdapter.java
index a63ea43..f1933cb 100644
--- a/app/src/main/java/com/rae/cnblogs/adapter/MomentImageAdapter.java
+++ b/app/src/main/java/com/rae/cnblogs/adapter/MomentImageAdapter.java
@@ -42,6 +42,6 @@ public void onBindViewHolder(final MomentImageHolder holder, int position, Strin
 
     @Override
     public void onClick(View v) {
-        AppRoute.jumpToImagePreview(v.getContext(), (ArrayList<String>) mDataList, mDataList.indexOf(v.getContentDescription().toString()));
+        AppRoute.routeToImagePreview(v.getContext(), (ArrayList<String>) mDataList, mDataList.indexOf(v.getContentDescription().toString()));
     }
 }
diff --git a/app/src/main/java/com/rae/cnblogs/adapter/MomentMessageAdapter.java b/app/src/main/java/com/rae/cnblogs/adapter/MomentMessageAdapter.java
index b56ff00..c4ea2bd 100644
--- a/app/src/main/java/com/rae/cnblogs/adapter/MomentMessageAdapter.java
+++ b/app/src/main/java/com/rae/cnblogs/adapter/MomentMessageAdapter.java
@@ -86,7 +86,7 @@ public OnSourceClickListener(String userAlias, String ingId) {
         @Override
         public void onClick(View v) {
             if (!TextUtils.isEmpty(mIngId)) {
-                AppRoute.jumpToMomentDetail(v.getContext(), mUserAlias, mIngId);
+                AppRoute.routeToMomentDetail(v.getContext(), mUserAlias, mIngId);
             }
         }
     }
@@ -102,7 +102,7 @@ public OnBloggerClickListener(String blogApp) {
         @Override
         public void onClick(View v) {
             if (!TextUtils.isEmpty(mBlogApp)) {
-                AppRoute.jumpToBlogger(v.getContext(), mBlogApp);
+                AppRoute.routeToBlogger(v.getContext(), mBlogApp);
             }
         }
     }
diff --git a/app/src/main/java/com/rae/cnblogs/dialog/impl/EditCommentDialog.java b/app/src/main/java/com/rae/cnblogs/dialog/impl/EditCommentDialog.java
index 54a0d16..d7f855c 100644
--- a/app/src/main/java/com/rae/cnblogs/dialog/impl/EditCommentDialog.java
+++ b/app/src/main/java/com/rae/cnblogs/dialog/impl/EditCommentDialog.java
@@ -84,7 +84,7 @@ protected void onWindowLayout(Window window, WindowManager.LayoutParams attr) {
     public void show() {
         // 没有登录
         if (!UserProvider.getInstance().isLogin()) {
-            AppRoute.jumpToLogin(getContext());
+            AppRoute.routeToLogin(getContext());
             return;
         }
         super.show();
diff --git a/app/src/main/java/com/rae/cnblogs/dialog/impl/ShareDialog.java b/app/src/main/java/com/rae/cnblogs/dialog/impl/ShareDialog.java
index 8a84838..3f6a701 100644
--- a/app/src/main/java/com/rae/cnblogs/dialog/impl/ShareDialog.java
+++ b/app/src/main/java/com/rae/cnblogs/dialog/impl/ShareDialog.java
@@ -353,7 +353,7 @@ void onCancelClick() {
     @OnClick(R.id.tv_share_font)
     void onFontSettingClick() {
         dismiss();
-        AppRoute.jumpToFontSetting(getContext());
+        AppRoute.routeToFontSetting(getContext());
     }
 
     @Override
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/BaseFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/BaseFragment.java
deleted file mode 100644
index a88fb66..0000000
--- a/app/src/main/java/com/rae/cnblogs/fragment/BaseFragment.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package com.rae.cnblogs.fragment;
-
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.rae.cnblogs.sdk.config.CnblogSdkConfig;
-
-import butterknife.ButterKnife;
-import butterknife.Unbinder;
-
-
-public abstract class BaseFragment extends Fragment {
-
-    private Unbinder mUnbinder;
-
-    @Nullable
-    @Override
-    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
-        View view = inflater.inflate(getLayoutId(), container, false);
-        mUnbinder = ButterKnife.bind(this, view);
-        onCreateView(view);
-        return view;
-    }
-
-
-    @Override
-    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        onLoadData();
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-    }
-
-    protected void onCreateView(View view) {
-    }
-
-    protected void onLoadData() {
-
-    }
-
-    protected abstract int getLayoutId();
-
-    protected CnblogSdkConfig config() {
-        // fix bug #478
-        return CnblogSdkConfig.getsInstance(getContext());
-    }
-
-    protected int parseInt(String text) {
-        if (TextUtils.isEmpty(text)) return 0;
-        try {
-            return Integer.valueOf(text);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        return 0;
-    }
-
-}
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/BlogCommentFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/BlogCommentFragment.java
index 88c85c0..e9fffcc 100644
--- a/app/src/main/java/com/rae/cnblogs/fragment/BlogCommentFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/fragment/BlogCommentFragment.java
@@ -2,6 +2,7 @@
 
 import android.content.DialogInterface;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.view.View;
 
@@ -41,7 +42,7 @@
  * 评论
  * Created by ChenRui on 2016/12/15 0015 19:22.
  */
-public class BlogCommentFragment extends BaseFragment implements IBlogCommentPresenter.IBlogCommentView, EditCommentDialog.OnEditCommentListener {
+public class BlogCommentFragment extends BasicFragment implements IBlogCommentPresenter.IBlogCommentView, EditCommentDialog.OnEditCommentListener {
 
     public static BlogCommentFragment newInstance(BlogBean blog, BlogType type) {
         Bundle args = new Bundle();
@@ -102,7 +103,7 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
     }
 
     @Override
-    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
 //        mParentView = (RaeDrawerLayout) view.getParent();
 //        mParentView.setDragDownHandler(new RaeDragDownCompat.DragDownHandler() {
@@ -197,7 +198,7 @@ public void onClick(View v) {
                     AppUI.failed(v.getContext(), "博主可能没有开通博客功能！[blogApp is null]");
                     return;
                 }
-                AppRoute.jumpToBlogger(v.getContext(), v.getTag().toString());
+                AppRoute.routeToBlogger(v.getContext(), v.getTag().toString());
             }
         });
 
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/BlogContentFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/BlogContentFragment.java
index e7d25f9..2c2f7d8 100644
--- a/app/src/main/java/com/rae/cnblogs/fragment/BlogContentFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/fragment/BlogContentFragment.java
@@ -4,6 +4,7 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.content.ContextCompat;
 import android.util.TypedValue;
@@ -20,12 +21,12 @@
 import com.rae.cnblogs.BuildConfig;
 import com.rae.cnblogs.R;
 import com.rae.cnblogs.RaeAnim;
+import com.rae.cnblogs.ThemeChangedEvent;
 import com.rae.cnblogs.ThemeCompat;
 import com.rae.cnblogs.dialog.IAppDialog;
 import com.rae.cnblogs.dialog.IAppDialogClickListener;
 import com.rae.cnblogs.dialog.impl.HintCardDialog;
 import com.rae.cnblogs.message.FontChangedEvent;
-import com.rae.cnblogs.message.ThemeChangedEvent;
 import com.rae.cnblogs.presenter.CnblogsPresenterFactory;
 import com.rae.cnblogs.presenter.IBlogContentPresenter;
 import com.rae.cnblogs.sdk.AppGson;
@@ -37,6 +38,7 @@
 import com.rae.cnblogs.widget.RaeWebView;
 import com.rae.cnblogs.widget.webclient.RaeJavaScriptBridge;
 import com.rae.cnblogs.widget.webclient.RaeWebViewClient;
+import com.rae.swift.Rx;
 
 import org.greenrobot.eventbus.EventBus;
 import org.greenrobot.eventbus.Subscribe;
@@ -96,7 +98,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
 
 
     @Override
-    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         mSourceTextZoom = mWebView.getSettings().getTextZoom();
         if (BuildConfig.DEBUG) {
@@ -260,7 +262,7 @@ public void onLikeError(boolean isCancel, String msg) {
     public void onLikeSuccess(boolean isCancel) {
         mLikeView.setEnabled(true);
         mLikeView.setSelected(!isCancel);
-        int like = parseInt(mBlog.getLikes());
+        int like = Rx.parseInt(mBlog.getLikes());
 
         if (mLikeView.isSelected()) {
             // 点赞数量加1
@@ -336,7 +338,7 @@ public void onNeedLogin() {
         mLikeAnimView.setVisibility(View.GONE);
         mLikeView.setVisibility(View.VISIBLE);
         if (getContext() != null)
-            AppRoute.jumpToLogin(getContext());
+            AppRoute.routeToLogin(getContext());
     }
 
     @Override
@@ -357,7 +359,7 @@ public WebViewClient getWebViewClient() {
 
             @Override
             public boolean shouldOverrideUrlLoading(WebView view, String url) {
-                AppRoute.jumpToWebNewTask(view.getContext(), url);
+                AppRoute.routeToWebNewTask(view.getContext(), url);
                 return true;
             }
         };
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/BlogListFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/BlogListFragment.java
index 34285fd..2477352 100644
--- a/app/src/main/java/com/rae/cnblogs/fragment/BlogListFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/fragment/BlogListFragment.java
@@ -1,6 +1,7 @@
 package com.rae.cnblogs.fragment;
 
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.view.View;
 
@@ -22,7 +23,6 @@
 import com.rae.cnblogs.widget.PlaceholderView;
 import com.rae.cnblogs.widget.RaeRecyclerView;
 import com.rae.swift.Rx;
-import com.umeng.analytics.MobclickAgent;
 
 import org.greenrobot.eventbus.EventBus;
 import org.greenrobot.eventbus.Subscribe;
@@ -38,7 +38,7 @@
  * 博客列表
  * Created by ChenRui on 2016/12/2 00:33.
  */
-public class BlogListFragment extends BaseFragment implements IBlogListPresenter.IBlogListView {
+public class BlogListFragment extends BasicFragment implements IBlogListPresenter.IBlogListView {
 
 
     public static BlogListFragment newInstance(int position, CategoryBean category, BlogType type) {
@@ -88,13 +88,11 @@ protected IBlogListPresenter getBlogListPresenter() {
     @Override
     public void onPause() {
         super.onPause();
-        MobclickAgent.onPageEnd("博客列表");
     }
 
     @Override
     public void onResume() {
         super.onResume();
-        MobclickAgent.onPageStart("博客列表");
         // 统计分类
         if (mCategory != null) {
             onMobclickAgent(mCategory);
@@ -132,7 +130,7 @@ protected String getTitle() {
     }
 
     @Override
-    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
 
         mPlaceholderView.setOnRetryClickListener(new View.OnClickListener() {
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/BloggerFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/BloggerFragment.java
index 25deb88..351e6eb 100644
--- a/app/src/main/java/com/rae/cnblogs/fragment/BloggerFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/fragment/BloggerFragment.java
@@ -2,6 +2,7 @@
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.view.View;
 
@@ -36,7 +37,7 @@
  * 博主
  * Created by ChenRui on 2017/2/9 0009 10:31.
  */
-public class BloggerFragment extends BaseFragment {
+public class BloggerFragment extends BasicFragment {
 
     @BindView(R.id.rec_friends_list)
     RaeRecyclerView mRecyclerView;
@@ -80,14 +81,14 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
     }
 
     @Override
-    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         mAdapter = new FriendsAdapter();
         mRecyclerView.setAdapter(mAdapter);
         mAdapter.setOnItemClickListener(new BaseItemAdapter.onItemClickListener<UserInfoBean>() {
             @Override
             public void onItemClick(UserInfoBean item) {
-                AppRoute.jumpToBlogger(getActivity(), item.getBlogApp());
+                AppRoute.routeToBlogger(getActivity(), item.getBlogApp());
             }
         });
         mRecyclerView.setLoadingMoreEnabled(true);
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/CategoriesFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/CategoriesFragment.java
index 5dd94eb..267d5eb 100644
--- a/app/src/main/java/com/rae/cnblogs/fragment/CategoriesFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/fragment/CategoriesFragment.java
@@ -42,7 +42,7 @@
  * 分类管理
  * Created by ChenRui on 2017/7/16 0016 22:58.
  */
-public class CategoriesFragment extends BaseFragment implements CategoriesOverallAdapter.CategoryDragListener {
+public class CategoriesFragment extends BasicFragment implements CategoriesOverallAdapter.CategoryDragListener {
 
     private Disposable mDisposable;
 
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/DiscoverFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/DiscoverFragment.java
index 140c3ad..be5836c 100644
--- a/app/src/main/java/com/rae/cnblogs/fragment/DiscoverFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/fragment/DiscoverFragment.java
@@ -2,8 +2,9 @@
 
 import android.content.Context;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.design.widget.DesignTabLayout;
+import android.support.design.widget.RaeTabLayout;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentStatePagerAdapter;
@@ -26,10 +27,10 @@
  * 发现
  * Created by ChenRui on 2017/10/26 0026 23:29.
  */
-public class DiscoverFragment extends BaseFragment {
+public class DiscoverFragment extends BasicFragment {
 
     @BindView(R.id.tab_layout)
-    DesignTabLayout mTabLayout;
+    RaeTabLayout mTabLayout;
     @BindView(R.id.view_pager)
     RaeViewPager mViewPager;
     private DiscoverFragmentAdapter mAdapter;
@@ -57,13 +58,13 @@ public void onDestroy() {
 
 
     @Override
-    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         mAdapter = new DiscoverFragmentAdapter(view.getContext(), getChildFragmentManager());
         mViewPager.setAdapter(mAdapter);
         // 相互关联
-        mViewPager.addOnPageChangeListener(new DesignTabLayout.TabLayoutOnPageChangeListener(mTabLayout));
-        mTabLayout.addOnTabSelectedListener(new DesignTabLayout.ViewPagerOnTabSelectedListener(mViewPager));
+        mViewPager.addOnPageChangeListener(new RaeTabLayout.TabLayoutOnPageChangeListener(mTabLayout));
+        mTabLayout.addOnTabSelectedListener(new RaeTabLayout.ViewPagerOnTabSelectedListener(mViewPager));
         mTabLayout.addOnTabSelectedListener(new DefaultOnTabSelectedListener());
         mTabLayout.getTabAt(0).select();
     }
@@ -73,9 +74,9 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
     public void onSearchClick() {
         int position = mTabLayout.getSelectedTabPosition();
         if (position == 0)
-            AppRoute.jumpToSearchNews(getContext());
+            AppRoute.routeToSearchNews(getContext());
         if (position == 1)
-            AppRoute.jumpToSearchKb(getContext());
+            AppRoute.routeToSearchKb(getContext());
     }
 
     @Subscribe
@@ -123,25 +124,25 @@ public int getCount() {
         }
     }
 
-    class DefaultOnTabSelectedListener implements DesignTabLayout.OnTabSelectedListener {
+    class DefaultOnTabSelectedListener implements RaeTabLayout.OnTabSelectedListener {
 
         @Override
-        public void onTabSelected(DesignTabLayout.Tab tab) {
+        public void onTabSelected(RaeTabLayout.Tab tab) {
             int count = mTabLayout.getTabCount();
             for (int i = 0; i < count; i++) {
-                DesignTabLayout.Tab tabAt = mTabLayout.getTabAt(i);
+                RaeTabLayout.Tab tabAt = mTabLayout.getTabAt(i);
                 if (tabAt == null) continue;
                 tabAt.setTextStyle(tab == tabAt ? 1 : 0);
             }
         }
 
         @Override
-        public void onTabUnselected(DesignTabLayout.Tab tab) {
+        public void onTabUnselected(RaeTabLayout.Tab tab) {
 
         }
 
         @Override
-        public void onTabReselected(DesignTabLayout.Tab tab) {
+        public void onTabReselected(RaeTabLayout.Tab tab) {
             onTabSelected(tab);
         }
     }
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/HomeFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/HomeFragment.java
index 939c2bf..c5d7f04 100644
--- a/app/src/main/java/com/rae/cnblogs/fragment/HomeFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/fragment/HomeFragment.java
@@ -5,7 +5,7 @@
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.design.widget.CoordinatorLayout;
-import android.support.design.widget.DesignTabLayout;
+import android.support.design.widget.RaeTabLayout;
 import android.support.v4.view.ViewPager;
 import android.view.View;
 import android.view.animation.Animation;
@@ -33,7 +33,7 @@
  * 首页
  * Created by ChenRui on 2016/12/1 22:34.
  */
-public class HomeFragment extends BaseFragment implements IHomePresenter.IHomeView {
+public class HomeFragment extends BasicFragment implements IHomePresenter.IHomeView {
 
     private BlogListAdapter mAdapter;
     private IHomePresenter mHomePresenter;
@@ -45,7 +45,7 @@ public static HomeFragment newInstance() {
     }
 
     @BindView(R.id.tab_category)
-    DesignTabLayout mTabLayout;
+    RaeTabLayout mTabLayout;
 
     @BindView(R.id.vp_blog_list)
     ViewPager mViewPager;
@@ -84,7 +84,7 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
 
     @OnClick(R.id.img_edit_category)
     public void onCategoryClick(View view) {
-        AppRoute.jumpToCategoryForResult(getActivity());
+        AppRoute.routeToCategoryForResult(getActivity());
     }
 
     @Override
@@ -102,19 +102,19 @@ public void onLoadCategory(List<CategoryBean> data) {
             mAdapter = new BlogListAdapter(getChildFragmentManager(), data);
             mViewPager.setAdapter(mAdapter);
             mTabLayout.setupWithViewPager(mViewPager);
-            mTabLayout.addOnTabSelectedListener(new DesignTabLayout.OnTabSelectedListener() {
+            mTabLayout.addOnTabSelectedListener(new RaeTabLayout.OnTabSelectedListener() {
                 @Override
-                public void onTabSelected(DesignTabLayout.Tab tab) {
+                public void onTabSelected(RaeTabLayout.Tab tab) {
 
                 }
 
                 @Override
-                public void onTabUnselected(DesignTabLayout.Tab tab) {
+                public void onTabUnselected(RaeTabLayout.Tab tab) {
 
                 }
 
                 @Override
-                public void onTabReselected(DesignTabLayout.Tab tab) {
+                public void onTabReselected(RaeTabLayout.Tab tab) {
                     goTop();
                 }
             });
@@ -157,7 +157,7 @@ public void run() {
 
     @OnClick(R.id.fl_search)
     public void onSearchClick() {
-        AppRoute.jumpToSearch(this.getContext());
+        AppRoute.routeToSearch(this.getContext());
     }
 
     @OnClick(R.id.img_actionbar_logo)
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/ImagePreviewFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/ImagePreviewFragment.java
deleted file mode 100644
index 2d8a477..0000000
--- a/app/src/main/java/com/rae/cnblogs/fragment/ImagePreviewFragment.java
+++ /dev/null
@@ -1,49 +0,0 @@
-//package com.rae.cnblogs.fragment;
-//
-//import android.os.Bundle;
-//import android.support.annotation.Nullable;
-//import android.view.View;
-//
-//import com.nostra13.universalimageloader.core.ImageLoader;
-//import com.rae.cnblogs.R;
-//import com.rae.cnblogs.RaeImageLoader;
-//import com.rae.cnblogs.image.RaeImageView;
-//
-//import butterknife.BindView;
-//
-///**
-// * Created by ChenRui on 2017/2/6 0006 17:01.
-// */
-//public class ImagePreviewFragment extends BaseFragment {
-//
-//    private String mUrl;
-//
-//
-//    @BindView(R.id.img_preview)
-//    RaeImageView mImageView;
-//
-//    public static ImagePreviewFragment newInstance(String url) {
-//        Bundle args = new Bundle();
-//        args.putString("url", url);
-//        ImagePreviewFragment fragment = new ImagePreviewFragment();
-//        fragment.setArguments(args);
-//        return fragment;
-//    }
-//
-//    @Override
-//    protected int getLayoutId() {
-//        return R.layout.item_image_preview;
-//    }
-//
-//    @Override
-//    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
-//        super.onViewCreated(view, savedInstanceState);
-//        ImageLoader.getInstance().displayImage(mUrl, mImageView, RaeImageLoader.fadeOptions(800).build(), mImageView);
-//    }
-//
-//    @Override
-//    public void onCreate(@Nullable Bundle savedInstanceState) {
-//        super.onCreate(savedInstanceState);
-//        mUrl = getArguments().getString("url");
-//    }
-//}
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/MineFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/MineFragment.java
index 7a093f2..74367c2 100644
--- a/app/src/main/java/com/rae/cnblogs/fragment/MineFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/fragment/MineFragment.java
@@ -42,7 +42,7 @@
  * 我的
  * Created by ChenRui on 2017/1/19 00:13.
  */
-public class MineFragment extends BaseFragment {
+public class MineFragment extends BasicFragment {
 
     public static MineFragment newInstance() {
         return new MineFragment();
@@ -175,7 +175,7 @@ protected void onLoginExpired() {
                         // 登录过期
                         loadNotLoginUI();
                         AppUI.toastInCenter(getContext(), getString(R.string.login_expired));
-                        AppRoute.jumpToLogin(getContext());
+                        AppRoute.routeToLogin(getContext());
                     }
 
                     @Override
@@ -212,19 +212,19 @@ public void onDestroy() {
     @OnClick(R.id.layout_account_fans)
     public void onFansClick() {
         if (isNotLogin()) {
-            AppRoute.jumpToLogin(getActivity());
+            AppRoute.routeToLogin(getActivity());
             return;
         }
-        AppRoute.jumpToFans(this.getContext(), getString(R.string.me), UserProvider.getInstance().getLoginUserInfo().getBlogApp());
+        AppRoute.routeToFans(this.getContext(), getString(R.string.me), UserProvider.getInstance().getLoginUserInfo().getBlogApp());
     }
 
     @OnClick(R.id.layout_account_follow)
     public void onFollowClick() {
         if (isNotLogin()) {
-            AppRoute.jumpToLogin(getActivity());
+            AppRoute.routeToLogin(getActivity());
             return;
         }
-        AppRoute.jumpToFollow(this.getContext(), getString(R.string.me), UserProvider.getInstance().getLoginUserInfo().getBlogApp());
+        AppRoute.routeToFollow(this.getContext(), getString(R.string.me), UserProvider.getInstance().getLoginUserInfo().getBlogApp());
     }
 
 
@@ -232,11 +232,11 @@ public void onFollowClick() {
     public void onLoginClick() {
         // 没有登录跳登录
         if (isNotLogin()) {
-            AppRoute.jumpToLogin(getActivity());
+            AppRoute.routeToLogin(getActivity());
             return;
         }
 
-        AppRoute.jumpToBlogger(getContext(), UserProvider.getInstance().getLoginUserInfo().getBlogApp());
+        AppRoute.routeToBlogger(getContext(), UserProvider.getInstance().getLoginUserInfo().getBlogApp());
     }
 
     /**
@@ -247,7 +247,7 @@ public void onFavoritesClick() {
         AppMobclickAgent.onClickEvent(getContext(), "Favorites");
         // 没有登录跳登录
         if (isNotLogin()) {
-            AppRoute.jumpToLogin(getActivity());
+            AppRoute.routeToLogin(getActivity());
             return;
         }
         AppRoute.jumpToFavorites(this.getActivity());
@@ -260,7 +260,16 @@ public void onFavoritesClick() {
     public void onFeedbackClick() {
         mFeedbackBadgeView.setVisibility(View.INVISIBLE);
         AppMobclickAgent.onClickEvent(getContext(), "Feedback");
-        AppRoute.jumpToFeedback(getContext());
+        AppRoute.routeToFeedback(getContext());
+    }
+
+    /**
+     * 浏览记录
+     */
+    @OnClick(R.id.ll_history)
+    public void onHistoryClick() {
+        AppMobclickAgent.onClickEvent(getContext(), "History");
+        AppRoute.routeToHistory(getContext());
     }
 
 
@@ -271,7 +280,7 @@ public void onFeedbackClick() {
     public void onSystemMessageClick() {
         AppMobclickAgent.onClickEvent(getContext(), "SystemMessage");
         mSystemMessageBadgeView.setVisibility(View.INVISIBLE);
-        AppRoute.jumpToSystemMessage(this.getContext());
+        AppRoute.routeToSystemMessage(this.getContext());
     }
 
     /**
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/MomentDetailFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/MomentDetailFragment.java
index 4a725b1..bccb973 100644
--- a/app/src/main/java/com/rae/cnblogs/fragment/MomentDetailFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/fragment/MomentDetailFragment.java
@@ -1,6 +1,7 @@
 package com.rae.cnblogs.fragment;
 
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.content.ContextCompat;
 import android.text.TextUtils;
@@ -44,7 +45,7 @@
  * 闪存详情
  * Created by ChenRui on 2017/11/2 0002 15:35.
  */
-public class MomentDetailFragment extends BaseFragment implements IMomentDetailContract.View {
+public class MomentDetailFragment extends BasicFragment implements IMomentDetailContract.View {
 
     @BindView(R.id.recycler_view)
     RaeRecyclerView mRecyclerView;
@@ -62,7 +63,7 @@ public class MomentDetailFragment extends BaseFragment implements IMomentDetailC
         @Override
         public void onClick(View v) {
             if (!UserProvider.getInstance().isLogin()) {
-                AppRoute.jumpToLogin(v.getContext());
+                AppRoute.routeToLogin(v.getContext());
                 return;
             }
             ((Button) v).setText("请稍后");
@@ -97,7 +98,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
 
 
     @Override
-    public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
 
         if (mData == null) {
@@ -160,7 +161,7 @@ public void onClick(View v) {
         mAdapter.setOnBloggerClickListener(new MomentAdapter.OnBloggerClickListener() {
             @Override
             public void onBloggerClick(String blogApp) {
-                AppRoute.jumpToBlogger(getContext(), blogApp);
+                AppRoute.routeToBlogger(getContext(), blogApp);
             }
         });
         mAdapter.setOnFollowClickListener(mOnFollowClickListener);
@@ -349,7 +350,7 @@ public void onLoadMoreNotLogin() {
             @Override
             public void onClick(IAppDialog dialog, int buttonType) {
                 dialog.dismiss();
-                AppRoute.jumpToLogin(getContext());
+                AppRoute.routeToLogin(getContext());
             }
         });
         dialog.show();
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/MomentFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/MomentFragment.java
index 3e12d34..395a040 100644
--- a/app/src/main/java/com/rae/cnblogs/fragment/MomentFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/fragment/MomentFragment.java
@@ -1,6 +1,7 @@
 package com.rae.cnblogs.fragment;
 
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.app.Fragment;
 import android.text.TextUtils;
@@ -33,7 +34,7 @@
  * 闪存列表
  * Created by ChenRui on 2017/10/27 0027 10:41.
  */
-public class MomentFragment extends BaseFragment implements IMomentContract.View {
+public class MomentFragment extends BasicFragment implements IMomentContract.View {
 
 
     /**
@@ -90,13 +91,13 @@ public void onDestroy() {
 
 
     @Override
-    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         mAdapter = new MomentAdapter();
         mAdapter.setOnBloggerClickListener(new MomentAdapter.OnBloggerClickListener() {
             @Override
             public void onBloggerClick(String blogApp) {
-                AppRoute.jumpToBlogger(getContext(), blogApp);
+                AppRoute.routeToBlogger(getContext(), blogApp);
             }
         });
 //        mAdapter.setOnDeleteClickListener(new MomentAdapter.OnDeleteClickListener() {
@@ -121,7 +122,7 @@ public void onBloggerClick(String blogApp) {
             @Override
             public void onItemClick(MomentBean item) {
                 if (item != null)
-                    AppRoute.jumpToMomentDetail(getContext(), item);
+                    AppRoute.routeToMomentDetail(getContext(), item);
             }
         });
         mRecyclerView.setAdapter(mAdapter);
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/MomentMessageFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/MomentMessageFragment.java
index 8b4335b..d3c1111 100644
--- a/app/src/main/java/com/rae/cnblogs/fragment/MomentMessageFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/fragment/MomentMessageFragment.java
@@ -1,6 +1,7 @@
 package com.rae.cnblogs.fragment;
 
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.view.View;
 
@@ -31,7 +32,7 @@
  * 回复我的消息
  * Created by ChenRui on 2017/11/6 0006 14:21.
  */
-public class MomentMessageFragment extends BaseFragment implements IMomentMessageContract.View {
+public class MomentMessageFragment extends BasicFragment implements IMomentMessageContract.View {
 
     @BindView(R.id.recycler_view)
     RaeRecyclerView mRecyclerView;
@@ -65,7 +66,7 @@ public void onDestroy() {
 
 
     @Override
-    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         mAdapter = new MomentMessageAdapter();
         mAdapter.setOnItemClickListener(new BaseItemAdapter.onItemClickListener<MomentCommentBean>() {
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/SNSFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/SNSFragment.java
index 099e2f2..116ce63 100644
--- a/app/src/main/java/com/rae/cnblogs/fragment/SNSFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/fragment/SNSFragment.java
@@ -6,8 +6,9 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.design.widget.DesignTabLayout;
+import android.support.design.widget.RaeTabLayout;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentStatePagerAdapter;
@@ -41,14 +42,14 @@
  * 朋友圈（闪存）
  * Created by ChenRui on 2017/10/26 0026 23:31.
  */
-public class SNSFragment extends BaseFragment {
+public class SNSFragment extends BasicFragment {
 
     public static SNSFragment newInstance() {
         return new SNSFragment();
     }
 
     @BindView(R.id.tab_layout)
-    DesignTabLayout mTabLayout;
+    RaeTabLayout mTabLayout;
     @BindView(R.id.view_pager)
     RaeViewPager mViewPager;
 
@@ -76,13 +77,13 @@ public void onDestroy() {
 
 
     @Override
-    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         mAdapter = new SNSFragmentAdapter(view.getContext(), getChildFragmentManager());
         mViewPager.setAdapter(mAdapter);
         // 相互关联
-        mViewPager.addOnPageChangeListener(new DesignTabLayout.TabLayoutOnPageChangeListener(mTabLayout));
-        mTabLayout.addOnTabSelectedListener(new DesignTabLayout.ViewPagerOnTabSelectedListener(mViewPager));
+        mViewPager.addOnPageChangeListener(new RaeTabLayout.TabLayoutOnPageChangeListener(mTabLayout));
+        mTabLayout.addOnTabSelectedListener(new RaeTabLayout.ViewPagerOnTabSelectedListener(mViewPager));
         mTabLayout.addOnTabSelectedListener(new DefaultOnTabSelectedListener());
         mViewPager.setOffscreenPageLimit(3);
     }
@@ -90,7 +91,7 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
     @Override
     public void onActivityCreated(@Nullable Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
-        DesignTabLayout.Tab tab = mTabLayout.getTabAt(0);
+        RaeTabLayout.Tab tab = mTabLayout.getTabAt(0);
         if (tab != null) {
             tab.select();
         }
@@ -101,9 +102,9 @@ public void onPostClick() {
         // 统计闪存发布按钮点击
         AppMobclickAgent.onClickEvent(getContext(), "PostMoment_Enter");
         if (!UserProvider.getInstance().isLogin()) {
-            AppRoute.jumpToLogin(getActivity(), 10256);
+            AppRoute.routeToLoginForResult(getActivity());
         } else {
-            AppRoute.jumpToPostMoment(getActivity());
+            AppRoute.routeToPostMoment(getActivity());
         }
     }
 
@@ -112,9 +113,9 @@ public void onPostClick() {
     public void onMessageClick() {
         if (UserProvider.getInstance().isLogin()) {
             dismissToast();
-            AppRoute.jumpToMomentMessage(this.getContext());
+            AppRoute.routeToMomentMessage(this.getContext());
         } else {
-            AppRoute.jumpToLogin(getContext());
+            AppRoute.routeToLogin(getContext());
         }
     }
 
@@ -123,7 +124,7 @@ public void onToastClick() {
         mToastView.dismiss();
         int type = mToastView.getType();
         if (type == ToolbarToastView.TYPE_REPLY_ME) {
-            AppRoute.jumpToMomentMessage(this.getContext());
+            AppRoute.routeToMomentMessage(this.getContext());
         }
         if (type == ToolbarToastView.TYPE_POST_SUCCESS && mAdapter != null && mViewPager.getCurrentItem() >= 0) {
             MomentFragment momentFragment = (MomentFragment) mAdapter.getItem(mViewPager.getCurrentItem());
@@ -137,8 +138,8 @@ public void onToastClick() {
     @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
-        if (resultCode == Activity.RESULT_OK && requestCode == 10256) {
-            AppRoute.jumpToPostMoment(getActivity());
+        if (resultCode == Activity.RESULT_OK && requestCode == AppRoute.REQ_LOGIN) {
+            AppRoute.routeToPostMoment(getActivity());
         }
     }
 
@@ -188,25 +189,25 @@ private void performTabEvent() {
         }
     }
 
-    class DefaultOnTabSelectedListener implements DesignTabLayout.OnTabSelectedListener {
+    class DefaultOnTabSelectedListener implements RaeTabLayout.OnTabSelectedListener {
 
         @Override
-        public void onTabSelected(DesignTabLayout.Tab tab) {
+        public void onTabSelected(RaeTabLayout.Tab tab) {
             int count = mTabLayout.getTabCount();
             for (int i = 0; i < count; i++) {
-                DesignTabLayout.Tab tabAt = mTabLayout.getTabAt(i);
+                RaeTabLayout.Tab tabAt = mTabLayout.getTabAt(i);
                 if (tabAt == null) continue;
                 tabAt.setTextStyle(tab == tabAt ? 1 : 0);
             }
         }
 
         @Override
-        public void onTabUnselected(DesignTabLayout.Tab tab) {
+        public void onTabUnselected(RaeTabLayout.Tab tab) {
 
         }
 
         @Override
-        public void onTabReselected(DesignTabLayout.Tab tab) {
+        public void onTabReselected(RaeTabLayout.Tab tab) {
             onTabSelected(tab);
             performTabEvent();
         }
@@ -232,7 +233,7 @@ public void onEvent(final PostMomentEvent event) {
                 public void onClick(IAppDialog dialog, int buttonType) {
                     dialog.dismiss();
                     // 跳转到闪存发布
-                    AppRoute.jumpToPostMoment(getActivity(), event.getMomentMetaData());
+                    AppRoute.routeToPostMoment(getActivity(), event.getMomentMetaData());
                 }
             });
             dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/SearchFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/SearchFragment.java
index d76bb1d..9e96817 100644
--- a/app/src/main/java/com/rae/cnblogs/fragment/SearchFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/fragment/SearchFragment.java
@@ -2,6 +2,7 @@
 
 import android.content.Context;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.design.widget.TabLayout;
 import android.support.v4.view.ViewPager;
@@ -38,7 +39,7 @@
  * 搜索
  * Created by ChenRui on 2017/8/28 0028 14:51.
  */
-public class SearchFragment extends BaseFragment implements ISearchContract.View, TabLayout.OnTabSelectedListener {
+public class SearchFragment extends BasicFragment implements ISearchContract.View, TabLayout.OnTabSelectedListener {
 
     private SearchBlogFragment mSearchBlogFragment;
     private SearchBloggerFragment mSearchBloggerFragment;
@@ -105,7 +106,7 @@ public void onDestroy() {
     }
 
     @Override
-    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         initView();
     }
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/WebLoginFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/WebLoginFragment.java
index 05b2bd4..38ac9ef 100644
--- a/app/src/main/java/com/rae/cnblogs/fragment/WebLoginFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/fragment/WebLoginFragment.java
@@ -3,6 +3,7 @@
 import android.app.Activity;
 import android.graphics.Bitmap;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.text.TextUtils;
 import android.view.View;
@@ -58,7 +59,7 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
     }
 
     @Override
-    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         ViewGroup parent = (ViewGroup) mContentLayout.getParent();
         mPlaceholderView = new LoginPlaceholderView(view.getContext());
diff --git a/app/src/main/java/com/rae/cnblogs/fragment/WebViewFragment.java b/app/src/main/java/com/rae/cnblogs/fragment/WebViewFragment.java
index 0b35e5b..fb25862 100644
--- a/app/src/main/java/com/rae/cnblogs/fragment/WebViewFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/fragment/WebViewFragment.java
@@ -5,6 +5,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.content.ContextCompat;
 import android.view.LayoutInflater;
@@ -39,7 +40,7 @@
  * 网页查看
  * Created by ChenRui on 2016/12/27 23:07.
  */
-public class WebViewFragment extends BaseFragment {
+public class WebViewFragment extends BasicFragment {
 
     private String mUrl;
     private String mRawUrl;
@@ -78,7 +79,7 @@ protected int getLayoutId() {
 
     @Nullable
     @Override
-    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
         View view = super.onCreateView(inflater, container, savedInstanceState);
 
 
@@ -131,7 +132,7 @@ public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header
 
     @SuppressLint({"SetJavaScriptEnabled", "AddJavascriptInterface", "JavascriptInterface"})
     @Override
-    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         mJavaScriptApi = new RaeJavaScriptBridge(getContext());
         WebSettings settings = mWebView.getSettings();
diff --git a/app/src/main/java/com/rae/cnblogs/image/ImagePreviewActivity.java b/app/src/main/java/com/rae/cnblogs/image/ImagePreviewActivity.java
index e93c34a..7cd4c0e 100644
--- a/app/src/main/java/com/rae/cnblogs/image/ImagePreviewActivity.java
+++ b/app/src/main/java/com/rae/cnblogs/image/ImagePreviewActivity.java
@@ -16,15 +16,17 @@
 import android.widget.CheckBox;
 import android.widget.TextView;
 
+import com.alibaba.android.arouter.facade.annotation.Route;
 import com.bumptech.glide.load.DataSource;
 import com.bumptech.glide.load.engine.GlideException;
 import com.bumptech.glide.request.RequestListener;
 import com.bumptech.glide.request.target.Target;
+import com.rae.cnblogs.AppRoute;
 import com.rae.cnblogs.AppUI;
 import com.rae.cnblogs.GlideApp;
 import com.rae.cnblogs.R;
 import com.rae.cnblogs.ThemeCompat;
-import com.rae.cnblogs.activity.BaseActivity;
+import com.rae.cnblogs.activity.BasicActivity;
 import com.rae.swift.Rx;
 
 import java.io.File;
@@ -47,7 +49,8 @@
  * 图片预览
  * Created by ChenRui on 2017/2/6 0006 15:48.
  */
-public class ImagePreviewActivity extends BaseActivity implements ViewPager.OnPageChangeListener, View.OnClickListener {
+@Route(path = AppRoute.PATH_IMAGE_PREVIEW)
+public class ImagePreviewActivity extends BasicActivity implements ViewPager.OnPageChangeListener, View.OnClickListener {
 
     @BindView(R.id.vp_image_preview)
     ViewPager mViewPager;
diff --git a/app/src/main/java/com/rae/cnblogs/model/FeedListFragment.java b/app/src/main/java/com/rae/cnblogs/model/FeedListFragment.java
index a65b8f5..d994368 100644
--- a/app/src/main/java/com/rae/cnblogs/model/FeedListFragment.java
+++ b/app/src/main/java/com/rae/cnblogs/model/FeedListFragment.java
@@ -2,7 +2,6 @@
 
 import android.os.Bundle;
 import android.support.annotation.Nullable;
-import android.support.v7.widget.LinearLayoutManager;
 import android.view.View;
 
 import com.jcodecraeer.xrecyclerview.XRecyclerView;
@@ -11,7 +10,7 @@
 import com.rae.cnblogs.RaeViewCompat;
 import com.rae.cnblogs.adapter.BaseItemAdapter;
 import com.rae.cnblogs.adapter.FeedItemAdapter;
-import com.rae.cnblogs.fragment.BaseFragment;
+import com.rae.cnblogs.fragment.BasicFragment;
 import com.rae.cnblogs.presenter.CnblogsPresenterFactory;
 import com.rae.cnblogs.presenter.IFeedPresenter;
 import com.rae.cnblogs.sdk.bean.UserFeedBean;
@@ -28,7 +27,7 @@
  * 作者的动态列表
  * Created by ChenRui on 2017/3/16 16:08.
  */
-public class FeedListFragment extends BaseFragment implements IFeedPresenter.IFeedView {
+public class FeedListFragment extends BasicFragment implements IFeedPresenter.IFeedView {
 
     private FeedItemAdapter mAdapter;
 
@@ -87,7 +86,7 @@ public void onItemClick(UserFeedBean item) {
                 if ("发表评论".equals(item.getAction())) {
                     return;
                 }
-                AppRoute.jumpToWeb(getContext(), item.getUrl());
+                AppRoute.routeToWeb(getContext(), item.getUrl());
             }
         });
         mRecyclerView.setLoadingListener(new XRecyclerView.LoadingListener() {
diff --git a/app/src/main/java/com/rae/cnblogs/presenter/impl/LoginPresenterImpl.java b/app/src/main/java/com/rae/cnblogs/presenter/impl/LoginPresenterImpl.java
index d80b599..887a090 100644
--- a/app/src/main/java/com/rae/cnblogs/presenter/impl/LoginPresenterImpl.java
+++ b/app/src/main/java/com/rae/cnblogs/presenter/impl/LoginPresenterImpl.java
@@ -12,7 +12,6 @@
 import com.rae.cnblogs.sdk.api.IUserApi;
 import com.rae.cnblogs.sdk.bean.UserInfoBean;
 import com.tencent.bugly.crashreport.CrashReport;
-import com.umeng.analytics.MobclickAgent;
 
 import org.greenrobot.eventbus.EventBus;
 
@@ -154,22 +153,33 @@ public LoginPresenterImpl(Context context, ILoginView view) {
     public void loadUserInfo() {
         // 同步COOKIE
         UserProvider.getInstance().cookieManager2CookieJar();
-
         createObservable(mUserApi.getUserBlogAppInfo())
                 .doOnSubscribe(new Consumer<Disposable>() {
                     @Override
-                    public void accept(Disposable disposable) throws Exception {
+                    public void accept(Disposable disposable) {
                         mView.onLoading(mContext.getString(R.string.loading_blog_app));
                     }
                 })
                 .flatMap(new Function<UserInfoBean, ObservableSource<UserInfoBean>>() {
                     @Override
-                    public ObservableSource<UserInfoBean> apply(UserInfoBean userInfoBean) throws Exception {
+                    public ObservableSource<UserInfoBean> apply(UserInfoBean userInfoBean) {
                         mBlogApp = userInfoBean.getBlogApp();
                         mView.onLoading(mContext.getString(R.string.loading_user_info, mBlogApp));
                         return createObservable(mUserApi.getUserInfo(userInfoBean.getBlogApp())); // 获取用户信息
                     }
                 })
+                .flatMap(new Function<UserInfoBean, ObservableSource<UserInfoBean>>() {
+                    @Override
+                    public ObservableSource<UserInfoBean> apply(final UserInfoBean userInfoBean) {
+                        return Observable.timer(2000, TimeUnit.MILLISECONDS).map(new Function<Long, UserInfoBean>() {
+                            @Override
+                            public UserInfoBean apply(Long aLong) {
+                                return userInfoBean;
+                            }
+                        });
+                    }
+                })
+                .observeOn(AndroidSchedulers.mainThread())
                 .subscribe(new ApiDefaultObserver<UserInfoBean>() {
                     @Override
                     public void onError(Throwable e) {
@@ -210,20 +220,11 @@ protected void accept(final UserInfoBean data) {
                         // 统计登录事件
                         AppMobclickAgent.onLoginEvent(mApplicationContext, data.getBlogApp(), true, "登录成功");
                         // 友盟统计用户
-                        MobclickAgent.onProfileSignIn(data.getBlogApp());
+                        AppMobclickAgent.onProfileSignIn(data.getBlogApp());
                         // [重要] 同步Cookie登录信息
                         UserProvider.getInstance().cookieJar2CookieManager();
                         EventBus.getDefault().post(new UserInfoEvent());
-
-                        Observable.timer(2000, TimeUnit.MILLISECONDS)
-                                .observeOn(AndroidSchedulers.mainThread())
-                                .subscribe(new Consumer<Long>() {
-                                    @Override
-                                    public void accept(Long aLong) throws Exception {
-                                        mView.onLoginSuccess(data);
-                                    }
-                                });
-
+                        mView.onLoginSuccess(data);
                     }
                 });
     }
diff --git a/app/src/main/java/com/rae/cnblogs/widget/PlaceholderView.java b/app/src/main/java/com/rae/cnblogs/widget/PlaceholderView.java
index 15e5c2b..413a707 100644
--- a/app/src/main/java/com/rae/cnblogs/widget/PlaceholderView.java
+++ b/app/src/main/java/com/rae/cnblogs/widget/PlaceholderView.java
@@ -92,7 +92,7 @@ protected void initView(AttributeSet attrs, int defStyleAttr) {
         setOnLoginListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
-                AppRoute.jumpToLogin(v.getContext());
+                AppRoute.routeToLogin(v.getContext());
             }
         });
     }
diff --git a/app/src/main/java/com/rae/cnblogs/widget/RaeSeekBar.java b/app/src/main/java/com/rae/cnblogs/widget/RaeSeekBar.java
index 3294258..b8e184b 100644
--- a/app/src/main/java/com/rae/cnblogs/widget/RaeSeekBar.java
+++ b/app/src/main/java/com/rae/cnblogs/widget/RaeSeekBar.java
@@ -66,16 +66,16 @@ protected void init() {
 
     @Override
     protected void onDraw(Canvas canvas) {
-//        super.onDraw(canvas);
+        super.onDraw(canvas);
         int max = getMax();
         int width = canvas.getWidth();
         int height = canvas.getHeight();
         int h2 = height / 2;
 
-        final int saveCount = canvas.save();
-        canvas.translate(getPaddingLeft() - getThumbOffset(), getPaddingTop() + h2 - mThumbHeight / 2);
-        getThumb().draw(canvas);
-        canvas.restoreToCount(saveCount);
+//        final int saveCount = canvas.save();
+//        canvas.translate(getPaddingLeft() - getThumbOffset(), getPaddingTop() + h2 - mThumbHeight / 2);
+//        getThumb().draw(canvas);
+//        canvas.restoreToCount(saveCount);
 
         // 画刻度背景
         mRect.left = getPaddingLeft();
diff --git a/app/src/main/java/com/rae/cnblogs/widget/webclient/RaeJavaScriptBridge.java b/app/src/main/java/com/rae/cnblogs/widget/webclient/RaeJavaScriptBridge.java
index b24478f..88118ce 100644
--- a/app/src/main/java/com/rae/cnblogs/widget/webclient/RaeJavaScriptBridge.java
+++ b/app/src/main/java/com/rae/cnblogs/widget/webclient/RaeJavaScriptBridge.java
@@ -52,7 +52,7 @@ public void onImageClick(String url, String images) {
         }.getType());
         int index = Math.max(0, imageList.indexOf(url));
         if (mReference.get() != null) {
-            AppRoute.jumpToImagePreview(mReference.get(), imageList, index);
+            AppRoute.routeToImagePreview(mReference.get(), imageList, index);
         }
     }
 
@@ -73,7 +73,7 @@ public void jumpToTest() {
     public void jumpToBlogger(String blogApp) {
         if (TextUtils.isEmpty(blogApp)) return;
         if (mReference.get() != null) {
-            AppRoute.jumpToBlogger(mReference.get(), blogApp);
+            AppRoute.routeToBlogger(mReference.get(), blogApp);
         }
     }
 
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index fdee1fb..11ca1d7 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -22,7 +22,7 @@
         android:background="@drawable/tab_main_background"
         android:paddingTop="4dp">
 
-        <com.rae.cnblogs.widget.RaeTabLayout
+        <com.rae.cnblogs.widget.RaeAppTabLayout
             android:id="@+id/tab_main"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/fm_blogger_info.xml b/app/src/main/res/layout/fm_blogger_info.xml
index f880552..7f3c937 100644
--- a/app/src/main/res/layout/fm_blogger_info.xml
+++ b/app/src/main/res/layout/fm_blogger_info.xml
@@ -267,7 +267,7 @@
         </android.support.design.widget.CollapsingToolbarLayout>
 
         <!--TAB-->
-        <android.support.design.widget.RaeSkinDesignTabLayout
+        <com.rae.cnblogs.widget.RaeSkinDesignTabLayout
             android:id="@+id/tab_category"
             android:layout_width="match_parent"
             android:layout_height="45dp"
@@ -297,7 +297,7 @@
                 android:layout_height="wrap_content"
                 android:text="博客"/>
 
-        </android.support.design.widget.RaeSkinDesignTabLayout>
+        </com.rae.cnblogs.widget.RaeSkinDesignTabLayout>
 
     </android.support.design.widget.AppBarLayout>
 
diff --git a/app/src/main/res/layout/fm_discover.xml b/app/src/main/res/layout/fm_discover.xml
index 23805ff..9eecfd5 100644
--- a/app/src/main/res/layout/fm_discover.xml
+++ b/app/src/main/res/layout/fm_discover.xml
@@ -18,7 +18,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content">
 
-            <android.support.design.widget.RaeSkinDesignTabLayout
+            <com.rae.cnblogs.widget.RaeSkinDesignTabLayout
                 android:id="@+id/tab_layout"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
@@ -43,7 +43,7 @@
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:text="@string/tab_library"/>
-            </android.support.design.widget.RaeSkinDesignTabLayout>
+            </com.rae.cnblogs.widget.RaeSkinDesignTabLayout>
 
             <com.rae.cnblogs.widget.RaeSkinImageView
                 xmlns:app="http://schemas.android.com/apk/res-auto"
diff --git a/app/src/main/res/layout/fm_home.xml b/app/src/main/res/layout/fm_home.xml
index 65fdc54..c1be326 100644
--- a/app/src/main/res/layout/fm_home.xml
+++ b/app/src/main/res/layout/fm_home.xml
@@ -78,7 +78,7 @@
             android:gravity="center_vertical"
             android:orientation="horizontal">
 
-            <android.support.design.widget.RaeSkinDesignTabLayout
+            <com.rae.cnblogs.widget.RaeSkinDesignTabLayout
                 android:id="@+id/tab_category"
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/fm_mine.xml b/app/src/main/res/layout/fm_mine.xml
index 408d673..a25f7d6 100644
--- a/app/src/main/res/layout/fm_mine.xml
+++ b/app/src/main/res/layout/fm_mine.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<ScrollView
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -22,7 +21,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:scaleType="centerCrop"
-                android:src="@drawable/bg_login"/>
+                android:src="@drawable/bg_login" />
 
 
             <com.makeramen.roundedimageview.RoundedImageView
@@ -33,7 +32,7 @@
                 android:layout_marginTop="56dp"
                 android:scaleType="centerCrop"
                 android:src="@drawable/boy"
-                app:riv_corner_radius="64dp"/>
+                app:riv_corner_radius="64dp" />
 
             <LinearLayout
                 android:layout_width="wrap_content"
@@ -50,7 +49,7 @@
                     android:text="@string/please_login"
                     android:textColor="@color/ph1"
                     android:textSize="18sp"
-                    android:visibility="gone"/>
+                    android:visibility="gone" />
 
                 <TextView
                     android:id="@+id/tv_no_login"
@@ -59,7 +58,7 @@
                     android:layout_marginTop="20dp"
                     android:text="@string/please_login"
                     android:textColor="@color/ph1"
-                    android:textSize="18sp"/>
+                    android:textSize="18sp" />
 
                 <LinearLayout
                     android:id="@+id/ll_follow_fans"
@@ -80,7 +79,7 @@
                             android:layout_height="wrap_content"
                             android:text="关注"
                             android:textColor="@color/ph3"
-                            android:textSize="@dimen/h3"/>
+                            android:textSize="@dimen/h3" />
 
                         <TextView
                             android:id="@+id/tv_follow_count"
@@ -90,7 +89,7 @@
                             android:text="0"
                             android:textColor="@color/ph1"
                             android:textSize="@dimen/h1"
-                            android:textStyle="bold"/>
+                            android:textStyle="bold" />
 
                     </LinearLayout>
 
@@ -107,7 +106,7 @@
                             android:layout_height="wrap_content"
                             android:text="粉丝"
                             android:textColor="@color/ph3"
-                            android:textSize="@dimen/h3"/>
+                            android:textSize="@dimen/h3" />
 
                         <TextView
                             android:id="@+id/tv_fans_count"
@@ -117,7 +116,7 @@
                             android:text="0"
                             android:textColor="@color/ph1"
                             android:textSize="@dimen/h1"
-                            android:textStyle="bold"/>
+                            android:textStyle="bold" />
 
 
                     </LinearLayout>
@@ -130,7 +129,7 @@
         <View
             android:layout_width="match_parent"
             android:layout_height="8dp"
-            android:background="@color/background_divider"/>
+            android:background="@color/background_divider" />
 
         <LinearLayout
             android:id="@+id/ll_night"
@@ -146,7 +145,7 @@
                 android:drawableRight="@android:color/transparent"
                 android:text="@string/night_mode"
                 android:textColor="@color/ph1"
-                android:textSize="16sp"/>
+                android:textSize="16sp" />
 
             <com.kyleduo.switchbutton.SwitchButton
                 android:id="@+id/sb_night_mode"
@@ -158,7 +157,21 @@
                 app:kswThumbDrawable="@drawable/sb_default_thumb_drawable"
                 app:kswThumbHeight="10dp"
                 app:kswThumbMargin="3dp"
-                app:kswThumbWidth="10dp"/>
+                app:kswThumbWidth="10dp" />
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/ll_history"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <TextView
+                style="@style/ItemStyle"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/label_history"
+                android:textColor="@color/ph1" />
 
         </LinearLayout>
 
@@ -172,7 +185,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:text="@string/my_favorites"
-                android:textColor="@color/ph1"/>
+                android:textColor="@color/ph1" />
 
         </LinearLayout>
 
@@ -187,7 +200,7 @@
                 android:layout_height="wrap_content"
                 android:text="@string/system_message"
                 android:textColor="@color/ph1"
-                android:textSize="16sp"/>
+                android:textSize="16sp" />
 
             <ImageView
                 android:id="@+id/img_system_message_badge"
@@ -196,12 +209,12 @@
                 android:layout_gravity="center"
                 android:layout_marginLeft="10dp"
                 android:background="@drawable/bg_badge"
-                android:visibility="invisible"/>
+                android:visibility="invisible" />
 
             <TextView
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:drawableRight="@drawable/default_right_arrow"/>
+                android:drawableRight="@drawable/default_right_arrow" />
 
         </LinearLayout>
 
@@ -217,7 +230,7 @@
                 android:layout_height="wrap_content"
                 android:text="@string/feedback"
                 android:textColor="@color/ph1"
-                android:textSize="16sp"/>
+                android:textSize="16sp" />
 
             <ImageView
                 android:id="@+id/img_feedback_badge"
@@ -226,12 +239,12 @@
                 android:layout_gravity="center"
                 android:layout_marginLeft="10dp"
                 android:background="@drawable/bg_badge"
-                android:visibility="invisible"/>
+                android:visibility="invisible" />
 
             <TextView
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:drawableRight="@drawable/default_right_arrow"/>
+                android:drawableRight="@drawable/default_right_arrow" />
         </LinearLayout>
 
 
@@ -245,7 +258,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:text="@string/setting"
-                android:textColor="@color/ph1"/>
+                android:textColor="@color/ph1" />
 
         </LinearLayout>
 
diff --git a/app/src/main/res/layout/fm_search.xml b/app/src/main/res/layout/fm_search.xml
index 453c9f9..101aefe 100644
--- a/app/src/main/res/layout/fm_search.xml
+++ b/app/src/main/res/layout/fm_search.xml
@@ -75,7 +75,7 @@
             android:orientation="vertical">
 
 
-            <com.rae.cnblogs.widget.RaeTabLayout
+            <com.rae.cnblogs.widget.RaeAppTabLayout
                 android:id="@+id/tab_category"
                 android:layout_width="match_parent"
                 android:layout_height="40dp"
diff --git a/app/src/main/res/layout/fm_sns.xml b/app/src/main/res/layout/fm_sns.xml
index 9d909ce..705c12d 100644
--- a/app/src/main/res/layout/fm_sns.xml
+++ b/app/src/main/res/layout/fm_sns.xml
@@ -55,7 +55,7 @@
                 android:src="@drawable/ic_moment_post"
                 app:nightColor="@color/nightIconColor"/>
 
-            <android.support.design.widget.RaeSkinDesignTabLayout
+            <com.rae.cnblogs.widget.RaeSkinDesignTabLayout
                 android:id="@+id/tab_layout"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
@@ -90,7 +90,7 @@
                     android:text="@string/tab_sns_mine"/>
 
 
-            </android.support.design.widget.RaeSkinDesignTabLayout>
+            </com.rae.cnblogs.widget.RaeSkinDesignTabLayout>
 
             <com.rae.cnblogs.widget.RaeSkinImageView
                 android:id="@+id/img_mine"
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index f80b2f2..76954db 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -1,10 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
 
-    <declare-styleable name="RaeSkinImageView">
-        <!--夜间模式颜色-->
-        <attr name="nightColor" format="color|reference"/>
-    </declare-styleable>
 
     <declare-styleable name="PlaceholderView">
         <attr name="mode">
@@ -24,7 +20,5 @@
             <enum name="fixed" value="1"/>
         </attr>
     </declare-styleable>
-    <declare-styleable name="DesignTabLayout">
-        <attr name="tabIndicatorWidth" format="dimension"/>
-    </declare-styleable>
+
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 28e51c3..5b30204 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -5,30 +5,5 @@
     <color name="colorPrimaryDark">#3ba7ee</color>
     <color name="colorPrimaryBlack">#409DDB</color>
     <color name="colorAccent">#1783CB</color>
-    <color name="colorHintPrimary">#EEEEEE</color>
-    <color name="buttonColorPrimary">#3ba7ee</color>
-    <color name="transparent">#00000000</color>
-    <color name="navBar">@color/white</color>
-    <color name="dividerPrimary">#b2b2b2</color>
-    <color name="dividerColor">#dedede</color>
-    <color name="ph1">#2d2d2d</color>
-    <color name="ph2">#595959</color>
-    <color name="moment_author">#576b95</color>
-    <color name="ph3">#929292</color>
-    <color name="ph4">#999999</color>
-    <color name="default_background">#f9f9f9</color>
-    <color name="background_divider">#f8f8f8</color>
-    <color name="colorToastPrimary">#daedfa</color>
-    <color name="commentAvatarColor">#6293b3</color>
-    <color name="blog_item_pressed">#F0F0F0</color>
-    <color name="white">#FFFFFF</color>
-    <color name="statusBarColor">@color/white</color>
-    <color name="red">#F00000</color>
-    <color name="badge_color">#fec63d</color>
-    <color name="homeTabTextColor">#aaaaaa</color>
-    <color name="homeTabSelectedTextColor">#2d2d2d</color>
-    <color name="bloggerStatusBarColor">#303135</color>
-    <color name="color_login_reg">#3ba7ee</color>
-    <color name="color_image_selection_post">#3ba7ee</color>
-    <color name="blogger_image_alpha_color">#4C000000</color>
+
 </resources>
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index d505ee2..d792730 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -18,6 +18,4 @@
     <dimen name="item_blog_list_thumb_height">86dp</dimen>
     <dimen name="item_blog_list_thumb_large_height">180dp</dimen>
     <dimen name="default_padding_top">0dp</dimen>
-    <!--夜间模式降低图片透明度-->
-    <integer name="imageAlpha_night">70</integer>
 </resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3e0372d..a3e7718 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -16,6 +16,7 @@
     <string name="view_source">查看原文</string>
     <string name="copy_link">复制链接</string>
     <string name="my_favorites">我的收藏</string>
+    <string name="history">浏览记录</string>
     <string name="cancel">取消</string>
     <string name="tips_loading">正在加载…</string>
     <string name="copy_link_success">复制链接成功</string>
@@ -36,7 +37,6 @@
     <string name="retry">重新加载</string>
     <string name="empty_comment">还没有评论 快来说两句</string>
     <string name="nomore_loading"> ——阅读完毕——</string>
-    <string name="no_more_tips">全部加载完毕</string>
     <string name="register">注册</string>
     <string name="login_tips">登录遇到问题？</string>
     <string name="login_contract">博客园APP隐私说明</string>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 4c9f8a1..b8e9468 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -101,12 +101,6 @@
         <item name="android:textStyle">bold</item>
     </style>
 
-    <style name="AppToolBar" parent="Widget.AppCompat.Toolbar">
-        <item name="contentInsetStart">0dp</item><!-- 设置该属性解决空白部分-->
-        <item name="contentInsetEnd">0dp</item>
-        <item name="contentInsetStartWithNavigation">0dp</item>
-        <item name="navigationIcon">@drawable/ic_back</item>
-    </style>
 
     <style name="progress_bar_web" parent="@style/Widget.AppCompat.ProgressBar.Horizontal">
         <item name="android:progressDrawable">@drawable/progress_web_view</item>
diff --git a/build.gradle b/build.gradle
index 7b3b816..f820cf5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,7 +6,7 @@ buildscript {
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.0.1'
+        classpath 'com.android.tools.build:gradle:3.1.2'
         classpath 'com.jakewharton:butterknife-gradle-plugin:9.0.0-SNAPSHOT'
         // 热更新插件
         classpath "com.tencent.bugly:tinker-support:1.0.8"
@@ -29,37 +29,9 @@ task clean(type: Delete) {
     delete rootProject.buildDir
 }
 
-// 清除缓存的maven库
-task cleanCache(type: Delete) {
-    doLast {
-        def dir = rootProject.gradle.gradleUserHomeDir.path
-        def cache = dir + '\\caches\\modules-2\\files-2.1\\'
-        def meta = dir + '\\caches\\modules-2\\metadata-2.23\\descriptors\\'
-
-        def list = [
-                'com.rae.core',
-                'com.rae.swift',
-                'com.rae.widget',
-                'com.squareup.okhttp3',
-                'com.github.raee'
-        ]
-
-        list.forEach { item ->
-            println '-------------> delete cache <----------------'
-            def itemCache = cache + item
-            def itemMeta = meta + item
-            delete itemCache
-            println itemCache
-            delete itemMeta
-            println itemMeta
-        }
-        println '-------------> end clean cache task <----------------'
-    }
-}
-
 ext {
     // 自动构建版本号
-    buildToolsVersion = '27.0.1'
+    buildToolsVersion = '27.0.3'
     compileSdkVersion = 27
     targetSdkVersion = 27
     minSdkVersion = 17
@@ -72,4 +44,7 @@ ext {
     rxandroid = 'io.reactivex.rxjava2:rxandroid:2.0.1'
     butterknife = 'com.jakewharton:butterknife:9.0.0-SNAPSHOT'
     butterknifeCompiler = 'com.jakewharton:butterknife-compiler:9.0.0-SNAPSHOT'
+    arouter = 'com.alibaba:arouter-api:1.3.1'
+    arouterCompiler = 'com.alibaba:arouter-compiler:1.1.4'
+    eventBus = 'org.greenrobot:eventbus:3.1.1'
 }
diff --git a/guide.jpg b/doc/guide.jpg
similarity index 100%
rename from guide.jpg
rename to doc/guide.jpg
diff --git a/gradle.properties b/gradle.properties
index 6d860d8..4bb0d59 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -12,4 +12,6 @@ org.gradle.jvmargs=-Xmx1536m
 # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
 # org.gradle.parallel=true
 # Enable/Disable Tinker HotFix
-enableHotfix=false
\ No newline at end of file
+enableHotfix=false
+# true: enable app module
+isModule=true
\ No newline at end of file
diff --git a/jar2dex.bat b/jar2dex.bat
deleted file mode 100644
index ae7743e..0000000
--- a/jar2dex.bat
+++ /dev/null
@@ -1,3 +0,0 @@
-@echo off
-d:\sdk\build-tools\27.0.1\dx.bat --dex --output="sdk\build\intermediates\bundles\debug\sdk.dex" sdk\build\intermediates\bundles\debug\classes.jar
-
diff --git a/module-basic/.gitignore b/module-basic/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/module-basic/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/module-basic/build.gradle b/module-basic/build.gradle
new file mode 100644
index 0000000..b079fcc
--- /dev/null
+++ b/module-basic/build.gradle
@@ -0,0 +1,60 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion rootProject.ext.compileSdkVersion
+    buildToolsVersion rootProject.ext.buildToolsVersion
+
+    defaultConfig {
+        minSdkVersion rootProject.ext.minSdkVersion
+        targetSdkVersion rootProject.ext.targetSdkVersion
+        versionCode 1
+        versionName "1.0"
+        multiDexEnabled true
+        // 阿里路由需要
+        javaCompileOptions {
+            annotationProcessorOptions {
+                arguments = [moduleName: project.getName()]
+            }
+        }
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation rootProject.ext.appcompat
+    implementation rootProject.ext.design
+    implementation rootProject.ext.cardview
+    implementation rootProject.ext.butterknife
+    api project(':sdk')
+    implementation project(':widget')
+    implementation project(':module-resource')
+
+    // 阿里路由
+    api(rootProject.ext.arouter, {
+        exclude group: 'com.android.support'
+    })
+    // 阿里路由注解
+    annotationProcessor rootProject.ext.arouterCompiler
+
+    // RX生命周期管理
+    implementation 'com.trello.rxlifecycle2:rxlifecycle-android-lifecycle:2.2.1'
+    // 美团渠道包库
+    implementation 'com.meituan.android.walle:library:1.1.6'
+    // 友盟统计-公共库
+    implementation 'com.umeng.sdk:common:1.4.1'
+    // 友盟统计-APP分析库
+    implementation 'com.umeng.sdk:analytics:7.4.1'
+    // 滑动返回
+    api('me.imid.swipebacklayout.lib:library:1.1.0', {
+        exclude group: 'com.android.support'
+    })
+
+}
diff --git a/module-basic/proguard-rules.pro b/module-basic/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/module-basic/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/module-basic/src/main/AndroidManifest.xml b/module-basic/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5e226f8
--- /dev/null
+++ b/module-basic/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.rae.cnblogs.basic" />
diff --git a/app/src/main/java/com/rae/cnblogs/AppMobclickAgent.java b/module-basic/src/main/java/com/rae/cnblogs/AppMobclickAgent.java
similarity index 83%
rename from app/src/main/java/com/rae/cnblogs/AppMobclickAgent.java
rename to module-basic/src/main/java/com/rae/cnblogs/AppMobclickAgent.java
index 5086785..5504f04 100644
--- a/app/src/main/java/com/rae/cnblogs/AppMobclickAgent.java
+++ b/module-basic/src/main/java/com/rae/cnblogs/AppMobclickAgent.java
@@ -8,6 +8,7 @@
 
 import com.rae.cnblogs.sdk.UserProvider;
 import com.umeng.analytics.MobclickAgent;
+import com.umeng.commonsdk.UMConfigure;
 
 import java.text.SimpleDateFormat;
 import java.util.Date;
@@ -20,6 +21,20 @@
  */
 public final class AppMobclickAgent {
 
+    /**
+     * 初始化友盟统计
+     *
+     * @param applicationContext 上下文
+     * @param appKey             应用KEY
+     * @param channel            渠道名称
+     */
+    public static void init(Context applicationContext,
+                            String appKey,
+                            String channel) {
+        UMConfigure.setLogEnabled(false);
+        UMConfigure.init(applicationContext, appKey, channel, UMConfigure.DEVICE_TYPE_PHONE, null);
+    }
+
     /**
      * 统计打开时间
      */
@@ -35,7 +50,7 @@ public static void onAppOpenEvent(Context context) {
     /**
      * 统计用户打开APP事件
      */
-    public static void onUserOpenEvent(Context context) {
+    private static void onUserOpenEvent(Context context) {
         try {
             String blogApp = UserProvider.getInstance().isLogin() ? UserProvider.getInstance().getLoginUserInfo().getBlogApp() : "Guest";
             MobclickAgent.onEvent(context, "APP_USER_OPEN_EVENT", blogApp);
@@ -150,4 +165,19 @@ public static void onUpdateEvent(Context context) {
     }
 
 
+    /**
+     * 退出登录事件
+     */
+    public static void onProfileSignOff() {
+        MobclickAgent.onProfileSignOff();
+    }
+
+    /**
+     * 登录事件
+     *
+     * @param blogApp 用户
+     */
+    public static void onProfileSignIn(String blogApp) {
+        MobclickAgent.onProfileSignIn(blogApp);
+    }
 }
diff --git a/module-basic/src/main/java/com/rae/cnblogs/AppRoute.java b/module-basic/src/main/java/com/rae/cnblogs/AppRoute.java
new file mode 100644
index 0000000..bd4a360
--- /dev/null
+++ b/module-basic/src/main/java/com/rae/cnblogs/AppRoute.java
@@ -0,0 +1,544 @@
+package com.rae.cnblogs;
+
+import android.app.Activity;
+import android.app.Application;
+import android.content.Context;
+import android.content.Intent;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.alibaba.android.arouter.facade.Postcard;
+import com.alibaba.android.arouter.launcher.ARouter;
+import com.rae.cnblogs.sdk.bean.BlogBean;
+import com.rae.cnblogs.sdk.bean.BlogType;
+import com.rae.cnblogs.sdk.bean.MomentBean;
+import com.rae.cnblogs.sdk.model.MomentMetaData;
+
+import java.util.ArrayList;
+
+/**
+ * 路由
+ * Created by ChenRui on 2016/12/6 23:49.
+ */
+public final class AppRoute {
+
+    // WEB 登录
+    public static final int REQ_CODE_WEB_LOGIN = 100;
+    /**
+     * 登录返回
+     */
+    public static final int REQ_LOGIN = 10086;
+    /*朋友界面 - 来自粉丝*/
+    public static final int ACTIVITY_FRIENDS_TYPE_FANS = 1;
+    /*朋友界面 - 来自关注*/
+    private static final int ACTIVITY_FRIENDS_TYPE_FOLLOW = 2;
+    // 分类
+    public static final int REQ_CODE_CATEGORY = 102;
+    // 收藏
+    public static final int REQ_CODE_FAVORITES = 103;
+    // 博主
+    public static final int REQ_CODE_BLOGGER = 104;
+    // 发布闪存
+//    public static final int REQ_POST_MOMENT = 105;
+    // 图片选择
+    public static final int REQ_IMAGE_SELECTION = 106;
+    // 图片选择
+    public static final int REQ_CODE_IMAGE_SELECTED = 107;
+
+    /**
+     * 博客内容
+     */
+    public static final String PATH_BLOG_CONTENT = "/app/blog/content";
+    /**
+     * 网页
+     */
+    public static final String PATH_WEB = "/web/index";
+    /**
+     * 意见反馈
+     */
+    public static final String PATH_FEEDBACK = "/app/feedback";
+    /**
+     * 首页
+     */
+    public static final String PATH_APP_HOME = "/app/home";
+    /**
+     * 登录
+     */
+    public static final String PATH_LOGIN = "/app/login";
+    /**
+     * 网页登录
+     */
+    public static final String PATH_WEB_LOGIN = "/web/login";
+    /**
+     * 粉丝以及关注
+     */
+    public static final String PATH_FRIENDS = "/app/friends";
+    /**
+     * 图片预览
+     */
+    public static final String PATH_IMAGE_PREVIEW = "/image/preview";
+    /**
+     * 博主主页
+     */
+    public static final String PATH_BLOGGER = "/blogger/index";
+
+    /**
+     * 栏目分类
+     */
+    public static final String PATH_CATEGORY = "/category/index";
+    /**
+     * 我的收藏
+     */
+    public static final String PATH_FAVORITE = "/favorite/index";
+    /**
+     * 设置
+     */
+    public static final String PATH_SETTING = "/app/setting";
+    /**
+     * 搜索
+     */
+    public static final String PATH_SEARCH = "/app/search";
+    /**
+     * 系统消息
+     */
+    public static final String PATH_SYSTEM_MESSAGE = "/system/message";
+    /**
+     * 字体设置
+     */
+    public static final String PATH_FONT_SETTING = "/font/setting";
+    /**
+     * 博客评论
+     */
+    public static final String PATH_BLOG_COMMENT = "/app/blog/comment";
+    /**
+     * 发布闪存
+     */
+    public static final String PATH_MOMENT_POST = "/moment/post";
+    /**
+     * 闪存详情
+     */
+    public static final String PATH_MOMENT_DETAIL = "/moment/detail";
+    /**
+     * 闪存消息
+     */
+    public static final String PATH_MOMENT_MESSAGE = "/moment/message";
+    /**
+     * 提到我的闪存消息
+     */
+    public static final String PATH_MOMENT_MENTION = "/moment/mention";
+    /**
+     * 图片选择
+     */
+    public static final String PATH_IMAGE_SELECTION = "/image/selection";
+    /**
+     * 关于我们
+     */
+    public static final String PATH_ABOUT_ME = "/app/about";
+    /**
+     * 浏览记录
+     */
+    public static final String PATH_BLOG_HISTORY = "/blog/history";
+
+    /**
+     * 初始化
+     */
+    static void init(@NonNull Application applicationContext, boolean debug) {
+        if (debug) {
+            debug();
+        }
+        ARouter.init(applicationContext);
+    }
+
+    public static void debug() {
+        ARouter.openDebug();
+        ARouter.openLog();
+        ARouter.printStackTrace();
+    }
+
+
+    public static void route(Context context, String path) {
+        ARouter.getInstance().build(path).navigation(context);
+    }
+
+    /**
+     * 博客正文界面
+     *
+     * @param blogId 博客ID
+     * @param type   博客类型
+     */
+    public static void routeToBlogContent(Context context, String blogId, BlogType type) {
+        ARouter.getInstance().build(PATH_BLOG_CONTENT)
+                .withString("blogId", blogId)
+                .withString("type", type.getTypeName())
+                .navigation(context);
+    }
+
+    /**
+     * 博客正文界面
+     *
+     * @param blog 博客实体
+     * @param type 博客类型
+     */
+    public static void routeToBlogContent(Context context, @NonNull BlogBean blog, @NonNull BlogType type) {
+        // 不传递摘要和正文这些过大的数据。进去博文正文之后再从数据库拉取。已经在BlogBean里面处理大数据问题
+        ARouter.getInstance().build(PATH_BLOG_CONTENT)
+                .withParcelable("blog", blog)
+                .withString("blogId", blog.getBlogId())
+                .withString("type", type.getTypeName())
+                .navigation(context);
+    }
+
+    /**
+     * 网页
+     *
+     * @param url 路径
+     */
+    public static void routeToWeb(Context context, String url) {
+        ARouter.getInstance().build(PATH_WEB).withString("url", url).navigation(context);
+    }
+
+    /**
+     * 用户反馈
+     */
+    public static void routeToFeedback(Context context) {
+        ARouter.getInstance().build(PATH_FEEDBACK)
+                .withFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+                .navigation(context);
+    }
+
+
+    /**
+     * 网页，新线程
+     *
+     * @param url 路径
+     */
+    public static void routeToWebNewTask(Context context, String url) {
+        ARouter.getInstance().build(PATH_FEEDBACK)
+                .withFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                .navigation(context);
+    }
+
+    /**
+     * 主界面
+     */
+    public static void routeToMain(Context context) {
+        ARouter.getInstance().build(PATH_APP_HOME)
+                .withFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
+                        | Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+                        | Intent.FLAG_ACTIVITY_SINGLE_TOP
+                        | Intent.FLAG_ACTIVITY_CLEAR_TOP)
+                .navigation(context);
+    }
+
+
+    /**
+     * 登录
+     */
+    public static void routeToLogin(Context context) {
+        route(context, PATH_LOGIN);
+    }
+
+    /**
+     * 登录带返回结果
+     */
+    public static void routeToLoginForResult(Activity context) {
+        ARouter.getInstance().build(PATH_LOGIN).navigation(context, REQ_LOGIN);
+    }
+
+    /**
+     * 跳转登录界面，有回调结果
+     */
+    public static void routeToWebLogin(Activity context) {
+        ARouter.getInstance().build(PATH_WEB_LOGIN).navigation(context, REQ_CODE_WEB_LOGIN);
+    }
+
+    /**
+     * 粉丝
+     *
+     * @param bloggerName 博主昵称
+     * @param blogApp     博主ID
+     */
+    public static void routeToFans(Context context, String bloggerName, String blogApp) {
+        routeToFriends(context, ACTIVITY_FRIENDS_TYPE_FANS, bloggerName, blogApp);
+    }
+
+    /**
+     * 关注
+     *
+     * @param bloggerName 博主昵称
+     * @param blogApp     博主ID
+     */
+    public static void routeToFollow(Context context, String bloggerName, String blogApp) {
+        routeToFriends(context, ACTIVITY_FRIENDS_TYPE_FOLLOW, bloggerName, blogApp);
+    }
+
+    /**
+     * 跳转到朋友界面
+     *
+     * @param type        来源类型，参考该类{@link #ACTIVITY_FRIENDS_TYPE_FANS}
+     * @param bloggerName 博主昵称
+     * @param blogApp     博主ID
+     */
+    private static void routeToFriends(Context context, int type, String bloggerName, String blogApp) {
+        ARouter.getInstance().build(PATH_FRIENDS)
+                .withString("blogApp", blogApp)
+                .withString("bloggerName", bloggerName)
+                .withInt("fromType", type)
+                .navigation(context);
+    }
+
+    /**
+     * 图片大图预览
+     *
+     * @param images   图片数组
+     * @param position 跳转到低几张图片，默认传0
+     */
+    public static void routeToImagePreview(Activity context, @NonNull ArrayList<String> images, int position, ArrayList<String> selectedImages, int maxCount) {
+        Postcard postcard = ARouter.getInstance().build(PATH_IMAGE_PREVIEW);
+        postcard.withStringArrayList("images", images)
+                .withInt("position", position)
+                .withInt("maxCount", maxCount);
+        if (selectedImages != null) {
+            postcard.withStringArrayList("selectedImages", selectedImages);
+        }
+        postcard.navigation(context, REQ_CODE_IMAGE_SELECTED);
+    }
+
+    /**
+     * 图片大图预览
+     *
+     * @param images   图片数组
+     * @param position 跳转到低几张图片，默认传0
+     */
+    public static void routeToImagePreview(Activity context, @NonNull ArrayList<String> images, int position) {
+        routeToImagePreview(context, images, position, null, 0);
+    }
+
+    /**
+     * 图片大图预览
+     *
+     * @param images   图片数组
+     * @param position 跳转到低几张图片，默认传0
+     */
+    public static void routeToImagePreview(Context context, @NonNull ArrayList<String> images, int position) {
+        ARouter.getInstance().build(PATH_IMAGE_PREVIEW)
+                .withStringArrayList("images", images)
+                .withInt("position", position)
+                .navigation(context);
+    }
+
+
+    /**
+     * 图片查看
+     *
+     * @param imgUrl 图片路径
+     */
+    public static void routeToImagePreview(Activity context, @NonNull String imgUrl) {
+        ArrayList<String> data = new ArrayList<>();
+        data.add(imgUrl);
+        routeToImagePreview(context, data, 0);
+    }
+
+
+    /**
+     * 图片查看
+     *
+     * @param imgUrl 图片路径
+     */
+    public static void routeToImagePreview(Context context, @NonNull String imgUrl) {
+        ArrayList<String> data = new ArrayList<>();
+        data.add(imgUrl);
+        routeToImagePreview(context, data, 0);
+    }
+
+    /**
+     * 博主界面
+     *
+     * @param blogApp 博客APP
+     */
+    public static void routeToBlogger(Context context, @Nullable String blogApp) {
+        if (TextUtils.isEmpty(blogApp)) {
+            Log.w("AppRoute", "route to blogger error: blog is null!");
+            return;
+        }
+        ARouter.getInstance().build(PATH_BLOGGER).withString("blogApp", blogApp).navigation(context);
+    }
+
+    /**
+     * 博主界面
+     *
+     * @param blogApp 博客APP
+     */
+    public static void routeToBlogger(Activity context, String blogApp) {
+        if (TextUtils.isEmpty(blogApp)) {
+            Log.w("AppRoute", "route to blogger error: blog is null!");
+            return;
+        }
+        ARouter.getInstance().build(PATH_BLOGGER).withString("blogApp", blogApp).navigation(context, REQ_CODE_BLOGGER);
+    }
+
+
+    /**
+     * 分类管理
+     */
+    public static void routeToCategoryForResult(Activity context) {
+        ARouter.getInstance().build(PATH_CATEGORY).navigation(context, REQ_CODE_CATEGORY);
+    }
+
+    /**
+     * 我的收藏
+     */
+    public static void jumpToFavorites(Activity context) {
+        ARouter.getInstance().build(PATH_FAVORITE).navigation(context, REQ_CODE_FAVORITES);
+    }
+
+    /**
+     * 设置
+     */
+    public static void jumpToSetting(Context context) {
+        route(context, PATH_SETTING);
+    }
+
+    /**
+     * 搜索
+     */
+    public static void routeToSearch(Context context) {
+        routeToSearch(context, 0);
+    }
+
+    /**
+     * 搜索-新闻
+     */
+    public static void routeToSearchNews(Context context) {
+        routeToSearch(context, 2);
+    }
+
+    /**
+     * 搜索-知识库
+     */
+    public static void routeToSearchKb(Context context) {
+        routeToSearch(context, 3);
+    }
+
+    /**
+     * 搜索
+     */
+    private static void routeToSearch(Context context, int position) {
+        ARouter.getInstance().build(PATH_SEARCH)
+                .withInt("position", position)
+                .navigation(context);
+    }
+
+    /**
+     * 搜索-博主
+     */
+    public static void routeToSearchBlogger(Context context, String blogApp, String nickName) {
+        ARouter.getInstance().build(PATH_SEARCH)
+                .withString("blogApp", blogApp)
+                .withString("nickName", nickName)
+                .navigation(context);
+    }
+
+    /**
+     * 系统消息
+     */
+    public static void routeToSystemMessage(Context context) {
+        route(context, PATH_SYSTEM_MESSAGE);
+    }
+
+    /**
+     * 字体设置
+     */
+    public static void routeToFontSetting(Context context) {
+        route(context, PATH_FONT_SETTING);
+    }
+
+    /**
+     * 博客评论
+     */
+    public static void routeToComment(Context context, BlogBean blog, BlogType type) {
+        ARouter.getInstance().build(PATH_BLOG_COMMENT)
+                .withString("type", type.getTypeName())
+                .withParcelable("blog", blog)
+                .navigation(context);
+    }
+
+    /**
+     * 发布闪存
+     */
+    public static void routeToPostMoment(Activity context) {
+        routeToPostMoment(context, null);
+    }
+
+    /**
+     * 发布闪存
+     */
+    public static void routeToPostMoment(Activity context, @Nullable MomentMetaData data) {
+        ARouter.getInstance().build(PATH_MOMENT_POST)
+                .withParcelable(Intent.EXTRA_TEXT, data)
+                .navigation(context);
+
+    }
+
+    /**
+     * 闪存详情
+     */
+    public static void routeToMomentDetail(Context context, MomentBean data) {
+        ARouter.getInstance().build(PATH_MOMENT_DETAIL)
+                .withParcelable("data", data)
+                .navigation(context);
+    }
+
+    /**
+     * 闪存详情
+     */
+    public static void routeToMomentDetail(Context context, String userAlias, String ingId) {
+        ARouter.getInstance().build(PATH_MOMENT_DETAIL)
+                .withString("ingId", ingId)
+                .withString("userId", userAlias)
+                .navigation(context);
+    }
+
+
+    /**
+     * 闪存消息
+     */
+    public static void routeToMomentMessage(Context context) {
+        ARouter.getInstance().build(PATH_MOMENT_MESSAGE).navigation(context);
+    }
+
+    /**
+     * 提到我的闪存
+     */
+    public static void jumpToMomentAtMe(Context context) {
+        ARouter.getInstance().build(PATH_MOMENT_MENTION).navigation(context);
+    }
+
+    /**
+     * 跳转到图片选择
+     */
+    public static void jumpToImageSelection(Activity context, ArrayList<String> selectedImages) {
+
+        Postcard postcard = ARouter.getInstance().build(PATH_IMAGE_SELECTION);
+        if (selectedImages != null) {
+            postcard.withStringArrayList("selectedImages", selectedImages);
+        }
+        postcard.navigation(context, REQ_IMAGE_SELECTION);
+    }
+
+    /**
+     * 关于我们
+     */
+    public static void jumpToAboutMe(Context context) {
+        route(context, PATH_ABOUT_ME);
+    }
+
+    /**
+     * 浏览记录
+     */
+    public static void routeToHistory(Context context) {
+        route(context, PATH_BLOG_HISTORY);
+    }
+}
diff --git a/app/src/main/java/com/rae/cnblogs/AppStatusBar.java b/module-basic/src/main/java/com/rae/cnblogs/AppStatusBar.java
similarity index 100%
rename from app/src/main/java/com/rae/cnblogs/AppStatusBar.java
rename to module-basic/src/main/java/com/rae/cnblogs/AppStatusBar.java
diff --git a/module-basic/src/main/java/com/rae/cnblogs/ApplicationCompat.java b/module-basic/src/main/java/com/rae/cnblogs/ApplicationCompat.java
new file mode 100644
index 0000000..297409d
--- /dev/null
+++ b/module-basic/src/main/java/com/rae/cnblogs/ApplicationCompat.java
@@ -0,0 +1,45 @@
+package com.rae.cnblogs;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.text.TextUtils;
+
+import com.meituan.android.walle.WalleChannelReader;
+
+/**
+ * 应用程序扩展类
+ */
+public final class ApplicationCompat {
+
+    /**
+     * 获取版本号
+     */
+    public static int getVersionCode(Context context) {
+        try {
+            return context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_META_DATA).versionCode;
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+        }
+        return 1;
+    }
+
+    /**
+     * 获取版本名称
+     */
+    public static String getVersionName(Context context) {
+        try {
+            return context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_META_DATA).versionName;
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+        }
+        return "1.0.0";
+    }
+
+    /**
+     * 获取渠道包，如果没有定义渠道包默认返回official
+     */
+    public static String getChannel(Context context) {
+        String channel = WalleChannelReader.getChannel(context);
+        return TextUtils.isEmpty(channel) ? "official" : channel;
+    }
+}
diff --git a/module-basic/src/main/java/com/rae/cnblogs/CnblogsTinkerApplication.java b/module-basic/src/main/java/com/rae/cnblogs/CnblogsTinkerApplication.java
new file mode 100644
index 0000000..45661de
--- /dev/null
+++ b/module-basic/src/main/java/com/rae/cnblogs/CnblogsTinkerApplication.java
@@ -0,0 +1,27 @@
+package com.rae.cnblogs;
+
+import android.app.Application;
+import android.content.Context;
+import android.support.multidex.MultiDex;
+
+import com.tencent.tinker.loader.app.TinkerApplication;
+import com.tencent.tinker.loader.shareutil.ShareConstants;
+
+public class CnblogsTinkerApplication extends TinkerApplication {
+
+    public CnblogsTinkerApplication() {
+        super(ShareConstants.TINKER_ENABLE_ALL, "com.rae.cnblogs.CnblogsApplicationProxy");
+    }
+
+
+    public Application getApplication() {
+        return this;
+    }
+
+    @Override
+    protected void attachBaseContext(Context base) {
+        MultiDex.install(base); // 解决Tinker存在的BUG，一定要在这之前初始化
+        super.attachBaseContext(base);
+    }
+
+}
diff --git a/app/src/main/java/com/rae/cnblogs/RxObservable.java b/module-basic/src/main/java/com/rae/cnblogs/RxObservable.java
similarity index 95%
rename from app/src/main/java/com/rae/cnblogs/RxObservable.java
rename to module-basic/src/main/java/com/rae/cnblogs/RxObservable.java
index d6f1462..5288051 100644
--- a/app/src/main/java/com/rae/cnblogs/RxObservable.java
+++ b/module-basic/src/main/java/com/rae/cnblogs/RxObservable.java
@@ -17,8 +17,10 @@
 
 /**
  * RX 接口回调处理
+ * [已过期] 推荐使用{@link com.rae.cnblogs.basic.LifecycleProvider}来管理生命周期
  * Created by ChenRui on 2017/5/5 0005 16:52.
  */
+@Deprecated
 public final class RxObservable {
 
     private final static Map<String, List<Disposable>> sObservableDisposableList = new WeakHashMap<>();
@@ -33,7 +35,7 @@ public static <T> Observable<T> create(Observable<T> observable, final String ta
         return observable
                 .doOnSubscribe(new Consumer<Disposable>() {
                     @Override
-                    public void accept(@NonNull Disposable disposable) throws Exception {
+                    public void accept(@NonNull Disposable disposable) {
                         put(disposable, tag);
                     }
                 })
@@ -44,7 +46,7 @@ public void accept(@NonNull Disposable disposable) throws Exception {
     public static Observable<Integer> newThread() {
         return Observable.just(0).doOnSubscribe(new Consumer<Disposable>() {
             @Override
-            public void accept(Disposable disposable) throws Exception {
+            public void accept(Disposable disposable) {
                 put(disposable, "thread");
             }
         }).subscribeOn(Schedulers.newThread());
diff --git a/app/src/main/java/com/rae/cnblogs/activity/BaseActivity.java b/module-basic/src/main/java/com/rae/cnblogs/activity/BasicActivity.java
similarity index 85%
rename from app/src/main/java/com/rae/cnblogs/activity/BaseActivity.java
rename to module-basic/src/main/java/com/rae/cnblogs/activity/BasicActivity.java
index 91acc51..de47012 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/BaseActivity.java
+++ b/module-basic/src/main/java/com/rae/cnblogs/activity/BasicActivity.java
@@ -13,10 +13,10 @@
 import android.widget.TextView;
 
 import com.rae.cnblogs.AppStatusBar;
-import com.rae.cnblogs.CnblogsApplication;
-import com.rae.cnblogs.R;
+import com.rae.cnblogs.ApplicationCompat;
 import com.rae.cnblogs.RxObservable;
-import com.rae.cnblogs.sdk.config.CnblogSdkConfig;
+import com.rae.cnblogs.basic.R;
+import com.rae.cnblogs.sdk.config.CnblogAppConfig;
 import com.rae.swift.Rx;
 import com.umeng.analytics.MobclickAgent;
 
@@ -28,24 +28,11 @@
  * 基类
  * Created by ChenRui on 2016/12/1 21:35.
  */
-public abstract class BaseActivity extends AppCompatActivity {
+public abstract class BasicActivity extends AppCompatActivity {
 
     protected View mBackView;
     protected Toolbar mToolBar;
 
-    private void bindView() {
-        ButterKnife.bind(this);
-        mBackView = findViewById(R.id.back);
-        if (mBackView != null) {
-            mBackView.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    onBackClick(v);
-                }
-            });
-        }
-    }
-
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         onStatusBarColorChanged();
@@ -77,7 +64,16 @@ protected void onDestroy() {
     @Override
     public void setContentView(@LayoutRes int layoutResID) {
         super.setContentView(layoutResID);
-        bindView();
+        ButterKnife.bind(this);
+        mBackView = findViewById(R.id.back);
+        if (mBackView != null) {
+            mBackView.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    onBackClick(v);
+                }
+            });
+        }
         View toolBar = findViewById(R.id.title_tool_bar);
         if (toolBar != null) {
             mToolBar = (Toolbar) toolBar;
@@ -92,7 +88,7 @@ public void setContentView(@LayoutRes int layoutResID) {
     public void setTitle(CharSequence title) {
         super.setTitle(title);
         if (mToolBar != null) {
-            TextView titleView = (TextView) mToolBar.findViewById(R.id.tv_title);
+            TextView titleView = mToolBar.findViewById(R.id.tv_title);
             if (titleView != null) {
                 titleView.setText(title);
             }
@@ -131,7 +127,7 @@ protected int getHomeAsUpIndicator() {
         return 0;
     }
 
-    protected BaseActivity getContext() {
+    protected BasicActivity getContext() {
         return this;
     }
 
@@ -160,22 +156,22 @@ protected void onLoginCallBack() {
     }
 
     public int getVersionCode() {
-        return ((CnblogsApplication) getApplication()).getVersionCode();
+        return ApplicationCompat.getVersionCode(getApplicationContext());
     }
 
     public String getVersionName() {
-        return ((CnblogsApplication) getApplication()).getVersionName();
+        return ApplicationCompat.getVersionName(getApplicationContext());
     }
 
     /**
      * 获取渠道包
      */
     public String getChannel() {
-        return ((CnblogsApplication) getApplication()).getChannel();
+        return ApplicationCompat.getChannel(getApplicationContext());
     }
 
-    protected CnblogSdkConfig config() {
-        return CnblogSdkConfig.getsInstance(getContext().getApplicationContext());
+    protected CnblogAppConfig config() {
+        return CnblogAppConfig.getsInstance(getContext().getApplicationContext());
     }
 
 }
diff --git a/app/src/main/java/com/rae/cnblogs/activity/SwipeBackBaseActivity.java b/module-basic/src/main/java/com/rae/cnblogs/activity/SwipeBackBasicActivity.java
similarity index 91%
rename from app/src/main/java/com/rae/cnblogs/activity/SwipeBackBaseActivity.java
rename to module-basic/src/main/java/com/rae/cnblogs/activity/SwipeBackBasicActivity.java
index 8d56e41..1636fcd 100644
--- a/app/src/main/java/com/rae/cnblogs/activity/SwipeBackBaseActivity.java
+++ b/module-basic/src/main/java/com/rae/cnblogs/activity/SwipeBackBasicActivity.java
@@ -15,10 +15,10 @@
 import me.imid.swipebacklayout.lib.app.SwipeBackActivityHelper;
 
 /**
- * 可以滑动返回的
+ * 可以滑动返回的Activity
  * Created by ChenRui on 2017/1/4 0004 10:03.
  */
-public abstract class SwipeBackBaseActivity extends BaseActivity implements SwipeBackActivityBase {
+public abstract class SwipeBackBasicActivity extends BasicActivity implements SwipeBackActivityBase {
 
     private SwipeBackActivityHelper mHelper;
 
@@ -37,6 +37,12 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
         }
     }
 
+    @Override
+    public void setContentView(int layoutResID) {
+        super.setContentView(layoutResID);
+        showHomeAsUp();
+    }
+
     @Override
     protected void onPostCreate(Bundle savedInstanceState) {
         super.onPostCreate(savedInstanceState);
diff --git a/module-basic/src/main/java/com/rae/cnblogs/basic/BasicPresenter.java b/module-basic/src/main/java/com/rae/cnblogs/basic/BasicPresenter.java
new file mode 100644
index 0000000..6dad212
--- /dev/null
+++ b/module-basic/src/main/java/com/rae/cnblogs/basic/BasicPresenter.java
@@ -0,0 +1,60 @@
+package com.rae.cnblogs.basic;
+
+import android.content.Context;
+
+/**
+ * MVP Presenter
+ * Created by ChenRui on 2018/4/18.
+ */
+public abstract class BasicPresenter<V extends IPresenterView> implements IPresenter {
+
+    private final V mView;
+
+    private LifecycleProvider mLifecycleProvider;
+
+    public BasicPresenter(V view) {
+        this.mView = checkNotNull(view);
+        mLifecycleProvider = new LifecycleProvider(view);
+    }
+
+    public V getView() {
+        return mView;
+    }
+
+    protected Context getContext() {
+        return mView.getContext();
+    }
+
+    @Override
+    public void start() {
+        onStart();
+    }
+
+    @Override
+    public void destroy() {
+        mLifecycleProvider.dispose();
+        onDestroy();
+    }
+
+    /**
+     * 释放数据
+     */
+    protected void onDestroy() {
+    }
+
+    /**
+     * 加载数据
+     */
+    protected abstract void onStart();
+
+
+    /**
+     * 检查是否为空，如果为空抛出异常
+     */
+    private <T> T checkNotNull(T object) {
+        if (object == null) {
+            throw new NullPointerException("object is null ");
+        }
+        return object;
+    }
+}
diff --git a/module-basic/src/main/java/com/rae/cnblogs/basic/IPresenter.java b/module-basic/src/main/java/com/rae/cnblogs/basic/IPresenter.java
new file mode 100644
index 0000000..fed876a
--- /dev/null
+++ b/module-basic/src/main/java/com/rae/cnblogs/basic/IPresenter.java
@@ -0,0 +1,18 @@
+package com.rae.cnblogs.basic;
+
+/**
+ * 公共的Presenter接口
+ * Created by ChenRui on 2018/4/18.
+ */
+public interface IPresenter {
+
+    /**
+     * 开始加载数据
+     */
+    void start();
+
+    /**
+     * 释放数据
+     */
+    void destroy();
+}
diff --git a/module-basic/src/main/java/com/rae/cnblogs/basic/IPresenterView.java b/module-basic/src/main/java/com/rae/cnblogs/basic/IPresenterView.java
new file mode 100644
index 0000000..9d66e86
--- /dev/null
+++ b/module-basic/src/main/java/com/rae/cnblogs/basic/IPresenterView.java
@@ -0,0 +1,21 @@
+package com.rae.cnblogs.basic;
+
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+/**
+ * 公共的Presenter接口
+ * Created by ChenRui on 2018/4/18.
+ */
+public interface IPresenterView extends LifecycleOwner {
+
+    @NonNull
+    @Override
+    Lifecycle getLifecycle();
+
+    @Nullable
+    Context getContext();
+}
diff --git a/module-basic/src/main/java/com/rae/cnblogs/basic/LifecycleProvider.java b/module-basic/src/main/java/com/rae/cnblogs/basic/LifecycleProvider.java
new file mode 100644
index 0000000..26fd66f
--- /dev/null
+++ b/module-basic/src/main/java/com/rae/cnblogs/basic/LifecycleProvider.java
@@ -0,0 +1,54 @@
+package com.rae.cnblogs.basic;
+
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import android.support.annotation.NonNull;
+
+import com.trello.lifecycle2.android.lifecycle.AndroidLifecycle;
+
+import io.reactivex.Observable;
+import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.functions.Consumer;
+
+/**
+ * 生命周期提供者，用于提供Presenter和View之间的关联
+ */
+public class LifecycleProvider {
+
+    // 生命周期绑定
+    private final com.trello.rxlifecycle2.LifecycleProvider<Lifecycle.Event> mLifecycleProvider;
+
+
+    @NonNull
+    private CompositeDisposable mCompositeDisposable;
+
+    public LifecycleProvider(LifecycleOwner view) {
+        this(AndroidLifecycle.createLifecycleProvider(view));
+    }
+
+    public LifecycleProvider(com.trello.rxlifecycle2.LifecycleProvider<Lifecycle.Event> mLifecycleProvider) {
+        this.mLifecycleProvider = mLifecycleProvider;
+        mCompositeDisposable = new CompositeDisposable();
+    }
+
+    /**
+     * 关联到生命周期里面去
+     */
+    public <T> Observable<T> with(Observable<T> observable) {
+        observable = observable.doOnSubscribe(new Consumer<Disposable>() {
+            @Override
+            public void accept(Disposable disposable) throws Exception {
+                mCompositeDisposable.add(disposable);
+            }
+        });
+        return observable.compose(mLifecycleProvider.<T>bindToLifecycle());
+    }
+
+    /**
+     * 释放所有，生命周期会自动释放
+     */
+    public void dispose() {
+        mCompositeDisposable.clear();
+    }
+}
diff --git a/module-basic/src/main/java/com/rae/cnblogs/fragment/BasicFragment.java b/module-basic/src/main/java/com/rae/cnblogs/fragment/BasicFragment.java
new file mode 100644
index 0000000..2e6ddc1
--- /dev/null
+++ b/module-basic/src/main/java/com/rae/cnblogs/fragment/BasicFragment.java
@@ -0,0 +1,67 @@
+package com.rae.cnblogs.fragment;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.rae.cnblogs.sdk.config.CnblogAppConfig;
+
+import butterknife.ButterKnife;
+import butterknife.Unbinder;
+
+
+/**
+ * Fragment基础类，所有的Fragment都需要继承这个
+ */
+public abstract class BasicFragment extends Fragment {
+
+    @Nullable
+    private Unbinder mUnBinder;
+
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+        View view = inflater.inflate(getLayoutId(), container, false);
+        mUnBinder = ButterKnife.bind(this, view);
+        onCreateView(view);
+        return view;
+    }
+
+    @Override
+    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        onLoadData();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mUnBinder != null) {
+            mUnBinder.unbind();
+            mUnBinder = null;
+        }
+    }
+
+    protected void onCreateView(View view) {
+    }
+
+    /**
+     * 实现类重写加载数据逻辑
+     */
+    protected void onLoadData() {
+    }
+
+    protected abstract int getLayoutId();
+
+    /**
+     * 获取配置文件
+     */
+    protected CnblogAppConfig config() {
+        // fix bug #478
+        return CnblogAppConfig.getsInstance(getContext());
+    }
+}
diff --git a/app/src/main/res/drawable-xhdpi/tab_channel_background.9.png b/module-basic/src/main/res/drawable-xhdpi/tab_channel_background.9.png
similarity index 100%
rename from app/src/main/res/drawable-xhdpi/tab_channel_background.9.png
rename to module-basic/src/main/res/drawable-xhdpi/tab_channel_background.9.png
diff --git a/app/src/main/res/drawable-xhdpi/tab_channel_background_night.9.png b/module-basic/src/main/res/drawable-xhdpi/tab_channel_background_night.9.png
similarity index 100%
rename from app/src/main/res/drawable-xhdpi/tab_channel_background_night.9.png
rename to module-basic/src/main/res/drawable-xhdpi/tab_channel_background_night.9.png
diff --git a/app/src/main/res/drawable-xxhdpi/ic_back.png b/module-basic/src/main/res/drawable-xxhdpi/ic_back.png
similarity index 100%
rename from app/src/main/res/drawable-xxhdpi/ic_back.png
rename to module-basic/src/main/res/drawable-xxhdpi/ic_back.png
diff --git a/app/src/main/res/drawable-xxhdpi/ic_back_night.png b/module-basic/src/main/res/drawable-xxhdpi/ic_back_night.png
similarity index 100%
rename from app/src/main/res/drawable-xxhdpi/ic_back_night.png
rename to module-basic/src/main/res/drawable-xxhdpi/ic_back_night.png
diff --git a/app/src/main/res/layout/view_title.xml b/module-basic/src/main/res/layout/view_title.xml
similarity index 78%
rename from app/src/main/res/layout/view_title.xml
rename to module-basic/src/main/res/layout/view_title.xml
index 70a3d5d..a4490a1 100644
--- a/app/src/main/res/layout/view_title.xml
+++ b/module-basic/src/main/res/layout/view_title.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:background="@drawable/tab_channel_background">
@@ -10,20 +9,19 @@
         style="@style/AppToolBar"
         android:layout_width="match_parent"
         android:layout_height="@dimen/action_bar_height"
+        android:fitsSystemWindows="false"
         android:gravity="center">
 
         <TextView
             android:id="@+id/tv_title"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginEnd="12dp"
-            android:layout_marginRight="12dp"
+            android:layout_marginEnd="24dp"
             android:ellipsize="end"
             android:singleLine="true"
             android:text="@string/title"
             android:textColor="@color/ph1"
-            android:textSize="18sp"/>
+            android:textSize="18sp" />
 
     </android.support.v7.widget.Toolbar>
-
-</FrameLayout>
\ No newline at end of file
+</FrameLayout>
diff --git a/module-basic/src/main/res/values/strings.xml b/module-basic/src/main/res/values/strings.xml
new file mode 100644
index 0000000..ec4e9e8
--- /dev/null
+++ b/module-basic/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<resources>
+    <string name="app_name">module-basic</string>
+    <string name="title">标题</string>
+</resources>
diff --git a/module-basic/src/main/res/values/styles.xml b/module-basic/src/main/res/values/styles.xml
new file mode 100644
index 0000000..48cda71
--- /dev/null
+++ b/module-basic/src/main/res/values/styles.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <style name="AppToolBar" parent="Widget.AppCompat.Toolbar">
+        <item name="contentInsetStart">0dp</item><!-- 设置该属性解决空白部分-->
+        <item name="contentInsetEnd">0dp</item>
+        <item name="contentInsetStartWithNavigation">0dp</item>
+        <item name="navigationIcon">@drawable/ic_back</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/module-blog/.gitignore b/module-blog/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/module-blog/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/module-blog/build.gradle b/module-blog/build.gradle
new file mode 100644
index 0000000..1136cfe
--- /dev/null
+++ b/module-blog/build.gradle
@@ -0,0 +1,45 @@
+if (isModule.toBoolean()) {
+    apply plugin: 'com.android.application'
+} else {
+    apply plugin: 'com.android.library'
+}
+
+android {
+    compileSdkVersion rootProject.ext.compileSdkVersion
+    defaultConfig {
+        minSdkVersion rootProject.ext.minSdkVersion
+        targetSdkVersion rootProject.ext.targetSdkVersion
+        versionCode 1
+        versionName "1.0"
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        multiDexEnabled true
+        javaCompileOptions {
+            annotationProcessorOptions {
+                arguments = [moduleName: project.getName()]
+            }
+        }
+
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation rootProject.ext.appcompat
+    implementation rootProject.ext.design
+    implementation rootProject.ext.butterknife
+    annotationProcessor rootProject.ext.butterknifeCompiler
+    // 阿里路由注解
+    annotationProcessor rootProject.ext.arouterCompiler
+    testImplementation 'junit:junit:4.12'
+    implementation project(':module-basic')
+    implementation project(':sdk')
+    implementation project(':widget')
+}
diff --git a/module-blog/proguard-rules.pro b/module-blog/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/module-blog/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/module-blog/src/androidTest/java/com/rae/cnblogs/blog/ExampleInstrumentedTest.java b/module-blog/src/androidTest/java/com/rae/cnblogs/blog/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..cb31d69
--- /dev/null
+++ b/module-blog/src/androidTest/java/com/rae/cnblogs/blog/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.rae.cnblogs.blog;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getTargetContext();
+
+        assertEquals("com.rae.cnblogs.blog.test", appContext.getPackageName());
+    }
+}
diff --git a/module-blog/src/main/AndroidManifest.xml b/module-blog/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5287877
--- /dev/null
+++ b/module-blog/src/main/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.rae.cnblogs.blog">
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_back"
+        android:label="@string/app_name"
+        android:fullBackupContent="true">
+        <activity
+            android:name=".activity.BlogHistoryActivity"
+            android:label="@string/label_history">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/module-blog/src/main/java/com/rae/cnblogs/blog/activity/BlogHistoryActivity.java b/module-blog/src/main/java/com/rae/cnblogs/blog/activity/BlogHistoryActivity.java
new file mode 100644
index 0000000..6d6560e
--- /dev/null
+++ b/module-blog/src/main/java/com/rae/cnblogs/blog/activity/BlogHistoryActivity.java
@@ -0,0 +1,28 @@
+package com.rae.cnblogs.blog.activity;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.RecyclerView;
+
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.rae.cnblogs.AppRoute;
+import com.rae.cnblogs.activity.SwipeBackBasicActivity;
+import com.rae.cnblogs.blog.R;
+
+import butterknife.BindView;
+
+/**
+ * 浏览记录
+ */
+@Route(path = AppRoute.PATH_BLOG_HISTORY)
+public class BlogHistoryActivity extends SwipeBackBasicActivity {
+
+    @BindView(R.id.recycler_view)
+    RecyclerView mRecyclerView;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_blog_history);
+    }
+}
diff --git a/module-blog/src/main/res/layout/activity_blog_history.xml b/module-blog/src/main/res/layout/activity_blog_history.xml
new file mode 100644
index 0000000..a44537f
--- /dev/null
+++ b/module-blog/src/main/res/layout/activity_blog_history.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v7.widget.FitWindowsLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true"
+    android:orientation="vertical">
+
+    <include layout="@layout/view_title" />
+
+    <com.rae.cnblogs.widget.RaeRecyclerView
+        android:id="@+id/recycler_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</android.support.v7.widget.FitWindowsLinearLayout>
\ No newline at end of file
diff --git a/module-blog/src/main/res/values/strings.xml b/module-blog/src/main/res/values/strings.xml
new file mode 100644
index 0000000..b1dde5d
--- /dev/null
+++ b/module-blog/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">博客模块</string>
+</resources>
diff --git a/module-blog/src/test/java/com/rae/cnblogs/blog/ExampleUnitTest.java b/module-blog/src/test/java/com/rae/cnblogs/blog/ExampleUnitTest.java
new file mode 100644
index 0000000..d545064
--- /dev/null
+++ b/module-blog/src/test/java/com/rae/cnblogs/blog/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.rae.cnblogs.blog;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}
\ No newline at end of file
diff --git a/module-resource/.gitignore b/module-resource/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/module-resource/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/module-resource/build.gradle b/module-resource/build.gradle
new file mode 100644
index 0000000..626d6a2
--- /dev/null
+++ b/module-resource/build.gradle
@@ -0,0 +1,25 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion rootProject.ext.compileSdkVersion
+    defaultConfig {
+        minSdkVersion rootProject.ext.minSdkVersion
+        targetSdkVersion rootProject.ext.targetSdkVersion
+        versionCode 1
+        versionName "1.0"
+
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+}
+
+dependencies {
+    api fileTree(dir: 'libs', include: ['*.jar'])
+    implementation rootProject.ext.appcompat
+}
diff --git a/app/libs/SocialSDK_QQ_Simplify.jar b/module-resource/libs/SocialSDK_QQ_Simplify.jar
similarity index 100%
rename from app/libs/SocialSDK_QQ_Simplify.jar
rename to module-resource/libs/SocialSDK_QQ_Simplify.jar
diff --git a/app/libs/SocialSDK_Sina_Simplify.jar b/module-resource/libs/SocialSDK_Sina_Simplify.jar
similarity index 100%
rename from app/libs/SocialSDK_Sina_Simplify.jar
rename to module-resource/libs/SocialSDK_Sina_Simplify.jar
diff --git a/app/libs/SocialSDK_WeChat_Simplify.jar b/module-resource/libs/SocialSDK_WeChat_Simplify.jar
similarity index 100%
rename from app/libs/SocialSDK_WeChat_Simplify.jar
rename to module-resource/libs/SocialSDK_WeChat_Simplify.jar
diff --git a/app/libs/umeng_social_api.jar b/module-resource/libs/umeng_social_api.jar
similarity index 100%
rename from app/libs/umeng_social_api.jar
rename to module-resource/libs/umeng_social_api.jar
diff --git a/app/libs/umeng_social_net.jar b/module-resource/libs/umeng_social_net.jar
similarity index 100%
rename from app/libs/umeng_social_net.jar
rename to module-resource/libs/umeng_social_net.jar
diff --git a/app/libs/umeng_social_tool.jar b/module-resource/libs/umeng_social_tool.jar
similarity index 100%
rename from app/libs/umeng_social_tool.jar
rename to module-resource/libs/umeng_social_tool.jar
diff --git a/module-resource/proguard-rules.pro b/module-resource/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/module-resource/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/module-resource/src/androidTest/java/com/rae/cnblogs/res/ExampleInstrumentedTest.java b/module-resource/src/androidTest/java/com/rae/cnblogs/res/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..9db2291
--- /dev/null
+++ b/module-resource/src/androidTest/java/com/rae/cnblogs/res/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.rae.cnblogs.res;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getTargetContext();
+
+        assertEquals("com.rae.cnblogs.res.test", appContext.getPackageName());
+    }
+}
diff --git a/module-resource/src/main/AndroidManifest.xml b/module-resource/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3cd38e5
--- /dev/null
+++ b/module-resource/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+<manifest package="com.rae.cnblogs.res" />
diff --git a/module-resource/src/main/res/values/colors.xml b/module-resource/src/main/res/values/colors.xml
new file mode 100644
index 0000000..26c44e9
--- /dev/null
+++ b/module-resource/src/main/res/values/colors.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorHintPrimary">#EEEEEE</color>
+    <color name="buttonColorPrimary">#3ba7ee</color>
+    <color name="transparent">#00000000</color>
+    <color name="navBar">@color/white</color>
+    <color name="dividerPrimary">#b2b2b2</color>
+    <color name="dividerColor">#dedede</color>
+    <color name="ph1">#2d2d2d</color>
+    <color name="ph2">#595959</color>
+    <color name="moment_author">#576b95</color>
+    <color name="ph3">#929292</color>
+    <color name="ph4">#999999</color>
+    <color name="default_background">#f9f9f9</color>
+    <color name="background_divider">#f8f8f8</color>
+    <color name="colorToastPrimary">#daedfa</color>
+    <color name="commentAvatarColor">#6293b3</color>
+    <color name="blog_item_pressed">#F0F0F0</color>
+    <color name="white">#FFFFFF</color>
+    <color name="statusBarColor">@color/white</color>
+    <color name="red">#F00000</color>
+    <color name="badge_color">#fec63d</color>
+    <color name="homeTabTextColor">#aaaaaa</color>
+    <color name="homeTabSelectedTextColor">#2d2d2d</color>
+    <color name="bloggerStatusBarColor">#303135</color>
+    <color name="color_login_reg">#3ba7ee</color>
+    <color name="color_image_selection_post">#3ba7ee</color>
+    <color name="blogger_image_alpha_color">#4C000000</color>
+</resources>
\ No newline at end of file
diff --git a/module-resource/src/main/res/values/dimens.xml b/module-resource/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..31f7656
--- /dev/null
+++ b/module-resource/src/main/res/values/dimens.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <dimen name="action_bar_height">52dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/module-resource/src/main/res/values/ids.xml b/module-resource/src/main/res/values/ids.xml
new file mode 100644
index 0000000..32e6c67
--- /dev/null
+++ b/module-resource/src/main/res/values/ids.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <item type="id" name="back" />
+    <item type="id" name="title_tool_bar" />
+    <item type="id" name="tv_title" />
+</resources>
\ No newline at end of file
diff --git a/module-resource/src/main/res/values/strings.xml b/module-resource/src/main/res/values/strings.xml
new file mode 100644
index 0000000..469beb7
--- /dev/null
+++ b/module-resource/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+<resources>
+    <string name="app_name">module-resource</string>
+    <string name="no_more_tips">全部加载完毕</string>
+    <string name="label_history">浏览记录</string>
+</resources>
diff --git a/module-resource/src/test/java/com/rae/cnblogs/res/ExampleUnitTest.java b/module-resource/src/test/java/com/rae/cnblogs/res/ExampleUnitTest.java
new file mode 100644
index 0000000..3acfadf
--- /dev/null
+++ b/module-resource/src/test/java/com/rae/cnblogs/res/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.rae.cnblogs.res;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}
\ No newline at end of file
diff --git a/push-sdk-patch.bat b/push-sdk-patch.bat
deleted file mode 100644
index a87d862..0000000
--- a/push-sdk-patch.bat
+++ /dev/null
@@ -1,4 +0,0 @@
-@echo off
-adb shell mkdir /sdcard/Android/data/com.rae.cnblogs/cache
-adb push sdk\build\intermediates\bundles\debug\sdk.dex /sdcard/Android/data/com.rae.cnblogs/cache/sdk-hotfix.patch
-
diff --git a/sdk/build.gradle b/sdk/build.gradle
index c00dbbf..2e204c1 100644
--- a/sdk/build.gradle
+++ b/sdk/build.gradle
@@ -4,14 +4,18 @@ android {
     compileSdkVersion rootProject.ext.compileSdkVersion
     buildToolsVersion rootProject.ext.buildToolsVersion
 
-    publishNonDefault true
-
     defaultConfig {
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
         testApplicationId "com.rae.cnblogs.sdk.test"
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
         buildConfigField "boolean", "LOG_DEBUG", "true"
+        multiDexEnabled true
+        javaCompileOptions {
+            annotationProcessorOptions {
+                arguments = [moduleName: project.getName()]
+            }
+        }
     }
     buildTypes {
         release {
@@ -28,9 +32,8 @@ android {
 
 dependencies {
     implementation fileTree(dir: 'libs', include: ['*.jar'])
-    androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
-        exclude group: 'com.android.support', module: 'support-annotations'
-    })
+    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+    androidTestImplementation 'com.github.raee:runit:1.0.0'
     testImplementation 'junit:junit:4.12'
 
     implementation rootProject.ext.annotations
@@ -38,19 +41,18 @@ dependencies {
     api rootProject.ext.rxjava
     api rootProject.ext.rxandroid
 
-//    compile "com.android.support:support-annotations:${rootProject.ext.supportVersion}"
-//    compile "com.android.support:support-v4:${rootProject.ext.supportVersion}"
-    androidTestImplementation 'com.github.raee:runit:1.0.0'
+
     api 'org.jsoup:jsoup:1.10.1'
-    api 'com.rae.swift:rae-library:1.0.1'
+    api ('com.rae.swift:rae-library:1.0.1',{
+        exclude group: 'com.android.support'
+    })
     implementation 'com.squareup.okhttp3:okhttp-ext:3.9.0'
     implementation 'com.squareup.okhttp3:okhttp-urlconnection:3.9.0'
     implementation 'com.squareup.retrofit2:retrofit:2.3.0'
     implementation 'com.squareup.okhttp3:logging-interceptor:3.6.0'
-    implementation 'com.michaelpardo:activeandroid:3.1.0-SNAPSHOT'
-    // RX JAVA
-//    compile 'io.reactivex.rxjava2:rxjava:2.1.0'
-//    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
+    // 数据库
+    api 'com.michaelpardo:activeandroid:3.1.0-SNAPSHOT'
+
     api 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
     api 'com.google.code.gson:gson:2.8.0'
     api 'com.tencent.bugly:crashreport_upgrade:1.3.4'
diff --git a/sdk/src/androidTest/java/rae/com/cnblogs/sdk/UserApiTest.java b/sdk/src/androidTest/java/rae/com/cnblogs/sdk/UserApiTest.java
index 1ef230d..c88c084 100644
--- a/sdk/src/androidTest/java/rae/com/cnblogs/sdk/UserApiTest.java
+++ b/sdk/src/androidTest/java/rae/com/cnblogs/sdk/UserApiTest.java
@@ -5,7 +5,6 @@
 import com.github.raee.runit.AndroidRUnit4ClassRunner;
 import com.rae.cnblogs.sdk.api.IUserApi;
 import com.rae.cnblogs.sdk.bean.UserInfoBean;
-import com.rae.cnblogs.sdk.utils.ApiEncrypt;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -40,9 +39,9 @@ protected void autoLogin() {
      */
     @Test
     public void testLogin() throws InterruptedException {
-        String userName = ApiEncrypt.encrypt("chenrui7");
-        String pwd = ApiEncrypt.encrypt("chenrui123456789");
-        runTest("testLogin", mApi.login(null, userName, pwd, true));
+//        String userName = ApiEncrypt.encrypt("chenrui7");
+//        String pwd = ApiEncrypt.encrypt("chenrui123456789");
+//        runTest("testLogin", mApi.login(null, userName, pwd, true));
     }
 
     /**
diff --git a/sdk/src/main/java/com/rae/cnblogs/sdk/UserProvider.java b/sdk/src/main/java/com/rae/cnblogs/sdk/UserProvider.java
index fd48097..04dc18e 100644
--- a/sdk/src/main/java/com/rae/cnblogs/sdk/UserProvider.java
+++ b/sdk/src/main/java/com/rae/cnblogs/sdk/UserProvider.java
@@ -6,7 +6,7 @@
 import android.webkit.CookieSyncManager;
 
 import com.rae.cnblogs.sdk.bean.UserInfoBean;
-import com.rae.cnblogs.sdk.config.CnblogSdkConfig;
+import com.rae.cnblogs.sdk.config.CnblogAppConfig;
 import com.rae.swift.session.SessionManager;
 
 import java.net.CookieHandler;
@@ -30,7 +30,7 @@ public final class UserProvider {
     private static UserProvider sUserProvider;
     private final Context mContext;
     private UserInfoBean mUserInfo;
-    private CnblogSdkConfig mConfig;
+    private CnblogAppConfig mConfig;
 
     public static UserProvider getInstance() {
         if (sUserProvider == null) {
@@ -47,7 +47,7 @@ public static void init(Context applicationContext) {
 
     public UserProvider(Context context) {
         mContext = context;
-        mConfig = CnblogSdkConfig.getsInstance(context);
+        mConfig = CnblogAppConfig.getsInstance(context);
     }
 
 
diff --git a/sdk/src/main/java/com/rae/cnblogs/sdk/config/CnblogSdkConfig.java b/sdk/src/main/java/com/rae/cnblogs/sdk/config/CnblogAppConfig.java
similarity index 71%
rename from sdk/src/main/java/com/rae/cnblogs/sdk/config/CnblogSdkConfig.java
rename to sdk/src/main/java/com/rae/cnblogs/sdk/config/CnblogAppConfig.java
index 5098478..e32a542 100644
--- a/sdk/src/main/java/com/rae/cnblogs/sdk/config/CnblogSdkConfig.java
+++ b/sdk/src/main/java/com/rae/cnblogs/sdk/config/CnblogAppConfig.java
@@ -4,39 +4,30 @@
 import android.content.SharedPreferences;
 import android.text.TextUtils;
 
-import com.google.gson.Gson;
+import com.rae.cnblogs.sdk.AppGson;
 import com.rae.cnblogs.sdk.bean.UserInfoBean;
 
 /**
  * 接口配置项
  * Created by ChenRui on 2017/1/14 01:53.
  */
-public class CnblogSdkConfig {
-
-    // 访问密钥 clientID:clientSecret
-    // cdfb6ec8-e78d-4c70-82df-7b1651a98808:TFgkwiEVDBGZwPncPL9b5a9_z7E2pipUPFHo9OWIeOXkGTImxr_-LQBMw9_gQLX94Faqkbs9VbL_CKk-
-//    public static final String API_AUTH_KEY = "Y2RmYjZlYzgtZTc4ZC00YzcwLTgyZGYtN2IxNjUxYTk4ODA4OlRGZ2t3aUVWREJHWndQbmNQTDliNWE5X3o3RTJwaXBVUEZIbzlPV0llT1hrR1RJbXhyXy1MUUJNdzlfZ1FMWDk0RmFxa2JzOVZiTF9DS2st";
-//    public static final String API_PUB_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp0wHYbg/NOPO3nzMD3dndwS0MccuMeXCHgVlGOoYyFwLdS24Im2e7YyhB0wrUsyYf0/nhzCzBK8ZC9eCWqd0aHbdgOQT6CuFQBMjbyGYvlVYU2ZP7kG9Ft6YV6oc9ambuO7nPZh+bvXH0zDKfi02prknrScAKC0XhadTHT3Al0QIDAQAB";
-    public static final byte[] API_PUB_KEY_BYTE = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp0wHYbg/NOPO3nzMD3dndwS0MccuMeXCHgVlGOoYyFwLdS24Im2e7YyhB0wrUsyYf0/nhzCzBK8ZC9eCWqd0aHbdgOQT6CuFQBMjbyGYvlVYU2ZP7kG9Ft6YV6oc9ambuO7nPZh+bvXH0zDKfi02prknrScAKC0XhadTHT3Al0QIDAQAB".getBytes();
-
+public class CnblogAppConfig {
     /**
      * 渠道
      */
     public static String APP_CHANNEL = "official";
+    private static CnblogAppConfig sInstance;
 
-    private static CnblogSdkConfig sInstance;
-    private Gson mGson = new Gson();
-
-    public static CnblogSdkConfig getsInstance(Context applicationContext) {
+    public static CnblogAppConfig getsInstance(Context applicationContext) {
         if (sInstance == null) {
-            sInstance = new CnblogSdkConfig(applicationContext.getApplicationContext());
+            sInstance = new CnblogAppConfig(applicationContext.getApplicationContext());
         }
         return sInstance;
     }
 
     private SharedPreferences mConfig;
 
-    private CnblogSdkConfig(Context context) {
+    private CnblogAppConfig(Context context) {
         mConfig = context.getApplicationContext().getSharedPreferences("CNBLOG_SDK_CONFIG", Context.MODE_PRIVATE);
     }
 
@@ -63,7 +54,7 @@ public void clearUserInfo() {
      */
     public void saveUserInfo(UserInfoBean userInfo) {
         if (userInfo != null)
-            mConfig.edit().putString("UserInfo", mGson.toJson(userInfo)).apply();
+            mConfig.edit().putString("UserInfo", AppGson.get().toJson(userInfo)).apply();
     }
 
     /**
@@ -76,7 +67,7 @@ public UserInfoBean getUserInfo() {
         if (TextUtils.isEmpty(json)) {
             return null;
         }
-        return mGson.fromJson(json, UserInfoBean.class);
+        return AppGson.get().fromJson(json, UserInfoBean.class);
     }
 
 
diff --git a/sdk/src/main/java/com/rae/cnblogs/sdk/interceptor/RequestInterceptor.java b/sdk/src/main/java/com/rae/cnblogs/sdk/interceptor/RequestInterceptor.java
index b78c011..e228b6a 100644
--- a/sdk/src/main/java/com/rae/cnblogs/sdk/interceptor/RequestInterceptor.java
+++ b/sdk/src/main/java/com/rae/cnblogs/sdk/interceptor/RequestInterceptor.java
@@ -13,7 +13,7 @@
 import com.rae.cnblogs.sdk.BuildConfig;
 import com.rae.cnblogs.sdk.JsonBody;
 import com.rae.cnblogs.sdk.UserProvider;
-import com.rae.cnblogs.sdk.config.CnblogSdkConfig;
+import com.rae.cnblogs.sdk.config.CnblogAppConfig;
 
 import java.io.IOException;
 import java.net.URLDecoder;
@@ -75,7 +75,7 @@ public Response intercept(@NonNull Chain chain) throws IOException {
         if (request.url().host().contains("rae")) {
             newBuilder.addHeader("APP-PACKAGE-NAME", this.packageName);
             newBuilder.addHeader("APP-VERSION-NAME", this.versionName);
-            newBuilder.addHeader("App-CHANNEL", CnblogSdkConfig.APP_CHANNEL);
+            newBuilder.addHeader("App-CHANNEL", CnblogAppConfig.APP_CHANNEL);
             newBuilder.addHeader("App-ENV", BuildConfig.BUILD_TYPE);
             newBuilder.addHeader("APP-VERSION-CODE", String.valueOf(this.versionCode));
         }
diff --git a/sdk/src/main/java/com/rae/cnblogs/sdk/utils/ApiEncrypt.java b/sdk/src/main/java/com/rae/cnblogs/sdk/utils/ApiEncrypt.java
deleted file mode 100644
index 2bf3ebd..0000000
--- a/sdk/src/main/java/com/rae/cnblogs/sdk/utils/ApiEncrypt.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.rae.cnblogs.sdk.utils;
-
-import android.util.Base64;
-
-import com.rae.cnblogs.sdk.config.CnblogSdkConfig;
-
-import java.security.KeyFactory;
-import java.security.PublicKey;
-import java.security.spec.X509EncodedKeySpec;
-
-import javax.crypto.Cipher;
-
-/**
- * 证书加密
- * Created by ChenRui on 2017/1/13 0013 12:55.
- */
-public final class ApiEncrypt {
-
-    private ApiEncrypt(){}
-
-    // 公钥加密
-    public static String encrypt(String content) {
-        try {
-            // 加载公钥
-            X509EncodedKeySpec data = new X509EncodedKeySpec(Base64.decode(CnblogSdkConfig.API_PUB_KEY_BYTE, Base64.DEFAULT));
-            KeyFactory factory = KeyFactory.getInstance("RSA");
-            PublicKey key = factory.generatePublic(data);
-
-            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
-            cipher.init(Cipher.ENCRYPT_MODE, key);
-            byte[] encryptData = cipher.doFinal(content.getBytes());
-
-            return Base64.encodeToString(encryptData, Base64.DEFAULT);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        return content;
-
-    }
-
-}
-
diff --git a/sdk/test.html b/sdk/test.html
deleted file mode 100644
index 7df6aeb..0000000
--- a/sdk/test.html
+++ /dev/null
@@ -1,1593 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-	<title>test</title>
-	<script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
-	<script type="text/javascript">
-		var model = function(){
-			this.categoryId;
-			this.parentId;
-			this.name;
-			this.type;
-		}
-
-		$(function () {
-
-			log('一级分类');
-			//log('----------------------------------------------');
-			var parentList = new Array();
-
-			// 首页
-			var home = new model();
-			home.categoryId = '808';
-			home.parentId = 0;
-			home.name = '首页';
-			home.type = 'SiteHome';
-			parentList.push(home);
-
-			// 推荐
-			var tuijian = new model();
-			tuijian.categoryId = '-2';
-			tuijian.parentId = 0;
-			tuijian.name = '推荐';
-			tuijian.type = 'Picked';
-
-			parentList.push(tuijian);
-
-			var count = 0;
-
-			$('#cate_item li').each(function(i,e){
-				var id = $(e).attr('id').replace('cate_item_','');
-				var text = $(e).find('a').text().replace(/\(.*\)/,'').trim();
-				var m = new model();
-				m.categoryId = id;
-				m.parentId = 0;
-				m.name = text;
-				m.type = 'TopSiteCategory';
-				if(id == "0" || id == "-1") return;
-
-				parentList.push(m);
-				count++;
-			});
-
-
- 			//log(JSON.stringify(parentList));
-
-			log('----------------------------------------------');
-
-			var subCate = $('#cate_sub_block .cate_content_block_wrapper');
-
-			subCate.each(function(index,e){
-				var parentId = $(e).attr('id').replace('cate_content_block_','');
-				$(e).find('.cate_content_block a').each(function(i,ce){
-					var text = $(ce).text().replace(/\(.*\)/,'').trim();
-					var href = 'http://www.cnblogs.com'+$(ce).attr('href');
-					count++;
-
-					$.get('http://api.yixiaoshipin.com',{'url': href},function(data){
-						$(data.data).find("script").each(function(i,item){
-							var aggSiteModel = $(item).text();
-							if(aggSiteModel.indexOf('aggSiteModel')>0){
-								aggSiteModel = aggSiteModel.replace('var aggSiteModel =','').replace(';','').trim();
-								var category = JSON.parse(aggSiteModel);
-
-								var m = new model();
-								m.categoryId = category.CategoryId;
-								m.parentId = category.ParentCategoryId;
-								m.name = text;
-								m.type = category.CategoryType;
-								parentList.push(m);
-								log(JSON.stringify(parentList));
-							}
-						});
-					})
-
-				});
-
-
-			
-			});
-		});
-
-		function log(s){
-			console.log(s);
-		}
-
-		function cateShow(id){}
-		function cateHidden(id){}
-	</script>
-</head>
-<body>
-
-<ul id="cate_item">
-<li id="cate_item_108698" onmouseover="cateShow(108698)" onmouseout="cateHidden(108698)" class="">
-<a href="/cate/108698/">.NET技术(7)</a>
-</li>
-<li id="cate_item_2" onmouseover="cateShow(2)" onmouseout="cateHidden(2)">
-<a href="/cate/2/">编程语言(23)</a>
-</li>
-<li id="cate_item_108701" onmouseover="cateShow(108701)" onmouseout="cateHidden(108701)">
-<a href="/cate/108701/">软件设计(1)</a>
-</li>
-<li id="cate_item_108703" onmouseover="cateShow(108703)" onmouseout="cateHidden(108703)">
-<a href="/cate/108703/">Web前端(11)</a>
-</li>
-<li id="cate_item_108704" onmouseover="cateShow(108704)" onmouseout="cateHidden(108704)">
-<a href="/cate/108704/">企业信息化(0)</a>
-</li>
-<li id="cate_item_108705" onmouseover="cateShow(108705)" onmouseout="cateHidden(108705)">
-<a href="/cate/108705/">手机开发(7)</a>
-</li>
-<li id="cate_item_108709" onmouseover="cateShow(108709)" onmouseout="cateHidden(108709)">
-<a href="/cate/108709/">软件工程(1)</a>
-</li>
-<li id="cate_item_108712" onmouseover="cateShow(108712)" onmouseout="cateHidden(108712)">
-<a href="/cate/108712/">数据库技术(6)</a>
-</li>
-<li id="cate_item_108724" onmouseover="cateShow(108724)" onmouseout="cateHidden(108724)">
-<a href="/cate/108724/">操作系统(6)</a>
-</li>
-<li id="cate_item_4" onmouseover="cateShow(4)" onmouseout="cateHidden(4)">
-<a href="/cate/4/">其他分类(11)</a>
-</li>
-<li id="cate_item_0" onmouseover="cateShow(0)" onmouseout="cateHidden(0)">
-<a href="/cate/all/">所有随笔(1622)</a>
-</li>
-<li id="cate_item_-1" onmouseover="cateShow(-1)" onmouseout="cateHidden(-1)">
-<a href="/comment/">所有评论(588)</a>
-</li>
-</ul>
-
-	<div id="cate_sub_block">
-		<div id="cate_content_block_108698" onmouseover="cateShow(108698)" onmouseout="cateHidden(108698)"
-		class="cate_content_block_wrapper" style="top: 30px; display: none;">
-		<div class="cate_content_top">
-		</div>
-		<div class="cate_content_block">
-			<ul>
-				<li>
-					<a href="/cate/beginner/">
-						.NET新手区(0)
-					</a>
-				</li>
-				<li>
-					<a href="/cate/aspnet/">
-						ASP.NET(2)
-					</a>
-				</li>
-				<li>
-					<a href="/cate/csharp/">
-						C#(1)
-					</a>
-				</li>
-				<li>
-					<a href="/cate/dotnetcore/">
-						.NET Core(2)
-					</a>
-				</li>
-				<li>
-					<a href="/cate/winform/">
-						WinForm(0)
-					</a>
-				</li>
-				<li>
-					<a href="/cate/silverlight/">
-						Silverlight(0)
-					</a>
-				</li>
-				<li>
-					<a href="/cate/wcf/">
-						WCF(0)
-					</a>
-				</li>
-				<li>
-					<a href="/cate/clr/">
-						CLR(0)
-					</a>
-				</li>
-				<li>
-					<a href="/cate/wpf/">
-						WPF(0)
-					</a>
-				</li>
-				<li>
-					<a href="/cate/xna/">
-						XNA(0)
-					</a>
-				</li>
-				<li>
-					<a href="/cate/vs2010/">
-						Visual Studio(0)
-					</a>
-				</li>
-				<li>
-					<a href="/cate/mvc/">
-						ASP.NET MVC(0)
-					</a>
-				</li>
-				<li>
-					<a href="/cate/control/">
-						控件开发(1)
-					</a>
-				</li>
-				<li>
-					<a href="/cate/ef/">
-						Entity Framework(0)
-					</a>
-				</li>
-				<li>
-					<a href="/cate/nhibernate/">
-						NHibernate(0)
-					</a>
-				</li>
-				<li>
-					<a href="/cate/winrt_metro/">
-						WinRT/Metro(0)
-					</a>
-				</li>
-			</ul>
-		</div>
-		<div class="cate_content_bottom">
-		</div>
-	</div>
-	<div id="cate_content_block_2" onmouseover="cateShow(2)" onmouseout="cateHidden(2)"
-	class="cate_content_block_wrapper" style="top: 58px; display: none;">
-	<div class="cate_content_top">
-	</div>
-	<div class="cate_content_block">
-		<ul>
-			<li>
-				<a href="/cate/java/">
-					Java(4)
-				</a>
-			</li>
-			<li>
-				<a href="/cate/cpp/">
-					C++(0)
-				</a>
-			</li>
-			<li>
-				<a href="/cate/php/">
-					PHP(1)
-				</a>
-			</li>
-			<li>
-				<a href="/cate/delphi/">
-					Delphi(0)
-				</a>
-			</li>
-			<li>
-				<a href="/cate/python/">
-					Python(2)
-				</a>
-			</li>
-			<li>
-				<a href="/cate/ruby/">
-					Ruby(0)
-				</a>
-			</li>
-			<li>
-				<a href="/cate/c/">
-					C语言(0)
-				</a>
-			</li>
-			<li>
-				<a href="/cate/erlang/">
-					Erlang(0)
-				</a>
-			</li>
-			<li>
-				<a href="/cate/go/">
-					Go(1)
-				</a>
-			</li>
-			<li>
-				<a href="/cate/swift/">
-					Swift(0)
-				</a>
-			</li>
-			<li>
-				<a href="/cate/scala/">
-					Scala(0)
-				</a>
-			</li>
-			<li>
-				<a href="/cate/r/">
-					R语言(0)
-				</a>
-			</li>
-			<li>
-				<a href="/cate/verilog/">
-					Verilog(0)
-				</a>
-			</li>
-			<li>
-				<a href="/cate/otherlang/">
-					其它语言(0)
-				</a>
-			</li>
-		</ul>
-	</div>
-	<div class="cate_content_bottom">
-	</div>
-</div>
-<div id="cate_content_block_108701" onmouseover="cateShow(108701)" onmouseout="cateHidden(108701)"
-class="cate_content_block_wrapper" style="top: 86px; display: none;">
-<div class="cate_content_top">
-</div>
-<div class="cate_content_block">
-	<ul>
-		<li>
-			<a href="/cate/design/">
-				架构设计(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/108702/">
-				面向对象(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/dp/">
-				设计模式(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/ddd/">
-				领域驱动设计(0)
-			</a>
-		</li>
-	</ul>
-</div>
-<div class="cate_content_bottom">
-</div>
-</div>
-<div id="cate_content_block_108703" onmouseover="cateShow(108703)" onmouseout="cateHidden(108703)"
-class="cate_content_block_wrapper" style="top: 114px; display: none;">
-<div class="cate_content_top">
-</div>
-<div class="cate_content_block">
-	<ul>
-		<li>
-			<a href="/cate/web/">
-				Html/Css(1)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/javascript/">
-				JavaScript(2)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/jquery/">
-				jQuery(1)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/html5/">
-				HTML5(1)
-			</a>
-		</li>
-	</ul>
-</div>
-<div class="cate_content_bottom">
-</div>
-</div>
-<div id="cate_content_block_108704" onmouseover="cateShow(108704)" onmouseout="cateHidden(108704)"
-class="cate_content_block_wrapper" style="top: 142px; display: none;">
-<div class="cate_content_top">
-</div>
-<div class="cate_content_block">
-	<ul>
-		<li>
-			<a href="/cate/sharepoint/">
-				SharePoint(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/gis/">
-				GIS技术(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/sap/">
-				SAP(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/OracleERP/">
-				Oracle ERP(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/dynamics/">
-				Dynamics CRM(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/k2/">
-				K2 BPM(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/infosec/">
-				信息安全(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/3/">
-				企业信息化其他(0)
-			</a>
-		</li>
-	</ul>
-</div>
-<div class="cate_content_bottom">
-</div>
-</div>
-<div id="cate_content_block_108705" onmouseover="cateShow(108705)" onmouseout="cateHidden(108705)"
-class="cate_content_block_wrapper" style="top: 170px; display: none;">
-<div class="cate_content_top">
-</div>
-<div class="cate_content_block">
-	<ul>
-		<li>
-			<a href="/cate/android/">
-				Android开发(1)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/ios/">
-				iOS开发(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/wp/">
-				Windows Phone(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/wm/">
-				Windows Mobile(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/mobile/">
-				其他手机开发(0)
-			</a>
-		</li>
-	</ul>
-</div>
-<div class="cate_content_bottom">
-</div>
-</div>
-<div id="cate_content_block_108709" onmouseover="cateShow(108709)" onmouseout="cateHidden(108709)"
-class="cate_content_block_wrapper" style="top:198px">
-<div class="cate_content_top">
-</div>
-<div class="cate_content_block">
-	<ul>
-		<li>
-			<a href="/cate/agile/">
-				敏捷开发(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/pm/">
-				项目与团队管理(1)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/Engineering/">
-				软件工程其他(0)
-			</a>
-		</li>
-	</ul>
-</div>
-<div class="cate_content_bottom">
-</div>
-</div>
-<div id="cate_content_block_108712" onmouseover="cateShow(108712)" onmouseout="cateHidden(108712)"
-class="cate_content_block_wrapper" style="top:226px">
-<div class="cate_content_top">
-</div>
-<div class="cate_content_block">
-	<ul>
-		<li>
-			<a href="/cate/sqlserver/">
-				SQL Server(1)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/oracle/">
-				Oracle(2)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/mysql/">
-				MySQL(2)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/nosql/">
-				NoSQL(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/bigdata/">
-				大数据(1)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/database/">
-				其它数据库(0)
-			</a>
-		</li>
-	</ul>
-</div>
-<div class="cate_content_bottom">
-</div>
-</div>
-<div id="cate_content_block_108724" onmouseover="cateShow(108724)" onmouseout="cateHidden(108724)"
-class="cate_content_block_wrapper" style="top:254px">
-<div class="cate_content_top">
-</div>
-<div class="cate_content_block">
-	<ul>
-		<li>
-			<a href="/cate/win7/">
-				Windows(1)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/winserver/">
-				Windows Server(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/linux/">
-				Linux(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/osx/">
-				OS X(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/eos/">
-				嵌入式(0)
-			</a>
-		</li>
-	</ul>
-</div>
-<div class="cate_content_bottom">
-</div>
-</div>
-<div id="cate_content_block_4" onmouseover="cateShow(4)" onmouseout="cateHidden(4)"
-class="cate_content_block_wrapper" style="top:282px">
-<div class="cate_content_top">
-</div>
-<div class="cate_content_block">
-	<ul>
-		<li>
-			<a href="/cate/life/">
-				非技术区(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/testing/">
-				软件测试(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/software/">
-				代码与软件发布(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/cg/">
-				计算机图形学(1)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/google/">
-				Google开发(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/gamedev/">
-				游戏开发(1)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/codelife/">
-				程序人生(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/job/">
-				求职面试(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/book/">
-				读书区(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/quoted/">
-				转载区(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/wince/">
-				Windows CE(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/translate/">
-				翻译区(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/opensource/">
-				开源研究(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/flex/">
-				Flex(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/cloud/">
-				云计算(0)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/algorithm/">
-				算法与数据结构(2)
-			</a>
-		</li>
-		<li>
-			<a href="/cate/misc/">
-				其他技术区(0)
-			</a>
-		</li>
-	</ul>
-</div>
-<div class="cate_content_bottom">
-</div>
-</div>
-</div>
-
-<div id="comments_pager_top">
-</div>
-<!--done-->
-
-
-
-<div id="comments_pager_top">
-	<div class="pager">
-		<span class="current">1</span><a href="#!comments" onclick="commentManager.renderComments(2,50);return false;">2</a><a href="#!comments" onclick="commentManager.renderComments(2,50);return false;">Next &gt;</a>
-	</div>
-</div>
-<br>
-<div class="feedback_area_title">
-	评论列表
-</div>
-<div class="feedbackNoItems">
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568082" class="layer">#1楼</a><a name="3568082" id="comment_anchor_3568082"></a><span class="comment_date">2016-11-29 16:54</span><a id="a_comment_author_3568082" href="http://www.cnblogs.com/-linqian/" target="_blank">送给未来的自己</a><a href="http://msg.cnblogs.com/send/%E9%80%81%E7%BB%99%E6%9C%AA%E6%9D%A5%E7%9A%84%E8%87%AA%E5%B7%B1" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568082" class="blog_comment_body">
-			明白 感谢分享
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568082,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568082,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568082_avatar" style="display:none;">http://pic.cnblogs.com/face/649611/20150922095609.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568137" class="layer">#2楼</a><a name="3568137" id="comment_anchor_3568137"></a><span class="comment_date">2016-11-29 17:43</span><a id="a_comment_author_3568137" href="http://www.cnblogs.com/xxcanghai/" target="_blank">小小沧海</a><a href="http://msg.cnblogs.com/send/%E5%B0%8F%E5%B0%8F%E6%B2%A7%E6%B5%B7" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568137" class="blog_comment_body">
-			好文收藏
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568137,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568137,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568137_avatar" style="display:none;">http://pic.cnblogs.com/face/746158/20150417145005.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568305" class="layer">#3楼</a><a name="3568305" id="comment_anchor_3568305"></a><span class="comment_date">2016-11-29 22:18</span><a id="a_comment_author_3568305" href="http://www.cnblogs.com/kvspas/" target="_blank">梁逸晨</a><a href="http://msg.cnblogs.com/send/%E6%A2%81%E9%80%B8%E6%99%A8" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568305" class="blog_comment_body">
-			楼主，我是冲着你的头像来的，几月？
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568305,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568305,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568305_avatar" style="display:none;">http://pic.cnblogs.com/face/20738/20161201171640.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568328" class="layer">#4楼</a><a name="3568328" id="comment_anchor_3568328"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-11-29 22:50</span><a id="a_comment_author_3568328" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568328" class="blog_comment_body">
-			<a href="#3568305" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3568305);">@</a> 梁逸晨<br>
-			九月
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568328,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568328,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568328_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568330" class="layer">#5楼</a><a name="3568330" id="comment_anchor_3568330"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-11-29 22:53</span><a id="a_comment_author_3568330" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568330" class="blog_comment_body">
-			<a href="#3568137" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3568137);">@</a> 小小沧海<br>
-			谢谢支持
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568330,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568330,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568330_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568331" class="layer">#6楼</a><a name="3568331" id="comment_anchor_3568331"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-11-29 22:53</span><a id="a_comment_author_3568331" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568331" class="blog_comment_body">
-			<a href="#3568082" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3568082);">@</a> 送给未来的自己<br>
-			谢谢支持
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568331,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568331,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568331_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568338" class="layer">#7楼</a><a name="3568338" id="comment_anchor_3568338"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-11-29 23:01</span><a id="a_comment_author_3568338" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568338" class="blog_comment_body">
-			<a href="#3568305" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3568305);">@</a> 梁逸晨<br>
-			登山行摄领队+程序员大神，膜拜
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568338,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568338,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568338_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568364" class="layer">#8楼</a><a name="3568364" id="comment_anchor_3568364"></a><span class="comment_date">2016-11-30 02:27</span><a id="a_comment_author_3568364" href="http://www.cnblogs.com/kvspas/" target="_blank">梁逸晨</a><a href="http://msg.cnblogs.com/send/%E6%A2%81%E9%80%B8%E6%99%A8" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568364" class="blog_comment_body">
-			<a href="#3568328" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3568328);">@</a> 凌承一<br>
-			<fieldset class="comment_quote">
-				<legend><a href="#3568328" title="查看引用原文">引用</a></legend>@<br>
-				梁逸晨<br>
-				九月
-			</fieldset>
-			<br>
-			<br>
-			下次再有空来找我，难得有it人士会在高原说话
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568364,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568364,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568364_avatar" style="display:none;">http://pic.cnblogs.com/face/20738/20161201171640.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568487" class="layer">#9楼</a><a name="3568487" id="comment_anchor_3568487"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-11-30 09:46</span><a id="a_comment_author_3568487" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568487" class="blog_comment_body">
-			<a href="#3568364" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3568364);">@</a> 梁逸晨<br>
-			头像是14年9月拍的，今年9月我又去了一次，可惜了，等以后的机会吧！
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568487,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568487,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568487_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568634" class="layer">#10楼</a><a name="3568634" id="comment_anchor_3568634"></a><span class="comment_date">2016-11-30 11:17</span><a id="a_comment_author_3568634" href="http://www.cnblogs.com/ilahsa/" target="_blank">但用此心</a><a href="http://msg.cnblogs.com/send/%E4%BD%86%E7%94%A8%E6%AD%A4%E5%BF%83" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568634" class="blog_comment_body">
-			登录系统2 后 sso 认证中心 如何判断 用户已经登录的
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568634,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568634,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568634_avatar" style="display:none;">http://pic.cnblogs.com/face/u19761.gif</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568644" class="layer">#11楼</a><a name="3568644" id="comment_anchor_3568644"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-11-30 11:21</span><a id="a_comment_author_3568644" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568644" class="blog_comment_body">
-			<a href="#3568634" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3568634);">@</a> 但用此心<br>
-			登录系统1时，已经有一次跳转到sso认证中心，那个时候还输入了用户名与密码信息，sso认证中心验证用户名密码正确后，将用户与sso认证中心的会话标记为“已登录”了，之后再跳转到sso认证中心，会话当然还是“已登录”状态
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568644,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568644,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568644_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568650" class="layer">#12楼</a><a name="3568650" id="comment_anchor_3568650"></a><span class="comment_date">2016-11-30 11:23</span><a id="a_comment_author_3568650" href="http://www.cnblogs.com/jiafuwei/" target="_blank">程序人生0407</a><a href="http://msg.cnblogs.com/send/%E7%A8%8B%E5%BA%8F%E4%BA%BA%E7%94%9F0407" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568650" class="blog_comment_body">
-			很详细
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568650,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568650,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568650_avatar" style="display:none;">http://pic.cnblogs.com/face/731200/20161121101030.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568654" class="layer">#13楼</a><a name="3568654" id="comment_anchor_3568654"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-11-30 11:25</span><a id="a_comment_author_3568654" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568654" class="blog_comment_body">
-			<a href="#3568650" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3568650);">@</a> 程序人生0407<br>
-			谢谢支持
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568654,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568654,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568654_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568662" class="layer">#14楼</a><a name="3568662" id="comment_anchor_3568662"></a><span class="comment_date">2016-11-30 11:27</span><a id="a_comment_author_3568662" href="http://www.cnblogs.com/ilahsa/" target="_blank">但用此心</a><a href="http://msg.cnblogs.com/send/%E4%BD%86%E7%94%A8%E6%AD%A4%E5%BF%83" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568662" class="blog_comment_body">
-			<a href="#3568644" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3568644);">@</a> 凌承一<br>
-			用户直接访问系统2 的资源的时候，在客户端是未登录的状态，不带sessionid 和其他的标示，sso 认证中心如何判断是已经登录的 ， 登录系统1 的时候 sso 认证后，是不是还有1步sso 跳转到系统1 的过程，客户端302 到系统1 ，系统1 返回应答，命令客户端写系统1 的sessionid 的cookie?
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568662,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568662,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568662_avatar" style="display:none;">http://pic.cnblogs.com/face/u19761.gif</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568684" class="layer">#15楼</a><a name="3568684" id="comment_anchor_3568684"></a><span class="comment_date">2016-11-30 11:43</span><a id="a_comment_author_3568684" href="http://www.cnblogs.com/ilahsa/" target="_blank">但用此心</a><a href="http://msg.cnblogs.com/send/%E4%BD%86%E7%94%A8%E6%AD%A4%E5%BF%83" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568684" class="blog_comment_body">
-			<a href="#3568644" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3568644);">@</a> 凌承一<br>
-			想清楚了 ，访问系统2的时候跳转到 sso 认证，客户端带上sso 认证的sessionid,根据这个id 可以定位用户 <br>
-			<br>
-			写的很详细 谢谢
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568684,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568684,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568684_avatar" style="display:none;">http://pic.cnblogs.com/face/u19761.gif</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568685" class="layer">#16楼</a><a name="3568685" id="comment_anchor_3568685"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-11-30 11:44</span><a id="a_comment_author_3568685" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568685" class="blog_comment_body">
-			<a href="#3568662" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3568662);">@</a> 但用此心<br>
-			sso认证中心只是判断用户与它之间的会话有没有标记为“已登录”，用户与系统1、系统2之间的会话状态，sso认证中心是不管的。
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568685,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568685,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568685_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568687" class="layer">#17楼</a><a name="3568687" id="comment_anchor_3568687"></a><span class="comment_date">2016-11-30 11:47</span><a id="a_comment_author_3568687" href="http://www.cnblogs.com/xcj26/" target="_blank">Sam Xiao</a><a href="http://msg.cnblogs.com/send/Sam%20Xiao" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568687" class="blog_comment_body">
-			前几天不是有一个哥们也写了 可跨域的单点登录(SSO)实现方案【附.net代码】 的博客吗？我还以为是同一个。
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568687,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568687,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568687_avatar" style="display:none;">http://pic.cnblogs.com/face/48022/20150116085345.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568694" class="layer">#18楼</a><a name="3568694" id="comment_anchor_3568694"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-11-30 11:51</span><a id="a_comment_author_3568694" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568694" class="blog_comment_body">
-			<a href="#3568687" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3568687);">@</a> Sam Xiao<br>
-			哈哈，我只是简单介绍了多系统跨域登录的解决方案，源码我有，并不复杂，不过还是自己实现一遍更好
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568694,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568694,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568694_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568774" class="layer">#19楼</a><a name="3568774" id="comment_anchor_3568774"></a><span class="comment_date">2016-11-30 13:35</span><a id="a_comment_author_3568774" href="http://www.cnblogs.com/guigujun/" target="_blank">鬼谷君</a><a href="http://msg.cnblogs.com/send/%E9%AC%BC%E8%B0%B7%E5%90%9B" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568774" class="blog_comment_body">
-			l不错，写的蛮详细
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568774,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568774,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568774_avatar" style="display:none;">http://pic.cnblogs.com/face/1071981/20161130110631.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568818" class="layer">#20楼</a><a name="3568818" id="comment_anchor_3568818"></a><span class="comment_date">2016-11-30 14:06</span><a id="a_comment_author_3568818" href="http://www.cnblogs.com/qtqq/" target="_blank">最爱晴天</a><a href="http://msg.cnblogs.com/send/%E6%9C%80%E7%88%B1%E6%99%B4%E5%A4%A9" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568818" class="blog_comment_body">
-			关于单点撤销，按楼主的方案是不是需要预先知道其他的子站点，然后一个站点退出了， 就循环去调用其他站点提供的清除回话的方法？
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568818,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568818,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568818_avatar" style="display:none;">http://pic.cnblogs.com/face/596290/20150813112422.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568823" class="layer">#21楼</a><a name="3568823" id="comment_anchor_3568823"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-11-30 14:08</span><a id="a_comment_author_3568823" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568823" class="blog_comment_body">
-			<a href="#3568818" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3568818);">@</a> 最爱晴天<br>
-			不用，单点登录过程中有一步是子站点向sso认证中心注册，注销的时候，sso认证中心取出这些注册系统地址，依次发送注销请求就行了
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568823,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568823,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568823_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568872" class="layer">#22楼</a><a name="3568872" id="comment_anchor_3568872"></a><span class="comment_date">2016-11-30 14:58</span><a id="a_comment_author_3568872" href="http://home.cnblogs.com/u/978596/" target="_blank">莹仔Ken</a><a href="http://msg.cnblogs.com/send/%E8%8E%B9%E4%BB%94Ken" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568872" class="blog_comment_body">
-			你这个方法如果我用不同公司的浏览器就不行了?
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568872,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568872,'Bury',this)">反对(0)</a>
-		</div>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3568883" class="layer">#23楼</a><a name="3568883" id="comment_anchor_3568883"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-11-30 15:03</span><a id="a_comment_author_3568883" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3568883" class="blog_comment_body">
-			<a href="#3568872" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3568872);">@</a> 莹仔Ken<br>
-			和浏览器没有关系，浏览器都实现了cookie
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3568883,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3568883,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3568883_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569011" class="layer">#24楼</a><a name="3569011" id="comment_anchor_3569011"></a><span class="comment_date">2016-11-30 16:50</span><a id="a_comment_author_3569011" href="http://www.cnblogs.com/pojianbing/" target="_blank">破剑冰</a><a href="http://msg.cnblogs.com/send/%E7%A0%B4%E5%89%91%E5%86%B0" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569011" class="blog_comment_body">
-			很详细, 终于搞明白了.
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569011,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569011,'Bury',this)">反对(0)</a>
-		</div>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569416" class="layer">#25楼</a><a name="3569416" id="comment_anchor_3569416"></a><span class="comment_date">2016-12-01 08:32</span><a id="a_comment_author_3569416" href="http://www.cnblogs.com/ginb/" target="_blank">gnib</a><a href="http://msg.cnblogs.com/send/gnib" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569416" class="blog_comment_body">
-			mark
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569416,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569416,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569416_avatar" style="display:none;">http://pic.cnblogs.com/face/612774/20150824213110.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569423" class="layer">#26楼</a><a name="3569423" id="comment_anchor_3569423"></a><span class="comment_date">2016-12-01 08:40</span><a id="a_comment_author_3569423" href="http://www.cnblogs.com/liubiaocai/" target="_blank">刘标才</a><a href="http://msg.cnblogs.com/send/%E5%88%98%E6%A0%87%E6%89%8D" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569423" class="blog_comment_body">
-			有几点不明白：<br>
-			1、为什么是系统1输入用户名密码，而不在sso中直接输入呢，这样密码更安全<br>
-			2、为什么拿到token后还要去sso验证是否正确呢，双方使用事先设置好的加密Key就可以保证安全了，没有必要多这一步<br>
-			3、同时注销真的不知道怎么实现，难道一次可以跳转N个地址吗<br>
-			<br>
-			其实楼主你去看下oauth就知道了，步骤基本相同
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569423,'Digg',this)">支持(2)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569423,'Bury',this)">反对(0)</a>
-		</div>comment_3569423_avatar
-		<span id="" style="display:none;">http://pic.cnblogs.com/face/u36499.jpg?id=30171922</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569560" class="layer">#27楼</a><a name="3569560" id="comment_anchor_3569560"></a><span class="comment_date">2016-12-01 10:08</span><a id="a_comment_author_3569560" href="http://www.cnblogs.com/hnzhangmeiqing/" target="_blank">活在當下</a><a href="http://msg.cnblogs.com/send/%E6%B4%BB%E5%9C%A8%E7%95%B6%E4%B8%8B" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569560" class="blog_comment_body">
-			如果被别人拦截到令牌，是不是就可以授权访问了？
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569560,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569560,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569560_avatar" style="display:none;">http://pic.cnblogs.com/face/u39262.jpg?id=16144336</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569565" class="layer">#28楼</a><a name="3569565" id="comment_anchor_3569565"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-12-01 10:12</span><a id="a_comment_author_3569565" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569565" class="blog_comment_body">
-			<a href="#3569423" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569423);">@</a> 刘标才<br>
-			系统1没有输入用户名密码，只有sso认证中心可以输入用户名密码；<br>
-			sso认证中心给了子系统token，但子系统使用token前必须去sso认证中心校验token有效性，一是因为sso认证中心要登记使用了此token的子系统，二是校验的过程中sso认证中心可以对子系统的合法性校验，即使token被截获，也不会有安全问题；<br>
-			注销不是用跳转来实现的，sso认证中心向所有登记过的系统发送带有“logout”参数的注销请求
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569565,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569565,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569565_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569567" class="layer">#29楼</a><a name="3569567" id="comment_anchor_3569567"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-12-01 10:13</span><a id="a_comment_author_3569567" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569567" class="blog_comment_body">
-			<a href="#3569560" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569560);">@</a> 活在當下<br>
-			看我28楼的回复
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569567,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569567,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569567_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569571" class="layer">#30楼</a><a name="3569571" id="comment_anchor_3569571"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-12-01 10:14</span><a id="a_comment_author_3569571" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569571" class="blog_comment_body">
-			<a href="#3569423" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569423);">@</a> 刘标才<br>
-			oauth和sso本质都是认证中心向子系统授权，原理一样，过程自然差不多
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569571,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569571,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569571_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569578" class="layer">#31楼</a><a name="3569578" id="comment_anchor_3569578"></a><span class="comment_date">2016-12-01 10:21</span><a id="a_comment_author_3569578" href="http://www.cnblogs.com/hnzhangmeiqing/" target="_blank">活在當下</a><a href="http://msg.cnblogs.com/send/%E6%B4%BB%E5%9C%A8%E7%95%B6%E4%B8%8B" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569578" class="blog_comment_body">
-			<a href="#3569567" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569567);">@</a> 凌承一<br>
-			“系统1拿到令牌，去sso认证中心校验令牌是否有效”,sso中心是怎么确认令牌有效的呢？
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569578,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569578,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569578_avatar" style="display:none;">http://pic.cnblogs.com/face/u39262.jpg?id=16144336</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569582" class="layer">#32楼</a><a name="3569582" id="comment_anchor_3569582"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-12-01 10:24</span><a id="a_comment_author_3569582" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569582" class="blog_comment_body">
-			<a href="#3569578" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569578);">@</a> 活在當下<br>
-			sso认证中心在用户输入正确用户名密码后创建授权令牌，存储在key-value数据库如redis中，令牌为key，令牌有效的意思就是数据库有这个key，并且还在有效期内，你知道，key-value数据库的key可以设置有效期的
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569582,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569582,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569582_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569611" class="layer">#33楼</a><a name="3569611" id="comment_anchor_3569611"></a><span class="comment_date">2016-12-01 10:46</span><a id="a_comment_author_3569611" href="http://www.cnblogs.com/hnzhangmeiqing/" target="_blank">活在當下</a><a href="http://msg.cnblogs.com/send/%E6%B4%BB%E5%9C%A8%E7%95%B6%E4%B8%8B" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569611" class="blog_comment_body">
-			<a href="#3569582" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569582);">@</a> 凌承一<br>
-			这个又回到我之前的问题了，如果别人截获令牌，怎么知道是不是正常的请求令牌还是被截获的，因为你是说只要判断这个键值是否在数据库中吧
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569611,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569611,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569611_avatar" style="display:none;">http://pic.cnblogs.com/face/u39262.jpg?id=16144336</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569616" class="layer">#34楼</a><a name="3569616" id="comment_anchor_3569616"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-12-01 10:51</span><a id="a_comment_author_3569616" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569616" class="blog_comment_body">
-			<a href="#3569611" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569611);">@</a> 活在當下<br>
-			理论上，不管谁拿到令牌，只要这个令牌有效，就是可以用的，但你拿着这个令牌去sso认证中心校验并注册的时候，sso认证中心是有一份系统白名单的，名单上的系统拿着令牌才能注册成功，其他系统sso认证中心都认为是非法的
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569616,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569616,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569616_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569626" class="layer">#35楼</a><a name="3569626" id="comment_anchor_3569626"></a><span class="comment_date">2016-12-01 10:59</span><a id="a_comment_author_3569626" href="http://www.cnblogs.com/dudu/" target="_blank">dudu</a><a href="http://msg.cnblogs.com/send/dudu" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569626" class="blog_comment_body">
-			在.NET平台上有一个单点登录认证中心的开源实现（支持.NET Core） <a href="https://identityserver.io/" target="_blank">https://identityserver.io/</a>
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569626,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569626,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569626_avatar" style="display:none;">http://pic.cnblogs.com/face/1/20150530204900.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569627" class="layer">#36楼</a><a name="3569627" id="comment_anchor_3569627"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-12-01 11:00</span><a id="a_comment_author_3569627" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569627" class="blog_comment_body">
-			<a href="#3569611" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569611);">@</a> 活在當下<br>
-			可能你在电视上看到了很多，拿着别人令牌开门、放人等等，因为那些人只认令牌不认人，系统白名单的作用就是认人，这样令牌被盗也没关系
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569627,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569627,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569627_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569628" class="layer">#37楼</a><a name="3569628" id="comment_anchor_3569628"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-12-01 11:01</span><a id="a_comment_author_3569628" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569628" class="blog_comment_body">
-			<a href="#3569626" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569626);">@</a> dudu<br>
-			谢谢支持，开源sso一直不缺的
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569628,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569628,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569628_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569636" class="layer">#38楼</a><a name="3569636" id="comment_anchor_3569636"></a><span class="comment_date">2016-12-01 11:05</span><a id="a_comment_author_3569636" href="http://www.cnblogs.com/youngerliu/" target="_blank">AlanCoder</a><a href="http://msg.cnblogs.com/send/AlanCoder" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569636" class="blog_comment_body">
-			<img src="http://images2015.cnblogs.com/blog/154634/201612/154634-20161201110512193-1520013145.jpg" alt="" border="0" "=""><br>
-			这里 如果是跨域 怎么知道之前已经登录过了，能具体点 ?
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569636,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569636,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569636_avatar" style="display:none;">http://pic.cnblogs.com/face/154634/20130121214245.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569640" class="layer">#39楼</a><a name="3569640" id="comment_anchor_3569640"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-12-01 11:08</span><a id="a_comment_author_3569640" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569640" class="blog_comment_body">
-			<a href="#3569636" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569636);">@</a> AlanCoder<br>
-			我相信你再仔细看一会就明白了，图加上文字说明以及开始介绍的会话、登录机制
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569640,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569640,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569640_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569644" class="layer">#40楼</a><a name="3569644" id="comment_anchor_3569644"></a><span class="comment_date">2016-12-01 11:11</span><a id="a_comment_author_3569644" href="http://www.cnblogs.com/karascanvas/" target="_blank">KarasCanvas</a><a href="http://msg.cnblogs.com/send/KarasCanvas" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569644" class="blog_comment_body">
-			jwt+oauth
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569644,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569644,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569644_avatar" style="display:none;">http://pic.cnblogs.com/face/206235/20160225211451.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569672" class="layer">#41楼</a><a name="3569672" id="comment_anchor_3569672"></a><span class="comment_date">2016-12-01 11:27</span><a id="a_comment_author_3569672" href="http://www.cnblogs.com/kingboy2008/" target="_blank">灵雨飘零</a><a href="http://msg.cnblogs.com/send/%E7%81%B5%E9%9B%A8%E9%A3%98%E9%9B%B6" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569672" class="blog_comment_body">
-			mark
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569672,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569672,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569672_avatar" style="display:none;">http://pic.cnblogs.com/face/u40764.jpg?id=23091233</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569674" class="layer">#42楼</a><a name="3569674" id="comment_anchor_3569674"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-12-01 11:28</span><a id="a_comment_author_3569674" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569674" class="blog_comment_body">
-			<a href="#3569644" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569644);">@</a> KarasCanvas<br>
-			这就涉及到sso-client与sso-server具体通信过程和安全问题了，如果有自己独立的ca系统，jwt或者https是非常好的选择
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569674,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569674,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569674_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569691" class="layer">#43楼</a><a name="3569691" id="comment_anchor_3569691"></a><span class="comment_date">2016-12-01 11:47</span><a id="a_comment_author_3569691" href="http://www.cnblogs.com/eggTwo/" target="_blank">梦亦晓</a><a href="http://msg.cnblogs.com/send/%E6%A2%A6%E4%BA%A6%E6%99%93" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569691" class="blog_comment_body">
-			site1,site2,sso认证中心的session过期时间能不能保持一致？
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569691,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569691,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569691_avatar" style="display:none;">http://pic.cnblogs.com/face/559600/20131127170112.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569698" class="layer">#44楼</a><a name="3569698" id="comment_anchor_3569698"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-12-01 11:53</span><a id="a_comment_author_3569698" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569698" class="blog_comment_body">
-			<a href="#3569691" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569691);">@</a> 梦亦晓<br>
-			原来你就是有人提到过的整那篇【可跨域的单点登录（sso）方案附.net代码】的淫<br>
-			子系统的会话持续时间是依赖sso认证中心的会话持续时间的，因为监听器的存在。要让子系统与sso认证中心保持一致的会话持续时间，可以这么做<br>
-			子系统向sso认证中心校验令牌时，sso认证中心返回这个令牌对应全局会话的剩余时间，子系统将这个时间设为局部会话的持续时间
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569698,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569698,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569698_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569706" class="layer">#45楼</a><a name="3569706" id="comment_anchor_3569706"></a><span class="comment_date">2016-12-01 12:00</span><a id="a_comment_author_3569706" href="http://www.cnblogs.com/eggTwo/" target="_blank">梦亦晓</a><a href="http://msg.cnblogs.com/send/%E6%A2%A6%E4%BA%A6%E6%99%93" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569706" class="blog_comment_body">
-			<a href="#3569698" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569698);">@</a> 凌承一<br>
-			session的过期时间不应该设置为滑动过期时间吗？如果登录成功后sso的过期时间设置为1分钟，site1的过期时间设置为1分钟，浏览器和site1持续交互，会导致site1会话session一直有效，而sso的会话session失效
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569706,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569706,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569706_avatar" style="display:none;">http://pic.cnblogs.com/face/559600/20131127170112.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569712" class="layer">#46楼</a><a name="3569712" id="comment_anchor_3569712"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-12-01 12:04</span><a id="a_comment_author_3569712" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569712" class="blog_comment_body">
-			<a href="#3569706" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569706);">@</a> 梦亦晓<br>
-			当然不会了，sso会话一旦失效，sso认证中心的会话监听器就会向所有注册子系统发出注销请求，子系统的会话也会失效，看下【三、单点登录】介绍的局部会话与全局会话的关系<br>
-			实际上，子系统的会话持续时间一定不会超过sso认证中心的会话持续时间
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569712,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569712,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569712_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569731" class="layer">#47楼</a><a name="3569731" id="comment_anchor_3569731"></a><span class="comment_date">2016-12-01 12:31</span><a id="a_comment_author_3569731" href="http://www.cnblogs.com/eggTwo/" target="_blank">梦亦晓</a><a href="http://msg.cnblogs.com/send/%E6%A2%A6%E4%BA%A6%E6%99%93" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569731" class="blog_comment_body">
-			<a href="#3569712" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569712);">@</a> 凌承一<br>
-			如果因为用户一直在操作site1而导致site1的会话session一直有效，而sso的会话session失效。怎么能强制注销site1的session会话呢？
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569731,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569731,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569731_avatar" style="display:none;">http://pic.cnblogs.com/face/559600/20131127170112.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569735" class="layer">#48楼</a><a name="3569735" id="comment_anchor_3569735"></a>[<span class="louzhu">楼主</span>] <span class="comment_date">2016-12-01 12:33</span><a id="a_comment_author_3569735" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569735" class="blog_comment_body">
-			<a href="#3569731" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569731);">@</a> 梦亦晓<br>
-			sso认证中心的会话监听器，参考文章的最后一节以及单点登录的注销过程
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569735,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569735,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569735_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569738" class="layer">#49楼</a><a name="3569738" id="comment_anchor_3569738"></a><span class="comment_date">2016-12-01 12:36</span><a id="a_comment_author_3569738" href="http://www.cnblogs.com/eggTwo/" target="_blank">梦亦晓</a><a href="http://msg.cnblogs.com/send/%E6%A2%A6%E4%BA%A6%E6%99%93" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569738" class="blog_comment_body">
-			我知道你是用http分发的方法注销分站的session。<br>
-			而我是说不能注销site1的会话，你总不能在用户和site1交互的过程中因为没有更新sso会话的过期时间而强制注销site1的会话吧。
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569738,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569738,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569738_avatar" style="display:none;">http://pic.cnblogs.com/face/559600/20131127170112.png</span>
-	</div>
-</div>
-<div class="feedbackItem">
-	<div class="feedbackListSubtitle">
-		<div class="feedbackManage">
-			<span class="comment_actions"></span>
-		</div>
-		<a href="#3569743" class="layer">#50楼</a><a name="3569743" id="comment_anchor_3569743"></a>[<span class="louzhu">楼主</span>]<span id="comment-maxId" style="display:none;">3569743</span><span id="comment-maxDate" style="display:none;">2016/12/1 12:44:19</span><span class="comment_date">2016-12-01 12:44</span><a id="a_comment_author_3569743" href="http://www.cnblogs.com/ywlaker/" target="_blank">凌承一</a><a href="http://msg.cnblogs.com/send/%E5%87%8C%E6%89%BF%E4%B8%80" title="发送站内短消息" class="sendMsg2This"></a>
-	</div>
-	<div class="feedbackCon">
-		<div id="comment_body_3569743" class="blog_comment_body">
-			<a href="#3569738" title="查看所回复的评论" onclick="commentManager.renderComments(0,50,3569738);">@</a> 梦亦晓<br>
-			授权的源头都失效了，site1怎么还能允许用户继续操作？<br>
-			你是想随着用户持续访问分站，而自动延长sso认证中心的会话时间是吧，那可以为分站的会话加一个监听器，监听时间变化，然后向sso认证中心发送加长会话持续时间的请求<br>
-			这样可以达到你的目的，但非常不合理，授权过程一切以sso认证中心为主，分站怎么能要求认证中心去做改变！<br>
-			如果不想用户访问过程突然中断，可以加长sso认证中心的会话持续时间
-		</div>
-		<div class="comment_vote">
-			<a href="javascript:void(0);" class="comment_digg" onclick="return voteComment(3569743,'Digg',this)">支持(0)</a><a href="javascript:void(0);" class="comment_bury" onclick="return voteComment(3569743,'Bury',this)">反对(0)</a>
-		</div>
-		<span id="comment_3569743_avatar" style="display:none;">http://pic.cnblogs.com/face/797930/20160128170544.png</span>
-	</div>
-</div>
-<div id="comments_pager_bottom">
-	<div class="pager">
-		<span class="current">1</span><a href="#!comments" onclick="commentManager.renderComments(2,50);return false;">2</a><a href="#!comments" onclick="commentManager.renderComments(2,50);return false;">Next &gt;</a>
-	</div>
-</div>
-
-
-
-</body>
-</html>
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 92abbc5..bcd3a92 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':sdk', ':app'
+include ':sdk', ':app', ':module-basic', ':module-blog', ':widget', ':module-resource'
diff --git a/widget/.gitignore b/widget/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/widget/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/widget/build.gradle b/widget/build.gradle
new file mode 100644
index 0000000..b47f6df
--- /dev/null
+++ b/widget/build.gradle
@@ -0,0 +1,54 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion rootProject.ext.compileSdkVersion
+    defaultConfig {
+        minSdkVersion rootProject.ext.minSdkVersion
+        targetSdkVersion rootProject.ext.targetSdkVersion
+        multiDexEnabled true
+        versionCode 1
+        versionName "1.0"
+        javaCompileOptions {
+            annotationProcessorOptions {
+                arguments = [moduleName: project.getName()]
+            }
+        }
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation rootProject.ext.appcompat
+    implementation rootProject.ext.design
+    implementation rootProject.ext.cardview
+    implementation rootProject.ext.eventBus
+    implementation project(':module-resource')
+    // 主题切换
+    api('skin.support:skin-support:2.1.2', {
+        exclude group: 'com.android.support'
+    })
+    // 主题切换-design package
+    api('skin.support:skin-support-design:1.2.5', {
+        exclude group: 'com.android.support'
+    })
+    // RAE-扩展的TAB_LAYOUT
+    api('com.rae.widget:design-ext:1.0.1', {
+        exclude group: 'com.android.support'
+    })
+    // 下拉刷新
+    api('in.srain.cube:ultra-ptr:1.0.11', {
+        exclude group: 'com.android.support'
+    })
+    // 上拉加载RecyclerView
+    api('com.github.raee:XRecyclerView:1.3.3', {
+        exclude group: 'com.android.support'
+    })
+}
diff --git a/widget/proguard-rules.pro b/widget/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/widget/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/widget/src/main/AndroidManifest.xml b/widget/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..9721264
--- /dev/null
+++ b/widget/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+<manifest package="com.rae.cnblogs.widget" />
diff --git a/app/src/main/java/com/rae/cnblogs/message/ThemeChangedEvent.java b/widget/src/main/java/com/rae/cnblogs/ThemeChangedEvent.java
similarity index 89%
rename from app/src/main/java/com/rae/cnblogs/message/ThemeChangedEvent.java
rename to widget/src/main/java/com/rae/cnblogs/ThemeChangedEvent.java
index 3ff846b..43f2563 100644
--- a/app/src/main/java/com/rae/cnblogs/message/ThemeChangedEvent.java
+++ b/widget/src/main/java/com/rae/cnblogs/ThemeChangedEvent.java
@@ -1,4 +1,4 @@
-package com.rae.cnblogs.message;
+package com.rae.cnblogs;
 
 /**
  * 主题更改事件
diff --git a/app/src/main/java/com/rae/cnblogs/ThemeCompat.java b/widget/src/main/java/com/rae/cnblogs/ThemeCompat.java
similarity index 95%
rename from app/src/main/java/com/rae/cnblogs/ThemeCompat.java
rename to widget/src/main/java/com/rae/cnblogs/ThemeCompat.java
index 9a0107c..e7815f7 100644
--- a/app/src/main/java/com/rae/cnblogs/ThemeCompat.java
+++ b/widget/src/main/java/com/rae/cnblogs/ThemeCompat.java
@@ -12,7 +12,6 @@
 import android.view.View;
 import android.view.Window;
 
-import com.rae.cnblogs.message.ThemeChangedEvent;
 import com.rae.cnblogs.widget.RaeSkinImageView;
 import com.rae.cnblogs.widget.RaeSkinImageViewV4;
 
@@ -30,7 +29,7 @@
  */
 public final class ThemeCompat {
 
-    public static String currentSkinName;
+//    static String currentSkinName;
 
     public static class CnblogsThemeHookInflater implements SkinLayoutInflater {
         @Override
@@ -73,12 +72,12 @@ public static boolean isNight() {
         return "night".equalsIgnoreCase(SkinCompatManager.getInstance().getCurSkinName());
     }
 
-    /**
-     * 在{@link #isNight()} 调用之前，用于判断资源文件
-     */
-    public static boolean isNightInResource() {
-        return !TextUtils.isEmpty(currentSkinName);
-    }
+//    /**
+//     * 在{@link #isNight()} 调用之前，用于判断资源文件
+//     */
+//    public static boolean isNightInResource() {
+//        return !TextUtils.isEmpty(currentSkinName);
+//    }
 
 
     /**
@@ -194,7 +193,7 @@ private static void changeMiUIStatusMode(Window window, boolean dark) {
         if (window != null) {
             Class clazz = window.getClass();
             try {
-                int darkModeFlag = 0;
+                int darkModeFlag;
                 Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
                 Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
                 darkModeFlag = field.getInt(layoutParams);
diff --git a/app/src/main/java/com/rae/cnblogs/widget/RaeTabLayout.java b/widget/src/main/java/com/rae/cnblogs/widget/RaeAppTabLayout.java
similarity index 87%
rename from app/src/main/java/com/rae/cnblogs/widget/RaeTabLayout.java
rename to widget/src/main/java/com/rae/cnblogs/widget/RaeAppTabLayout.java
index be448e0..c252455 100644
--- a/app/src/main/java/com/rae/cnblogs/widget/RaeTabLayout.java
+++ b/widget/src/main/java/com/rae/cnblogs/widget/RaeAppTabLayout.java
@@ -4,10 +4,7 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 
-import com.rae.cnblogs.R;
-
 import skin.support.content.res.SkinCompatResources;
-import skin.support.design.widget.SkinMaterialTabLayout;
 import skin.support.widget.SkinCompatBackgroundHelper;
 import skin.support.widget.SkinCompatHelper;
 import skin.support.widget.SkinCompatSupportable;
@@ -15,24 +12,24 @@
 import static skin.support.widget.SkinCompatHelper.INVALID_ID;
 
 
-public class RaeTabLayout extends SkinMaterialTabLayout implements SkinCompatSupportable {
+public class RaeAppTabLayout extends RaeSkinDesignTabLayout implements SkinCompatSupportable {
     private SkinCompatBackgroundHelper mBackgroundTintHelper;
 
     private int mIndicatorColorResId = INVALID_ID;
     private int mTextSelectColorResId = INVALID_ID;
     private int mTextUnselectColorResId = INVALID_ID;
 
-    public RaeTabLayout(Context context) {
+    public RaeAppTabLayout(Context context) {
         super(context);
         initViews(null, 0);
     }
 
-    public RaeTabLayout(Context context, AttributeSet attrs) {
+    public RaeAppTabLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
         initViews(attrs, 0);
     }
 
-    public RaeTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+    public RaeAppTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         initViews(attrs, defStyleAttr);
     }
diff --git a/app/src/main/java/com/rae/cnblogs/widget/RaeLoadMoreView.java b/widget/src/main/java/com/rae/cnblogs/widget/RaeLoadMoreView.java
similarity index 91%
rename from app/src/main/java/com/rae/cnblogs/widget/RaeLoadMoreView.java
rename to widget/src/main/java/com/rae/cnblogs/widget/RaeLoadMoreView.java
index 8c6c39a..8e1faad 100644
--- a/app/src/main/java/com/rae/cnblogs/widget/RaeLoadMoreView.java
+++ b/widget/src/main/java/com/rae/cnblogs/widget/RaeLoadMoreView.java
@@ -7,7 +7,6 @@
 import android.widget.TextView;
 
 import com.jcodecraeer.xrecyclerview.LoadingMoreFooter;
-import com.rae.cnblogs.R;
 
 /**
  * 加载更多
@@ -49,8 +48,8 @@ public void setNoMoreText(String text) {
     public void setState(int state) {
         if (state == STATE_NOMORE) {
             mTextView.setText(mNoMoreText);
-            mProgressBar.setVisibility(View.GONE);
-            this.setVisibility(View.VISIBLE);
+            mProgressBar.setVisibility(GONE);
+            this.setVisibility(VISIBLE);
             return;
         }
         super.setState(state);
diff --git a/app/src/main/java/com/rae/cnblogs/widget/RaeRecyclerView.java b/widget/src/main/java/com/rae/cnblogs/widget/RaeRecyclerView.java
similarity index 97%
rename from app/src/main/java/com/rae/cnblogs/widget/RaeRecyclerView.java
rename to widget/src/main/java/com/rae/cnblogs/widget/RaeRecyclerView.java
index 491c930..23e88fb 100644
--- a/app/src/main/java/com/rae/cnblogs/widget/RaeRecyclerView.java
+++ b/widget/src/main/java/com/rae/cnblogs/widget/RaeRecyclerView.java
@@ -8,6 +8,7 @@
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.StaggeredGridLayoutManager;
 import android.util.AttributeSet;
+import android.view.View;
 import android.view.ViewParent;
 
 import com.jcodecraeer.xrecyclerview.ProgressStyle;
@@ -59,7 +60,7 @@ private void init(AttributeSet attrs, int defStyle) {
         setLayoutManager(new LinearLayoutManager(getContext()));
         setPullRefreshEnabled(false);
         mFootView = new RaeLoadMoreView(getContext());
-        mFootView.setVisibility(GONE);
+        mFootView.setVisibility(View.GONE);
         setLoadingMoreProgressStyle(ProgressStyle.BallScaleMultiple);
         setFootView(mFootView);
     }
@@ -148,7 +149,7 @@ public void computeScroll() {
         super.computeScroll();
         if (mIsCoordinatorLayout) {
             // 兼容CoordinatorLayout
-            LayoutManager layoutManager = getLayoutManager();
+            RecyclerView.LayoutManager layoutManager = getLayoutManager();
             int lastVisibleItemPosition = -1;
             if (layoutManager instanceof LinearLayoutManager) {
                 lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
diff --git a/app/src/main/java/android/support/design/widget/RaeSkinDesignTabLayout.java b/widget/src/main/java/com/rae/cnblogs/widget/RaeSkinDesignTabLayout.java
similarity index 59%
rename from app/src/main/java/android/support/design/widget/RaeSkinDesignTabLayout.java
rename to widget/src/main/java/com/rae/cnblogs/widget/RaeSkinDesignTabLayout.java
index f62b25e..387defc 100644
--- a/app/src/main/java/android/support/design/widget/RaeSkinDesignTabLayout.java
+++ b/widget/src/main/java/com/rae/cnblogs/widget/RaeSkinDesignTabLayout.java
@@ -1,11 +1,10 @@
-package android.support.design.widget;
+package com.rae.cnblogs.widget;
 
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.support.design.widget.RaeTabLayout;
 import android.util.AttributeSet;
 
-import com.rae.cnblogs.R;
-
 import skin.support.content.res.SkinCompatResources;
 import skin.support.widget.SkinCompatBackgroundHelper;
 import skin.support.widget.SkinCompatHelper;
@@ -14,7 +13,7 @@
 import static skin.support.widget.SkinCompatHelper.INVALID_ID;
 
 
-public class RaeSkinDesignTabLayout extends DesignTabLayout implements SkinCompatSupportable {
+public class RaeSkinDesignTabLayout extends RaeTabLayout implements SkinCompatSupportable {
 
     private SkinCompatBackgroundHelper mBackgroundTintHelper;
     private int mTabIndicatorColorResId;
@@ -45,37 +44,37 @@ private void initViews(AttributeSet attrs, int defStyleAttr) {
     }
 
     private void obtainAttributes(Context context, AttributeSet attrs) {
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabLayout);
-        mIndicatorColorResId = a.getResourceId(R.styleable.TabLayout_tabIndicatorColor, INVALID_ID);
+        TypedArray a = context.obtainStyledAttributes(attrs, android.support.design.widget.R.styleable.TabLayout);
+        mIndicatorColorResId = a.getResourceId(android.support.design.widget.R.styleable.TabLayout_tabIndicatorColor, INVALID_ID);
         mIndicatorColorResId = SkinCompatHelper.checkResourceId(mIndicatorColorResId);
 
 
-        mTextSelectColorResId = a.getResourceId(R.styleable.TabLayout_tabSelectedTextColor, INVALID_ID);
+        mTextSelectColorResId = a.getResourceId(android.support.design.widget.R.styleable.TabLayout_tabSelectedTextColor, INVALID_ID);
         mTextSelectColorResId = SkinCompatHelper.checkResourceId(mTextSelectColorResId);
 
-        mTextUnselectColorResId = a.getResourceId(R.styleable.TabLayout_tabTextColor, INVALID_ID);
+        mTextUnselectColorResId = a.getResourceId(android.support.design.widget.R.styleable.TabLayout_tabTextColor, INVALID_ID);
         mTextUnselectColorResId = SkinCompatHelper.checkResourceId(mTextUnselectColorResId);
 
         this.mTabIndicatorColorResId = 0;
         this.mTabTextColorsResId = 0;
         this.mTabSelectedTextColorResId = 0;
-        this.mTabIndicatorColorResId = a.getResourceId(R.styleable.TabLayout_tabIndicatorColor, 0);
-        int tabTextAppearance = a.getResourceId(R.styleable.TabLayout_tabTextAppearance, R.style.TextAppearance_Design_Tab);
-        TypedArray ta = context.obtainStyledAttributes(tabTextAppearance, R.styleable.SkinTextAppearance);
-
-
-        try {
-            this.mTabTextColorsResId = ta.getResourceId(R.styleable.SkinTextAppearance_android_textColor, 0);
-        } finally {
-            ta.recycle();
-        }
-
-        if (a.hasValue(R.styleable.TabLayout_tabTextColor)) {
-            this.mTabTextColorsResId = a.getResourceId(R.styleable.TabLayout_tabTextColor, 0);
+        this.mTabIndicatorColorResId = a.getResourceId(android.support.design.widget.R.styleable.TabLayout_tabIndicatorColor, 0);
+        int tabTextAppearance = a.getResourceId(android.support.design.widget.R.styleable.TabLayout_tabTextAppearance, android.support.design.widget.R.style.TextAppearance_Design_Tab);
+//        TypedArray ta = context.obtainStyledAttributes(tabTextAppearance, R.styleable.SkinTextAppearance);
+//
+//
+//        try {
+//            this.mTabTextColorsResId = ta.getResourceId(R.styleable.SkinTextAppearance_android_textColor, 0);
+//        } finally {
+//            ta.recycle();
+//        }
+
+        if (a.hasValue(android.support.design.widget.R.styleable.TabLayout_tabTextColor)) {
+            this.mTabTextColorsResId = a.getResourceId(android.support.design.widget.R.styleable.TabLayout_tabTextColor, 0);
         }
 
-        if (a.hasValue(R.styleable.TabLayout_tabSelectedTextColor)) {
-            this.mTabSelectedTextColorResId = a.getResourceId(R.styleable.TabLayout_tabSelectedTextColor, 0);
+        if (a.hasValue(android.support.design.widget.R.styleable.TabLayout_tabSelectedTextColor)) {
+            this.mTabSelectedTextColorResId = a.getResourceId(android.support.design.widget.R.styleable.TabLayout_tabSelectedTextColor, 0);
         }
 
         a.recycle();
diff --git a/app/src/main/java/com/rae/cnblogs/widget/RaeSkinImageView.java b/widget/src/main/java/com/rae/cnblogs/widget/RaeSkinImageView.java
similarity index 90%
rename from app/src/main/java/com/rae/cnblogs/widget/RaeSkinImageView.java
rename to widget/src/main/java/com/rae/cnblogs/widget/RaeSkinImageView.java
index 8d40e93..eaaa052 100644
--- a/app/src/main/java/com/rae/cnblogs/widget/RaeSkinImageView.java
+++ b/widget/src/main/java/com/rae/cnblogs/widget/RaeSkinImageView.java
@@ -6,7 +6,6 @@
 import android.graphics.PorterDuff;
 import android.util.AttributeSet;
 
-import com.rae.cnblogs.R;
 import com.rae.cnblogs.ThemeCompat;
 
 import skin.support.widget.SkinCompatImageView;
@@ -29,10 +28,8 @@ public RaeSkinImageView(Context context, AttributeSet attrs, int defStyleAttr) {
         int count = a.getIndexCount();
         for (int i = 0; i < count; i++) {
             int index = a.getIndex(i);
-            switch (index) {
-                case R.styleable.RaeSkinImageView_nightColor:
-                    mNightColor = a.getColor(index, 0);
-                    break;
+            if (index == R.styleable.RaeSkinImageView_nightColor) {
+                mNightColor = a.getColor(index, 0);
             }
         }
         a.recycle();
diff --git a/app/src/main/java/com/rae/cnblogs/widget/RaeSkinImageViewV4.java b/widget/src/main/java/com/rae/cnblogs/widget/RaeSkinImageViewV4.java
similarity index 99%
rename from app/src/main/java/com/rae/cnblogs/widget/RaeSkinImageViewV4.java
rename to widget/src/main/java/com/rae/cnblogs/widget/RaeSkinImageViewV4.java
index e93fe02..3a3c7e3 100644
--- a/app/src/main/java/com/rae/cnblogs/widget/RaeSkinImageViewV4.java
+++ b/widget/src/main/java/com/rae/cnblogs/widget/RaeSkinImageViewV4.java
@@ -6,7 +6,6 @@
 import android.util.AttributeSet;
 import android.widget.ImageView;
 
-import com.rae.cnblogs.R;
 import com.rae.cnblogs.ThemeCompat;
 
 import skin.support.widget.SkinCompatBackgroundHelper;
diff --git a/widget/src/main/res/values/attrs.xml b/widget/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..e0f1195
--- /dev/null
+++ b/widget/src/main/res/values/attrs.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <declare-styleable name="RaeSkinImageView">
+        <!--夜间模式颜色-->
+        <attr name="nightColor" format="color|reference" />
+    </declare-styleable>
+    <declare-styleable name="DesignTabLayout">
+        <attr name="tabIndicatorWidth" format="dimension" />
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/widget/src/main/res/values/dimens.xml b/widget/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..6ebee0c
--- /dev/null
+++ b/widget/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!--夜间模式降低图片透明度-->
+    <integer name="imageAlpha_night">70</integer>
+</resources>
\ No newline at end of file
diff --git a/widget/src/main/res/values/strings.xml b/widget/src/main/res/values/strings.xml
new file mode 100644
index 0000000..35e6cb6
--- /dev/null
+++ b/widget/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">widget</string>
+</resources>
