Commit 77f51b17 by Wyh

修改文件夾

parent fa3ae8bf

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

apply plugin: 'com.jfrog.bintray'
apply plugin: 'com.github.dcendents.android-maven'
Properties properties = new Properties()
boolean isHasFile = false
if (project.rootProject.findProject('local.properties') != null){
isHasFile = true
properties.load(project.rootProject.file('local.properties').newDataInputStream())
}
def gitUrl = 'https://github.com/JessYanCoding/MVPArms.git' // Git仓库的url
def siteUrl = 'https://github.com/JessYanCoding/MVPArms' // 项目的主页
version = rootProject.ext.android["versionName"]
group = "me.jessyan"
bintray {
user = isHasFile ? properties.getProperty("bintray.user") : System.getenv("bintray_user")
key = isHasFile ? properties.getProperty("bintray.apikey") : System.getenv("bintray_apikey")
pkg {
repo = 'maven'
name = 'MVPArms'
licenses = ["Apache-2.0"]
websiteUrl = siteUrl
vcsUrl = gitUrl
publish = true // 是否是公开项目。
version {
name = rootProject.ext.android["versionName"]
desc = 'A common Architecture for Android Applications developing based on MVP,integrates many Open Source Projects( like Dagger2,RxJava,Retrofit... ),to make your developing quicker and easier.'
released = new Date()
vcsTag = 'v' + rootProject.ext.android["versionName"]
attributes = ['gradle-plugin': 'com.use.less:com.use.less.gradle:gradle-useless-plugin']
}
}
configurations = ['archives']
}
install {
repositories.mavenInstaller {
// This generates POM.xml with proper parameters
pom {
project {
packaging 'aar'
// Add your description here
name 'MVPArms'
description 'A common Architecture for Android Applications developing based on MVP,integrates many Open Source Projects( like Dagger2,RxJava,Retrofit... ),to make your developing quicker and easier.'
url siteUrl
// Set your license
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id 'JessYanCoding' //填写bintray或者github的用户名
name 'jessyan' //姓名,可以是中文
email 'jess.yan.effort@gmail.com'
}
}
scm {
connection gitUrl
developerConnection gitUrl
url siteUrl
}
}
}
}
}
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
task javadoc(type: Javadoc) {
failOnError false
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives javadocJar
archives sourcesJar
}
ext.alwaysLib = true //虽然apply了cc-settings-2.gradle,但一直作为library编译,否则别的组件依赖此module时会报错
apply from: rootProject.file("cc-settings.gradle")
android {
compileSdkVersion rootProject.ext.android["compileSdkVersion"]
useLibrary 'org.apache.http.legacy'
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
minSdkVersion rootProject.ext.android["minSdkVersion"]
targetSdkVersion rootProject.ext.android["targetSdkVersion"]
versionCode rootProject.ext.android["versionCode"]
versionName rootProject.ext.android["versionName"]
}
buildTypes {
// Test {
// buildConfigField "boolean", "LOG_DEBUG", "false"
// buildConfigField "boolean", "USE_CANARY", "false"
// minifyEnabled false
// shrinkResources false
// zipAlignEnabled false
// proguardFiles 'proguard.cfg'
// }
debug {
buildConfigField "boolean", "LOG_DEBUG", "true"
buildConfigField "boolean", "USE_CANARY", "true"
minifyEnabled false
proguardFiles 'proguard.cfg'
}
release {
buildConfigField "boolean", "LOG_DEBUG", "false"
buildConfigField "boolean", "USE_CANARY", "false"
minifyEnabled true
// shrinkResources true
zipAlignEnabled true
proguardFiles 'proguard.cfg'
}
}
}
buildscript {
repositories {
jcenter()
}
}
dependencies {
//fragmentation
api project(':fragmentation_core')
//support
api(rootProject.ext.dependencies["support-v4"]) {
exclude module: 'support-annotations'
}
api(rootProject.ext.dependencies["appcompat-v7"]) {
exclude module: 'support-annotations'
exclude module: 'support-v4'
}
api(rootProject.ext.dependencies["design"]) {
exclude module: 'support-annotations'
exclude module: 'appcompat-v7'
exclude module: 'support-v4'
}
api rootProject.ext.dependencies["annotations"]
//view
api(rootProject.ext.dependencies["autolayout"]) {
exclude group: 'com.android.support'
}
api(rootProject.ext.dependencies["butterknife"]) {
exclude module: 'support-annotations'
}
//rx
api rootProject.ext.dependencies["rxjava2"]
api(rootProject.ext.dependencies["rxandroid2"]) {
exclude module: 'rxjava'
}
api(rootProject.ext.dependencies["rxcache2"]) {
exclude module: 'rxjava'
exclude module: 'dagger'
}
implementation(rootProject.ext.dependencies["rxcache-jolyglot-gson"]) {
exclude module: 'gson'
}
api(rootProject.ext.dependencies["rxlifecycle2"]) {
exclude module: 'rxjava'
exclude module: 'jsr305'
}
api(rootProject.ext.dependencies["rxlifecycle2-android"]) {
exclude module: 'support-annotations'
exclude module: 'rxjava'
exclude module: 'rxandroid'
exclude module: 'rxlifecycle'
}
api(rootProject.ext.dependencies["rxpermissions2"]) {
exclude module: 'rxjava'
exclude module: 'support-annotations'
}
api rootProject.ext.dependencies['rxerrorhandler2']
api rootProject.ext.dependencies["rxbinding2"]
//network
api(rootProject.ext.dependencies["retrofit"]) {
exclude module: 'okhttp'
exclude module: 'okio'
}
api(rootProject.ext.dependencies["retrofit-converter-gson"]) {
exclude module: 'gson'
exclude module: 'okhttp'
exclude module: 'okio'
exclude module: 'retrofit'
}
api(rootProject.ext.dependencies["retrofit-adapter-rxjava2"]) {
exclude module: 'rxjava'
exclude module: 'okhttp'
exclude module: 'retrofit'
exclude module: 'okio'
}
api rootProject.ext.dependencies["okhttp3"]
api(rootProject.ext.dependencies["glide"]) {
exclude module: 'support-v4'
}
annotationProcessor(rootProject.ext.dependencies["glide-compiler"]) {
exclude module: 'jsr305'
}
//tools
compileOnly rootProject.ext.dependencies["javax.annotation"]
api rootProject.ext.dependencies["dagger2"]
annotationProcessor rootProject.ext.dependencies["dagger2-compiler"]
api rootProject.ext.dependencies["androideventbus"]
api rootProject.ext.dependencies["gson"]
api rootProject.ext.dependencies["butterknife"]
implementation rootProject.ext.dependencies["multidex"]
implementation rootProject.ext.dependencies["recyclerview-v7"]
implementation rootProject.ext.dependencies["cardview-v7"]
//可長按拖動 側滑刪除的recyclerview
api rootProject.ext.dependencies["yzjRecyclerView"]
//test
api rootProject.ext.dependencies["timber"]
implementation rootProject.ext.dependencies["retrofit-url-manager"]
//解决Glide找不到Android声明库问题
annotationProcessor 'androidx.annotation:annotation:1.1.0'
// testApi rootProject.ext.dependencies["junit"]
api rootProject.ext.dependencies["immersionbar-components"]
api rootProject.ext.dependencies["immersionbar"]
}
//apply from: 'bintray.gradle'
\ No newline at end of file
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/jess/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# 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 *;
#}
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose
-printmapping priguardMapping.txt
-optimizations !code/simplification/artithmetic,!field/*,!class/merging/*
################common###############
-keep public class * implements com.jess.arms.integration.ConfigModule
#实体类不参与混淆
-keep class com.jess.arms.widget.** { *; } #自定义控件不参与混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keepnames class * implements java.io.Serializable
-keepattributes Signature
-keep class **.R$* {*;}
-ignorewarnings
-keepclassmembers class **.R$* {
public static <fields>;
}
-keepclasseswithmembernames class * { # 保持native方法不被混淆
native <methods>;
}
-keepclassmembers enum * { # 使用enum类型时需要注意避免以下两个方法混淆,因为enum类的特殊性,以下两个方法会被反射调用,
public static **[] values();
public static ** valueOf(java.lang.String);
}
################support###############
-keep class android.support.** { *; }
-keep interface android.support.** { *; }
-dontwarn android.support.**
################alipay###############
-keep class com.alipay.android.app.IAlixPay{*;}
-keep class com.alipay.android.app.IAlixPay$Stub{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
-keep class com.alipay.sdk.app.PayTask{ public *;}
-keep class com.alipay.sdk.app.AuthTask{ public *;}
################retrofit###############
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions
################butterknife###############
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
@butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
@butterknife.* <methods>;
}
################gson###############
-keepattributes Signature
-keepattributes *Annotation*
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.sunloto.shandong.bean.** { *; }
################glide###############
-keep public class * implements com.bumptech.glide.module.AppGlideModule
-keep public class * implements com.bumptech.glide.module.LibraryGlideModule
-keep class com.bumptech.glide.** { *; }
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
################okhttp###############
-keepattributes Signature
-keepattributes *Annotation*
-keep class com.squareup.okhttp.** { *; }
-keep interface com.squareup.okhttp.** { *; }
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn com.squareup.okhttp.**
################androidEventBus###############
-keep class org.simple.** { *; }
-keep interface org.simple.** { *; }
-keepclassmembers class * {
@org.simple.eventbus.Subscriber <methods>;
}
-keepattributes *Annotation*
################autolayout###############
-keep class com.zhy.autolayout.** { *; }
-keep interface com.zhy.autolayout.** { *; }
################RxJava and RxAndroid###############
-dontwarn org.mockito.**
-dontwarn org.junit.**
-dontwarn org.robolectric.**
-keep class io.reactivex.** { *; }
-keep interface io.reactivex.** { *; }
-keepattributes Signature
-keepattributes *Annotation*
-keep class com.squareup.okhttp.** { *; }
-dontwarn okio.**
-keep interface com.squareup.okhttp.** { *; }
-dontwarn com.squareup.okhttp.**
-dontwarn io.reactivex.**
-dontwarn retrofit.**
-keep class retrofit.** { *; }
-keepclasseswithmembers class * {
@retrofit.http.* <methods>;
}
-keep class sun.misc.Unsafe { *; }
-dontwarn java.lang.invoke.*
-keep class io.reactivex.schedulers.Schedulers {
public static <methods>;
}
-keep class io.reactivex.schedulers.ImmediateScheduler {
public <methods>;
}
-keep class io.reactivex.schedulers.TestScheduler {
public <methods>;
}
-keep class io.reactivex.schedulers.Schedulers {
public static ** test();
}
-keepclassmembers class io.reactivex.internal.util.unsafe.*ArrayQueue*Field* {
long producerIndex;
long consumerIndex;
}
-keepclassmembers class io.reactivex.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
long producerNode;
long consumerNode;
}
-keepclassmembers class io.reactivex.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
io.reactivex.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class io.reactivex.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
io.reactivex.internal.util.atomic.LinkedQueueNode consumerNode;
}
-dontwarn io.reactivex.internal.util.unsafe.**
################espresso###############
-keep class android.support.test.espresso.** { *; }
-keep interface android.support.test.espresso.** { *; }
################annotation###############
-keep class android.support.annotation.** { *; }
-keep interface android.support.annotation.** { *; }
################RxLifeCycle#################
-keep class com.trello.rxlifecycle2.** { *; }
-keep interface com.trello.rxlifecycle2.** { *; }
################RxPermissions#################
-keep class com.tbruyelle.rxpermissions2.** { *; }
-keep interface com.tbruyelle.rxpermissions2.** { *; }
################RxCache#################
-dontwarn io.rx_cache2.internal.**
-keep class io.rx_cache2.internal.Record { *; }
-keep class io.rx_cache2.Source { *; }
-keep class io.victoralbertos.jolyglot.** { *; }
-keep interface io.victoralbertos.jolyglot.** { *; }
################RxErrorHandler#################
-keep class me.jessyan.rxerrorhandler.** { *; }
-keep interface me.jessyan.rxerrorhandler.** { *; }
################Timber#################
-dontwarn org.jetbrains.annotations.**
################Canary#################
-dontwarn com.squareup.haha.guava.**
-dontwarn com.squareup.haha.perflib.**
-dontwarn com.squareup.haha.trove.**
-dontwarn com.squareup.leakcanary.**
-keep class com.squareup.haha.** { *; }
-keep class com.squareup.leakcanary.** { *; }
# Marshmallow removed Notification.setLatestEventInfo()
-dontwarn android.app.Notification
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.jess.arms">
</manifest>
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.base;
import android.os.Parcelable;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
/**
* ================================================
* 基类 {@link FragmentStatePagerAdapter}
* <p>
* Created by JessYan on 22/03/2016
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public class AdapterViewPager extends FragmentStatePagerAdapter {
private List<Fragment> mList;
private CharSequence[] mTitles;
public AdapterViewPager(FragmentManager fragmentManager, List<Fragment> list) {
super(fragmentManager);
this.mList = list;
}
public AdapterViewPager(FragmentManager fragmentManager, List<Fragment> list, CharSequence[] titles) {
super(fragmentManager);
this.mList = list;
this.mTitles = titles;
}
@Override
public Fragment getItem(int position) {
return mList.get(position);
}
@Override
public CharSequence getPageTitle(int position) {
if (mTitles != null) {
return mTitles[position];
}
return super.getPageTitle(position);
}
@Override
public int getCount() {
return mList.size();
}
@Override
public Parcelable saveState() {
return null;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment f = (Fragment) super.instantiateItem(container, position);
View view = f.getView();
if (view != null)
container.addView(view);
return f;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
View view = mList.get(position).getView();
if (view != null)
container.removeView(view);
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.base;
import com.jess.arms.di.component.AppComponent;
import androidx.annotation.NonNull;
/**
* ================================================
* 框架要求框架中的每个 {@link android.app.Application} 都需要实现此类,以满足规范
*
* @see BaseApplication
* Created by JessYan on 25/04/2017 14:54
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public interface App {
@NonNull
AppComponent getAppComponent();
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.base;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.jess.arms.base.delegate.IActivity;
import com.jess.arms.integration.cache.Cache;
import com.jess.arms.integration.cache.CacheType;
import com.jess.arms.integration.lifecycle.ActivityLifecycleable;
import com.jess.arms.mvp.IPresenter;
import com.jess.arms.utils.AndroidWorkaround;
import com.jess.arms.utils.ArmsUtils;
import com.qmuiteam.qmui.util.QMUIDeviceHelper;
import com.trello.rxlifecycle2.android.ActivityEvent;
import javax.inject.Inject;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import io.reactivex.subjects.BehaviorSubject;
import io.reactivex.subjects.Subject;
import static com.jess.arms.utils.Preconditions.checkNotNull;
import static com.jess.arms.utils.ThirdViewUtil.convertAutoView;
/**
* ================================================
* 因为 Java 只能单继承,所以如果要用到需要继承特定 {@link Activity} 的三方库,那你就需要自己自定义 {@link Activity}
* 继承于这个特定的 {@link Activity},然后再按照 {@link BaseActivity} 的格式,将代码复制过去,记住一定要实现{@link IActivity}
* <p>
* Created by JessYan on 22/03/2016
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public abstract class BaseActivity<P extends IPresenter> extends AppCompatActivity implements IActivity, ActivityLifecycleable {
protected final String TAG = this.getClass().getSimpleName();
private final BehaviorSubject<ActivityEvent> mLifecycleSubject = BehaviorSubject.create();
private Cache<String, Object> mCache;
private Unbinder mUnbinder;
protected Context mContext;
@Inject
@Nullable
protected P mPresenter;//如果当前页面逻辑简单, Presenter 可以为 null
@NonNull
@Override
public synchronized Cache<String, Object> provideCache() {
if (mCache == null) {
mCache = ArmsUtils.obtainAppComponentFromContext(this).cacheFactory().build(CacheType.ACTIVITY_CACHE);
}
return mCache;
}
@NonNull
@Override
public final Subject<ActivityEvent> provideLifecycleSubject() {
return mLifecycleSubject;
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
View view = convertAutoView(name, context, attrs);
return view == null ? super.onCreateView(name, context, attrs) : view;
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
if (QMUIDeviceHelper.isTablet(this)) {
//平板端按照系統方向來
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
//DecorView的背景对我来说无用,但是会产生一次Overdraw,这里去掉(过度绘制优化)
getWindow().setBackgroundDrawable(null);
try {
int layoutResID = initView(savedInstanceState);
//如果initView返回0,框架则不会调用setContentView(),当然也不会 Bind ButterKnife
if (layoutResID != 0) {
setContentView(layoutResID);
String name = getClass().getSimpleName();
if (!name.equals("DownloadActivity")) {
AndroidWorkaround.assistActivity(findViewById(android.R.id.content));
}
//绑定到butterknife
mUnbinder = ButterKnife.bind(this);
}
} catch (Exception e) {
e.printStackTrace();
}
initIntent();
initTopBar();
initLanguage();
initLayoutParams();
initLayoutVisible();
initData(savedInstanceState);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mUnbinder != null && mUnbinder != Unbinder.EMPTY)
mUnbinder.unbind();
this.mUnbinder = null;
if (mPresenter != null)
mPresenter.onDestroy();//释放资源
this.mPresenter = null;
}
/**
* 是否使用eventBus,默认为使用(true),
*
* @return
*/
@Override
public boolean useEventBus() {
return true;
}
/**
* 这个Activity是否会使用Fragment,框架会根据这个属性判断是否注册
* 如果返回false,那意味着这个Activity不需要绑定Fragment,那你再在这个Activity中绑定继承于 {@link com.jess.arms.base.BaseFragment} 的Fragment将不起任何作用
*
* @return
*/
@Override
public boolean useFragment() {
return true;
}
public void showMessage(@NonNull String message) {
ArmsUtils.makeText(this, message);
}
public void launchActivity(@NonNull Intent intent) {
checkNotNull(intent);
ArmsUtils.startActivity(intent);
}
public void killMyself() {
finish();
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.base;
import android.app.Application;
import android.content.Context;
import com.jess.arms.base.delegate.AppDelegate;
import com.jess.arms.base.delegate.AppLifecycles;
import com.jess.arms.di.component.AppComponent;
import com.jess.arms.utils.ArmsUtils;
import com.jess.arms.utils.Preconditions;
import androidx.annotation.NonNull;
import androidx.multidex.MultiDex;
import androidx.multidex.MultiDexApplication;
/**
* ================================================
* 本框架由 MVP + Dagger2 + Retrofit + RxJava + Androideventbus + Butterknife 组成
*
* @see <a href="https://github.com/JessYanCoding/MVPArms/wiki">请配合官方 Wiki 文档学习本框架</a>
* Created by JessYan on 22/03/2016
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public class BaseApplication extends Application implements App {
private AppLifecycles mAppDelegate;
/**
* 这里会在 {@link BaseApplication#onCreate} 之前被调用,可以做一些较早的初始化
* 常用于 MultiDex 以及插件化框架的初始化
*
* @param base
*/
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
if (mAppDelegate == null) {
this.mAppDelegate = new AppDelegate(base);
}
this.mAppDelegate.attachBaseContext(base);
}
@Override
public void onCreate() {
super.onCreate();
if (mAppDelegate != null) {
this.mAppDelegate.onCreate(this);
}
}
/**
* 在模拟环境中程序终止时会被调用
*/
@Override
public void onTerminate() {
super.onTerminate();
if (mAppDelegate != null) {
this.mAppDelegate.onTerminate(this);
}
}
/**
* 将 {@link AppComponent} 返回出去, 供其它地方使用, {@link AppComponent} 接口中声明的方法所返回的实例, 在 {@link #getAppComponent()} 拿到对象后都可以直接使用
*
* @see ArmsUtils#obtainAppComponentFromContext(Context) 可直接获取 {@link AppComponent}
* @return AppComponent
*/
@NonNull
@Override
public AppComponent getAppComponent() {
Preconditions.checkNotNull(mAppDelegate, "%s cannot be null", AppDelegate.class.getName());
Preconditions.checkState(mAppDelegate instanceof App, "%s must be implements %s", AppDelegate.class.getName(), App.class.getName());
return ((App) mAppDelegate).getAppComponent();
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.base;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
import com.jess.arms.utils.ThirdViewUtil;
import com.zhy.autolayout.utils.AutoUtils;
/**
* ================================================
* 基类 {@link RecyclerView.ViewHolder}
* <p>
* Created by JessYan on 2015/11/24.
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public abstract class BaseHolder<T> extends RecyclerView.ViewHolder implements View.OnClickListener {
protected OnViewClickListener mOnViewClickListener = null;
protected DefaultAdapter.OnLongClickListener mOnLongClickListener = null;
protected final String TAG = this.getClass().getSimpleName();
public BaseHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);//点击事件
itemView.setOnLongClickListener(v -> {
if (mOnLongClickListener != null) {
mOnLongClickListener.onLongClick(v, getAdapterPosition());
}
return false;
});
if (ThirdViewUtil.USE_AUTOLAYOUT == 1) AutoUtils.autoSize(itemView);//适配z
ThirdViewUtil.bindTarget(this, itemView);//绑定
}
/**
* 设置数据
*
* @param data
* @param position
*/
public abstract void setData(T data, int position);
/**
* 释放资源
*/
protected void onRelease() {
}
@Override
public void onClick(View view) {
if (mOnViewClickListener != null) {
mOnViewClickListener.onViewClick(view, this.getAdapterPosition());
}
}
public interface OnViewClickListener {
void onViewClick(View view, int position);
}
public void setOnItemClickListener(OnViewClickListener listener) {
this.mOnViewClickListener = listener;
}
public void setmOnLongClickListener(DefaultAdapter.OnLongClickListener mOnLongClickListener) {
this.mOnLongClickListener = mOnLongClickListener;
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.base;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import org.simple.eventbus.EventBus;
import androidx.annotation.Nullable;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
/**
* ================================================
* 基类 {@link Service}
* <p>
* Created by jess on 2016/5/6.
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public abstract class BaseService extends Service {
protected final String TAG = this.getClass().getSimpleName();
protected CompositeDisposable mCompositeDisposable;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
EventBus.getDefault().register(this);
init();
}
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
unDispose();//解除订阅
this.mCompositeDisposable = null;
}
protected void addDispose(Disposable disposable) {
if (mCompositeDisposable == null) {
mCompositeDisposable = new CompositeDisposable();
}
mCompositeDisposable.add(disposable);//将所有subscription放入,集中处理
}
protected void unDispose() {
if (mCompositeDisposable != null) {
mCompositeDisposable.clear();//保证activity结束时取消所有正在执行的订阅
}
}
/**
* 初始化
*/
abstract public void init();
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.base;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
/**
* ================================================
* 基类 {@link RecyclerView.Adapter} ,如果需要实现非常复杂的 {@link RecyclerView} ,请尽量使用其他优秀的三方库
* <p>
* Created by jess on 2015/11/27.
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public abstract class DefaultAdapter<T> extends RecyclerView.Adapter<BaseHolder<T>> {
protected List<T> mInfos;
protected OnRecyclerViewItemClickListener mOnItemClickListener = null;
protected OnLongClickListener mOnLongClickListener = null;
private BaseHolder<T> mHolder;
public DefaultAdapter(List<T> infos) {
super();
this.mInfos = infos;
}
/**
* 创建 {@link BaseHolder}
*
* @param parent
* @param viewType
* @return
*/
@Override
public BaseHolder<T> onCreateViewHolder(ViewGroup parent, final int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(getLayoutId(viewType), parent, false);
mHolder = getHolder(view, viewType);
//设置Item点击事件
mHolder.setOnItemClickListener((view1, position) -> {
if (mOnItemClickListener != null && mInfos != null && mInfos.size() > 0) {
mOnItemClickListener.onItemClick(view1, viewType, mInfos.get(position), position);
}
});
mHolder.setmOnLongClickListener(mOnLongClickListener);
return mHolder;
}
/**
* 绑定数据
*
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(BaseHolder<T> holder, int position) {
holder.setData(mInfos.get(position), position);
}
/**
* 更新局部数据
* @param holder
* @param position
* @param payloads
*/
// @Override
// public void onBindViewHolder(@NonNull BaseHolder<T> holder, int position, @NonNull List<Object> payloads) {
//
// }
/**
* 返回数据的个数
*
* @return
*/
@Override
public int getItemCount() {
return mInfos.size();
}
public List<T> getInfos() {
return mInfos;
}
/**
* 获得某个 {@code position} 上的 item 的数据
*
* @param position
* @return
*/
public T getItem(int position) {
return mInfos == null ? null : mInfos.get(position);
}
/**
* 让子类实现用以提供 {@link BaseHolder}
*
* @param v
* @param viewType
* @return
*/
public abstract BaseHolder<T> getHolder(View v, int viewType);
/**
* 提供用于 {@code item} 布局的 {@code layoutId}
*
* @param viewType
* @return
*/
public abstract int getLayoutId(int viewType);
/**
* 遍历所有{@link BaseHolder},释放他们需要释放的资源
*
* @param recyclerView
*/
public static void releaseAllHolder(RecyclerView recyclerView) {
if (recyclerView == null) return;
for (int i = recyclerView.getChildCount() - 1; i >= 0; i--) {
final View view = recyclerView.getChildAt(i);
RecyclerView.ViewHolder viewHolder = recyclerView.getChildViewHolder(view);
if (viewHolder != null && viewHolder instanceof BaseHolder) {
((BaseHolder) viewHolder).onRelease();
}
}
}
public interface OnRecyclerViewItemClickListener<T> {
void onItemClick(View view, int viewType, T data, int position);
}
public interface OnLongClickListener {
boolean onLongClick(View v, int position);
}
public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
this.mOnItemClickListener = listener;
}
public void setmOnLongClickListener(OnLongClickListener mOnLongClickListener) {
this.mOnLongClickListener = mOnLongClickListener;
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.base.delegate;
import android.app.Activity;
import android.os.Bundle;
import androidx.annotation.Nullable;
/**
* ================================================
* {@link Activity} 代理类,用于框架内部在每个 {@link Activity} 的对应生命周期中插入需要的逻辑
*
* @see ActivityDelegateImpl
* @see <a href="https://github.com/JessYanCoding/MVPArms/wiki#3.13">ActivityDelegate wiki 官方文档</a>
* Created by JessYan on 26/04/2017 20:23
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public interface ActivityDelegate {
String LAYOUT_LINEARLAYOUT = "LinearLayout";
String LAYOUT_FRAMELAYOUT = "FrameLayout";
String LAYOUT_RELATIVELAYOUT = "RelativeLayout";
String ACTIVITY_DELEGATE = "activity_delegate";
void onCreate(@Nullable Bundle savedInstanceState);
void onStart();
void onResume();
void onPause();
void onStop();
void onSaveInstanceState(Bundle outState);
void onDestroy();
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.base.delegate;
import android.app.Activity;
import android.os.Bundle;
import com.jess.arms.utils.ArmsUtils;
import org.simple.eventbus.EventBus;
import androidx.annotation.Nullable;
/**
* ================================================
* {@link ActivityDelegate} 默认实现类
* <p>
* Created by JessYan on 26/04/2017 20:23
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public class ActivityDelegateImpl implements ActivityDelegate {
private Activity mActivity;
private IActivity iActivity;
public ActivityDelegateImpl(Activity activity) {
this.mActivity = activity;
this.iActivity = (IActivity) activity;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
//如果要使用 EventBus 请将此方法返回 true
if (iActivity.useEventBus()){
//注册到事件主线
EventBus.getDefault().register(mActivity);
}
//这里提供 AppComponent 对象给 BaseActivity 的子类, 用于 Dagger2 的依赖注入
iActivity.setupActivityComponent(ArmsUtils.obtainAppComponentFromContext(mActivity));
}
@Override
public void onStart() {
}
@Override
public void onResume() {
}
@Override
public void onPause() {
}
@Override
public void onStop() {
}
@Override
public void onSaveInstanceState(Bundle outState) {
}
@Override
public void onDestroy() {
//如果要使用 EventBus 请将此方法返回 true
if (iActivity != null && iActivity.useEventBus())
EventBus.getDefault().unregister(mActivity);
this.iActivity = null;
this.mActivity = null;
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.base.delegate;
import android.app.Application;
import android.content.Context;
/**
* ================================================
* 用于代理 {@link Application} 的生命周期
*
* @see AppDelegate
* Created by JessYan on 18/07/2017 17:43
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public interface AppLifecycles {
void attachBaseContext(Context base);
void onCreate(Application application);
void onTerminate(Application application);
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.base.delegate;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
/**
* ================================================
* {@link Fragment} 代理类,用于框架内部在每个 {@link Fragment} 的对应生命周期中插入需要的逻辑
*
* @see FragmentDelegateImpl
* @see <a href="https://github.com/JessYanCoding/MVPArms/wiki#3.13">FragmentDelegate wiki 官方文档</a>
* Created by JessYan on 29/04/2017 14:30
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public interface FragmentDelegate {
String FRAGMENT_DELEGATE = "fragment_delegate";
void onAttach(Context context);
void onCreate(@Nullable Bundle savedInstanceState);
void onCreateView(@Nullable View view, @Nullable Bundle savedInstanceState);
void onActivityCreate(@Nullable Bundle savedInstanceState);
void onStart();
void onResume();
void onPause();
void onStop();
void onSaveInstanceState(@NonNull Bundle outState);
void onDestroyView();
void onDestroy();
void onDetach();
/**
* Return true if the fragment is currently added to its activity.
*/
boolean isAdded();
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.base.delegate;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import com.jess.arms.utils.ArmsUtils;
import org.simple.eventbus.EventBus;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import timber.log.Timber;
/**
* ================================================
* {@link FragmentDelegate} 默认实现类
* <p>
* Created by JessYan on 29/04/2017 16:12
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public class FragmentDelegateImpl implements FragmentDelegate {
private FragmentManager mFragmentManager;
private Fragment mFragment;
private IFragment iFragment;
private Unbinder mUnbinder;
public FragmentDelegateImpl(FragmentManager fragmentManager, Fragment fragment) {
this.mFragmentManager = fragmentManager;
this.mFragment = fragment;
this.iFragment = (IFragment) fragment;
}
@Override
public void onAttach(Context context) {
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
if (iFragment.useEventBus())//如果要使用eventbus请将此方法返回true
EventBus.getDefault().register(mFragment);//注册到事件主线
iFragment.setupFragmentComponent(ArmsUtils.obtainAppComponentFromContext(mFragment.getActivity()));
}
@Override
public void onCreateView(@Nullable View view, @Nullable Bundle savedInstanceState) {
//绑定到butterknife
if (view != null)
mUnbinder = ButterKnife.bind(mFragment, view);
}
@Override
public void onActivityCreate(@Nullable Bundle savedInstanceState) {
iFragment.initData(savedInstanceState);
}
@Override
public void onStart() {
}
@Override
public void onResume() {
}
@Override
public void onPause() {
}
@Override
public void onStop() {
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
}
@Override
public void onDestroyView() {
if (mUnbinder != null && mUnbinder != Unbinder.EMPTY) {
try {
mUnbinder.unbind();
} catch (IllegalStateException e) {
e.printStackTrace();
//fix Bindings already cleared
Timber.w("onDestroyView: " + e.getMessage());
}
}
}
@Override
public void onDestroy() {
if (iFragment != null && iFragment.useEventBus())//如果要使用eventbus请将此方法返回true
EventBus.getDefault().unregister(mFragment);//注册到事件主线
this.mUnbinder = null;
this.mFragmentManager = null;
this.mFragment = null;
this.iFragment = null;
}
@Override
public void onDetach() {
}
/**
* Return true if the fragment is currently added to its activity.
*/
@Override
public boolean isAdded() {
return mFragment != null && mFragment.isAdded();
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.base.delegate;
import android.app.Activity;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentManager;
import com.jess.arms.base.BaseActivity;
import com.jess.arms.base.BaseFragment;
import com.jess.arms.di.component.AppComponent;
import com.jess.arms.integration.ActivityLifecycle;
import com.jess.arms.integration.cache.Cache;
import com.jess.arms.integration.cache.LruCache;
import org.simple.eventbus.EventBus;
/**
* ================================================
* 框架要求框架中的每个 {@link Activity} 都需要实现此类,以满足规范
*
* @see BaseActivity
* Created by JessYan on 26/04/2017 21:42
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public interface IActivity {
/**
* 提供在 {@link Activity} 生命周期内的缓存容器, 可向此 {@link Activity} 存取一些必要的数据
* 此缓存容器和 {@link Activity} 的生命周期绑定, 如果 {@link Activity} 在屏幕旋转或者配置更改的情况下
* 重新创建, 那此缓存容器中的数据也会被清空, 如果你想避免此种情况请使用 <a href="https://github.com/JessYanCoding/LifecycleModel">LifecycleModel</a>
*
* @return like {@link LruCache}
*/
@NonNull
Cache<String, Object> provideCache();
/**
* 提供 AppComponent (提供所有的单例对象) 给实现类, 进行 Component 依赖
*
* @param appComponent
*/
void setupActivityComponent(@NonNull AppComponent appComponent);
/**
* 是否使用 {@link EventBus}
*
* @return
*/
boolean useEventBus();
/**
* 初始化 View, 如果 {@link #initView(Bundle)} 返回 0, 框架则不会调用 {@link Activity#setContentView(int)}
*
* @param savedInstanceState
* @return
*/
int initView(@Nullable Bundle savedInstanceState);
/**
* 初始化数据
*
* @param savedInstanceState
*/
void initData(@Nullable Bundle savedInstanceState);
/**
* 这个 Activity 是否会使用 Fragment,框架会根据这个属性判断是否注册 {@link FragmentManager.FragmentLifecycleCallbacks}
* 如果返回{@code false},那意味着这个 Activity 不需要绑定 Fragment,那你再在这个 Activity 中绑定继承于 {@link BaseFragment} 的 Fragment 将不起任何作用
*
* @return
* @see ActivityLifecycle# (Fragment 的注册过程)
*/
boolean useFragment();
void initIntent();
//初始化标题栏
void initTopBar();
void initLanguage();
void initLayoutParams();
void initLayoutVisible();
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.base.delegate;
import android.app.Activity;
import android.os.Bundle;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.jess.arms.base.BaseFragment;
import com.jess.arms.di.component.AppComponent;
import com.jess.arms.integration.cache.Cache;
import com.jess.arms.integration.cache.LruCache;
import org.simple.eventbus.EventBus;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
/**
* ================================================
* 框架要求框架中的每个 {@link Fragment} 都需要实现此类,以满足规范
*
* @see BaseFragment
* Created by JessYan on 29/04/2017 14:31
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public interface IFragment {
/**
* 提供在 {@link Fragment} 生命周期内的缓存容器, 可向此 {@link Fragment} 存取一些必要的数据
* 此缓存容器和 {@link Fragment} 的生命周期绑定, 如果 {@link Fragment} 在屏幕旋转或者配置更改的情况下
* 重新创建, 那此缓存容器中的数据也会被清空, 如果你想避免此种情况请使用 <a href="https://github.com/JessYanCoding/LifecycleModel">LifecycleModel</a>
*
* @return like {@link LruCache}
*/
@NonNull
Cache<String, Object> provideCache();
/**
* 提供 AppComponent (提供所有的单例对象) 给实现类, 进行 Component 依赖
*
* @param appComponent
*/
void setupFragmentComponent(@NonNull AppComponent appComponent);
/**
* 是否使用 {@link EventBus}
*
* @return
*/
boolean useEventBus();
/**
* 初始化 View
*
* @param inflater
* @param container
* @param savedInstanceState
* @return
*/
View initView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState);
/**
* 初始化数据
*
* @param savedInstanceState
*/
void initData(@Nullable Bundle savedInstanceState);
/**
* 通过此方法可以使 Fragment 能够与外界做一些交互和通信, 比如说外部的 Activity 想让自己持有的某个 Fragment 对象执行一些方法,
* 建议在有多个需要与外界交互的方法时, 统一传 {@link Message}, 通过 what 字段来区分不同的方法, 在 {@link #setData(Object)}
* 方法中就可以 {@code switch} 做不同的操作, 这样就可以用统一的入口方法做多个不同的操作, 可以起到分发的作用
* <p>
* 调用此方法时请注意调用时 Fragment 的生命周期, 如果调用 {@link #setData(Object)} 方法时 {@link Fragment#onCreate(Bundle)} 还没执行
* 但在 {@link #setData(Object)} 里却调用了 Presenter 的方法, 是会报空的, 因为 Dagger 注入是在 {@link Fragment#onCreate(Bundle)} 方法中执行的
* 然后才创建的 Presenter, 如果要做一些初始化操作,可以不必让外部调用 {@link #setData(Object)}, 在 {@link #initData(Bundle)} 中初始化就可以了
* <p>
* Example usage:
* <pre>
* public void setData(@Nullable Object data) {
* if (data != null && data instanceof Message) {
* switch (((Message) data).what) {
* case 0:
* loadData(((Message) data).arg1);
* break;
* case 1:
* refreshUI();
* break;
* default:
* //do something
* break;
* }
* }
* }
*
* // call setData(Object):
* Message data = new Message();
* data.what = 0;
* data.arg1 = 1;
* fragment.setData(data);
* </pre>
*
* {@link #setData(Object)} 框架是不会调用的, 是拿给开发者自己去调用的, 让 {@link Activity} 或者其他类可以和 {@link Fragment} 通信,
* 并且因为 {@link #setData(Object)} 是 {@link IFragment} 的方法, 所以你可以通过多态, 持有父类,
* 不持有具体子类的方式就可以和子类 {@link Fragment} 通信, 这样如果需要替换子类, 就不会影响到其他地方,
* 并且 {@link #setData(Object)} 可以通过传入 {@link Message} 作为参数, 使外部统一调用 {@link #setData(Object)},
* 方法内部再通过 {@code switch(message.what)} 的方式, 从而在外部调用方式不变的情况下, 却可以扩展更多的方法,
* 让方法扩展更多的参数, 这样不管 {@link Fragment} 子类怎么变, 它内部的方法以及方法的参数怎么变, 却不会影响到外部调用的任何一行代码
*
* @param data 当不需要参数时 {@code data} 可以为 {@code null}
*/
void setData(@Nullable Object data);
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.di.component;
import android.app.Application;
import android.content.Context;
import com.google.gson.Gson;
import com.jess.arms.base.delegate.AppDelegate;
import com.jess.arms.di.module.AppModule;
import com.jess.arms.di.module.ClientModule;
import com.jess.arms.di.module.GlobalConfigModule;
import com.jess.arms.http.imageloader.ImageLoader;
import com.jess.arms.integration.AppManager;
import com.jess.arms.integration.IRepositoryManager;
import com.jess.arms.integration.cache.Cache;
import com.jess.arms.utils.ArmsUtils;
import java.io.File;
import javax.inject.Singleton;
import dagger.BindsInstance;
import dagger.Component;
import me.jessyan.rxerrorhandler.core.RxErrorHandler;
import okhttp3.OkHttpClient;
/**
* ================================================
* 可通过 {@link ArmsUtils#obtainAppComponentFromContext(Context)} 拿到此接口的实现类
* 拥有此接口的实现类即可调用对应的方法拿到 Dagger 提供的对应实例
*
* @see <a href="https://github.com/JessYanCoding/MVPArms/wiki#2.2">AppComponent wiki 官方文档</a>
* Created by JessYan on 8/4/2016
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
@Singleton
@Component(modules = {AppModule.class, ClientModule.class, GlobalConfigModule.class})
public interface AppComponent {
Application application();
//用于管理所有 activity
AppManager appManager();
//用于管理网络请求层,以及数据缓存层
IRepositoryManager repositoryManager();
//RxJava 错误处理管理类
RxErrorHandler rxErrorHandler();
//图片管理器,用于加载图片的管理类,默认使用 Glide ,使用策略模式,可在运行时替换框架
ImageLoader imageLoader();
OkHttpClient okHttpClient();
//gson
Gson gson();
//缓存文件根目录(RxCache 和 Glide 的缓存都已经作为子文件夹放在这个根目录下),应该将所有缓存都放到这个根目录下,便于管理和清理,可在 GlobalConfigModule 里配置
File cacheFile();
//用来存取一些整个App公用的数据,切勿大量存放大容量数据
Cache<String, Object> extras();
//用于创建框架所需缓存对象的工厂
Cache.Factory cacheFactory();
void inject(AppDelegate delegate);
@Component.Builder
interface Builder {
@BindsInstance
Builder application(Application application);
Builder globalConfigModule(GlobalConfigModule globalConfigModule);
AppComponent build();
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.di.module;
import android.app.Application;
import android.content.Context;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.jess.arms.integration.ActivityLifecycle;
import com.jess.arms.integration.FragmentLifecycle;
import com.jess.arms.integration.IRepositoryManager;
import com.jess.arms.integration.RepositoryManager;
import com.jess.arms.integration.cache.Cache;
import com.jess.arms.integration.cache.CacheType;
import com.jess.arms.integration.lifecycle.ActivityLifecycleForRxLifecycle;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Named;
import javax.inject.Singleton;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentManager;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
/**
* ================================================
* 提供一些框架必须的实例的 {@link Module}
* <p>
* Created by JessYan on 8/4/2016.
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
@Module
public abstract class AppModule {
@Singleton
@Provides
static Gson provideGson(Application application, @Nullable GsonConfiguration configuration) {
GsonBuilder builder = new GsonBuilder();
if (configuration != null)
configuration.configGson(application, builder);
return builder.create();
}
@Binds
abstract IRepositoryManager bindRepositoryManager(RepositoryManager repositoryManager);
@Singleton
@Provides
static Cache<String, Object> provideExtras(Cache.Factory cacheFactory) {
return cacheFactory.build(CacheType.EXTRAS);
}
@Binds
@Named("ActivityLifecycle")
abstract Application.ActivityLifecycleCallbacks bindActivityLifecycle(ActivityLifecycle activityLifecycle);
@Binds
@Named("ActivityLifecycleForRxLifecycle")
abstract Application.ActivityLifecycleCallbacks bindActivityLifecycleForRxLifecycle(ActivityLifecycleForRxLifecycle activityLifecycleForRxLifecycle);
@Binds
abstract FragmentManager.FragmentLifecycleCallbacks bindFragmentLifecycle(FragmentLifecycle fragmentLifecycle);
@Singleton
@Provides
static List<FragmentManager.FragmentLifecycleCallbacks> provideFragmentLifecycles(){
return new ArrayList<>();
}
public interface GsonConfiguration {
void configGson(Context context, GsonBuilder builder);
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.di.module;
import android.app.Application;
import android.content.Context;
import com.google.gson.Gson;
import com.jess.arms.http.GlobalHttpHandler;
import com.jess.arms.http.log.RequestInterceptor;
import com.jess.arms.utils.DataHelper;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Named;
import javax.inject.Singleton;
import androidx.annotation.Nullable;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import io.rx_cache2.internal.RxCache;
import io.victoralbertos.jolyglot.GsonSpeaker;
import me.jessyan.rxerrorhandler.core.RxErrorHandler;
import me.jessyan.rxerrorhandler.handler.listener.ResponseErrorListener;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Response;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* ================================================
* 提供一些三方库客户端实例的 {@link Module}
* <p>
* Created by JessYan on 2016/3/14.
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
@Module
public abstract class ClientModule {
public static int TIME_OUT = 20;
/**
* 提供 {@link Retrofit}
*
* @param application
* @param configuration
* @param builder
* @param client
* @param httpUrl
* @param gson
* @return {@link Retrofit}
*/
@Singleton
@Provides
static Retrofit provideRetrofit(Application application, @Nullable RetrofitConfiguration configuration, Retrofit.Builder builder, OkHttpClient client
, HttpUrl httpUrl, Gson gson) {
builder
.baseUrl(httpUrl)//域名
.client(client);//设置okhttp
if (configuration != null)
configuration.configRetrofit(application, builder);
builder
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//使用 Rxjava
.addConverterFactory(GsonConverterFactory.create(gson));//使用 Gson
return builder.build();
}
/**
* 提供 {@link OkHttpClient}
*
* @param application
* @param configuration
* @param builder
* @param intercept
* @param interceptors
* @param handler
* @return {@link OkHttpClient}
*/
@Singleton
@Provides
static OkHttpClient provideClient(Application application, @Nullable OkhttpConfiguration configuration, OkHttpClient.Builder builder, Interceptor intercept
, @Nullable List<Interceptor> interceptors, @Nullable GlobalHttpHandler handler) {
builder
.connectTimeout(TIME_OUT, TimeUnit.SECONDS)
.readTimeout(TIME_OUT, TimeUnit.SECONDS)
.writeTimeout(TIME_OUT, TimeUnit.SECONDS)
.addNetworkInterceptor(intercept);
if (handler != null)
builder.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
return chain.proceed(handler.onHttpRequestBefore(chain, chain.request()));
}
});
if (interceptors != null) {//如果外部提供了interceptor的集合则遍历添加
for (Interceptor interceptor : interceptors) {
builder.addInterceptor(interceptor);
}
}
if (configuration != null)
configuration.configOkhttp(application, builder);
return builder.build();
}
@Singleton
@Provides
static Retrofit.Builder provideRetrofitBuilder() {
return new Retrofit.Builder();
}
@Singleton
@Provides
static OkHttpClient.Builder provideClientBuilder() {
return new OkHttpClient.Builder();
}
@Binds
abstract Interceptor bindInterceptor(RequestInterceptor interceptor);
/**
* 提供 {@link RxCache}
*
* @param application
* @param configuration
* @param cacheDirectory cacheDirectory RxCache缓存路径
* @return {@link RxCache}
*/
@Singleton
@Provides
static RxCache provideRxCache(Application application, @Nullable RxCacheConfiguration configuration, @Named("RxCacheDirectory") File cacheDirectory) {
RxCache.Builder builder = new RxCache.Builder();
RxCache rxCache = null;
if (configuration != null) {
rxCache = configuration.configRxCache(application, builder);
}
if (rxCache != null) return rxCache;
return builder
.persistence(cacheDirectory, new GsonSpeaker());
}
/**
* 需要单独给 {@link RxCache} 提供缓存路径
*
* @param cacheDir
* @return {@link File}
*/
@Singleton
@Provides
@Named("RxCacheDirectory")
static File provideRxCacheDirectory(File cacheDir) {
File cacheDirectory = new File(cacheDir, "RxCache");
return DataHelper.makeDirs(cacheDirectory);
}
/**
* 提供处理 RxJava 错误的管理器
*
* @param application
* @param listener
* @return {@link RxErrorHandler}
*/
@Singleton
@Provides
static RxErrorHandler proRxErrorHandler(Application application, ResponseErrorListener listener) {
return RxErrorHandler
.builder()
.with(application)
.responseErrorListener(listener)
.build();
}
public interface RetrofitConfiguration {
void configRetrofit(Context context, Retrofit.Builder builder);
}
public interface OkhttpConfiguration {
void configOkhttp(Context context, OkHttpClient.Builder builder);
}
public interface RxCacheConfiguration {
/**
* 若想自定义 RxCache 的缓存文件夹或者解析方式, 如改成 fastjson
* 请 {@code return rxCacheBuilder.persistence(cacheDirectory, new FastJsonSpeaker());}, 否则请 {@code return null;}
*
* @param context
* @param builder
* @return {@link RxCache}
*/
RxCache configRxCache(Context context, RxCache.Builder builder);
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.di.scope;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import javax.inject.Scope;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* A scoping annotation to permit objects whose lifetime should
* conform to the life of the activity to be memorized in the
* correct component.
*/
@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScope {}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.di.scope;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import javax.inject.Scope;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* A scoping annotation to permit objects whose lifetime should
* conform to the life of the fragment to be memorized in the
* correct component.
*/
@Scope
@Documented
@Retention(RUNTIME)
public @interface FragmentScope {}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.http;
import okhttp3.HttpUrl;
/**
* ================================================
* 针对于 BaseUrl 在 App 启动时不能确定,需要请求服务器接口动态获取的应用场景
* <p>
* Created by JessYan on 11/07/2017 14:58
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public interface BaseUrl {
/**
* 在调用 Retrofit API 接口之前,使用 Okhttp 或其他方式,请求到正确的 BaseUrl 并通过此方法返回
*
* @return
*/
HttpUrl url();
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.http;
import com.jess.arms.di.module.GlobalConfigModule;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
/**
* ================================================
* 处理 Http 请求和响应结果的处理类
* 使用 {@link GlobalConfigModule.Builder#globalHttpHandler(GlobalHttpHandler)} 方法配置
*
* @see <a href="https://github.com/JessYanCoding/MVPArms/wiki#3.2">GlobalHttpHandler Wiki 官方文档</a>
* Created by JessYan on 8/30/16 17:47
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public interface GlobalHttpHandler {
Response onHttpResultResponse(String httpResult, Interceptor.Chain chain, Response response);
Request onHttpRequestBefore(Interceptor.Chain chain, Request request);
//空实现
GlobalHttpHandler EMPTY = new GlobalHttpHandler() {
@Override
public Response onHttpResultResponse(String httpResult, Interceptor.Chain chain, Response response) {
//不管是否处理,都必须将response返回出去
return response;
}
@Override
public Request onHttpRequestBefore(Interceptor.Chain chain, Request request) {
//不管是否处理,都必须将request返回出去
return request;
}
};
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.http;
import android.os.Build;
import android.util.Log;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.HttpException;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.util.ContentLengthInputStream;
import com.bumptech.glide.util.Synthetic;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import androidx.annotation.NonNull;
import okhttp3.Call;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
/**
* Fetches an {@link InputStream} using the okhttp library.
*/
public class OkHttpStreamFetcher implements DataFetcher<InputStream>,
okhttp3.Callback {
private static final String TAG = "OkHttpFetcher";
private final Call.Factory client;
private final GlideUrl url;
@Synthetic InputStream stream;
@Synthetic ResponseBody responseBody;
private volatile Call call;
private DataCallback<? super InputStream> callback;
public OkHttpStreamFetcher(Call.Factory client, GlideUrl url) {
this.client = client;
this.url = url;
}
@Override
public void loadData(Priority priority, final DataCallback<? super InputStream> callback) {
Request.Builder requestBuilder = new Request.Builder().url(url.toStringUrl());
for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
String key = headerEntry.getKey();
requestBuilder.addHeader(key, headerEntry.getValue());
}
Request request = requestBuilder.build();
this.callback = callback;
call = client.newCall(request);
// if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O) {
if (Build.VERSION.SDK_INT != 26) {
call.enqueue(this);
} else {
try {
// Calling execute instead of enqueue is a workaround for #2355, where okhttp throws a
// ClassCastException on O.
onResponse(call, call.execute());
} catch (IOException e) {
onFailure(call, e);
} catch (ClassCastException e) {
// It's not clear that this catch is necessary, the error may only occur even on O if
// enqueue is used.
onFailure(call, new IOException("Workaround for framework bug on O", e));
}
}
}
@Override
public void onFailure(Call call, IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "OkHttp failed to obtain result", e);
}
callback.onLoadFailed(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
responseBody = response.body();
if (response.isSuccessful()) {
long contentLength = responseBody.contentLength();
stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
callback.onDataReady(stream);
} else {
callback.onLoadFailed(new HttpException(response.message(), response.code()));
}
}
@Override
public void cleanup() {
try {
if (stream != null) {
stream.close();
}
} catch (IOException e) {
// Ignored
}
if (responseBody != null) {
responseBody.close();
}
callback = null;
}
@Override
public void cancel() {
Call local = call;
if (local != null) {
local.cancel();
}
}
@NonNull
@Override
public Class<InputStream> getDataClass() {
return InputStream.class;
}
@NonNull
@Override
public DataSource getDataSource() {
return DataSource.REMOTE;
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.http;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
import java.io.InputStream;
import okhttp3.Call;
import okhttp3.OkHttpClient;
/**
* A simple model loader for fetching media over http/https using OkHttp.
*/
public class OkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
private final Call.Factory client;
public OkHttpUrlLoader(Call.Factory client) {
this.client = client;
}
@Override
public boolean handles(GlideUrl url) {
return true;
}
@Override
public LoadData<InputStream> buildLoadData(GlideUrl model, int width, int height,
Options options) {
return new LoadData<>(model, new OkHttpStreamFetcher(client, model));
}
/**
* The default factory for {@link OkHttpUrlLoader}s.
*/
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
private static volatile Call.Factory internalClient;
private Call.Factory client;
private static Call.Factory getInternalClient() {
if (internalClient == null) {
synchronized (Factory.class) {
if (internalClient == null) {
internalClient = new OkHttpClient();
}
}
}
return internalClient;
}
/**
* Constructor for a new Factory that runs requests using a static singleton client.
*/
public Factory() {
this(getInternalClient());
}
/**
* Constructor for a new Factory that runs requests using given client.
*
* @param client this is typically an instance of {@code OkHttpClient}.
*/
public Factory(Call.Factory client) {
this.client = client;
}
@Override
public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new OkHttpUrlLoader(client);
}
@Override
public void teardown() {
// Do nothing, this instance doesn't own the client.
}
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.http.imageloader;
import android.content.Context;
/**
* ================================================
* 图片加载策略,实现 {@link BaseImageLoaderStrategy}
* 并通过 {@link ImageLoader#setLoadImgStrategy(BaseImageLoaderStrategy)} 配置后,才可进行图片请求
* <p>
* Created by JessYan on 8/5/2016 15:50
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public interface BaseImageLoaderStrategy<T extends ImageConfig> {
/**
* 加载图片
*
* @param ctx
* @param config
*/
void loadImage(Context ctx, T config);
/**
* 停止加载
*
* @param ctx
* @param config
*/
void clear(Context ctx, T config);
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.http.imageloader;
import android.widget.ImageView;
/**
* ================================================
* 这里是图片加载配置信息的基类,定义一些所有图片加载框架都可以用的通用参数
* 每个 {@link BaseImageLoaderStrategy} 应该对应一个 {@link ImageConfig} 实现类
* <p>
* Created by JessYan on 8/5/16 15:19
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public class ImageConfig {
protected String url;
protected ImageView imageView;
protected int placeholder;//占位符
protected int errorPic;//错误占位符
public String getUrl() {
return url;
}
public ImageView getImageView() {
return imageView;
}
public int getPlaceholder() {
return placeholder;
}
public int getErrorPic() {
return errorPic;
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.http.imageloader;
import android.content.Context;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* ================================================
* {@link ImageLoader} 使用策略模式和建造者模式,可以动态切换图片请求框架(比如说切换成 Picasso )
* 当需要切换图片请求框架或图片请求框架升级后变更了 Api 时
* 这里可以将影响范围降到最低,所以封装 {@link ImageLoader} 是为了屏蔽这个风险
*
* @see <a href="https://github.com/JessYanCoding/MVPArms/wiki#3.4">ImageLoader wiki 文档</a>
* Created by JessYan on 8/5/16 15:57
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
@Singleton
public final class ImageLoader {
@Inject
BaseImageLoaderStrategy mStrategy;
@Inject
public ImageLoader() {
}
/**
* 加载图片
*
* @param context
* @param config
* @param <T>
*/
public <T extends ImageConfig> void loadImage(Context context, T config) {
this.mStrategy.loadImage(context, config);
}
/**
* 停止加载或清理缓存
*
* @param context
* @param config
* @param <T>
*/
public <T extends ImageConfig> void clear(Context context, T config) {
this.mStrategy.clear(context, config);
}
/**
* 可在运行时随意切换 {@link BaseImageLoaderStrategy}
*
* @param strategy
*/
public void setLoadImgStrategy(BaseImageLoaderStrategy strategy) {
this.mStrategy = strategy;
}
public BaseImageLoaderStrategy getLoadImgStrategy() {
return mStrategy;
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.http.imageloader.glide;
import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.jess.arms.http.imageloader.BaseImageLoaderStrategy;
/**
* ================================================
* 如果你想具有配置 @{@link Glide} 的权利,则需要让 {@link BaseImageLoaderStrategy}
* 的实现类也必须实现 {@link GlideAppliesOptions}
* <p>
* Created by JessYan on 13/08/2017 22:02
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public interface GlideAppliesOptions {
/**
* 配置 @{@link Glide} 的自定义参数,此方法在 @{@link Glide} 初始化时执行(@{@link Glide} 在第一次被调用时初始化),只会执行一次
*
* @param context
* @param builder {@link GlideBuilder} 此类被用来创建 Glide
*/
void applyGlideOptions(Context context, GlideBuilder builder);
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.http.imageloader.glide;
import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.Registry;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool;
import com.bumptech.glide.load.engine.cache.DiskCache;
import com.bumptech.glide.load.engine.cache.DiskLruCacheWrapper;
import com.bumptech.glide.load.engine.cache.LruResourceCache;
import com.bumptech.glide.load.engine.cache.MemorySizeCalculator;
import com.bumptech.glide.module.AppGlideModule;
import com.jess.arms.di.component.AppComponent;
import com.jess.arms.http.imageloader.BaseImageLoaderStrategy;
import com.jess.arms.utils.ArmsUtils;
import com.jess.arms.utils.DataHelper;
import java.io.File;
/**
* ================================================
* {@link AppGlideModule} 的默认实现类
* 用于配置缓存文件夹,切换图片请求框架等操作
* <p>
* Created by JessYan on 16/4/15.
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
@GlideModule(glideName = "GlideArms")
public class GlideConfiguration extends AppGlideModule {
public static final int IMAGE_DISK_CACHE_MAX_SIZE = 100 * 1024 * 1024;//图片缓存文件最大值为100Mb
@Override
public void applyOptions(Context context, GlideBuilder builder) {
AppComponent appComponent = ArmsUtils.obtainAppComponentFromContext(context);
builder.setDiskCache(new DiskCache.Factory() {
@Override
public DiskCache build() {
// Careful: the external cache directory doesn't enforce permissions
return DiskLruCacheWrapper.create(DataHelper.makeDirs(new File(appComponent.cacheFile(), "Glide")), IMAGE_DISK_CACHE_MAX_SIZE);
}
});
MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context).build();
int defaultMemoryCacheSize = calculator.getMemoryCacheSize();
int defaultBitmapPoolSize = calculator.getBitmapPoolSize();
int customMemoryCacheSize = (int) (1.2 * defaultMemoryCacheSize);
int customBitmapPoolSize = (int) (1.2 * defaultBitmapPoolSize);
builder.setMemoryCache(new LruResourceCache(customMemoryCacheSize));
builder.setBitmapPool(new LruBitmapPool(customBitmapPoolSize));
//将配置 Glide 的机会转交给 GlideImageLoaderStrategy,如你觉得框架提供的 GlideImageLoaderStrategy
//并不能满足自己的需求,想自定义 BaseImageLoaderStrategy,那请你最好实现 GlideAppliesOptions
//因为只有成为 GlideAppliesOptions 的实现类,这里才能调用 applyGlideOptions(),让你具有配置 Glide 的权利
BaseImageLoaderStrategy loadImgStrategy = appComponent.imageLoader().getLoadImgStrategy();
if (loadImgStrategy instanceof GlideAppliesOptions) {
((GlideAppliesOptions) loadImgStrategy).applyGlideOptions(context, builder);
}
}
//TODO 这里暂时不用Okhttp,有些地方加载图片会将http转为https导致图片加载失败
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
//Glide 默认使用 HttpURLConnection 做网络请求,在这切换成 Okhttp 请求
// AppComponent appComponent = ArmsUtils.obtainAppComponentFromContext(context);
// registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(appComponent.okHttpClient()));
}
@Override
public boolean isManifestParsingEnabled() {
return false;
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.http.imageloader.glide;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import com.jess.arms.di.module.GlobalConfigModule;
import com.jess.arms.http.imageloader.BaseImageLoaderStrategy;
import com.jess.arms.http.imageloader.ImageConfig;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
/**
* ================================================
* 此类只是简单的实现了 Glide 加载的策略,方便快速使用,但大部分情况会需要应对复杂的场景
* 这时可自行实现 {@link BaseImageLoaderStrategy} 和 {@link ImageConfig} 替换现有策略
*
* @see GlobalConfigModule.Builder#imageLoaderStrategy(BaseImageLoaderStrategy)
* Created by JessYan on 8/5/16 16:28
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public class GlideImageLoaderStrategy implements BaseImageLoaderStrategy<ImageConfigImpl>, GlideAppliesOptions {
@Override
public void loadImage(Context ctx, ImageConfigImpl config) {
if (ctx == null) throw new NullPointerException("Context is required");
if (config == null) throw new NullPointerException("ImageConfigImpl is required");
// if (TextUtils.isEmpty(network.config.getUrl())) throw new NullPointerException("Url is required");
if (config.getUrl() == null) throw new NullPointerException("Url is required");
if (config.getImageView() == null) throw new NullPointerException("Imageview is required");
GlideRequests requests;
requests = GlideArms.with(ctx);//如果context是activity则自动使用Activity的生命周期
GlideRequest<Drawable> glideRequest = requests.load(config.getUrl())
.transition(DrawableTransitionOptions.withCrossFade())
.centerCrop();
switch (config.getCacheStrategy()) {//缓存策略
case 0:
glideRequest.diskCacheStrategy(DiskCacheStrategy.ALL);
break;
case 1:
glideRequest.diskCacheStrategy(DiskCacheStrategy.NONE);
break;
case 2:
glideRequest.diskCacheStrategy(DiskCacheStrategy.RESOURCE);
break;
case 3:
glideRequest.diskCacheStrategy(DiskCacheStrategy.DATA);
break;
case 4:
glideRequest.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC);
break;
default:
glideRequest.diskCacheStrategy(DiskCacheStrategy.ALL);
break;
}
if (config.getTransformation() != null) {//glide用它来改变图形的形状
glideRequest.transform(config.getTransformation());
}
if (config.getPlaceholder() != 0)//设置占位符
glideRequest.placeholder(config.getPlaceholder());
if (config.getErrorPic() != 0)//设置错误的图片
glideRequest.error(config.getErrorPic());
if (config.getFallback() != 0)//设置请求 url 为空图片
glideRequest.fallback(config.getFallback());
glideRequest
.into(config.getImageView());
}
@Override
public void clear(final Context ctx, ImageConfigImpl config) {
if (ctx == null) throw new NullPointerException("Context is required");
if (config == null) throw new NullPointerException("ImageConfigImpl is required");
if (config.getImageViews() != null && config.getImageViews().length > 0) {//取消在执行的任务并且释放资源
for (ImageView imageView : config.getImageViews()) {
GlideArms.get(ctx).getRequestManagerRetriever().get(ctx).clear(imageView);
}
}
if (config.isClearDiskCache()) {//清除本地缓存
Observable.just(0)
.observeOn(Schedulers.io())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
Glide.get(ctx).clearDiskCache();
}
});
}
if (config.isClearMemory()) {//清除内存缓存
Observable.just(0)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
Glide.get(ctx).clearMemory();
}
});
}
}
@Override
public void applyGlideOptions(Context context, GlideBuilder builder) {
Timber.w("applyGlideOptions");
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.http.imageloader.glide;
import android.widget.ImageView;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import com.jess.arms.http.imageloader.ImageConfig;
/**
* ================================================
* 这里存放图片请求的配置信息,可以一直扩展字段,如果外部调用时想让图片加载框架
* 做一些操作,比如清除缓存或者切换缓存策略,则可以定义一个 int 类型的变量,内部根据 switch(int) 做不同的操作
* 其他操作同理
* <p>
* Created by JessYan on 8/5/16 15:19
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public class ImageConfigImpl extends ImageConfig {
private int cacheStrategy;//0对应DiskCacheStrategy.all,1对应DiskCacheStrategy.NONE,2对应DiskCacheStrategy.SOURCE,3对应DiskCacheStrategy.RESULT
private int fallback; //请求 url 为空,则使用此图片作为占位符
private BitmapTransformation transformation;//glide用它来改变图形的形状
private ImageView[] imageViews;
private boolean isClearMemory;//清理内存缓存
private boolean isClearDiskCache;//清理本地缓存
private ImageConfigImpl(Builder builder) {
this.url = builder.url;
this.imageView = builder.imageView;
this.placeholder = builder.placeholder;
this.errorPic = builder.errorPic;
this.fallback = builder.fallback;
this.cacheStrategy = builder.cacheStrategy;
this.transformation = builder.transformation;
this.imageViews = builder.imageViews;
this.isClearMemory = builder.isClearMemory;
this.isClearDiskCache = builder.isClearDiskCache;
}
public int getCacheStrategy() {
return cacheStrategy;
}
public BitmapTransformation getTransformation() {
return transformation;
}
public ImageView[] getImageViews() {
return imageViews;
}
public boolean isClearMemory() {
return isClearMemory;
}
public boolean isClearDiskCache() {
return isClearDiskCache;
}
public int getFallback() {
return fallback;
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private String url;
private ImageView imageView;
private int placeholder;
private int errorPic;
private int fallback; //请求 url 为空,则使用此图片作为占位符
private int cacheStrategy;//0对应DiskCacheStrategy.all,1对应DiskCacheStrategy.NONE,2对应DiskCacheStrategy.SOURCE,3对应DiskCacheStrategy.RESULT
private BitmapTransformation transformation;//glide用它来改变图形的形状
private ImageView[] imageViews;
private boolean isClearMemory;//清理内存缓存
private boolean isClearDiskCache;//清理本地缓存
private Builder() {
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder placeholder(int placeholder) {
this.placeholder = placeholder;
return this;
}
public Builder errorPic(int errorPic) {
this.errorPic = errorPic;
return this;
}
public Builder fallback(int fallback) {
this.fallback = fallback;
return this;
}
public Builder imageView(ImageView imageView) {
this.imageView = imageView;
return this;
}
public Builder cacheStrategy(int cacheStrategy) {
this.cacheStrategy = cacheStrategy;
return this;
}
public Builder transformation(BitmapTransformation transformation) {
this.transformation = transformation;
return this;
}
public Builder imageViews(ImageView... imageViews) {
this.imageViews = imageViews;
return this;
}
public Builder isClearMemory(boolean isClearMemory) {
this.isClearMemory = isClearMemory;
return this;
}
public Builder isClearDiskCache(boolean isClearDiskCache) {
this.isClearDiskCache = isClearDiskCache;
return this;
}
public ImageConfigImpl build() {
return new ImageConfigImpl(this);
}
}
}
/*
* Copyright 2018 JessYan
*
* 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 com.jess.arms.http.log;
import com.jess.arms.di.module.GlobalConfigModule;
import java.util.List;
import okhttp3.MediaType;
import okhttp3.Request;
/**
* ================================================
* 对 OkHttp 的请求和响应信息进行更规范和清晰的打印, 开发者可更根据自己的需求自行扩展打印格式
*
* @see DefaultFormatPrinter
* @see GlobalConfigModule.Builder#formatPrinter(FormatPrinter)
* Created by JessYan on 31/01/2018 17:36
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public interface FormatPrinter {
/**
* 打印网络请求信息, 当网络请求时 {{@link okhttp3.RequestBody}} 可以解析的情况
*
* @param request
* @param bodyString 发送给服务器的请求体中的数据(已解析)
*/
void printJsonRequest(Request request, String bodyString);
/**
* 打印网络请求信息, 当网络请求时 {{@link okhttp3.RequestBody}} 为 {@code null} 或不可解析的情况
*
* @param request
*/
void printFileRequest(Request request);
/**
* 打印网络响应信息, 当网络响应时 {{@link okhttp3.ResponseBody}} 可以解析的情况
*
* @param chainMs 服务器响应耗时(单位毫秒)
* @param isSuccessful 请求是否成功
* @param code 响应码
* @param headers 请求头
* @param contentType 服务器返回数据的数据类型
* @param bodyString 服务器返回的数据(已解析)
* @param segments 域名后面的资源地址
* @param message 响应信息
* @param responseUrl 请求地址
*/
void printJsonResponse(long chainMs, boolean isSuccessful, int code, String headers, MediaType contentType,
String bodyString, List<String> segments, String message, String responseUrl);
/**
* 打印网络响应信息, 当网络响应时 {{@link okhttp3.ResponseBody}} 为 {@code null} 或不可解析的情况
*
* @param chainMs 服务器响应耗时(单位毫秒)
* @param isSuccessful 请求是否成功
* @param code 响应码
* @param headers 请求头
* @param segments 域名后面的资源地址
* @param message 响应信息
* @param responseUrl 请求地址
*/
void printFileResponse(long chainMs, boolean isSuccessful, int code, String headers,
List<String> segments, String message, String responseUrl);
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.http.log;
import com.jess.arms.di.module.GlobalConfigModule;
import com.jess.arms.http.GlobalHttpHandler;
import com.jess.arms.utils.CharacterHandler;
import com.jess.arms.utils.ZipHelper;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Singleton;
import androidx.annotation.Nullable;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import timber.log.Timber;
/**
* ================================================
* 解析框架中的网络请求和响应结果,并以日志形式输出,调试神器
* 可使用 {@link GlobalConfigModule.Builder#printHttpLogLevel(Level)} 控制或关闭日志
* <p>
* Created by JessYan on 7/1/2016.
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
@Singleton
public class RequestInterceptor implements Interceptor {
@Inject
@Nullable
GlobalHttpHandler mHandler;
@Inject
FormatPrinter mPrinter;
@Inject
Level printLevel;
public enum Level {
NONE, //不打印log
REQUEST, //只打印请求信息
RESPONSE, //只打印响应信息
ALL //所有数据全部打印
}
@Inject
public RequestInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
boolean logRequest = printLevel == Level.ALL || (printLevel != Level.NONE && printLevel == Level.REQUEST);
if (logRequest) {
//打印请求信息
if (request.body() != null && isParseable(request.body().contentType())) {
mPrinter.printJsonRequest(request, parseParams(request));
} else {
mPrinter.printFileRequest(request);
}
}
boolean logResponse = printLevel == Level.ALL || (printLevel != Level.NONE && printLevel == Level.RESPONSE);
long t1 = logResponse ? System.nanoTime() : 0;
Response originalResponse;
try {
originalResponse = chain.proceed(request);
} catch (Exception e) {
Timber.w("Http Error: " + e);
throw e;
}
long t2 = logResponse ? System.nanoTime() : 0;
ResponseBody responseBody = originalResponse.body();
//打印响应结果
String bodyString = null;
if (responseBody != null && isParseable(responseBody.contentType())) {
bodyString = printResult(request, originalResponse, logResponse);
}
if (logResponse) {
final List<String> segmentList = request.url().encodedPathSegments();
final String header = originalResponse.headers().toString();
final int code = originalResponse.code();
final boolean isSuccessful = originalResponse.isSuccessful();
final String message = originalResponse.message();
final String url = originalResponse.request().url().toString();
if (responseBody != null && isParseable(responseBody.contentType())) {
mPrinter.printJsonResponse(TimeUnit.NANOSECONDS.toMillis(t2 - t1), isSuccessful,
code, header, responseBody.contentType(), bodyString, segmentList, message, url);
} else {
mPrinter.printFileResponse(TimeUnit.NANOSECONDS.toMillis(t2 - t1),
isSuccessful, code, header, segmentList, message, url);
}
}
if (mHandler != null)//这里可以比客户端提前一步拿到服务器返回的结果,可以做一些操作,比如token超时,重新获取
return mHandler.onHttpResultResponse(bodyString, chain, originalResponse);
return originalResponse;
}
/**
* 打印响应结果
*
* @param request
* @param response
* @param logResponse
* @return
* @throws IOException
*/
@Nullable
private String printResult(Request request, Response response, boolean logResponse) throws IOException {
try {
//读取服务器返回的结果
ResponseBody responseBody = response.newBuilder().build().body();
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE); // Buffer the entire body.
Buffer buffer = source.buffer();
//获取content的压缩类型
String encoding = response
.headers()
.get("Content-Encoding");
Buffer clone = buffer.clone();
//解析response content
return parseContent(responseBody, encoding, clone);
} catch (IOException e) {
e.printStackTrace();
return "{\"error\": \"" + e.getMessage() + "\"}";
}
}
/**
* 解析服务器响应的内容
*
* @param responseBody
* @param encoding
* @param clone
* @return
*/
private String parseContent(ResponseBody responseBody, String encoding, Buffer clone) {
Charset charset = Charset.forName("UTF-8");
MediaType contentType = responseBody.contentType();
if (contentType != null) {
charset = contentType.charset(charset);
}
if (encoding != null && encoding.equalsIgnoreCase("gzip")) {//content使用gzip压缩
return ZipHelper.decompressForGzip(clone.readByteArray(), convertCharset(charset));//解压
} else if (encoding != null && encoding.equalsIgnoreCase("zlib")) {//content使用zlib压缩
return ZipHelper.decompressToStringForZlib(clone.readByteArray(), convertCharset(charset));//解压
} else {//content没有被压缩
return clone.readString(charset);
}
}
/**
* 解析请求服务器的请求参数
*
* @param request
* @return
* @throws UnsupportedEncodingException
*/
public static String parseParams(Request request) throws UnsupportedEncodingException {
try {
RequestBody body = request.newBuilder().build().body();
if (body == null) return "";
Buffer requestbuffer = new Buffer();
body.writeTo(requestbuffer);
Charset charset = Charset.forName("UTF-8");
MediaType contentType = body.contentType();
if (contentType != null) {
charset = contentType.charset(charset);
}
return CharacterHandler.jsonFormat(URLDecoder.decode(requestbuffer.readString(charset), convertCharset(charset)));
} catch (IOException e) {
e.printStackTrace();
return "{\"error\": \"" + e.getMessage() + "\"}";
}
}
/**
* 是否可以解析
*
* @param mediaType
* @return
*/
public static boolean isParseable(MediaType mediaType) {
return isText(mediaType) || isPlain(mediaType)
|| isJson(mediaType) || isForm(mediaType)
|| isHtml(mediaType) || isXml(mediaType);
}
public static boolean isText(MediaType mediaType) {
if (mediaType == null || mediaType.type() == null) return false;
return mediaType.type().equals("text");
}
public static boolean isPlain(MediaType mediaType) {
if (mediaType == null || mediaType.subtype() == null) return false;
return mediaType.subtype().toLowerCase().contains("plain");
}
public static boolean isJson(MediaType mediaType) {
if (mediaType == null || mediaType.subtype() == null) return false;
return mediaType.subtype().toLowerCase().contains("json");
}
public static boolean isXml(MediaType mediaType) {
if (mediaType == null || mediaType.subtype() == null) return false;
return mediaType.subtype().toLowerCase().contains("xml");
}
public static boolean isHtml(MediaType mediaType) {
if (mediaType == null || mediaType.subtype() == null) return false;
return mediaType.subtype().toLowerCase().contains("html");
}
public static boolean isForm(MediaType mediaType) {
if (mediaType == null || mediaType.subtype() == null) return false;
return mediaType.subtype().toLowerCase().contains("x-www-form-urlencoded");
}
public static String convertCharset(Charset charset) {
String s = charset.toString();
int i = s.indexOf("[");
if (i == -1)
return s;
return s.substring(i + 1, s.length() - 1);
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.integration;
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import com.jess.arms.base.BaseFragment;
import com.jess.arms.base.delegate.ActivityDelegate;
import com.jess.arms.base.delegate.ActivityDelegateImpl;
import com.jess.arms.base.delegate.FragmentDelegate;
import com.jess.arms.base.delegate.IActivity;
import com.jess.arms.integration.cache.Cache;
import com.jess.arms.utils.Preconditions;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import dagger.Lazy;
/**
* ================================================
* {@link Application.ActivityLifecycleCallbacks} 默认实现类
* 通过 {@link ActivityDelegate} 管理 {@link Activity}
*
* @see <a href="http://www.jianshu.com/p/75a5c24174b2">ActivityLifecycleCallbacks 分析文章</a>
* Created by JessYan on 21/02/2017 14:23
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
@Singleton
public class ActivityLifecycle implements Application.ActivityLifecycleCallbacks {
@Inject
AppManager mAppManager;
@Inject
Application mApplication;
@Inject
Cache<String, Object> mExtras;
@Inject
Lazy<FragmentManager.FragmentLifecycleCallbacks> mFragmentLifecycle;
@Inject
Lazy<List<FragmentManager.FragmentLifecycleCallbacks>> mFragmentLifecycles;
@Inject
public ActivityLifecycle() {
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
//如果 intent 包含了此字段,并且为 true 说明不加入到 list 进行统一管理
boolean isNotAdd = false;
if (activity.getIntent() != null)
isNotAdd = activity.getIntent().getBooleanExtra(AppManager.IS_NOT_ADD_ACTIVITY_LIST, false);
if (!isNotAdd)
mAppManager.addActivity(activity);
//配置ActivityDelegate
if (activity instanceof IActivity) {
ActivityDelegate activityDelegate = fetchActivityDelegate(activity);
if (activityDelegate == null) {
Cache<String, Object> cache = getCacheFromActivity((IActivity) activity);
activityDelegate = new ActivityDelegateImpl(activity);
cache.put(ActivityDelegate.ACTIVITY_DELEGATE, activityDelegate);
}
activityDelegate.onCreate(savedInstanceState);
}
registerFragmentCallbacks(activity);
}
@Override
public void onActivityStarted(Activity activity) {
ActivityDelegate activityDelegate = fetchActivityDelegate(activity);
if (activityDelegate != null) {
activityDelegate.onStart();
}
}
@Override
public void onActivityResumed(Activity activity) {
mAppManager.setCurrentActivity(activity);
ActivityDelegate activityDelegate = fetchActivityDelegate(activity);
if (activityDelegate != null) {
activityDelegate.onResume();
}
}
@Override
public void onActivityPaused(Activity activity) {
ActivityDelegate activityDelegate = fetchActivityDelegate(activity);
if (activityDelegate != null) {
activityDelegate.onPause();
}
}
@Override
public void onActivityStopped(Activity activity) {
if (mAppManager.getCurrentActivity() == activity) {
mAppManager.setCurrentActivity(null);
}
ActivityDelegate activityDelegate = fetchActivityDelegate(activity);
if (activityDelegate != null) {
activityDelegate.onStop();
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
ActivityDelegate activityDelegate = fetchActivityDelegate(activity);
if (activityDelegate != null) {
activityDelegate.onSaveInstanceState(outState);
}
}
@Override
public void onActivityDestroyed(Activity activity) {
mAppManager.removeActivity(activity);
ActivityDelegate activityDelegate = fetchActivityDelegate(activity);
if (activityDelegate != null) {
activityDelegate.onDestroy();
getCacheFromActivity((IActivity) activity).clear();
}
}
/**
* 给每个 Activity 的所有 Fragment 设置监听其生命周期, Activity 可以通过 {@link IActivity#useFragment()}
* 设置是否使用监听,如果这个 Activity 返回 false 的话,这个 Activity 下面的所有 Fragment 将不能使用 {@link FragmentDelegate}
* 意味着 {@link BaseFragment} 也不能使用
*
* @param activity
*/
private void registerFragmentCallbacks(Activity activity) {
boolean useFragment = activity instanceof IActivity ? ((IActivity) activity).useFragment() : true;
if (activity instanceof FragmentActivity && useFragment) {
//mFragmentLifecycle 为 Fragment 生命周期实现类, 用于框架内部对每个 Fragment 的必要操作, 如给每个 Fragment 配置 FragmentDelegate
//注册框架内部已实现的 Fragment 生命周期逻辑
((FragmentActivity) activity).getSupportFragmentManager().registerFragmentLifecycleCallbacks(mFragmentLifecycle.get(), true);
if (mExtras.containsKey(ConfigModule.class.getName())) {
List<ConfigModule> modules = (List<ConfigModule>) mExtras.get(ConfigModule.class.getName());
for (ConfigModule module : modules) {
module.injectFragmentLifecycle(mApplication, mFragmentLifecycles.get());
}
mExtras.remove(ConfigModule.class.getName());
}
//注册框架外部, 开发者扩展的 Fragment 生命周期逻辑
for (FragmentManager.FragmentLifecycleCallbacks fragmentLifecycle : mFragmentLifecycles.get()) {
((FragmentActivity) activity).getSupportFragmentManager().registerFragmentLifecycleCallbacks(fragmentLifecycle, true);
}
}
}
private ActivityDelegate fetchActivityDelegate(Activity activity) {
ActivityDelegate activityDelegate = null;
if (activity instanceof IActivity) {
Cache<String, Object> cache = getCacheFromActivity((IActivity) activity);
activityDelegate = (ActivityDelegate) cache.get(ActivityDelegate.ACTIVITY_DELEGATE);
}
return activityDelegate;
}
@NonNull
private Cache<String, Object> getCacheFromActivity(IActivity activity) {
Cache<String, Object> cache = activity.provideCache();
Preconditions.checkNotNull(cache, "%s cannot be null on Activity", Cache.class.getName());
return cache;
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.integration;
import android.app.Application;
import android.content.Context;
import com.jess.arms.base.delegate.AppLifecycles;
import com.jess.arms.di.module.GlobalConfigModule;
import java.util.List;
import androidx.fragment.app.FragmentManager;
/**
* ================================================
* {@link ConfigModule} 可以给框架配置一些参数,需要实现 {@link ConfigModule} 后,在 AndroidManifest 中声明该实现类
*
* @see <a href="https://github.com/JessYanCoding/MVPArms/wiki#2.1">ConfigModule wiki 官方文档</a>
* Created by JessYan on 12/04/2017 11:37
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public interface ConfigModule {
/**
* 使用{@link GlobalConfigModule.Builder}给框架配置一些配置参数
*
* @param context
* @param builder
*/
void applyOptions(Context context, GlobalConfigModule.Builder builder);
/**
* 使用{@link AppLifecycles}在Application的生命周期中注入一些操作
*
* @param context
* @param lifecycles
*/
void injectAppLifecycle(Context context, List<AppLifecycles> lifecycles);
/**
* 使用{@link Application.ActivityLifecycleCallbacks}在Activity的生命周期中注入一些操作
*
* @param context
* @param lifecycles
*/
void injectActivityLifecycle(Context context, List<Application.ActivityLifecycleCallbacks> lifecycles);
/**
* 使用{@link FragmentManager.FragmentLifecycleCallbacks}在Fragment的生命周期中注入一些操作
*
* @param context
* @param lifecycles
*/
void injectFragmentLifecycle(Context context, List<FragmentManager.FragmentLifecycleCallbacks> lifecycles);
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.integration;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import com.jess.arms.base.delegate.FragmentDelegate;
import com.jess.arms.base.delegate.FragmentDelegateImpl;
import com.jess.arms.base.delegate.IFragment;
import com.jess.arms.integration.cache.Cache;
import com.jess.arms.utils.Preconditions;
import javax.inject.Inject;
import javax.inject.Singleton;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import timber.log.Timber;
/**
* ================================================
* {@link FragmentManager.FragmentLifecycleCallbacks} 默认实现类
* 通过 {@link FragmentDelegate} 管理 {@link Fragment}
* <p>
* Created by JessYan on 04/09/2017 16:04
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
@Singleton
public class FragmentLifecycle extends FragmentManager.FragmentLifecycleCallbacks {
@Inject
public FragmentLifecycle() {
}
@Override
public void onFragmentAttached(FragmentManager fm, Fragment f, Context context) {
Timber.w(f.toString() + " - onFragmentAttached");
if (f instanceof IFragment) {
FragmentDelegate fragmentDelegate = fetchFragmentDelegate(f);
if (fragmentDelegate == null || !fragmentDelegate.isAdded()) {
Cache<String, Object> cache = getCacheFromFragment((IFragment) f);
fragmentDelegate = new FragmentDelegateImpl(fm, f);
cache.put(FragmentDelegate.FRAGMENT_DELEGATE, fragmentDelegate);
}
fragmentDelegate.onAttach(context);
}
}
@Override
public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {
Timber.w(f.toString() + " - onFragmentCreated");
FragmentDelegate fragmentDelegate = fetchFragmentDelegate(f);
if (fragmentDelegate != null) {
fragmentDelegate.onCreate(savedInstanceState);
}
}
@Override
public void onFragmentViewCreated(FragmentManager fm, Fragment f, View v, Bundle savedInstanceState) {
Timber.w(f.toString() + " - onFragmentViewCreated");
FragmentDelegate fragmentDelegate = fetchFragmentDelegate(f);
if (fragmentDelegate != null) {
fragmentDelegate.onCreateView(v, savedInstanceState);
}
}
@Override
public void onFragmentActivityCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {
Timber.w(f.toString() + " - onFragmentActivityCreated");
FragmentDelegate fragmentDelegate = fetchFragmentDelegate(f);
if (fragmentDelegate != null) {
fragmentDelegate.onActivityCreate(savedInstanceState);
}
}
@Override
public void onFragmentStarted(FragmentManager fm, Fragment f) {
Timber.w(f.toString() + " - onFragmentStarted");
FragmentDelegate fragmentDelegate = fetchFragmentDelegate(f);
if (fragmentDelegate != null) {
fragmentDelegate.onStart();
}
}
@Override
public void onFragmentResumed(FragmentManager fm, Fragment f) {
Timber.w(f.toString() + " - onFragmentResumed");
FragmentDelegate fragmentDelegate = fetchFragmentDelegate(f);
if (fragmentDelegate != null) {
fragmentDelegate.onResume();
}
}
@Override
public void onFragmentPaused(FragmentManager fm, Fragment f) {
Timber.w(f.toString() + " - onFragmentPaused");
FragmentDelegate fragmentDelegate = fetchFragmentDelegate(f);
if (fragmentDelegate != null) {
fragmentDelegate.onPause();
}
}
@Override
public void onFragmentStopped(FragmentManager fm, Fragment f) {
Timber.w(f.toString() + " - onFragmentStopped");
FragmentDelegate fragmentDelegate = fetchFragmentDelegate(f);
if (fragmentDelegate != null) {
fragmentDelegate.onStop();
}
}
@Override
public void onFragmentSaveInstanceState(FragmentManager fm, Fragment f, Bundle outState) {
Timber.w(f.toString() + " - onFragmentSaveInstanceState");
FragmentDelegate fragmentDelegate = fetchFragmentDelegate(f);
if (fragmentDelegate != null) {
fragmentDelegate.onSaveInstanceState(outState);
}
}
@Override
public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {
Timber.w(f.toString() + " - onFragmentViewDestroyed");
FragmentDelegate fragmentDelegate = fetchFragmentDelegate(f);
if (fragmentDelegate != null) {
fragmentDelegate.onDestroyView();
}
}
@Override
public void onFragmentDestroyed(FragmentManager fm, Fragment f) {
Timber.w(f.toString() + " - onFragmentDestroyed");
FragmentDelegate fragmentDelegate = fetchFragmentDelegate(f);
if (fragmentDelegate != null) {
fragmentDelegate.onDestroy();
}
}
@Override
public void onFragmentDetached(FragmentManager fm, Fragment f) {
Timber.w(f.toString() + " - onFragmentDetached");
FragmentDelegate fragmentDelegate = fetchFragmentDelegate(f);
if (fragmentDelegate != null) {
fragmentDelegate.onDetach();
}
}
private FragmentDelegate fetchFragmentDelegate(Fragment fragment) {
if (fragment instanceof IFragment) {
Cache<String, Object> cache = getCacheFromFragment((IFragment) fragment);
return (FragmentDelegate) cache.get(FragmentDelegate.FRAGMENT_DELEGATE);
}
return null;
}
@NonNull
private Cache<String, Object> getCacheFromFragment(IFragment fragment) {
Cache<String, Object> cache = fragment.provideCache();
Preconditions.checkNotNull(cache, "%s cannot be null on Fragment", Cache.class.getName());
return cache;
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.integration;
import android.content.Context;
import com.jess.arms.mvp.IModel;
/**
* ================================================
* 用来管理网络请求层,以及数据缓存层,以后可能添加数据库请求层
* 提供给 {@link IModel} 必要的 Api 做数据处理
*
* @see <a href="https://github.com/JessYanCoding/MVPArms/wiki#2.3">RepositoryManager wiki 官方文档</a>
* Created by JessYan on 17/03/2017 11:15
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public interface IRepositoryManager {
/**
* 根据传入的 Class 获取对应的 Retrofit service
*
* @param service
* @param <T>
* @return
*/
<T> T obtainRetrofitService(Class<T> service);
/**
* 根据传入的 Class 获取对应的 RxCache service
*
* @param cache
* @param <T>
* @return
*/
<T> T obtainCacheService(Class<T> cache);
/**
* 清理所有缓存
*/
void clearAllCache();
Context getContext();
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.integration;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import java.util.ArrayList;
import java.util.List;
/**
* ================================================
* 用于解析 AndroidManifest 中的 Meta 属性
* 配合 {@link ConfigModule} 使用
* <p>
* Created by JessYan on 12/04/2017 14:41
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public final class ManifestParser {
private static final String MODULE_VALUE = "ConfigModule";
private final Context context;
public ManifestParser(Context context) {
this.context = context;
}
public List<ConfigModule> parse() {
List<ConfigModule> modules = new ArrayList<ConfigModule>();
try {
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
context.getPackageName(), PackageManager.GET_META_DATA);
if (appInfo.metaData != null) {
for (String key : appInfo.metaData.keySet()) {
if (MODULE_VALUE.equals(appInfo.metaData.get(key))) {
modules.add(parseModule(key));
}
}
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException("Unable to find metadata to parse ConfigModule", e);
}
return modules;
}
private static ConfigModule parseModule(String className) {
Class<?> clazz;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Unable to find ConfigModule implementation", e);
}
Object module;
try {
module = clazz.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Unable to instantiate ConfigModule implementation for " + clazz, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to instantiate ConfigModule implementation for " + clazz, e);
}
if (!(module instanceof ConfigModule)) {
throw new RuntimeException("Expected instanceof ConfigModule, but found: " + module);
}
return (ConfigModule) module;
}
}
\ No newline at end of file
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.integration;
import android.app.Application;
import android.content.Context;
import com.jess.arms.integration.cache.Cache;
import com.jess.arms.integration.cache.CacheType;
import com.jess.arms.mvp.IModel;
import com.jess.arms.utils.Preconditions;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Lazy;
import io.rx_cache2.internal.RxCache;
import retrofit2.Retrofit;
/**
* ================================================
* 用来管理网络请求层,以及数据缓存层,以后可能添加数据库请求层
* 提供给 {@link IModel} 层必要的 Api 做数据处理
*
* @see <a href="https://github.com/JessYanCoding/MVPArms/wiki#2.3">RepositoryManager wiki 官方文档</a>
* Created by JessYan on 13/04/2017 09:52
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
@Singleton
public class RepositoryManager implements IRepositoryManager {
@Inject
Lazy<Retrofit> mRetrofit;
@Inject
Lazy<RxCache> mRxCache;
@Inject
Application mApplication;
@Inject
Cache.Factory mCachefactory;
private Cache<String, Object> mRetrofitServiceCache;
private Cache<String, Object> mCacheServiceCache;
@Inject
public RepositoryManager() {
}
/**
* 根据传入的 Class 获取对应的 Retrofit service
*
* @param service
* @param <T>
* @return
*/
@Override
public synchronized <T> T obtainRetrofitService(Class<T> service) {
if (mRetrofitServiceCache == null)
mRetrofitServiceCache = mCachefactory.build(CacheType.RETROFIT_SERVICE_CACHE);
Preconditions.checkNotNull(mRetrofitServiceCache, "Cannot return null from a Cache.Factory#build(int) method");
T retrofitService = (T) mRetrofitServiceCache.get(service.getCanonicalName());
if (retrofitService == null) {
retrofitService = mRetrofit.get().create(service);
mRetrofitServiceCache.put(service.getCanonicalName(), retrofitService);
}
return retrofitService;
}
/**
* 根据传入的 Class 获取对应的 RxCache service
*
* @param cache
* @param <T>
* @return
*/
@Override
public synchronized <T> T obtainCacheService(Class<T> cache) {
if (mCacheServiceCache == null)
mCacheServiceCache = mCachefactory.build(CacheType.CACHE_SERVICE_CACHE);
Preconditions.checkNotNull(mCacheServiceCache, "Cannot return null from a Cache.Factory#build(int) method");
T cacheService = (T) mCacheServiceCache.get(cache.getCanonicalName());
if (cacheService == null) {
cacheService = mRxCache.get().using(cache);
mCacheServiceCache.put(cache.getCanonicalName(), cacheService);
}
return cacheService;
}
/**
* 清理所有缓存
*/
@Override
public void clearAllCache() {
mRxCache.get().evictAll();
}
@Override
public Context getContext() {
return mApplication;
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.integration.cache;
import android.app.Application;
import com.jess.arms.di.module.GlobalConfigModule;
import java.util.Set;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* ================================================
* 用于缓存框架中所必需的组件,开发者可通过 {@link GlobalConfigModule.Builder#cacheFactory(Factory)} 为框架提供缓存策略
* 开发者也可以用于自己日常中的使用
*
* @see GlobalConfigModule#provideCacheFactory(Application)
* @see LruCache
* Created by JessYan on 25/09/2017 16:36
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public interface Cache<K, V> {
interface Factory {
/**
* Returns a new cache
*
* @param type 框架中需要缓存的模块类型
* @return
*/
@NonNull
Cache build(CacheType type);
}
/**
* 返回当前缓存已占用的总 size
*
* @return
*/
int size();
/**
* 返回当前缓存所能允许的最大 size
*
* @return
*/
int getMaxSize();
/**
* 返回这个 {@code key} 在缓存中对应的 {@code value}, 如果返回 {@code null} 说明这个 {@code key} 没有对应的 {@code value}
*
* @param key
* @return
*/
@Nullable
V get(K key);
/**
* 将 {@code key} 和 {@code value} 以条目的形式加入缓存,如果这个 {@code key} 在缓存中已经有对应的 {@code value}
* 则此 {@code value} 被新的 {@code value} 替换并返回,如果为 {@code null} 说明是一个新条目
*
* @param key
* @param value
* @return
*/
@Nullable
V put(K key, V value);
/**
* 移除缓存中这个 {@code key} 所对应的条目,并返回所移除条目的 value
* 如果返回为 {@code null} 则有可能时因为这个 {@code key} 对应的 value 为 {@code null} 或条目不存在
*
* @param key
* @return
*/
@Nullable
V remove(K key);
/**
* 如果这个 {@code key} 在缓存中有对应的 value 并且不为 {@code null}, 则返回 {@code true}
*
* @param key
* @return
*/
boolean containsKey(K key);
/**
* 返回当前缓存中含有的所有 {@code key}
*
* @return
*/
Set<K> keySet();
/**
* 清除缓存中所有的内容
*/
void clear();
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.integration.cache;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import com.jess.arms.di.component.AppComponent;
import com.jess.arms.integration.RepositoryManager;
import androidx.fragment.app.Fragment;
/**
* ================================================
* 构建 {@link Cache} 时,使用 {@link CacheType} 中声明的类型,来区分不同的模块
* 从而为不同的模块构建不同的缓存策略
*
* @see Cache.Factory#build(CacheType)
* Created by JessYan on 25/09/2017 18:05
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public interface CacheType {
int RETROFIT_SERVICE_CACHE_TYPE_ID = 0;
int CACHE_SERVICE_CACHE_TYPE_ID = 1;
int EXTRAS_TYPE_ID = 2;
int ACTIVITY_CACHE_TYPE_ID = 3;
int FRAGMENT_CACHE_TYPE_ID = 4;
/**
* {@link RepositoryManager}中存储 Retrofit Service 的容器
*/
CacheType RETROFIT_SERVICE_CACHE = new CacheType() {
private static final int MAX_SIZE = 150;
private static final float MAX_SIZE_MULTIPLIER = 0.002f;
@Override
public int getCacheTypeId() {
return RETROFIT_SERVICE_CACHE_TYPE_ID;
}
@Override
public int calculateCacheSize(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
int targetMemoryCacheSize = (int) (activityManager.getMemoryClass() * MAX_SIZE_MULTIPLIER * 1024);
if (targetMemoryCacheSize >= MAX_SIZE) {
return MAX_SIZE;
}
return targetMemoryCacheSize;
}
};
/**
* {@link RepositoryManager} 中储存 Cache Service 的容器
*/
CacheType CACHE_SERVICE_CACHE = new CacheType() {
private static final int MAX_SIZE = 150;
private static final float MAX_SIZE_MULTIPLIER = 0.002f;
@Override
public int getCacheTypeId() {
return CACHE_SERVICE_CACHE_TYPE_ID;
}
@Override
public int calculateCacheSize(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
int targetMemoryCacheSize = (int) (activityManager.getMemoryClass() * MAX_SIZE_MULTIPLIER * 1024);
if (targetMemoryCacheSize >= MAX_SIZE) {
return MAX_SIZE;
}
return targetMemoryCacheSize;
}
};
/**
* {@link AppComponent} 中的 extras
*/
CacheType EXTRAS = new CacheType() {
private static final int MAX_SIZE = 500;
private static final float MAX_SIZE_MULTIPLIER = 0.005f;
@Override
public int getCacheTypeId() {
return EXTRAS_TYPE_ID;
}
@Override
public int calculateCacheSize(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
int targetMemoryCacheSize = (int) (activityManager.getMemoryClass() * MAX_SIZE_MULTIPLIER * 1024);
if (targetMemoryCacheSize >= MAX_SIZE) {
return MAX_SIZE;
}
return targetMemoryCacheSize;
}
};
/**
* {@link Activity} 中存储数据的容器
*/
CacheType ACTIVITY_CACHE = new CacheType() {
private static final int MAX_SIZE = 80;
private static final float MAX_SIZE_MULTIPLIER = 0.0008f;
@Override
public int getCacheTypeId() {
return ACTIVITY_CACHE_TYPE_ID;
}
@Override
public int calculateCacheSize(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
int targetMemoryCacheSize = (int) (activityManager.getMemoryClass() * MAX_SIZE_MULTIPLIER * 1024);
if (targetMemoryCacheSize >= MAX_SIZE) {
return MAX_SIZE;
}
return targetMemoryCacheSize;
}
};
/**
* {@link Fragment} 中存储数据的容器
*/
CacheType FRAGMENT_CACHE = new CacheType() {
private static final int MAX_SIZE = 80;
private static final float MAX_SIZE_MULTIPLIER = 0.0008f;
@Override
public int getCacheTypeId() {
return FRAGMENT_CACHE_TYPE_ID;
}
@Override
public int calculateCacheSize(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
int targetMemoryCacheSize = (int) (activityManager.getMemoryClass() * MAX_SIZE_MULTIPLIER * 1024);
if (targetMemoryCacheSize >= MAX_SIZE) {
return MAX_SIZE;
}
return targetMemoryCacheSize;
}
};
/**
* 返回框架内需要缓存的模块对应的 {@code id}
*
* @return
*/
int getCacheTypeId();
/**
* 计算对应模块需要的缓存大小
*
* @return
*/
int calculateCacheSize(Context context);
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.integration.cache;
import android.app.Application;
import com.jess.arms.di.module.GlobalConfigModule;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import androidx.annotation.Nullable;
/**
* ================================================
* LRU 即 Least Recently Used,最近最少使用,也就是说,当缓存满了,会优先淘汰那些最近最不常访问的数据
* 此种缓存策略为框架默认提供,可自行实现其他缓存策略,如磁盘缓存,为框架或开发者提供缓存的功能
*
* @see GlobalConfigModule#provideCacheFactory(Application)
* @see Cache
* Created by JessYan on 25/09/2017 16:57
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public class LruCache<K, V> implements Cache<K, V> {
private final LinkedHashMap<K, V> cache = new LinkedHashMap<>(100, 0.75f, true);
private final int initialMaxSize;
private int maxSize;
private int currentSize = 0;
/**
* Constructor for LruCache.
*
* @param size 这个缓存的最大 size,这个 size 所使用的单位必须和 {@link #getItemSize(Object)} 所使用的单位一致.
*/
public LruCache(int size) {
this.initialMaxSize = size;
this.maxSize = size;
}
/**
* 设置一个系数应用于当时构造函数中所传入的 size, 从而得到一个新的 {@link #maxSize}
* 并会立即调用 {@link #evict} 开始清除满足条件的条目
*
* @param multiplier 系数
*/
public synchronized void setSizeMultiplier(float multiplier) {
if (multiplier < 0) {
throw new IllegalArgumentException("Multiplier must be >= 0");
}
maxSize = Math.round(initialMaxSize * multiplier);
evict();
}
/**
* 返回每个 {@code item} 所占用的 size,默认为1,这个 size 的单位必须和构造函数所传入的 size 一致
* 子类可以重写这个方法以适应不同的单位,比如说 bytes
*
* @param item 每个 {@code item} 所占用的 size
*/
protected int getItemSize(V item) {
return 1;
}
/**
* 当缓存中有被驱逐的条目时,会回调此方法,默认空实现,子类可以重写这个方法
*
* @param key 被驱逐条目的 {@code key}
* @param value 被驱逐条目的 {@code value}
*/
protected void onItemEvicted(K key, V value) {
// optional override
}
/**
* 返回当前缓存所能允许的最大 size
*/
@Override
public synchronized int getMaxSize() {
return maxSize;
}
/**
* 返回当前缓存已占用的总 size
*/
@Override
public synchronized int size() {
return currentSize;
}
/**
* 如果这个 {@code key} 在缓存中有对应的 {@code value} 并且不为 {@code null},则返回 true
*
* @param key 用来映射的 {@code key}
*/
@Override
public synchronized boolean containsKey(K key) {
return cache.containsKey(key);
}
/**
* 返回当前缓存中含有的所有 {@code key}
*
* @return
*/
@Override
public Set<K> keySet() {
return cache.keySet();
}
/**
* 返回这个 {@code key} 在缓存中对应的 {@code value}, 如果返回 {@code null} 说明这个 {@code key} 没有对应的 {@code value}
*
* @param key 用来映射的 {@code key}
*/
@Override
@Nullable
public synchronized V get(K key) {
return cache.get(key);
}
/**
* 将 {@code key} 和 {@code value} 以条目的形式加入缓存,如果这个 {@code key} 在缓存中已经有对应的 {@code value}
* 则此 {@code value} 被新的 {@code value} 替换并返回,如果为 {@code null} 说明是一个新条目
* <p>
* 如果 {@link #getItemSize} 返回的 size 大于或等于缓存所能允许的最大 size, 则不能向缓存中添加此条目
* 此时会回调 {@link #onItemEvicted(Object, Object)} 通知此方法当前被驱逐的条目
*
* @param key 通过这个 {@code key} 添加条目
* @param value 需要添加的 {@code value}
*/
@Override
@Nullable
public synchronized V put(K key, V value) {
final int itemSize = getItemSize(value);
if (itemSize >= maxSize) {
onItemEvicted(key, value);
return null;
}
final V result = cache.put(key, value);
if (value != null) {
currentSize += getItemSize(value);
}
if (result != null) {
currentSize -= getItemSize(result);
}
evict();
return result;
}
/**
* 移除缓存中这个 {@code key} 所对应的条目,并返回所移除条目的 {@code value}
* 如果返回为 {@code null} 则有可能时因为这个 {@code key} 对应的 {@code value} 为 {@code null} 或条目不存在
*
* @param key 使用这个 {@code key} 移除对应的条目
*/
@Override
@Nullable
public synchronized V remove(K key) {
final V value = cache.remove(key);
if (value != null) {
currentSize -= getItemSize(value);
}
return value;
}
/**
* 清除缓存中所有的内容
*/
@Override
public void clear() {
trimToSize(0);
}
/**
* 当指定的 size 小于当前缓存已占用的总 size 时,会开始清除缓存中最近最少使用的条目
*
* @param size
*/
protected synchronized void trimToSize(int size) {
Map.Entry<K, V> last;
while (currentSize > size) {
last = cache.entrySet().iterator().next();
final V toRemove = last.getValue();
currentSize -= getItemSize(toRemove);
final K key = last.getKey();
cache.remove(key);
onItemEvicted(key, toRemove);
}
}
/**
* 当缓存中已占用的总 size 大于所能允许的最大 size ,会使用 {@link #trimToSize(int)} 开始清除满足条件的条目
*/
private void evict() {
trimToSize(maxSize);
}
}
/*
* Copyright 2017 JessYan
*
* 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 com.jess.arms.integration.lifecycle;
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import com.trello.rxlifecycle2.RxLifecycle;
import com.trello.rxlifecycle2.android.ActivityEvent;
import javax.inject.Inject;
import javax.inject.Singleton;
import androidx.fragment.app.FragmentActivity;
import dagger.Lazy;
import io.reactivex.subjects.Subject;
/**
* ================================================
* 配合 {@link ActivityLifecycleable} 使用,使 {@link Activity} 具有 {@link RxLifecycle} 的特性
* <p>
* Created by JessYan on 25/08/2017 18:56
* <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
@Singleton
public class ActivityLifecycleForRxLifecycle implements Application.ActivityLifecycleCallbacks {
@Inject
Lazy<FragmentLifecycleForRxLifecycle> mFragmentLifecycle;
@Inject
public ActivityLifecycleForRxLifecycle() {
}
/**
* 通过桥梁对象 {@code BehaviorSubject<ActivityEvent> mLifecycleSubject}
* 在每个 Activity 的生命周期中发出对应的生命周期事件
*/
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (activity instanceof ActivityLifecycleable) {
obtainSubject(activity).onNext(ActivityEvent.CREATE);
if (activity instanceof FragmentActivity) {
((FragmentActivity) activity).getSupportFragmentManager().registerFragmentLifecycleCallbacks(mFragmentLifecycle.get(), true);
}
}
}
@Override
public void onActivityStarted(Activity activity) {
if (activity instanceof ActivityLifecycleable) {
obtainSubject(activity).onNext(ActivityEvent.START);
}
}
@Override
public void onActivityResumed(Activity activity) {
if (activity instanceof ActivityLifecycleable) {
obtainSubject(activity).onNext(ActivityEvent.RESUME);
}
}
@Override
public void onActivityPaused(Activity activity) {
if (activity instanceof ActivityLifecycleable) {
obtainSubject(activity).onNext(ActivityEvent.PAUSE);
}
}
@Override
public void onActivityStopped(Activity activity) {
if (activity instanceof ActivityLifecycleable) {
obtainSubject(activity).onNext(ActivityEvent.STOP);
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
if (activity instanceof ActivityLifecycleable) {
obtainSubject(activity).onNext(ActivityEvent.DESTROY);
}
}
/**
* 从 {@link com.jess.arms.base.BaseActivity} 中获得桥梁对象 {@code BehaviorSubject<ActivityEvent> mLifecycleSubject}
*
* @see <a href="https://mcxiaoke.gitbooks.io/rxdocs/content/Subject.html">BehaviorSubject 官方中文文档</a>
*/
private Subject<ActivityEvent> obtainSubject(Activity activity) {
return ((ActivityLifecycleable) activity).provideLifecycleSubject();
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment