Android开启定时任务通用的封装 先看接入步骤: Step 1. Add the JitPack repository to your build file Add it in your root build.gradle at the end of repositories:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Step 2. Add the dependency
dependencies {
implementation 'com.github.linpeixu:DialogManager:1.0.1'
//或者implementation 'com.gitlab.linpeixu:dialogmanager:1.0.1'
}
背景:
之前我写过一篇Android优雅实现弹窗优先级管理来介绍如何在android日常开发中管理多个弹窗的优先级,这个解决方案也被其他开发者所采用,在这里也感谢大家的认可。
我们常说世界上唯一不变的就是变化,开发的业务场景也在不停地变化,所以之前的弹窗管理解决方案在某些业务上则有场景的限制了,比如Android优雅实现弹窗优先级管理更偏向于将各个自定义的Dialog(api上的Dialog)纳入管理,且每个Dialog需要实现我们约定好的“Dialog Interface”,这样的话某种意义上已经对自定义Dialog构成代码的入侵了,再比如我们的DialogActivity也是可以实现Dialog的效果的,业务上的需要有时候需要我们使用DialogActivity来完成弹窗的业务,这个时候其实我们所说的“弹窗”的概念已经需要包括Dialog和Activity等不同组件了,由此我在思考,能不能有一种通用上的“弹窗”概念,而不仅仅局限于日常的自定义的Dialog,且不要对我们需要被管理的“弹窗”有任何代码上的入侵。
想法是美好的,开始行动,最终我重新封装了DialogManager,先看使用实例。
这里我们为了说明“弹窗”只是一个概念,需要纳入优先级管理的class不限制具体的类型,我们使用这几种不同“概念”的弹窗来看看怎么应用,例如自定义MessageDialog(继承Dialog)、AlertDialog、以及String类型的“弹窗”。
首先把对应的“概念”弹窗纳入管理只需要调用
DialogManager.getInstance().add(Config<Type> config);
在需要展示弹窗的时候只需要调用
DialogManager.getInstance().show();
接下来我们看看上边提到的三种“概念”弹窗是怎么纳入优先级管理的
MessageDialog“概念”弹窗的配置
DialogManager.Config<MessageDialog> config1= new DialogManager
.Config
.Builder<MessageDialog>()
.as(new DialogManager.BaseType<MessageDialog>(new MessageDialog(context, "第一个弹窗")) {
@Override
void init(DialogManager.Config<MessageDialog> config) {
config.getBaseType().getType().setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
/*需要记住,在合适的地方一定要调用方法config.dispatch()通知DialogManager
*接着展示下个“概念”弹窗
*/
config.dispatch();
}
});
}
@Override
void show(DialogManager.Config<MessageDialog> config) {
/*这是DialogManager执行到展示“概念”弹窗的逻辑,需外部实现相应的show逻辑*/
config.getBaseType().getType().show();
}
@Override
void dismiss(DialogManager.Config<MessageDialog> config) {
/*这是DialogManager执行到隐藏“概念”弹窗的逻辑,需外部实现相应的dismiss逻辑*/
config.getBaseType().getType().dismiss();
}
})
.priority(1)
.onShowCheckListener(new DialogManager.OnShowCheckListener() {
@Override
public boolean isCanShow() {
/*这是告诉DialogManager此“概念”弹窗可否展示*/
/*如我们有些业务需要首页处于哪个Tab时才可展示此弹窗
*可以在这里自行实现判断条件
*/
return true;
}
})
.build();
String“概念”弹窗的配置
DialogManager.Config<String> config2 = new DialogManager
.Config
.Builder<String>()
.as(new DialogManager.BaseType<String>("这是个文字") {
@Override
void init(DialogManager.Config<String> config) {
}
@Override
void show(DialogManager.Config<String> config) {
DialogManager.getInstance().getILog().onPrintLog("DialogManager", "show->" + config.getBaseType().getType());
Toast.makeText(context, "String Type show->这是个文字", Toast.LENGTH_SHORT).show();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
/*需要记住,在合适的地方一定要调用方法config.dispatch()通知DialogManager
*接着展示下个“概念”弹窗
*/
config.dispatch();
}
}, 3000);
}
@Override
void dismiss(DialogManager.Config<String> config) {
DialogManager.getInstance().getILog().onPrintLog("DialogManager", "dismiss->" + config.getBaseType().getType());
}
})
.priority(2)
.onShowCheckListener(new DialogManager.OnShowCheckListener() {
@Override
public boolean isCanShow() {
return true;
}
})
.build();
AlertDialog“概念”弹窗的配置
AlertDialog alertDialog = new AlertDialog.Builder(context).create();
alertDialog.setMessage("第三个弹窗");
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "我知道了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
alertDialog.dismiss();
}
});
DialogManager.Config<AlertDialog> config3 = new DialogManager
.Config
.Builder<AlertDialog>()
.as(new DialogManager.BaseType<AlertDialog>(alertDialog) {
@Override
void init(DialogManager.Config<AlertDialog> config) {
config.getBaseType().getType().setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
config.dispatch();
}
});
}
@Override
void show(DialogManager.Config<AlertDialog> config) {
config.getBaseType().getType().show();
}
@Override
void dismiss(DialogManager.Config<AlertDialog> config) {
config.getBaseType().getType().dismiss();
}
})
.priority(3)
.onShowCheckListener(new DialogManager.OnShowCheckListener() {
@Override
public boolean isCanShow() {
return true;
}
})
.build();
将三种“概念”弹窗纳入管理
DialogManager.getInstance().add(config1);
DialogManager.getInstance().add(config2);
DialogManager.getInstance().add(config3);
在需要展示的地方只需要调用
DialogManager.getInstance().show();
这样我们做到了对“概念”弹窗无代码入侵,待添加的“概念”弹窗对应的Config只需要在合适的地方调用方法config.dispatch()通知DialogManager接着展示下个“概念”弹窗,实现show和dismiss逻辑,实现isCanshow逻辑即可,从代码的角度上来看逻辑更加清晰。
另外,可调用如下方法设置是否开启调试模式,调试模式带日志
DialogManager.getInstance().(boolean debug);
可调用如下方法代理调试模式下日志的输出,如
DialogManager.getInstance().setILog(new DialogManager.ILog() {
@Override
public void onPrintLog(String TAG, String log) {
LogUtil.d(TAG, log);
}
});
完整的DialogManager代码如下
import android.util.Log;
import java.util.UUID;
import java.util.Vector;
/**
* 作者:lpx on 2021/11/18 17:39
* Email : 1966353889@qq.com
* Describe:“概念”弹窗优先级管理(支持设置弹窗优先级)
* update on 2021/11/20 11:29
*/
public class DialogManager {
private final String TAG = "DialogManager";
/*由于instance = new Singleton(),这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情。
1.给 instance 分配内存
2.调用 Singleton 的构造函数来初始化成员变量
3.将instance对象指向分配的内存空间(执行完这步instance就为非null了)
但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。
我们只需要将 instance 变量声明成 volatile 就可以了。*/
private static volatile DialogManager mInstance;
/**
* 所有已添加的Type集合(线程安全的List,用法和ArrayList类似)
*/
private Vector<Config> mConfigs = new Vector<>();
/**
* 当前正在显示的Type
*/
private Config mCurrentConfig;
/**
* 日志输出类
*/
private ILog mILog;
/**
* 是否调试模式
*/
private boolean debug = BuildConfig.DEBUG;
private DialogManager() {
mILog = new ILog() {
@Override
public void onPrintLog(String TAG, String log) {
Log.d(TAG, log);
}
};
}
/**
* 打印log(内部调用)
*/
private void printLog(String log) {
if (debug && mILog != null) {
mILog.onPrintLog(TAG, log);
}
}
/**
* 尝试移除相同优先级的Config
*/
private <Type> void tryRemoveType(Config<Type> config) {
if (mConfigs != null && !mConfigs.isEmpty() && config != null) {
for (int i = 0, size = mConfigs.size(); i < size; i++) {
if (mConfigs.get(i).getPriority() == config.getPriority()) {
mConfigs.get(i).dismiss();
mConfigs.remove(mConfigs.get(i));
break;
}
}
}
}
/**
* 获取已添加的最大优先级的且满足显示条件的Type
*/
private <Type> Config<Type> getMaxPriorityCanShowType() {
if (mConfigs != null && !mConfigs.isEmpty()) {
int size = mConfigs.size();
int position = size - 1;
for (int i = size - 1; i >= 0; i--) {
if (mConfigs.get(i).isCanShow() && mConfigs.get(i).getPriority() > mConfigs.get(position).getPriority()) {
position = i;
}
}
return mConfigs.get(position).isCanShow() ? mConfigs.get(position) : null;
}
return null;
}
/**
* 移除对应的Type
*/
private <Type> void remove(Config<Type> config) {
if (mConfigs != null && !mConfigs.isEmpty() && config != null) {
int size = mConfigs.size();
boolean result = mConfigs.remove(config);
printLog("remove(object)\nresult:" + result + "\nsize:" + size + "->" + mConfigs.size());
} else {
printLog("remove(object)\nresult:does not perform\nsize:" + (mConfigs != null ? mConfigs.size() : "configs is null"));
}
}
/**
* 显示下个Type
*/
private void showNext() {
show();
}
/**
* 返回当前实例
*/
public static DialogManager getInstance() {
if (mInstance == null) {
synchronized (DialogManager.class) {
if (mInstance == null) {
mInstance = new DialogManager();
}
}
}
return mInstance;
}
/**
* 设置对应的log处理类
*/
public void setILog(ILog log) {
if (log != null) {
mILog = log;
}
}
public ILog getILog() {
return mILog;
}
/**
* 是否打开调试模式
*/
public void enableDebug(boolean debug) {
this.debug = debug;
}
/**
* 添加Type(支持添加相同优先级的Type)
*/
public <Type> void add(Config<Type> config) {
if (mConfigs != null) {
mConfigs.add(config);
}
}
/**
* 添加Type(支持添加相同优先级的Type)
*
* @param priorityReplace 是否替换存在的相同优先级的Type
*/
public <Type> void add(Config<Type> config, boolean priorityReplace) {
if (priorityReplace) {
tryRemoveType(config);
}
if (mConfigs != null) {
mConfigs.add(config);
}
}
/**
* 显示Type
*/
public <Type> void show() {
Config<Type> config = getMaxPriorityCanShowType();
if (config != null) {
if (mCurrentConfig != null) {
if (mCurrentConfig.getUuid().equals(config.getUuid())) {
printLog("show()->getMaxPriorityCanShowType()\nresult:success"
+ "\ntype:" + (config.getBaseType() != null && config.getBaseType().getType() != null ? config.getBaseType().getType().getClass().getSimpleName() : "unknown")
+ "\npriority:" + config.getPriority()
+ "\nextra:the same as current type uuid");
return;
}
mCurrentConfig.closeByManager();
mCurrentConfig.dismiss();
}
printLog("show()->getMaxPriorityCanShowType()\nresult:success"
+ "\ntype:" + (config.getBaseType() != null && config.getBaseType().getType() != null ? config.getBaseType().getType().getClass().getSimpleName() : "unknown")
+ "\npriority:" + config.getPriority());
mCurrentConfig = config;
config.show();
} else {
printLog("show()->getMaxPriorityCanShowType():\nresult:target not found");
}
}
/**
* 日志输出接口
*/
public interface ILog {
void onPrintLog(String TAG, String log);
}
/**
* Type配置类
*/
public static class Config<Type> {
/**
* 标识码
*/
private final String uuid;
/**
* Type包装类(泛型)
*/
private BaseType<Type> baseType;
/**
* 弹窗优先级(值越大优先级越高)
*/
private int priority;
/**
* 是否通过优先级机制关闭的Type(若为true则可能重新被打开)
*/
private boolean closeByManager;
private OnShowCheckListener onShowCheckListener;
private OnDismissCheckListener onDismissCheckListener;
private Config(Builder<Type> builder) {
uuid = UUID.randomUUID().toString();
this.baseType = builder.baseType;
this.priority = builder.priority;
this.onShowCheckListener = builder.onShowCheckListener;
this.onDismissCheckListener = builder.onDismissCheckListener;
if (this.baseType != null) {
this.baseType.init(this);
}
}
/**
* 当前Type是否可以show(不用考虑其它Type的情况)
*/
private boolean isCanShow() {
return (getBaseType() != null && getBaseType().getType() != null) && (onShowCheckListener == null || onShowCheckListener.isCanShow());
}
/**
* 当前Type是否可以dismiss(不用考虑其它Type的情况)
*/
private boolean isCanDismiss() {
return (getBaseType() != null && getBaseType().getType() != null) && (onDismissCheckListener == null || onDismissCheckListener.isCanDismiss());
}
/**
* 内部调用
*/
private void closeByManager() {
closeByManager = true;
}
/**
* 分发事件(Type dismiss后调用)
*/
protected void dispatch() {
/*被优先级机制暂时隐藏的弹窗不移除*/
if (!closeByManager) {
DialogManager.getInstance().remove(this);
}
DialogManager.getInstance().showNext();
}
public BaseType<Type> getBaseType() {
return baseType;
}
public int getPriority() {
return priority;
}
public String getUuid() {
return uuid;
}
/**
* 展示当前Type
*/
private void show() {
if (isCanShow()) {
/*有些Type是优先级机制暂时关闭的,重新show之后需要重置closeByManager,这样在主动dismiss当前的时候才会remove掉*/
closeByManager = false;
getBaseType().show(this);
}
}
/**
* 隐藏当前Type
*/
private void dismiss() {
if (isCanShow()) {
getBaseType().dismiss(this);
}
}
public static class Builder<Type> {
private BaseType<Type> baseType;
private int priority;
private OnShowCheckListener onShowCheckListener;
private OnDismissCheckListener onDismissCheckListener;
public Builder<Type> as(BaseType<Type> baseType) {
this.baseType = baseType;
return this;
}
public Builder<Type> priority(int priority) {
this.priority = priority;
return this;
}
public Builder<Type> onShowCheckListener(OnShowCheckListener onShowCheckListener) {
this.onShowCheckListener = onShowCheckListener;
return this;
}
public Builder<Type> onDismissCheckListener(OnDismissCheckListener onDismissCheckListener) {
this.onDismissCheckListener = onDismissCheckListener;
return this;
}
public Config<Type> build() {
return new Config<>(this);
}
}
}
/**
* Type包装类
*/
public abstract static class BaseType<Type> {
private Type type;
public BaseType(Type type) {
this.type = type;
}
public Type getType() {
return type;
}
abstract void init(Config<Type> config);
abstract void show(Config<Type> config);
abstract void dismiss(Config<Type> config);
}
/**
* 当前Type show条件检测(不用考虑其它Type的情况)-默认返回true,可外部实现更改判断条件
*/
public interface OnShowCheckListener {
boolean isCanShow();
}
/**
* 当前Type dismiss条件检测(不用考虑其它Type的情况)-默认返回true,可外部实现更改判断条件)
*/
public interface OnDismissCheckListener {
boolean isCanDismiss();
}
}
代码的注释挺详细的,就不做过多说明了,有问题的话可随时联系我或评论区留言。