令人懊恼的alarmmanager setrepeating()

如果有人有比较好的关于alarmmanager用法的资料麻烦分享一下,网上的大多雷同,基本都是一个抄一个。

最近在用android studio做一个小东西,用到在后台定时任务,我的想法是每3小时请求一下网络数据,每天0点更新一下农历时间。并进行android各版本适配。由于是后台长期运行的服务,所以最佳方案就是用alarmmanager+handler。但在实践过程中发现各种问题,翻遍了网上的几乎所有帖子,都没有完全解决我的问题。

使用的代码:

alarmmanagerunits.java(alarmmanager的封装类)

myservices.java(服务)

我遇问题有以下几个:

1、4.4以上版本0点定时任务不启动。这个问题现在已解决,因为在4.4以上版本的android都是一次性任务,再加任务的时候由于代码的原因,pendingintent中的request code并没改变,所以只有一个最后的定时任务生效。代码中使用不同的requestcode就可以解决。我也不知道这是不是正统的解决办法,我看网上有人用回调的方法,但我不知道那是什么。

2、在4.4以下的版本中,使用setrepeating实现定时循环。这个也是最让我头痛的问题,在我找的代码里看起来实现很容易,实际上也是很容易就实现了每天定时循环任务和0点定时任务,同样代码实现了不同的功能,很好。但我把这些代码重新另外做一个例程使用的时候,0点任务无论如何也不执行。我把alarmmanagernuites.java和myservices.java代码完全拷贝过去,只是改了个UI,代码就不行了,上网逛了很久也没找到解决办法。

我现在知道的setrepeating()"可能"内在机制是这样的:

public void getUpAlarmManagerStartWork() {
        calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY,0);
        calendar.set(Calendar.MINUTE,0);
        calendar.set(Calendar.SECOND,0);
        
        am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(context, MyService.class);
        pendingIntent = PendingIntent.getService(context, 0, intent, 0);
        am.setRepeating(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(), TIME_INTERVAL, pendingIntent);

    }

定个时,再写个动作,再延迟执行就可以了。这里面又有几个问题很久才搞明白:1、设置了时间后,getTimeInMillis()获取的就是设定的时间,不设定时间,getTimeInMillis()就是当前系统时间。2、如果设置的启动时间在当前时间之前,setrepeating会马上执行,并按照时间间隔重复执行任务,并不会再在设定时间执行。这个也是最让人头痛的地方,我觉得这是setrepeating正常的工作逻辑,但我做的有一个例程却可以在启动app当时执行,再按时间间隔执行,但0点的定时任务也可以执行,让我非常不理解,到现在也没找到原因。但只有这一个例程是这样,其他的全都不行,0点任务不会执行。3、setrepeating在app启动当时执行后的后一次执行并不是按照设定好的时间间隔,一般比设定时间短。我在网上找到一个帖子是这样说的:比如设定的时间间隔是5分钟,第二次循环任务是执行时间加上5的整数倍的最小值。比如启动时间为10点8分,第二次启动时间为10点10分,再后面的时间间隔正常,分别为10点15分,10点20分。。。真是让人无语。4、在版本大于4.4的时候,精确的定时任务不能再用setrepeating,6.0以上是setExactAndAllowWhileIdle,4.4以上是setExact,但另一个问题又来了,这两个都是一次性任务,要定时循环就在services里面回调。而且如果两个函数的pendingintent都用的一个requestcode(网上很多代码都是用的0),也会导致0点的任务不会执行。必须用不同的requestcode才可以,其实就是设置了两个定时任务,都是0的话,只有最后一个任务会执行,前面的都会被清掉。

下面是我的一个例程,当前状况是,无论哪个版本android,0点任务都不会执行。大家看看要怎么改才行:

alarmmanagerunits.java

package com.example.alarmmanagertest;

import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import java.util.Calendar;

public class AlarmManagerUtils {

    private static final long TIME_INTERVAL = 61 * 1000;//闹钟执行任务的时间间隔
    private Context context;

    public static AlarmManager am;
    public static PendingIntent pendingIntent;
    private Calendar calendar;
    //
    private AlarmManagerUtils(Context aContext) {
        this.context = aContext;
    }

    //singleton
    private static AlarmManagerUtils instance = null;//instance 实例

    public static AlarmManagerUtils getInstance(Context aContext) {
        //getInstance跟new类似,在单例模式(保证一个类仅有一个实例,并提供一个访问它的全局访问点)的类中常见,
        // 用来生成唯一的实例,getInstance往往是static的。
        //详见https://www.csdn.net/tags/NtDakgwsNzAwMjYtYmxvZwO0O0OO0O0O.html
        if (instance == null) {
            synchronized (AlarmManagerUtils.class) {
                //synchronized用于多线程访问,被synchronized修饰的部分不能同时被执行,是代码同步的一种方式。
                if (instance == null) {
                    instance = new AlarmManagerUtils(aContext);
                }
            }
        }
        return instance;
    }

    public void createGetUpAlarmManager() {
        am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(context, MyService.class);
        //pendingIntent = PendingIntent.getService(context, 0, intent, 0);//每隔1小时启动一次服务
        //在android8.0以后的版本,pendingIntent实现方法与之前的不同,需要版本适配
        //Build.VERSION_CODES.O 为api level 26,即android8.0 见:build.class及avd manager

        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            pendingIntent = PendingIntent.getForegroundService(context, 0, intent, 0);
        }else{
            pendingIntent = PendingIntent.getService(context, 0, intent, 0);
        }
    }

    @SuppressLint("NewApi")//@SuppressLint忽略警告
    public void getUpAlarmManagerStartWork() {
        calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY,0);//HOUR_of_DAY of是小写,是12小时方法
        calendar.set(Calendar.MINUTE,10);
        calendar.set(Calendar.SECOND,50);


        //版本适配 System.currentTimeMillis()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {// 6.0及以上
            am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
                    calendar.getTimeInMillis(), pendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {// 4.4及以上
            am.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),pendingIntent);
            System.out.println("4.4版本以上启动成功");
        } else { //其实setExact和setExactAndAllowWhileIdle都是一次性闹钟,要想重复使用要重复注册。
            am.setRepeating(AlarmManager.RTC_WAKEUP,
                    calendar.getTimeInMillis(), TIME_INTERVAL, pendingIntent);
            System.out.println("4.4版本以下启动成功");
            //setRepeating 会先判断时间是否到设定时间,如果未到不执行,如果已过按间隔时间执行。
            //疑问:如果设定时间已过,设定时间无效,但本例为什么还是会在设定时间执行?然后再间隔执行?2022.9.19
        }
    }
    @SuppressLint("NewApi")
    public void getUpAlarmManagerWorkOnOthers() {

        //高版本重复设置闹钟达到低版本中setRepeating相同效果
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {// 6.0及以上
            am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
                    System.currentTimeMillis() + TIME_INTERVAL, pendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {// 4.4及以上
            am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
                    + TIME_INTERVAL, pendingIntent);
        }
    }
}

myservices.java

package com.example.alarmmanagertest1;

import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;

import androidx.annotation.RequiresApi;

public class MyService extends Service {
    private static final String TAG = "MyService";
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");

    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "run: ");
                Message msg =new Message();
                msg.what=1;
                msg.obj= String.valueOf(System.currentTimeMillis());
                MainActivity.handler.sendMessage(msg);
            }
        }).start();
        AlarmManagerUtils.getInstance(getApplicationContext()).getUpAlarmManagerWorkOnOthers();
        return super.onStartCommand(intent, flags, startId);
    }
}

myservice注册到androidmanifest.xml

<service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"></service>

MainActivity.java

package com.example.alarmmanagertest1;

import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;

import java.text.SimpleDateFormat;
import java.util.Date;

public class MainActivity extends AppCompatActivity {

    private AlarmManagerUtils alarmManagerUtils;
    private TextView textView3;
    private TextView textView5;
    public int i=0;


    public static Handler handler;
    @SuppressLint("HandlerLeak")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView3=(TextView)findViewById(R.id.textView3);
        textView5=(TextView)findViewById(R.id.textView5);

        alarmManagerUtils = AlarmManagerUtils.getInstance(this);
        alarmManagerUtils.createGetUpAlarmManager();
        alarmManagerUtils.getUpAlarmManagerStartWork();

        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                // TODO 自动生成的方法存根
                // super.handleMessage(msg);

                long time=System.currentTimeMillis();
                if (msg.what == 1) {
                    String text = (String) msg.obj;
                    i++;
                    String s = Integer.toString(i);
                    textView3.setText(s);
                    SimpleDateFormat f1= new SimpleDateFormat("MM-dd HH:mm:ss");
                    Date d1=new Date(time);
                    String t1=f1.format(d1);
                    textView5.setText(t1);
                }
            }
        };// handler
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/dsrw"
        android:textColor="#EF6C00"
        android:textSize="25sp"
        app:layout_constraintBottom_toTopOf="@+id/guideline4"
        app:layout_constraintStart_toStartOf="@+id/guideline5"
        app:layout_constraintTop_toTopOf="@+id/guideline3" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/dsjishuqi"
        android:textColor="?attr/colorPrimary"
        android:textSize="20sp"
        app:layout_constraintBottom_toTopOf="@+id/guideline6"
        app:layout_constraintEnd_toStartOf="@+id/guideline8"
        app:layout_constraintTop_toTopOf="@+id/guideline4" />

    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/purple_200"
        android:textSize="20sp"
        app:layout_constraintBottom_toTopOf="@+id/guideline6"
        app:layout_constraintStart_toStartOf="@+id/guideline8"
        app:layout_constraintTop_toTopOf="@+id/guideline4" />

    <TextView
        android:id="@+id/textView4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/dstime"
        android:textColor="#009688"
        android:textSize="20sp"
        app:layout_constraintBottom_toTopOf="@+id/guideline7"
        app:layout_constraintEnd_toStartOf="@+id/guideline8"
        app:layout_constraintTop_toTopOf="@+id/guideline6" />

    <TextView
        android:id="@+id/textView5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#009688"
        android:textSize="20sp"
        app:layout_constraintBottom_toTopOf="@+id/guideline7"
        app:layout_constraintStart_toStartOf="@+id/guideline8"
        app:layout_constraintTop_toTopOf="@+id/guideline6" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_begin="122dp" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_begin="173dp" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_begin="61dp" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_begin="220dp" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline7"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_begin="272dp" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline8"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_begin="162dp" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline9"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_begin="222dp" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline10"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_begin="20dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

运行界面:只做了一个计数器和任务执行时间

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kim5659

你的鼓励是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值