public class MD5 {

    public static String generate(String source) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.update(source.getBytes());
            return new BigInteger(1, messageDigest.digest()).toString(16);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return source;
    }

} public class Aequorea extends Application {
    private static Aequorea mApp;
    private static String mCurrentTheme;
    private static ExecutorService mExecutor;
    
    @Override
    public void onCreate() {
        super.onCreate();
        
        mApp = this;
        mCurrentTheme = ThemeHelper.getTheme();
        mExecutor = Executors.newCachedThreadPool();
    
        initCache();
        initFlurry();
    }

    private void initFlurry() {
        new FlurryAgent.Builder()
                .withDataSaleOptOut(false) //CCPA - the default value is false
                .withCaptureUncaughtExceptions(true)
                .withIncludeBackgroundSessionsInMetrics(true)
                .withLogLevel(Log.VERBOSE)
                .withPerformanceMetrics(FlurryPerformance.ALL)
                .build(this, "CXH6FJ9VS4P8K3K8BFN9");
    }
    
    private void initCache() {
        File cacheDir = new File(getCacheDir().getAbsolutePath() + File.separator + Constants.ARTICLE_PIC_CACHE);
        RichText.initCacheDir(cacheDir);
        ArticleCache.getCache();
    }
    
    public static Application getApp() {
        return mApp;
    }
    
    public static String getCurrentTheme() {
        return mCurrentTheme;
    }
    
    public static boolean isLightTheme() {
        return mCurrentTheme.equals(Constants.THEME_LIGHT);
    }
    
    public static void setCurrentTheme(String theme) {
        mCurrentTheme = theme;
    }
    
    public static ExecutorService getExecutor(){
        return mExecutor;
    }
} public class Constants {
    public static final String AUTHOR = "author";
    public static final String TAG = "tag";
    public static final String ARTICLE_ID = "article_id";
    public static final String AUTHOR_ID = "author_id";
    public static final String TAG_ID = "tag_id";
    public static final String SEARCH_KEY = "search_key";
    public static final String PHOTO = "photo";
    public static final String ARG_URL = "arg_url";
    
    // Article Type
    public static final String ARTICLE_TYPE_MAGAZINE = "magazine_article"; // 封面故事
    public static final String ARTICLE_TYPE_MAGAZINE_V2 = "magazine"; // 封面故事
    public static final String ARTICLE_TYPE_NORMAL = "normal_article"; // 一般文章
    public static final String ARTICLE_TYPE_NORMAL_V2 = "normal"; // 一般文章
    public static final String ARTICLE_TYPE_THEME = "theme"; // 专题
    public static final String ARTICLE_TYPE_TOP_ARTICLE = "top_article"; // 头条故事
    public static final String ARTICLE_TYPE_SUBJECT = "subject"; // 单行本


    public static final int TYPE_MAGAZINE = 1;
    public static final int TYPE_NORMAL = 2;
    public static final int TYPE_THEME = 3;
    public static final int TYPE_TOP_ARTICLE = 4;
    public static final int TYPE_SUBJECT = 5;
    
    // Theme
    public static final String THEME = "theme";
    public static final String THEME_LIGHT = "theme_light";
    public static final String THEME_DARK = "theme_dark";
    
    // Trigger
    public static final int AUTO_LOAD_TRIGGER = 30;
    
    // Key of SharePreference
    public static final String SP_LATEST_MAIN_PAGE = "sp_latest_main_page";
    public static final String SP_HD_SCREENSHOT = "sp_hd_screenshot";
    public static final String SP_ENABLE_SELECTION = "sp_enable_selection";
    public static final String SP_OFFLINE_CACHE = "sp_offline_cache";
    public static final String SP_DISABLE_RECOMMEND_ARTICLE = "sp_disable_recommend_article";
    
    // Cache dir
    public static final String ARTICLE_CACHE = "article_cache";
    public static final String ARTICLE_PIC_CACHE = "article_pic_cache";
} public class ArticleCache {
    private static final int MAX_CACHED_MEMORY_ARTICLE_SIZE = 20;
    private static final int MAX_CACHED_DISK_ARTICLE_SIZE = 1024 * 1024 * 10; // 10M
    
    private final LruCache<Long, String> mCache;
    private DiskLruCache mDiskCache;
    
    private ArticleCache() {
        this.mCache = new LruCache<>(MAX_CACHED_MEMORY_ARTICLE_SIZE);
        initDiskCache();
    }
    
    private void initDiskCache() {
        Aequorea.getExecutor().execute(new Runnable() {
            @Override
            public void run() {
                try {
                    File cacheDir = new File(Aequorea.getApp().getCacheDir().getAbsolutePath() + File.separator + Constants.ARTICLE_CACHE);
                    mDiskCache = DiskLruCache.open(cacheDir, BuildConfig.VERSION_CODE, 1, MAX_CACHED_DISK_ARTICLE_SIZE);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }
    
    public void remove(final String key) {
        try {
            mDiskCache.remove(key);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private static class ArticlePoolHolder {
        private static final ArticleCache ARTICLE_CACHE_POOL = new ArticleCache();
    }
    
    public static ArticleCache getCache() {
        return ArticlePoolHolder.ARTICLE_CACHE_POOL;
    }
    
    public void cache(final long id, final String articleData) {
        mCache.put(id, articleData);
        
        Aequorea.getExecutor().execute(new Runnable() {
            @Override
            public void run() {
                cacheInStorage(id, articleData);
            }
        });
    }
    
    // cache in internal storage
    private void cacheInStorage(long id, String articleData) {
        String key = Long.toString(id);
        if (mDiskCache != null) {
            try {
                DiskLruCache.Snapshot snapshot = mDiskCache.get(key);
                
                if (snapshot == null) {
                    DiskLruCache.Editor editor = mDiskCache.edit(key);
    
                    OutputStream out = editor.newOutputStream(0);
                    out.write(articleData.getBytes());
                    out.close();
                    editor.commit();
                } else {
                    snapshot.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    public boolean isCachedInStorage(long id) {
        String key = Long.toString(id);
        
        if (mDiskCache != null) {
            try {
                DiskLruCache.Snapshot snapshot = mDiskCache.get(key);
                if (snapshot != null) {
                    return true;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }
    
    public String loadCacheFromStorage(long id) {
        String key = Long.toString(id);
        
        if (mDiskCache != null) {
            try {
                DiskLruCache.Snapshot snapshot = mDiskCache.get(key);
                InputStream in = snapshot.getInputStream(0);
                if (in != null) {
                    BufferedInputStream bis = new BufferedInputStream(in);
                    ByteArrayOutputStream buf = new ByteArrayOutputStream();
                    int result = bis.read();
                    while (result != -1) {
                        buf.write((byte) result);
                        result = bis.read();
                    }
                    return buf.toString("UTF-8");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return "";
    }
    
    public String loadCache(long id) {
        return mCache.get(id);
    }
    
    public void recycle() {
        mCache.evictAll();
    }
} public interface NetworkService {

    // main page
    @GET("/v4/first_page_infos")
    Observable<Data> getMainPageInfo(@Query("page") int page, @Query("per") int per);

    // article details
    @GET("/v4/articles/{article_id}/")
    Observable<DataWrapper> getArticleDetailInfo(@Path("article_id") long articleId);

    // article that belonging to specific author
    @GET("/v4/authors/{author_id}/articles")
    Observable<Data> getArticleList(@Path("author_id") long authorId, @Query("page") int page, @Query("per") int per);
    
    // recommended article
    @GET("/v4/articles/{article_id}/recommendations")
    Observable<Data> getRecommendedArticle(@Path("article_id") long articleId);
    
    // article with tags
    @GET("v4/topics/{tag_id}/articles")
    Observable<Data> getTagsList(@Path("tag_id") long id, @Query("page") int page, @Query("per") int per);
    
    // article with keyword
    @GET("v4/pg_search_documents")
    Observable<SearchData> getArticleListWithKeyword(@Query("page") int page, @Query("per") int per, @Query("query") String keyword, @Query("group") boolean group, @Query("type") String type);
} public class RequestManager {
    private Retrofit mRetrofit;
    private static final int CONNECT_TIMEOUT = 5;
    private static final int READ_TIMEOUT = 5;
    private static final int WRITE_TIMEOUT = 5;
    
    private static final String PLATFORM = "Android";
    private static final String VERSION = "4.0.1";
    
    private RequestManager() {
        init();
    }
    
    private void init() {
        OkHttpClient client = new OkHttpClient.Builder().readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
            .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
            .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
            .addNetworkInterceptor(chain -> {
                Request original = chain.request();
    
                LinkedHashMap<String, Object> queryHashMap = new LinkedHashMap<>();
                for ( int i = 0; i < original.url().querySize(); i++) {
                    queryHashMap.put(original.url().queryParameterName(i), original.url().queryParameterValue(i));
                }
    
                String signature = SignatureHelper.jaMi(queryHashMap);
                
                Request request = original.newBuilder()
                    .addHeader("Authorization", "")
                    .addHeader("ClientPlatform", PLATFORM)
                    .addHeader("ClientVersion", VERSION)
                    .addHeader("X-Signature", signature)
                    .build();
                
                return chain.proceed(request);
            })
            .addNetworkInterceptor(new ChuckerInterceptor(Aequorea.getApp()))
            .build();
        
        Gson gson = new GsonBuilder().registerTypeAdapter(Datum.class, new DatumDeserializer()).create();
        
        mRetrofit = new Retrofit.Builder().baseUrl(NetworkConstants.CBNWEEK_HTTPS_HOST)
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .addConverterFactory(GsonConverterFactory.create(gson))
            .client(client)
            .build();
    }
    
    public Retrofit getRetrofit() {
        return mRetrofit;
    }
    
    public static RequestManager getInstance() {
        return RequestManagerHolder.sInstance;
    }
    
    private static class RequestManagerHolder {
        private static RequestManager sInstance = new RequestManager();
    }
} public class CacheUtils {
    
    private static final int MAX_BITMAP_SIZE = 60 * 1024 * 1024;
    
    public static DiskLruCache getArticlePicDiskLruCache() {
        
        File cacheDir = new File(Aequorea.getApp()
            .getCacheDir()
            .getAbsolutePath() + File.separator + Constants.ARTICLE_PIC_CACHE);
        
        try {
            return DiskLruCache.open(checkDir(cacheDir), BitmapPool.getVersion(), 2, MAX_BITMAP_SIZE);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    @SuppressWarnings("ResultOfMethodCallIgnored")
    private static File checkDir(File dir) {
        File cacheDir = new File(dir, "_rt");
        if (!cacheDir.exists()) {
            cacheDir.mkdir();
        }
        return cacheDir;
    }
} public class FilterUtils {
    public static boolean underSupport(String type) {
        return !((Constants.ARTICLE_TYPE_THEME.equals(type)) || (Constants.ARTICLE_TYPE_MAGAZINE.equals(type)) || (Constants.ARTICLE_TYPE_MAGAZINE_V2.equals(type)) || (Constants.ARTICLE_TYPE_SUBJECT.equals(type)));
    }
    
    public static List<Datum> filterData(List<Datum> data) {
        Iterator<Datum> iterator = data.iterator();
        
        while (iterator.hasNext()) {
            Datum d = iterator.next();
            if (!FilterUtils.underSupport(d.getArticleType()) || !FilterUtils.underSupport(d.getType())) {
                iterator.remove();
            }
        }
        return data;
    }
    
    public static List<SearchDatum> filterSearchData(List<SearchDatum> data) {
        Iterator<SearchDatum> iterator = data.iterator();
        
        while (iterator.hasNext()) {
            Content content = iterator.next().getContent();
            if (!FilterUtils.underSupport(content.getArticleType())) {
                iterator.remove();
            }
        }
        return data;
    }
} public class NetworkUtils {

    public static boolean isNetworkAvailable() {
        Context context = Aequorea.getApp();
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    
        NetworkInfo info = null;
        if (connectivityManager != null) {
            info = connectivityManager.getActiveNetworkInfo();
        }
        return info != null && info.isAvailable();
    }
    
    public static boolean isWiFiNetwork() {
        Context context = Aequorea.getApp();
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    
        NetworkInfo info = null;
        if (connectivityManager != null) {
            info = connectivityManager.getActiveNetworkInfo();
        }
    
        return info != null && info.getType() == ConnectivityManager.TYPE_WIFI && info.isAvailable();
    }
} public class SPUtils {
    private static SharedPreferences mSp;
    
    private static SharedPreferences getSp() {
        if (mSp == null) {
            mSp = Aequorea.getApp().getSharedPreferences("Aequorea", Context.MODE_PRIVATE);
        }
        return mSp;
    }
    
    public static void setString(String key, String value) {
        getSp().edit().putString(key, value).apply();
    }
    
    public static String getString(String key) {
        return getSp().getString(key, "");
    }
    
    public static String getString(String key, String defaultValue) {
        return getSp().getString(key, defaultValue);
    }
    
    public static void setInt(String key, int value) {
        getSp().edit().putInt(key, value).apply();
    }
    
    public static int getInt(String key) {
        return getSp().getInt(key, 0);
    }
    
    public static int getInt(String key, int defaultValue) {
        return getSp().getInt(key, defaultValue);
    }
    
    public static void setBoolean(String key, boolean value) {
        getSp().edit().putBoolean(key, value).apply();
    }
    
    public static boolean getBoolean(String key) {
        return getSp().getBoolean(key, false);
    }
} public class Data {

    @SerializedName("data")
    private List<Datum> mData;
    @SerializedName("meta")
    private Meta mMeta;

    public List<Datum> getData() {
        return mData;
    }

    public void setData(List<Datum> data) {
        mData = data;
    }
    
    public Meta getMeta() {
        return mMeta;
    }
    
    public void setMeta(Meta meta) {
        mMeta = meta;
    }
} public class DataWrapper {

    @SerializedName("data")
    private Datum mData;

    public Datum getData() {
        return mData;
    }

    public void setData(Datum data) {
        mData = data;
    }

} public class Datum {

    @SerializedName("anonymous_purchase_total_count")
    private Long mAnonymousPurchaseTotalCount;
    @SerializedName("anonymous_purchases")
    private List<AnonymousPurchase> mAnonymousPurchases;
    @SerializedName("anonymous_share_price")
    private String mAnonymousSharePrice;
    @SerializedName("article_type")
    private String mArticleType;
    @SerializedName("authors")
    private List<Author> mAuthors;
    @SerializedName("column")
    private Column mColumn;
    @SerializedName("comment_times")
    private Long mCommentTimes;
    @SerializedName("data")
    private List<Datum> mData;
    @SerializedName("display_time")
    private String mDisplayTime;
    @SerializedName("editor_choice_comments")
    private List<Object> mEditorChoiceComments;
    @SerializedName("is_favorited")
    private Boolean mIsFavorited;
    @SerializedName("is_like")
    private Boolean mIsLike;
    @SerializedName("like_times")
    private Long mLikeTimes;
    @SerializedName("magazine")
    private Magazine mMagazine;
    @SerializedName("read_time")
    private Long mReadTime;
    @SerializedName("share_url")
    private String mShareUrl;
    @SerializedName("share_visit_limit")
    private Long mShareVisitLimit;
    @SerializedName("share_visit_times")
    private Long mShareVisitTimes;
    @SerializedName("summary")
    private String mSummary;
    @SerializedName("title")
    private String mTitle;
    @SerializedName("topics")
    private List<Topic> mTopics;
    @SerializedName("type")
    private String mType;
    @SerializedName("visit_times")
    private Long mVisitTimes;
    @SerializedName("articles_count")
    private Long mArticlesCount;
    private String mCoverUrl;
    @SerializedName("description")
    private Object mDescription;
    @SerializedName("id")
    private Long mId;
    @SerializedName("illustration")
    private String mIllustration;
    @SerializedName("name")
    private String mName;
    @SerializedName("content")
    private String mContent;
    @SerializedName("total_read_times")
    private Long mTotalReadTimes;
    @SerializedName("total_visit_times")
    private Long mTotalVisitTimes;

    public Long getArticlesCount() {
        return mArticlesCount;
    }

    public void setArticlesCount(Long articlesCount) {
        mArticlesCount = articlesCount;
    }


    public Object getDescription() {
        return mDescription;
    }

    public void setDescription(Object description) {
        mDescription = description;
    }

    public String getIllustration() {
        return mIllustration;
    }

    public void setIllustration(String illustration) {
        mIllustration = illustration;
    }

    public String getName() {
        return mName;
    }

    public void setName(String name) {
        mName = name;
    }

    public Long getTotalReadTimes() {
        return mTotalReadTimes;
    }

    public void setTotalReadTimes(Long totalReadTimes) {
        mTotalReadTimes = totalReadTimes;
    }

    public Long getTotalVisitTimes() {
        return mTotalVisitTimes;
    }

    public void setTotalVisitTimes(Long totalVisitTimes) {
        mTotalVisitTimes = totalVisitTimes;
    }


    public Long getAnonymousPurchaseTotalCount() {
        return mAnonymousPurchaseTotalCount;
    }

    public void setAnonymousPurchaseTotalCount(Long anonymousPurchaseTotalCount) {
        mAnonymousPurchaseTotalCount = anonymousPurchaseTotalCount;
    }

    public List<AnonymousPurchase> getAnonymousPurchases() {
        return mAnonymousPurchases;
    }

    public void setAnonymousPurchases(List<AnonymousPurchase> anonymousPurchases) {
        mAnonymousPurchases = anonymousPurchases;
    }

    public String getAnonymousSharePrice() {
        return mAnonymousSharePrice;
    }

    public void setAnonymousSharePrice(String anonymousSharePrice) {
        mAnonymousSharePrice = anonymousSharePrice;
    }

    public String getArticleType() {
        return mArticleType;
    }

    public void setArticleType(String articleType) {
        mArticleType = articleType;
    }

    public List<Author> getAuthors() {
        return mAuthors;
    }

    public void setAuthors(List<Author> authors) {
        mAuthors = authors;
    }

    public Column getColumn() {
        return mColumn;
    }

    public void setColumn(Column column) {
        mColumn = column;
    }

    public Long getCommentTimes() {
        return mCommentTimes;
    }

    public void setCommentTimes(Long commentTimes) {
        mCommentTimes = commentTimes;
    }

    public String getCoverUrl() {
        return mCoverUrl;
    }

    public void setCoverUrl(String coverUrl) {
        mCoverUrl = coverUrl;
    }

    public List<Datum> getData() {
        return mData;
    }

    public void setData(List<Datum> data) {
        mData = data;
    }

    public String getDisplayTime() {
        return mDisplayTime;
    }

    public void setDisplayTime(String displayTime) {
        mDisplayTime = displayTime;
    }

    public List<Object> getEditorChoiceComments() {
        return mEditorChoiceComments;
    }

    public void setEditorChoiceComments(List<Object> editorChoiceComments) {
        mEditorChoiceComments = editorChoiceComments;
    }

    public Long getId() {
        return mId;
    }

    public void setId(Long id) {
        mId = id;
    }

    public Boolean getIsFavorited() {
        return mIsFavorited;
    }

    public void setIsFavorited(Boolean isFavorited) {
        mIsFavorited = isFavorited;
    }

    public Boolean getIsLike() {
        return mIsLike;
    }

    public void setIsLike(Boolean isLike) {
        mIsLike = isLike;
    }

    public Long getLikeTimes() {
        return mLikeTimes;
    }

    public void setLikeTimes(Long likeTimes) {
        mLikeTimes = likeTimes;
    }

    public Magazine getMagazine() {
        return mMagazine;
    }

    public void setMagazine(Magazine magazine) {
        mMagazine = magazine;
    }

    public Long getReadTime() {
        return mReadTime;
    }

    public void setReadTime(Long readTime) {
        mReadTime = readTime;
    }

    public String getShareUrl() {
        return mShareUrl;
    }

    public void setShareUrl(String shareUrl) {
        mShareUrl = shareUrl;
    }

    public Long getShareVisitLimit() {
        return mShareVisitLimit;
    }

    public void setShareVisitLimit(Long shareVisitLimit) {
        mShareVisitLimit = shareVisitLimit;
    }

    public Long getShareVisitTimes() {
        return mShareVisitTimes;
    }

    public void setShareVisitTimes(Long shareVisitTimes) {
        mShareVisitTimes = shareVisitTimes;
    }

    public String getSummary() {
        return mSummary;
    }

    public void setSummary(String summary) {
        mSummary = summary;
    }

    public String getTitle() {
        return mTitle;
    }

    public void setTitle(String title) {
        mTitle = title;
    }

    public List<Topic> getTopics() {
        return mTopics;
    }

    public void setTopics(List<Topic> topics) {
        mTopics = topics;
    }

    public String getType() {
        return mType;
    }

    public void setType(String type) {
        mType = type;
    }

    public Long getVisitTimes() {
        return mVisitTimes;
    }

    public void setVisitTimes(Long visitTimes) {
        mVisitTimes = visitTimes;
    }
    
    public String getContent() {
        return mContent;
    }
    
    public void setContent(String content) {
        mContent = content;
    }
}
import android.annotation.SuppressLint;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.WifiManager;
import android.os.IBinder;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import android.text.TextUtils;
import android.util.Log;
import com.google.gson.Gson;
import com.jakewharton.disklrucache.DiskLruCache;
import com.zzhoujay.richtext.ext.MD5;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import nich.work.aequorea.Aequorea;
import nich.work.aequorea.R;
import nich.work.aequorea.common.Constants;
import nich.work.aequorea.common.cache.ArticleCache;
import nich.work.aequorea.common.network.NetworkService;
import nich.work.aequorea.common.network.RequestManager;
import nich.work.aequorea.common.utils.CacheUtils;
import nich.work.aequorea.common.utils.FilterUtils;
import nich.work.aequorea.common.utils.NetworkUtils;
import nich.work.aequorea.common.utils.SPUtils;
import nich.work.aequorea.model.entity.Data;
import nich.work.aequorea.model.entity.DataWrapper;
import nich.work.aequorea.model.entity.Datum;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
package nich.work.aequorea.common.service;





public class CacheService extends Service {
    
    private OkHttpClient mClient;
    private NetworkService mService;
    private CompositeDisposable mComposite;
    private LinkedList<Long> mArticleList;
    private HashSet<Call> mDownloadTasks;
    private DiskLruCache mDiskLruCache;
    
    private NotificationManager mNotificationManager;
    private BroadcastReceiver mNetworkChangeReceiver;
    
    private int mPicToCacheSize;
    private int mPicCachedCount;
    private static final String CHANNEL_ID = "aequorea_cache";
    
    private static final Pattern IMAGE_TAG_PATTERN = Pattern.compile("<(img|IMG)(.*?)>");
    private static final Pattern IMAGE_SRC_PATTERN = Pattern.compile("(src|SRC)=\"(.*?)\"");
    
    private static final String TAG = CacheService.class.getSimpleName();
    
    @Override
    public void onCreate() {
        super.onCreate();
        
        mClient = new OkHttpClient();
        IntentFilter intentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
        mService = RequestManager.getInstance().getRetrofit().create(NetworkService.class);
        mComposite = new CompositeDisposable();
        
        mNetworkChangeReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (!NetworkUtils.isWiFiNetwork()) {
                    cancelCaching();
                }
            }
        };
        
        registerReceiver(mNetworkChangeReceiver, intentFilter);
        
        Data dataToCache = getMainPageDataFromSP();
        if (dataToCache != null) {
            preCache(dataToCache);
            
            if (mArticleList.size() != 0) {
                showNotification();
                startCaching();
            }
        }
    }
    
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mNetworkChangeReceiver);
        hideNotification();
        if (mComposite != null) {
            mComposite.clear();
        }
    }
    
    @SuppressLint("UseSparseArrays")
    private void preCache(Data data) {
        mArticleList = new LinkedList<>();
        mDownloadTasks = new HashSet<>();
        mDiskLruCache = CacheUtils.getArticlePicDiskLruCache();