Android 时间到了需要不同的界面都要显示抢红包
参考文章
Android 8.0以后使用后台Service服务JobIntentService的使用
Android O 行为变更适配方案
博文修改
2019-10-21 修改该博文
需求描述
公司需要做一个抢红包的功能,然后只要用户在这个APP内,不管你在APP的哪个界面,你都要弹出红包出来。
开始骚操作
我最开始的想法是这样的,我想创建红包activity当成一个dialog来使用,因为我看到也有很多人这样说。然后在主Activity里面开启一个service,当然不要忘记destory的生活关闭它。
遇到的问题
1、如果直接在service启动Activity是无法启动的,你需要这样写代码才可以启动
Intent intentv = new Intent(RedEnvelopesService.this, RedEnvelopesActivity.class);
intentv.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intentv);
<!--红包的activity singleTop一直启动相同的界面 excludeFromRecents 表示这个Activity不会显示在最近列表里面。-->
<activity android:name=".activity.shop.RedEnvelopesActivity"
android:screenOrientation="portrait"
android:theme="@style/Redenvelopes.NoactonBar"/>
2、启动后,发现activity启动后,不是透明的背景,然后我就做了下面的事情,将背景弄为透明
<style name="Redenvelopes.NoactonBar" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowFullscreen">true</item>
<item name="android:windowBackground">@color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
</style>
3、Service里面的倒计时
private Disposable disposable;
final int SECONDSEND = 30;
volatile int counDown = 0;
//开始倒计时
private void counDownTime() {
Observable.interval(1, TimeUnit.SECONDS)
.subscribeOn(Schedulers.newThread())
.filter(new Predicate<Long>() {
@Override
public boolean test(Long aLong) throws Exception {
counDown = counDown + 1;
if (counDown>=SECONDSEND){//只有满了30秒才会执行后面的操作
counDown = 0;
return true;
}
return false;
}
}).subscribeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Long>() {
@Override
public void onSubscribe(Disposable d) {
disposable = d;
}
@Override
public void onNext(Long aLong) {
//时间到了启动Activity
Intent intentv = new Intent(RedEnvelopesService.this, RedEnvelopesActivity.class);
intentv.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intentv);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
Log.i(TAG,"onComplete");
}
});
}
在service里面,我很开心的启动了红包activity,只要时间一到就开启红包activity,就在我开心之余,我按下了Home键,到了某个时间点,它又自动回到APP内(帮你吊起APP),打开红包activity,对于这么恶心的问题肯定是不行的,所以要放弃上面的步骤方法
新思路
我们继续秉承着在service开启倒计时的方案,我们到了某个时间点,就本地广播一次,我们可以再主activity里面监听广播,监听到后,我们拿当前最前面的一个activity来帮我们开启一个popuwindow。
1、首先需要在service开启广播,如果到了某个时间的点,就本地广播
Observable.interval(1, TimeUnit.SECONDS)
.subscribeOn(Schedulers.newThread())
.filter(new Predicate<Long>() {
@Override
public boolean test(Long aLong) throws Exception {
counDown = counDown + 1;
if (counDown>=SECONDSEND){//只有满了30秒才会执行后面的操作
counDown = 0;
return true;
}
return false;
}
}).subscribeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Long>() {
@Override
public void onSubscribe(Disposable d) {
disposable = d;
}
@Override
public void onNext(Long aLong) {
Log.i(TAG, "onNext");
//发送广播
Intent intent = new Intent(START_REDENVELOPES_ACTION);
LocalBroadcastManager.getInstance(RedEnvelopesService.this).sendBroadcast(intent);
}
@Override
public void onError(Throwable e) {
Log.i(TAG,e.getLocalizedMessage());
}
@Override
public void onComplete() {
}
...省略代码
2、创建一个popuwindow,获取最前面的一个Activity进行展示,有一点需要注意,Popuwindow里面的activity需要采用弱引用
...省略代码
private View drowView;
private PopupWindow popupWindow;
private WeakReference<SPBaseActivity> weakReference;
public RedEnvelopesPopuWindow(BaseActivity activity) {
weakReference = new WeakReference<BaseActivity >(activity);//注意这里
drowView = weakReference.get().getWindow().getDecorView();
}
/**
* @date :2019/9/18 0018
* @author : gaoxiaoxiong
* @description:销毁popuwindow
**/
public void onDestoryPopuWindow(){
activityWeakReference.clear();
redEnvelopesListenerWeakReference.clear();
this.onRedEnvelopesListener = redEnvelopesListenerWeakReference.get();
popupWindow.dismiss();
}
3、在MainActivity接收广播,注意需要在destory的时候移除广播监听
private RedEnvelopesPopuWindow popuWindow;//popuwindow
private Activity preActivity;//前一个Activity
@Override
public void initData() {
//注册广播监听
if (localBroadcastReceiver == null) {
localBroadcastReceiver = new LocalBroadcastReceiver();
localBroadcastReceiver.setOnClassNameListener(new LocalBroadcastReceiver.OnBroadCastNameListener() {
@Override
public void onBroadCastName(String name, Intent intent123) {
if (preActivity!=null){
//获取到当前的popuwindow,注意这里我们需要获取一个弱引用给currentActivity
Activity currentActivity = AppActivityManager.getInstance().currentActivity();
if (popuWindow == null) {
popuWindow = new RedEnvelopesPopuWindow(currentActivity);
}
preActivity = AppActivityManager.getInstance().currentActivity();
}
popuWindow.showPowuWindow();
}
});
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(START_REDENVELOPES_ACTION);
LocalBroadcastManager.getInstance(this).registerReceiver(localBroadcastReceiver, intentFilter);
}
}
特别注意:AppActivityManager.getInstance().currentActivity(),拿到的是一个弱引用
其它知识点小结
1、采用Observable.interval开启1秒循环,如果想终止的话,需要调用disposable.dispose()方法。如果没有终止,继续调用Observable.interval来开启倒计时,会开启2个线程,你会看到aLong里面会出现一个大的值,一个小的值,所以你要关闭一个
.filter(new Predicate<Long>() {
@Override
public boolean test(Long aLong) throws Exception {
counDown = counDown + 1;
if (counDown>=SECONDSEND){//只有满了30秒才会执行后面的操作
counDown = 0;
return true;
}
return false;
}
})
特别注意:
1、android 8.0过后,有对service做限制操作,我们不可以按下home按键,然后启动后台service,这样的启动方式会直接导致我们崩溃。如果我们想按下home按键,想启动service,我们需要调用 Context.startForegroundService()启动,并且在service里面调用startForeground()且通知栏有Notification显示该Service正在运行
2、如果采用JobService来执行任务,是有时间限制的,不管它是处于前台还是后台,因此 Job Scheduler 适用于短耗时的后台任务,不适用于连续的长时间的后台服务。