安排将来要执行的事件(Android 方式的 Cron Job)

Sky*_*net 5 java cron android android-alarms

我有某些日期一旦获得就失去了相关性,并且应该计算数据库中这些字段的新日期,我知道我可以利用 AlarmManager 类来实现这一点,但是我对此有一些担忧:

1) 注意:从 API 19 (KITKAT) 开始,警报传递是不准确的:操作系统将转移警报以最大程度地减少唤醒和电池使用。有新的 API 来支持需要严格交付保证的应用程序;请参阅 setWindow(int, long, long, PendingIntent) 和 setExact(int, long, PendingIntent)。targetSdkVersion 早于 API 19 的应用程序将继续看到以前的行为,其中所有警报都在请求时准确传递。

那么我是否需要分别为这两种情况编码,或者如果我的目标是 kitkat,这也适用于旧版本吗?此外,由于我的代码执行时间紧迫,比如在某个日期的午夜 12 点之后,我的数据失去了相关性,如何克服警报的转移。

2) 已注册的警报在设备处于睡眠状态时会保留(如果在此期间它们关闭,可以选择唤醒设备),但如果关闭并重新启动,则会清除。

2.1) 在您的应用程序清单中设置 RECEIVE_BOOT_COMPLETED 权限。这允许您的应用程序接收在系统完成启动后广播的 ACTION_BOOT_COMPLETED (这仅在用户已启动应用程序至少一次时才有效)

2.1.1) 如果我在 12 点设置了闹钟,与此闹钟相关的服务在 12 点触发,现在当我重新启动设备时,“12 点”时间已经过去,闹钟将立即再次触发,服务会被再次调用吗?

在重新启动时,我需要实施什么机制才能在特定时间坚持我的代码执行策略?如果用户没有启动我的应用程序,我该如何设置闹钟?

第三件事是如果我的应用程序被卸载了我想清除我的代码设置的所有警报,我如何在应用程序卸载时监听?

另外我想知道,我的应用程序对时间要求非常严格,我的数据库中的值在每天晚上 12 点前就过时了,当我更新应用程序时,如果用户选择在我 12 点使用我的应用程序,结果会怎样?使用服务来更新它并在后台运行?

编辑:到目前为止我尝试过的:

我有一个数据库,其中的记录在午夜过后变得陈旧,在 12:00 说尖锐。我调用了一个警报管理器(在一个测试项目中,因为我喜欢隔离问题代码)来启动一个服务。我还在设备上获取了 PARTIAL_WAKE_LOCK,以便正确完成我庞大的数据库操作。我还实现了一个线程来完成我的耗时任务。以下是我在 12 时调用的 MainActivity 类以启动警报(用于测试目的的随机时间):

public class MainActivity extends Activity {
    private AlarmManager alarmMgr;
    private PendingIntent alarmIntent;
    BroadcastReceiver br;
    TextView t; 
    int sum; 


    public void setSum(int s){
        sum = s; 

    //  t = (TextView)findViewById(R.id.textView1);
    //  t.setText(sum);
        System.out.println("In Set Sum"+s);
    }



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setup(); 
        t = (TextView)findViewById(R.id.textView1);

        ComponentName receiver = new ComponentName(getApplicationContext(), SampleBootReceiver.class);
        PackageManager pm = getApplicationContext().getPackageManager();

        pm.setComponentEnabledSetting(receiver,
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                PackageManager.DONT_KILL_APP);


        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.set(Calendar.HOUR_OF_DAY, 17);
        calendar.set(Calendar.MINUTE, 05); // Particular minute
        calendar.set(Calendar.SECOND, 0);
        alarmMgr = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
        alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
                1000*60*60*24, alarmIntent);
    }


    public void setup() {
        br = new BroadcastReceiver() {
            @Override
            public void onReceive(Context c, Intent i) {
                Toast.makeText(c, "Rise and Shine!", Toast.LENGTH_LONG).show();
                //Invoke the service here Put the wake lock and initiate bind service
                t.setText("Hello Alarm set");

                startService(new Intent(MainActivity.this, MyService.class));
                stopService(new Intent(MainActivity.this, MyService.class));

            }
        };
        registerReceiver(br, new IntentFilter("com.testrtc") );
        alarmIntent = PendingIntent.getBroadcast( this, 0, new Intent("com.testrtc"),0 );
        alarmMgr = (AlarmManager)(this.getSystemService( Context.ALARM_SERVICE ));
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}
Run Code Online (Sandbox Code Playgroud)

这是我的 SampleBootReceiver 类,用于检查重启并在重启后再次设置警报,我不确定它是否按预期工作。我找不到任何方法来测试这是否正常工作,但我确实收到了有关启动完成的 Toast 消息。

public class SampleBootReceiver extends BroadcastReceiver {
    private AlarmManager alarmMgr;
    private PendingIntent alarmIntent;
    BroadcastReceiver br;
    TextView t; 
    MainActivity main; 

    @Override
    public void onReceive(Context context, Intent intent) {
        main= new MainActivity(); 
        if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
            Toast.makeText(context, "Hello from Bootloader", 10000).show();
            Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(System.currentTimeMillis());
            calendar.set(Calendar.HOUR_OF_DAY, 15);
            calendar.set(Calendar.MINUTE, 50); // Particular minute
            calendar.set(Calendar.SECOND, 0);
            alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
            alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
                    1000*60*60*24, alarmIntent);

            context.getApplicationContext().registerReceiver(br, new IntentFilter("com.testrtc") );
            alarmIntent = PendingIntent.getBroadcast( context.getApplicationContext(), 0, new Intent("com.testrtc"),
                    0 );
            alarmMgr = (AlarmManager)(context.getApplicationContext().getSystemService( Context.ALARM_SERVICE ));

        }
    }

}
Run Code Online (Sandbox Code Playgroud)

以下是我的服务类,不确定我在 onStartCommand 方法中所做的返回:

public class MyService extends Service {

    int a = 2; 
    int b = 2; 
    int c = a+b; 


    public MainActivity main = new MainActivity();

    public MyService() {
    }



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

    @Override
    public void onCreate() {
        Toast.makeText(this, "The new Service was Created", Toast.LENGTH_LONG).show();

    }

    @Override
    public int onStartCommand(Intent i, int flags , int startId){
        WakeLock wakeLock = null;

        try{
            PowerManager mgr = (PowerManager)getApplicationContext().getSystemService(Context.POWER_SERVICE);
            wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
            wakeLock.acquire();

            Toast.makeText(this, " Service Started", Toast.LENGTH_LONG).show();
             new Thread(new Runnable() { 
                    public void run(){


                        //Will be substituted with a time consuming long task. 

                        main.setSum(c); 

                    }
            }).start();

        }catch(Exception e){
            System.out.println(e);
        }finally{

            wakeLock.release();

        }

        return 1; 

    }

    @Override
    public void onDestroy() {
        Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();

    }
}
Run Code Online (Sandbox Code Playgroud)

同样在上面,我想知道我正在启动的线程是否会干扰我获取唤醒锁的方式。另外,如果我可以使用异步任务并在 onPostExecute 中释放唤醒锁?

最后这是我的清单:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.testrtc"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.testrtc.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver
            android:name=".SampleBootReceiver"
            android:enabled="false" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" >
                </action>
            </intent-filter>
        </receiver>

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

    </application>

</manifest>
Run Code Online (Sandbox Code Playgroud)

这是我重启后的日志 Cat,有很多日志消息,但是我发现这些与应用程序相关,它们包含 epackage 名称:

01-22 15:18:35.652: V/ActivityManager(419): getTasks: max=1, flags=0, receiver=null
01-22 15:18:35.652: V/ActivityManager(419): com.xxx.xxx/.MainActivity: task=TaskRecord{425c58f0 #5 A com.xxx.xxx U 0}
01-22 15:18:35.653: V/ActivityManager(419): We have pending thumbnails: null
Run Code Online (Sandbox Code Playgroud)

更多问题:我应该在哪里设置闹钟,如果我在启动画面的 onCreate 中设置闹钟,每次应用程序启动时都会调用它,可能会覆盖旧值。

其次,我想在我的服务运行时获取对数据库的锁定,如果此时用户尝试打开我的应用程序,我该怎么办?(随着数据的更新,我没有什么可显示的)。

上面代码中的第三个我仍然发现重新启动后注册警报的问题。

小智 2

1) 要在准确的时间处理警报,您有 2 个选项: a) 将最低 SDK 级别设置为 18 而不是 19。这将使您的应用程序在准确的时间在 kitkat 上运行 b) 使用 setExact() 方法告诉 Kitkat 保持您在指定的确切时间发出闹钟来源:http ://developer.android.com/about/versions/android-4.4.html

2)启动完成通知是最好的方法。您可以设置广播接收器以在重新启动时收到通知。您可以将警报存储在数据库中并在重新启动时检索。

2.1)做一些用户不希望你做的事情是可怕的编程习惯,即即使用户没有打开应用程序也设置警报。

3) 只要您不在SD卡上创建文件,卸载应用程序时所有应用程序数据都会被删除

4)您应该在写入或读取数据时锁定/解锁数据,以解决您的 12AM 问题。如果数据被锁定,则在用户提交事务之前它不会受到影响。如果您使用 SQLite,您可以从以下位置获取更多信息:http://www.sqlite.org/lockingv3.html