parent
3facefa1c1
commit
9ae4ddd23f
@ -1,116 +0,0 @@ |
||||
package com.android.base.app; |
||||
|
||||
import android.app.Application; |
||||
import android.content.Context; |
||||
import android.content.IntentFilter; |
||||
import android.content.res.Configuration; |
||||
import android.net.ConnectivityManager; |
||||
|
||||
import com.android.base.receiver.NetStateReceiver; |
||||
import com.android.base.utils.BaseUtils; |
||||
import com.blankj.utilcode.util.AppUtils; |
||||
import com.blankj.utilcode.util.Utils; |
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean; |
||||
|
||||
import io.reactivex.Flowable; |
||||
import io.reactivex.processors.BehaviorProcessor; |
||||
import timber.log.Timber; |
||||
|
||||
/** |
||||
* @author Ztiany |
||||
* Email: ztiany3@gmail.com |
||||
* Date : 2018-10-12 18:19 |
||||
*/ |
||||
@SuppressWarnings("WeakerAccess,unused") |
||||
public final class ApplicationDelegate { |
||||
|
||||
private Application mApplication; |
||||
|
||||
private CrashHandler mCrashHandler; |
||||
private BehaviorProcessor<Boolean> mAppStatus; |
||||
|
||||
private AtomicBoolean mOnCreateCalled = new AtomicBoolean(false); |
||||
private AtomicBoolean mOnAttachBaseCalled = new AtomicBoolean(false); |
||||
|
||||
ApplicationDelegate() { |
||||
} |
||||
|
||||
public void attachBaseContext(@SuppressWarnings("unused") Context base) { |
||||
if (!mOnAttachBaseCalled.compareAndSet(false, true)) { |
||||
throw new IllegalStateException("Can only be called once"); |
||||
} |
||||
//no op
|
||||
} |
||||
|
||||
public void onCreate(Application application) { |
||||
if (!mOnCreateCalled.compareAndSet(false, true)) { |
||||
throw new IllegalStateException("Can only be called once"); |
||||
} |
||||
mApplication = application; |
||||
//工具类初始化
|
||||
BaseUtils.init(application); |
||||
//异常日志记录
|
||||
mCrashHandler = CrashHandler.register(application); |
||||
//网络状态
|
||||
listenNetworkState(); |
||||
//App前台后台
|
||||
listenActivityLifecycleCallbacks(); |
||||
} |
||||
|
||||
public void onTerminate() { |
||||
//no op
|
||||
} |
||||
|
||||
public void onConfigurationChanged(Configuration newConfig) { |
||||
//no op
|
||||
} |
||||
|
||||
public void onTrimMemory(int level) { |
||||
//no op
|
||||
} |
||||
|
||||
public void onLowMemory() { |
||||
//no op
|
||||
} |
||||
|
||||
private void listenNetworkState() { |
||||
NetStateReceiver netStateReceiver = new NetStateReceiver(); |
||||
mApplication.registerReceiver(netStateReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); |
||||
} |
||||
|
||||
private void listenActivityLifecycleCallbacks() { |
||||
mAppStatus = BehaviorProcessor.create(); |
||||
AppUtils.registerAppStatusChangedListener(this, new Utils.OnAppStatusChangedListener() { |
||||
@Override |
||||
public void onForeground() { |
||||
Timber.d("app进入前台"); |
||||
mAppStatus.onNext(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onBackground() { |
||||
Timber.d("app进入后台"); |
||||
mAppStatus.onNext(false); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* 获取可观察的 app 生命周期 |
||||
*/ |
||||
Flowable<Boolean> appAppState() { |
||||
return mAppStatus; |
||||
} |
||||
|
||||
void setCrashProcessor(Sword.CrashProcessor crashProcessor) { |
||||
if (mCrashHandler != null) { |
||||
mCrashHandler.setCrashProcessor(crashProcessor); |
||||
} |
||||
} |
||||
|
||||
Application getApplication() { |
||||
return mApplication; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,76 @@ |
||||
package com.android.base.app |
||||
|
||||
import android.app.Application |
||||
import android.content.Context |
||||
import android.content.IntentFilter |
||||
import android.content.res.Configuration |
||||
import android.net.ConnectivityManager |
||||
import com.android.base.app.utils.CrashHandler |
||||
import com.android.base.receiver.NetStateReceiver |
||||
import com.android.base.utils.BaseUtils |
||||
import com.blankj.utilcode.util.AppUtils |
||||
import com.blankj.utilcode.util.Utils.OnAppStatusChangedListener |
||||
import io.reactivex.processors.BehaviorProcessor |
||||
import timber.log.Timber |
||||
import java.util.concurrent.atomic.AtomicBoolean |
||||
|
||||
/** |
||||
* @author Ztiany |
||||
* Email: ztiany3@gmail.com |
||||
* Date : 2018-10-12 18:19 |
||||
*/ |
||||
class ApplicationDelegate internal constructor() { |
||||
|
||||
lateinit var application: Application |
||||
private lateinit var crashHandler: CrashHandler |
||||
|
||||
/** 获取可观察的 app 生命周期 */ |
||||
val appStatus: BehaviorProcessor<Boolean> = BehaviorProcessor.create() |
||||
|
||||
private val onCreateCalled = AtomicBoolean(false) |
||||
private val onAttachBaseCalled = AtomicBoolean(false) |
||||
|
||||
fun attachBaseContext(base: Context) { |
||||
check(onAttachBaseCalled.compareAndSet(false, true)) { "Can only be called once" } |
||||
} |
||||
|
||||
fun onCreate(application: Application) { |
||||
check(onCreateCalled.compareAndSet(false, true)) { "Can only be called once" } |
||||
this.application = application |
||||
//工具类初始化 |
||||
BaseUtils.init(application) |
||||
//异常日志记录 |
||||
crashHandler = CrashHandler.register(application) |
||||
//网络状态 |
||||
application.registerReceiver(NetStateReceiver(), IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)) |
||||
//App前台后台 |
||||
listenActivityLifecycleCallbacks() |
||||
} |
||||
|
||||
fun onTerminate() {} |
||||
|
||||
fun onConfigurationChanged(newConfig: Configuration) {} |
||||
|
||||
fun onTrimMemory(level: Int) {} |
||||
|
||||
fun onLowMemory() {} |
||||
|
||||
private fun listenActivityLifecycleCallbacks() { |
||||
AppUtils.registerAppStatusChangedListener(this, object : OnAppStatusChangedListener { |
||||
override fun onForeground() { |
||||
Timber.d("app进入前台") |
||||
appStatus.onNext(true) |
||||
} |
||||
|
||||
override fun onBackground() { |
||||
Timber.d("app进入后台") |
||||
appStatus.onNext(false) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
internal fun setCrashProcessor(crashProcessor: CrashProcessor) { |
||||
crashHandler.setCrashProcessor(crashProcessor) |
||||
} |
||||
|
||||
} |
@ -1,46 +0,0 @@ |
||||
package com.android.base.app; |
||||
|
||||
import android.app.Application; |
||||
import android.content.Context; |
||||
import android.content.res.Configuration; |
||||
|
||||
|
||||
public class BaseAppContext extends Application { |
||||
|
||||
@Override |
||||
protected void attachBaseContext(Context base) { |
||||
super.attachBaseContext(base); |
||||
Sword.get().getApplicationDelegate().attachBaseContext(base); |
||||
} |
||||
|
||||
@Override |
||||
public void onCreate() { |
||||
super.onCreate(); |
||||
Sword.get().getApplicationDelegate().onCreate(this); |
||||
} |
||||
|
||||
@Override |
||||
public void onLowMemory() { |
||||
super.onLowMemory(); |
||||
Sword.get().getApplicationDelegate().onLowMemory(); |
||||
} |
||||
|
||||
@Override |
||||
public void onTrimMemory(int level) { |
||||
super.onTrimMemory(level); |
||||
Sword.get().getApplicationDelegate().onTrimMemory(level); |
||||
} |
||||
|
||||
@Override |
||||
public void onConfigurationChanged(Configuration newConfig) { |
||||
super.onConfigurationChanged(newConfig); |
||||
Sword.get().getApplicationDelegate().onConfigurationChanged(newConfig); |
||||
} |
||||
|
||||
@Override |
||||
public void onTerminate() { |
||||
super.onTerminate(); |
||||
Sword.get().getApplicationDelegate().onTerminate(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,55 @@ |
||||
package com.android.base.app |
||||
|
||||
import android.app.Application |
||||
import android.content.Context |
||||
import android.content.res.Configuration |
||||
import dagger.android.DispatchingAndroidInjector |
||||
import dagger.android.HasAndroidInjector |
||||
import javax.inject.Inject |
||||
|
||||
open class BaseAppContext : Application() { |
||||
|
||||
override fun attachBaseContext(base: Context) { |
||||
super.attachBaseContext(base) |
||||
Sword.applicationDelegate.attachBaseContext(base) |
||||
} |
||||
|
||||
override fun onCreate() { |
||||
super.onCreate() |
||||
Sword.applicationDelegate.onCreate(this) |
||||
} |
||||
|
||||
override fun onLowMemory() { |
||||
super.onLowMemory() |
||||
Sword.applicationDelegate.onLowMemory() |
||||
} |
||||
|
||||
override fun onTrimMemory(level: Int) { |
||||
super.onTrimMemory(level) |
||||
Sword.applicationDelegate.onTrimMemory(level) |
||||
} |
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) { |
||||
super.onConfigurationChanged(newConfig) |
||||
Sword.applicationDelegate.onConfigurationChanged(newConfig) |
||||
} |
||||
|
||||
override fun onTerminate() { |
||||
super.onTerminate() |
||||
Sword.applicationDelegate.onTerminate() |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
*@author Ztiany |
||||
* Email: ztiany3@gmail.com |
||||
* Date : 2019-10-12 10:59 |
||||
*/ |
||||
open class InjectorBaseAppContext : BaseAppContext(), HasAndroidInjector { |
||||
|
||||
@Inject lateinit var androidInjector: DispatchingAndroidInjector<Any> |
||||
|
||||
override fun androidInjector() = androidInjector |
||||
|
||||
} |
@ -1,113 +0,0 @@ |
||||
package com.android.base.app; |
||||
|
||||
import android.annotation.SuppressLint; |
||||
import android.app.Application; |
||||
import android.content.Context; |
||||
import android.os.Build; |
||||
import android.os.Process; |
||||
|
||||
import com.blankj.utilcode.util.AppUtils; |
||||
|
||||
import java.io.File; |
||||
import java.io.PrintStream; |
||||
import java.text.DateFormat; |
||||
import java.text.SimpleDateFormat; |
||||
import java.util.Date; |
||||
|
||||
import timber.log.Timber; |
||||
|
||||
/** |
||||
* 全局异常处理 |
||||
*/ |
||||
final class CrashHandler implements Thread.UncaughtExceptionHandler { |
||||
|
||||
private Context mContext; |
||||
private Sword.CrashProcessor mCrashProcessor; |
||||
|
||||
public static CrashHandler register(Application application) { |
||||
CrashHandler crashHandler = new CrashHandler(application); |
||||
Thread.setDefaultUncaughtExceptionHandler(crashHandler); |
||||
return crashHandler; |
||||
} |
||||
|
||||
void setCrashProcessor(Sword.CrashProcessor crashProcessor) { |
||||
mCrashProcessor = crashProcessor; |
||||
} |
||||
|
||||
private CrashHandler(Context context) { |
||||
this.mContext = context; |
||||
} |
||||
|
||||
@Override |
||||
public void uncaughtException(Thread thread, Throwable ex) { |
||||
|
||||
if (mCrashProcessor != null) { |
||||
mCrashProcessor.uncaughtException(thread, ex); |
||||
} else { |
||||
// 收集异常信息,写入到sd卡
|
||||
restoreCrash(thread, ex); |
||||
//退出
|
||||
killProcess(); |
||||
} |
||||
} |
||||
|
||||
private void restoreCrash(@SuppressWarnings("unused") Thread thread, Throwable ex) { |
||||
ex.printStackTrace(System.err); |
||||
// 收集异常信息,写入到sd卡
|
||||
File dir = new File(mContext.getExternalFilesDir(null) + File.separator + "crash"); |
||||
|
||||
if (!dir.exists()) { |
||||
boolean mkdirs = dir.mkdirs(); |
||||
if (!mkdirs) { |
||||
Timber.e("CrashHandler create dir fail"); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
try { |
||||
@SuppressLint("SimpleDateFormat") |
||||
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
||||
String name = dateFormat.format(new Date(System.currentTimeMillis())) + ".log"; |
||||
File fileName = new File(dir, name); |
||||
if (!fileName.exists()) { |
||||
@SuppressWarnings("unused") |
||||
boolean newFile = fileName.createNewFile(); |
||||
} |
||||
|
||||
PrintStream err = new PrintStream(fileName); |
||||
|
||||
err.println("--------------------------------AppInfo--------------------------------"); |
||||
err.println("AndroidVersion: " + AppUtils.getAppVersionName()); |
||||
err.println(); |
||||
err.println(); |
||||
err.println("--------------------------------SystemInfo:--------------------------------"); |
||||
err.println("Product: " + android.os.Build.PRODUCT); |
||||
err.println("CPU_ABI: " + android.os.Build.CPU_ABI); |
||||
err.println("TAGS: " + android.os.Build.TAGS); |
||||
err.println("VERSION_CODES.BASE:" + android.os.Build.VERSION_CODES.BASE); |
||||
err.println("MODEL: " + android.os.Build.MODEL); |
||||
err.println("SDK: " + Build.VERSION.SDK_INT); |
||||
err.println("VERSION.RELEASE: " + android.os.Build.VERSION.RELEASE); |
||||
err.println("DEVICE: " + android.os.Build.DEVICE); |
||||
err.println("DISPLAY: " + android.os.Build.DISPLAY); |
||||
err.println("BRAND: " + android.os.Build.BRAND); |
||||
err.println("BOARD: " + android.os.Build.BOARD); |
||||
err.println("FINGERPRINT: " + android.os.Build.FINGERPRINT); |
||||
err.println("ID: " + android.os.Build.ID); |
||||
err.println("MANUFACTURER: " + android.os.Build.MANUFACTURER); |
||||
err.println("USER: " + android.os.Build.USER); |
||||
err.println(); |
||||
err.println(); |
||||
err.println("--------------------------------CrashContent--------------------------------"); |
||||
ex.printStackTrace(err); |
||||
err.println(); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
private void killProcess() { |
||||
Process.killProcess(Process.myPid()); |
||||
} |
||||
|
||||
} |
@ -1,18 +0,0 @@ |
||||
package com.android.base.app |
||||
|
||||
import dagger.android.DispatchingAndroidInjector |
||||
import dagger.android.HasAndroidInjector |
||||
import javax.inject.Inject |
||||
|
||||
/** |
||||
*@author Ztiany |
||||
* Email: ztiany3@gmail.com |
||||
* Date : 2019-10-12 10:59 |
||||
*/ |
||||
open class InjectorBaseAppContext : BaseAppContext(), HasAndroidInjector { |
||||
|
||||
@Inject lateinit var androidInjector: DispatchingAndroidInjector<Any> |
||||
|
||||
override fun androidInjector() = androidInjector |
||||
|
||||
} |
@ -1,230 +0,0 @@ |
||||
package com.android.base.app; |
||||
|
||||
import android.app.Activity; |
||||
import android.app.Application; |
||||
import android.content.Context; |
||||
import android.os.Bundle; |
||||
|
||||
import com.android.base.app.dagger.Injectable; |
||||
import com.android.base.app.fragment.animator.FragmentAnimator; |
||||
import com.android.base.app.fragment.tools.FragmentConfig; |
||||
import com.android.base.app.ui.LoadingViewFactory; |
||||
import com.android.base.app.ui.PageNumber; |
||||
import com.android.base.app.ui.RefreshLoadViewFactory; |
||||
import com.android.base.app.ui.RefreshViewFactory; |
||||
import com.android.base.interfaces.ActivityLifecycleCallbacksAdapter; |
||||
import com.android.base.receiver.NetworkState; |
||||
import com.blankj.utilcode.util.ActivityUtils; |
||||
import com.blankj.utilcode.util.AppUtils; |
||||
|
||||
import androidx.annotation.NonNull; |
||||
import androidx.annotation.Nullable; |
||||
import androidx.annotation.UiThread; |
||||
import androidx.fragment.app.Fragment; |
||||
import androidx.fragment.app.FragmentActivity; |
||||
import androidx.fragment.app.FragmentManager; |
||||
import dagger.android.AndroidInjection; |
||||
import dagger.android.support.AndroidSupportInjection; |
||||
import io.reactivex.Flowable; |
||||
|
||||
/** |
||||
* useful tools for android development, just like a sword. |
||||
* |
||||
* @author Ztiany |
||||
* Email: ztiany3@gmail.com |
||||
* Date : 2018-04-16 17:12 |
||||
*/ |
||||
@SuppressWarnings("unused") |
||||
@UiThread |
||||
public final class Sword { |
||||
|
||||
private static final Sword ONLY_BASE = new Sword(); |
||||
|
||||
private Sword() { |
||||
mApplicationDelegate = new ApplicationDelegate(); |
||||
} |
||||
|
||||
public static Sword get() { |
||||
return ONLY_BASE; |
||||
} |
||||
|
||||
/** |
||||
* LoadingView |
||||
*/ |
||||
private LoadingViewFactory mLoadingViewFactory; |
||||
|
||||
/** |
||||
* Application lifecycle delegate |
||||
*/ |
||||
private ApplicationDelegate mApplicationDelegate; |
||||
|
||||
/** |
||||
* 错误类型检查 |
||||
*/ |
||||
private ErrorClassifier mErrorClassifier; |
||||
|
||||
/** |
||||
* dialog 最小展示时间 |
||||
*/ |
||||
private long minimumShowingDialogMills; |
||||
|
||||
/** |
||||
* 获取 Application 代理 |
||||
*/ |
||||
public ApplicationDelegate getApplicationDelegate() { |
||||
return mApplicationDelegate; |
||||
} |
||||
|
||||
public Sword registerLoadingFactory(LoadingViewFactory loadingViewFactory) { |
||||
if (mLoadingViewFactory != null) { |
||||
throw new UnsupportedOperationException("LoadingViewFactory had already set"); |
||||
} |
||||
mLoadingViewFactory = loadingViewFactory; |
||||
return this; |
||||
} |
||||
|
||||
public LoadingViewFactory getLoadingViewFactory() { |
||||
if (mLoadingViewFactory == null) { |
||||
throw new NullPointerException("you have not set the LoadingViewFactory by AndroidBase"); |
||||
} |
||||
return mLoadingViewFactory; |
||||
} |
||||
|
||||
public Flowable<NetworkState> networkState() { |
||||
return NetworkState.observableState(); |
||||
} |
||||
|
||||
|
||||
public interface CrashProcessor { |
||||
void uncaughtException(Thread thread, Throwable ex); |
||||
} |
||||
|
||||
public Sword setCrashProcessor(CrashProcessor crashProcessor) { |
||||
mApplicationDelegate.setCrashProcessor(crashProcessor); |
||||
return this; |
||||
} |
||||
|
||||
public Sword setDefaultPageStart(int pageStart) { |
||||
PageNumber.setDefaultPageStart(pageStart); |
||||
return this; |
||||
} |
||||
|
||||
public Sword setDefaultPageSize(int defaultPageSize) { |
||||
PageNumber.setDefaultPageSize(defaultPageSize); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 给 {@link com.android.base.app.fragment.tools.Fragments } 设置一个默认的容器 id,在使用 其相关方法而没有传入特定的容器 id 时,则使用默认的容器 id。 |
||||
* |
||||
* @param defaultContainerId 容器id |
||||
*/ |
||||
public Sword setDefaultFragmentContainerId(int defaultContainerId) { |
||||
FragmentConfig.setDefaultContainerId(defaultContainerId); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 设置 dialog 最小展示时间,建议不超过 1 秒 |
||||
*/ |
||||
public Sword setMinimumShowingDialogMills(long minimumShowingDialogMills) { |
||||
this.minimumShowingDialogMills = minimumShowingDialogMills; |
||||
return this; |
||||
} |
||||
|
||||
public Sword setDefaultFragmentAnimator(FragmentAnimator animator) { |
||||
FragmentConfig.setDefaultFragmentAnimator(animator); |
||||
return this; |
||||
} |
||||
|
||||
public Sword enableAutoInject() { |
||||
Application.ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { |
||||
|
||||
@Override |
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) { |
||||
if (activity instanceof Injectable) { |
||||
if (((Injectable) activity).enableInject()) { |
||||
AndroidInjection.inject(activity); |
||||
if (activity instanceof FragmentActivity) { |
||||
handedFragmentInject((FragmentActivity) activity); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void handedFragmentInject(FragmentActivity activity) { |
||||
activity.getSupportFragmentManager().registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() { |
||||
@Override |
||||
public void onFragmentAttached(@NonNull FragmentManager fm, @NonNull Fragment f, @NonNull Context context) { |
||||
if (f instanceof Injectable) { |
||||
if (((Injectable) f).enableInject()) { |
||||
AndroidSupportInjection.inject(f); |
||||
} |
||||
} |
||||
} |
||||
}, true); |
||||
} |
||||
}; |
||||
mApplicationDelegate.getApplication().registerActivityLifecycleCallbacks(activityLifecycleCallbacks); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 获取可观察的 app 生命周期 |
||||
*/ |
||||
public Flowable<Boolean> appState() { |
||||
return mApplicationDelegate.appAppState(); |
||||
} |
||||
|
||||
/** |
||||
* 获取当前resume的Activity |
||||
* |
||||
* @return activity |
||||
*/ |
||||
@Nullable |
||||
public Activity getTopActivity() { |
||||
return ActivityUtils.getTopActivity(); |
||||
} |
||||
|
||||
/** |
||||
* App是否在前台运行 |
||||
* |
||||
* @return true 表示App在前台运行 |
||||
*/ |
||||
public boolean isForeground() { |
||||
return AppUtils.isAppForeground(); |
||||
} |
||||
|
||||
public interface ErrorClassifier { |
||||
boolean isNetworkError(Throwable throwable); |
||||
|
||||
boolean isServerError(Throwable throwable); |
||||
} |
||||
|
||||
public Sword setErrorClassifier(ErrorClassifier errorClassifier) { |
||||
if (mErrorClassifier != null) { |
||||
throw new UnsupportedOperationException("ErrorClassifier had already set"); |
||||
} |
||||
mErrorClassifier = errorClassifier; |
||||
return this; |
||||
} |
||||
|
||||
public ErrorClassifier errorClassifier() { |
||||
return mErrorClassifier; |
||||
} |
||||
|
||||
public Sword registerRefreshLoadViewFactory(RefreshLoadViewFactory.Factory factory) { |
||||
RefreshLoadViewFactory.registerFactory(factory); |
||||
return this; |
||||
} |
||||
|
||||
public Sword registerRefreshViewFactory(RefreshViewFactory.Factory factory) { |
||||
RefreshViewFactory.registerFactory(factory); |
||||
return this; |
||||
} |
||||
|
||||
public long minimumShowingDialogMills() { |
||||
return minimumShowingDialogMills; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,138 @@ |
||||
package com.android.base.app |
||||
|
||||
import android.app.Activity |
||||
import android.app.Application.ActivityLifecycleCallbacks |
||||
import android.content.Context |
||||
import android.os.Bundle |
||||
import androidx.fragment.app.Fragment |
||||
import androidx.fragment.app.FragmentActivity |
||||
import androidx.fragment.app.FragmentManager |
||||
import androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks |
||||
import com.android.base.app.dagger.Injectable |
||||
import com.android.base.app.fragment.animator.FragmentAnimator |
||||
import com.android.base.app.fragment.tools.FragmentConfig |
||||
import com.android.base.app.ui.LoadingView |
||||
import com.android.base.app.ui.PageNumber |
||||
import com.android.base.app.ui.RefreshLoadViewFactory |
||||
import com.android.base.app.ui.RefreshLoadViewFactory.Factory |
||||
import com.android.base.app.ui.RefreshViewFactory |
||||
import com.android.base.interfaces.ActivityLifecycleCallbacksAdapter |
||||
import com.android.base.receiver.NetworkState |
||||
import com.blankj.utilcode.util.ActivityUtils |
||||
import com.blankj.utilcode.util.AppUtils |
||||
import dagger.android.AndroidInjection |
||||
import dagger.android.support.AndroidSupportInjection |
||||
import io.reactivex.Flowable |
||||
|
||||
/** |
||||
* useful tools for android development, just like a sword. |
||||
* |
||||
* @author Ztiany |
||||
* Email: ztiany3@gmail.com |
||||
* Date : 2018-04-16 17:12 |
||||
*/ |
||||
object Sword { |
||||
|
||||
/** Application lifecycle delegate */ |
||||
val applicationDelegate = ApplicationDelegate() |
||||
|
||||
/** 错误类型分类器 */ |
||||
var errorClassifier: ErrorClassifier? = null |
||||
|
||||
/** dialog 最小展示时间 */ |
||||
var minimumShowingDialogMills: Long = 0 |
||||
|
||||
/** 用于创建 LoadingView*/ |
||||
var loadingViewFactory: ((Context) -> LoadingView)? = null |
||||
|
||||
/**网络状态监听器*/ |
||||
fun networkState(): Flowable<NetworkState> = NetworkState.observableState() |
||||
|
||||
fun setCrashProcessor(crashProcessor: CrashProcessor): Sword { |
||||
applicationDelegate.setCrashProcessor(crashProcessor) |
||||
return this |
||||
} |
||||
|
||||
fun setDefaultPageStart(pageStart: Int): Sword { |
||||
PageNumber.setDefaultPageStart(pageStart) |
||||
return this |
||||
} |
||||
|
||||
fun setDefaultPageSize(defaultPageSize: Int): Sword { |
||||
PageNumber.setDefaultPageSize(defaultPageSize) |
||||
return this |
||||
} |
||||
|
||||
/** 设置一个默认的布局 id,在使用 Fragments 中相关方法时,如果没有传入特定的容器 id 时,则使用设置的默认布局 id。 */ |
||||
fun setDefaultFragmentContainerId(defaultContainerId: Int): Sword { |
||||
FragmentConfig.setDefaultContainerId(defaultContainerId) |
||||
return this |
||||
} |
||||
|
||||
/**设置默认的 Fragment 转场动画*/ |
||||
fun setDefaultFragmentAnimator(animator: FragmentAnimator?): Sword { |
||||
FragmentConfig.setDefaultFragmentAnimator(animator) |
||||
return this |
||||
} |
||||
|
||||
fun enableAutoInject(): Sword { |
||||
val activityLifecycleCallbacks: ActivityLifecycleCallbacks = object : ActivityLifecycleCallbacksAdapter { |
||||
override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) { |
||||
if (activity is Injectable) { |
||||
if ((activity as Injectable).enableInject()) { |
||||
AndroidInjection.inject(activity) |
||||
if (activity is FragmentActivity) { |
||||
handedFragmentInject(activity as FragmentActivity) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private fun handedFragmentInject(activity: FragmentActivity) { |
||||
activity.supportFragmentManager.registerFragmentLifecycleCallbacks(object : FragmentLifecycleCallbacks() { |
||||
override fun onFragmentAttached(fm: FragmentManager, f: Fragment, context: Context) { |
||||
if (f is Injectable) { |
||||
if ((f as Injectable).enableInject()) { |
||||
AndroidSupportInjection.inject(f) |
||||
} |
||||
} |
||||
} |
||||
}, true) |
||||
} |
||||
} |
||||
applicationDelegate.application.registerActivityLifecycleCallbacks(activityLifecycleCallbacks) |
||||
return this |
||||
} |
||||
|
||||
/** 获取可观察的 app 生命周期 */ |
||||
val appState: Flowable<Boolean> |
||||
get() = applicationDelegate.appStatus |
||||
|
||||
/** 获取当前 resume 的 Activity */ |
||||
val topActivity: Activity? |
||||
get() = ActivityUtils.getTopActivity() |
||||
|
||||
/** App是否在前台运行 */ |
||||
val isForeground: Boolean |
||||
get() = AppUtils.isAppForeground() |
||||
|
||||
fun registerRefreshLoadViewFactory(factory: Factory): Sword { |
||||
RefreshLoadViewFactory.registerFactory(factory) |
||||
return this |
||||
} |
||||
|
||||
fun registerRefreshViewFactory(factory: RefreshViewFactory.Factory): Sword { |
||||
RefreshViewFactory.registerFactory(factory) |
||||
return this |
||||
} |
||||
|
||||
} |
||||
|
||||
interface CrashProcessor { |
||||
fun uncaughtException(thread: Thread, ex: Throwable) |
||||
} |
||||
|
||||
interface ErrorClassifier { |
||||
fun isNetworkError(throwable: Throwable): Boolean |
||||
fun isServerError(throwable: Throwable): Boolean |
||||
} |
@ -1,9 +0,0 @@ |
||||
package com.android.base.app.ui |
||||
|
||||
import android.content.Context |
||||
|
||||
interface LoadingViewFactory { |
||||
|
||||
fun createLoadingDelegate(context: Context): LoadingView |
||||
|
||||
} |
@ -0,0 +1,109 @@ |
||||
package com.android.base.app.utils |
||||
|
||||
import android.annotation.SuppressLint |
||||
import android.app.Application |
||||
import android.content.Context |
||||
import android.os.Build |
||||
import android.os.Build.VERSION |
||||
import android.os.Build.VERSION_CODES |
||||
import android.os.Process |
||||
import com.android.base.app.CrashProcessor |
||||
import com.blankj.utilcode.util.AppUtils |
||||
import timber.log.Timber |
||||
import java.io.File |
||||
import java.io.PrintStream |
||||
import java.lang.Thread.UncaughtExceptionHandler |
||||
import java.text.DateFormat |
||||
import java.text.SimpleDateFormat |
||||
import java.util.* |
||||
|
||||
/** |
||||
* 全局异常处理 |
||||
*/ |
||||
internal class CrashHandler private constructor( |
||||
private val context: Context |
||||
) : UncaughtExceptionHandler { |
||||
|
||||
private var _crashProcessor: CrashProcessor? = null |
||||
|
||||
fun setCrashProcessor(crashProcessor: CrashProcessor) { |
||||
_crashProcessor = crashProcessor |
||||
} |
||||
|
||||
override fun uncaughtException(thread: Thread, ex: Throwable) { |
||||
val crashProcessor = _crashProcessor |
||||
if (crashProcessor != null) { |
||||
crashProcessor.uncaughtException(thread, ex) |
||||
} else { |
||||
// 收集异常信息,写入到sd卡 |
||||
restoreCrash(thread, ex) |
||||
//退出 |
||||
killProcess() |
||||
} |
||||
} |
||||
|
||||
private fun restoreCrash(thread: Thread, ex: Throwable) { |
||||
ex.printStackTrace(System.err) |
||||
// 收集异常信息,写入到sd卡 |
||||
val externalFilesDir = context.getExternalFilesDir(null) ?: return |
||||
val dir = File(externalFilesDir.toString() + File.separator + "crash") |
||||
|
||||
if (!dir.exists()) { |
||||
val mkdirs = dir.mkdirs() |
||||
if (!mkdirs) { |
||||
Timber.e("CrashHandler create dir fail") |
||||
return |
||||
} |
||||
} |
||||
|
||||
try { |
||||
@SuppressLint("SimpleDateFormat") val dateFormat: DateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") |
||||
val name = dateFormat.format(Date(System.currentTimeMillis())) + ".log" |
||||
val targetFile = File(dir, name) |
||||
if (!targetFile.exists()) { |
||||
targetFile.createNewFile() |
||||
} |
||||
val err = PrintStream(targetFile) |
||||
err.println("--------------------------------AppInfo--------------------------------") |
||||
err.println("AndroidVersion: " + AppUtils.getAppVersionName()) |
||||
err.println() |
||||
err.println() |
||||
err.println("--------------------------------SystemInfo:--------------------------------") |
||||
err.println("Product: " + Build.PRODUCT) |
||||
err.println("CPU_ABI: " + Build.CPU_ABI) |
||||
err.println("TAGS: " + Build.TAGS) |
||||
err.println("VERSION_CODES.BASE:" + VERSION_CODES.BASE) |
||||
err.println("MODEL: " + Build.MODEL) |
||||
err.println("SDK: " + VERSION.SDK_INT) |
||||
err.println("VERSION.RELEASE: " + VERSION.RELEASE) |
||||
err.println("DEVICE: " + Build.DEVICE) |
||||
err.println("DISPLAY: " + Build.DISPLAY) |
||||
err.println("BRAND: " + Build.BRAND) |
||||
err.println("BOARD: " + Build.BOARD) |
||||
err.println("FINGERPRINT: " + Build.FINGERPRINT) |
||||
err.println("ID: " + Build.ID) |
||||
err.println("MANUFACTURER: " + Build.MANUFACTURER) |
||||
err.println("USER: " + Build.USER) |
||||
err.println() |
||||
err.println() |
||||
err.println("--------------------------------CrashContent--------------------------------") |
||||
ex.printStackTrace(err) |
||||
err.println() |
||||
} catch (e: Exception) { |
||||
e.printStackTrace() |
||||
} |
||||
} |
||||
|
||||
private fun killProcess() { |
||||
Process.killProcess(Process.myPid()) |
||||
} |
||||
|
||||
companion object { |
||||
fun register(application: Application): CrashHandler { |
||||
val crashHandler = CrashHandler(application) |
||||
Thread.setDefaultUncaughtExceptionHandler(crashHandler) |
||||
return crashHandler |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,109 @@ |
||||
package com.android.base.rx.autodispose |
||||
|
||||
import androidx.lifecycle.LiveData |
||||
import androidx.lifecycle.MutableLiveData |
||||
import com.android.base.data.State |
||||
import com.github.dmstocking.optional.java.util.Optional |
||||
import com.uber.autodispose.* |
||||
|
||||
fun <T> ObservableSubscribeProxy<T>.toResourceLiveData(): LiveData<State<T>> { |
||||
val mutableLiveData = MutableLiveData<State<T>>() |
||||
mutableLiveData.value = State.loading() |
||||
subscribe( |
||||
{ |
||||
mutableLiveData.postValue(State.success(it)) |
||||
}, |
||||
{ |
||||
mutableLiveData.postValue(State.error(it)) |
||||
} |
||||
) |
||||
return mutableLiveData |
||||
} |
||||
|
||||
fun <T> ObservableSubscribeProxy<Optional<T>>.optionalToResourceLiveData(): LiveData<State<T>> { |
||||
val mutableLiveData = MutableLiveData<State<T>>() |
||||
mutableLiveData.value = State.loading() |
||||
subscribe( |
||||
{ |
||||
mutableLiveData.postValue(State.success(it.orElse(null))) |
||||
}, |
||||
{ |
||||
mutableLiveData.postValue(State.error(it)) |
||||
} |
||||
) |
||||
return mutableLiveData |
||||
} |
||||
|
||||
fun <T> FlowableSubscribeProxy<T>.toResourceLiveData(): LiveData<State<T>> { |
||||
val mutableLiveData = MutableLiveData<State<T>>() |
||||
mutableLiveData.value = State.loading() |
||||
subscribe( |
||||
{ |
||||
mutableLiveData.postValue(State.success(it)) |
||||
}, |
||||
{ |
||||
mutableLiveData.postValue(State.error(it)) |
||||
} |
||||
) |
||||
return mutableLiveData |
||||
} |
||||
|
||||
fun <T> FlowableSubscribeProxy<Optional<T>>.optionalToResourceLiveData(): LiveData<State<T>> { |
||||
val mutableLiveData = MutableLiveData<State<T>>() |
||||
mutableLiveData.value = State.loading() |
||||
subscribe( |
||||
{ |
||||
mutableLiveData.postValue(State.success(it.orElse(null))) |
||||
}, |
||||
{ |
||||
mutableLiveData.postValue(State.error(it)) |
||||
} |
||||
) |
||||
return mutableLiveData |
||||
} |
||||
|
||||
fun CompletableSubscribeProxy.toResourceLiveData(): LiveData<State<Any>> { |
||||
val mutableLiveData = MutableLiveData<State<Any>>() |
||||
mutableLiveData.value = State.loading() |
||||
subscribe( |
||||
{ |
||||
mutableLiveData.postValue(State.success()) |
||||
}, |
||||
{ |
||||
mutableLiveData.postValue(State.error(it)) |
||||
} |
||||
) |
||||
return mutableLiveData |
||||
} |
||||
|
||||
fun <T> ObservableSubscribeProxy<T>.toLiveData(): LiveData<T> { |
||||
val liveData = MutableLiveData<T>() |
||||
this.subscribeIgnoreError { |
||||
liveData.postValue(it) |
||||
} |
||||
return liveData |
||||
} |
||||
|
||||
fun <T> FlowableSubscribeProxy<T>.toLiveData(): LiveData<T> { |
||||
val liveData = MutableLiveData<T>() |
||||
this.subscribeIgnoreError { |
||||
liveData.postValue(it) |
||||
} |
||||
return liveData |
||||
} |
||||
|
||||
fun <T> SingleSubscribeProxy<T>.toLiveData(): LiveData<T> { |
||||
val liveData = MutableLiveData<T>() |
||||
this.subscribeIgnoreError { |
||||
liveData.postValue(it) |
||||
} |
||||
return liveData |
||||
} |
||||
|
||||
fun <T> MaybeSubscribeProxy<T>.toLiveData(): LiveData<T> { |
||||
val liveData = MutableLiveData<T>() |
||||
this.subscribeIgnoreError { |
||||
liveData.postValue(it) |
||||
} |
||||
return liveData |
||||
} |
@ -1,21 +0,0 @@ |
||||
package com.android.base.rx; |
||||
|
||||
import android.app.Application; |
||||
import android.content.Context; |
||||
|
||||
import com.android.base.app.Sword; |
||||
|
||||
/** |
||||
* @author Ztiany |
||||
* Email: ztiany3@gmail.com |
||||
* Date : 2018-11-28 17:50 |
||||
*/ |
||||
public class TestApplication extends Application { |
||||
|
||||
@Override |
||||
protected void attachBaseContext(Context base) { |
||||
super.attachBaseContext(base); |
||||
Sword.get().getApplicationDelegate().attachBaseContext(base); |
||||
} |
||||
|
||||
} |
@ -1,29 +0,0 @@ |
||||
package com.android.base.rx; |
||||
|
||||
import android.os.Bundle; |
||||
import android.support.annotation.Nullable; |
||||
|
||||
import com.android.base.app.activity.BaseActivity; |
||||
|
||||
/** |
||||
* @author Ztiany |
||||
* Email: 1169654504@qq.com |
||||
* Date : 2017-06-22 09:37 |
||||
*/ |
||||
public class TestBaseActivity extends BaseActivity { |
||||
|
||||
@Override |
||||
protected void initialize(@Nullable Bundle savedInstanceState) { |
||||
super.initialize(savedInstanceState); |
||||
} |
||||
|
||||
@Override |
||||
protected Object layout() { |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
protected void setupView(@Nullable Bundle savedInstanceState) { |
||||
} |
||||
|
||||
} |
@ -1,28 +0,0 @@ |
||||
package com.android.base.rx; |
||||
|
||||
import android.os.Bundle; |
||||
import android.support.annotation.NonNull; |
||||
import android.support.annotation.Nullable; |
||||
import android.view.View; |
||||
|
||||
import com.android.base.adapter.recycler.MultiTypeAdapter; |
||||
import com.android.base.app.fragment.BaseListFragment; |
||||
import com.android.base.app.ui.AutoPageNumber; |
||||
|
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
/** |
||||
* @author Ztiany |
||||
* Email: ztiany3@gmail.com |
||||
* Date : 2018-04-27 15:05 |
||||
*/ |
||||
public class TestListFragment extends BaseListFragment<String> { |
||||
|
||||
@Override |
||||
protected void onViewPrepared(@NotNull @NonNull View view, @Nullable Bundle savedInstanceState) { |
||||
super.onViewPrepared(view, savedInstanceState); |
||||
MultiTypeAdapter recyclerAdapter = new MultiTypeAdapter(getContext()); |
||||
setupLoadMore(recyclerAdapter, new AutoPageNumber(this,recyclerAdapter)); |
||||
} |
||||
|
||||
} |
@ -1,46 +0,0 @@ |
||||
package com.android.base.rx; |
||||
|
||||
import android.content.Context; |
||||
import android.os.Bundle; |
||||
import android.support.annotation.NonNull; |
||||
import android.support.annotation.Nullable; |
||||
import android.view.View; |
||||
|
||||
import com.android.base.app.fragment.BaseStateFragment; |
||||
|
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
/** |
||||
* @author Ztiany |
||||
* Email: 1169654504@qq.com |
||||
* Date : 2017-06-22 09:38 |
||||
*/ |
||||
public class TestStateFragment extends BaseStateFragment { |
||||
|
||||
@Override |
||||
public void onAttach(@NotNull Context context) { |
||||
super.onAttach(context); |
||||
} |
||||
|
||||
@Override |
||||
public void onCreate(Bundle savedInstanceState) { |
||||
super.onCreate(savedInstanceState); |
||||
} |
||||
|
||||
@Override |
||||
protected void onViewPrepared(@NotNull @NonNull View view, @Nullable Bundle savedInstanceState) { |
||||
super.onViewPrepared(view, savedInstanceState); |
||||
getStateLayoutConfig(); |
||||
} |
||||
|
||||
@Override |
||||
protected void onRefresh() { |
||||
super.onRefresh(); |
||||
} |
||||
|
||||
@Override |
||||
public void refreshCompleted() { |
||||
super.refreshCompleted(); |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue