Привет, Хабр. Данная статья адресована к постигающим искусство Android-разработки, как и я.

Недавно мне нужно было сделать таймер, запускающий некую задачу, через определенный промежуток времени.
Но отличия от стандартного решения заключались, в следующем:
Задача была решена следующим образом.
Напишем тестовое приложение.Создайте проект, с пустой активностью.
Форма:
Создадим новый IntentService, с именем UniversalService.
В MainActivity внесем изменения в onCreate:
И добавим метод:
В данном коде (в методе onCreate), мы сохраняем в Общие Настройки, с именем «AlertTest», состояние чекбоксов, и (в методе setServiceAlarm) запускаем таймер с периодичностью 5 секунд, на выполнение службы UniversalService.
Также, мы хотим получать ответ от службы, об успешности выполнения операции.
Для этого добавим в MainActivity класс:
И в конец onCreate добавим:
А в UniversalService поменяем код на следующий:
Как видите, метод verify, эмулирует нашу задачу. В том случае, если она выполнена неуспешно — создается Intent, запускающий MyBroadRec, который опять стартует нашу службу. Настроить мы можем через чекбокс «Очень важная опция». Если мы переведем чекбокс «Автоматический запуск» в неактивное состояние, то работа таймера будет прекращена. Как только задача будет выполнена — переходим в штатный режим.
Но у нас еще осталась задача автозапуска нашего приложения. Для этого создаем класс UniversalReceiver наследующий от BroadcastReceiver:
И в манифесте добавляем следующие строки:
Все, задача решена. Засим позвольте откланяться. Надеюсь кому-то это окажется полезным. Если у кого-то есть замечания и подсказки, с большим удовольствием их выслушаю.
P.S.: Классы полностью:
P.P.S.: Внес изменения в BroadcastReceiver, на ошибки, которые указали мне Handy и BlackStream.
Сейчас разбираюсь с SyncAdapter.

Недавно мне нужно было сделать таймер, запускающий некую задачу, через определенный промежуток времени.
Но отличия от стандартного решения заключались, в следующем:
- Во-первых, если задача не смогла выполниться, тут же необходимо повторить её. И так, до победного конца. После этого, возвращаемся в штатный режим.
- Во-вторых, может возникнуть необходимость запустить задачу вручную, не дожидаясь наступления следующего срабатывания таймера.
- И в-третьих, таймер должен запуститься в том случае, если устройство перезагружено, или была восстановлена связь с интернетом.
Задача была решена следующим образом.
Напишем тестовое приложение.Создайте проект, с пустой активностью.
Форма:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="ru.alerttest.MainActivity"> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Автоматический запуск" android:id="@+id/checkBox" android:checked="true" android:layout_below="@+id/button" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Ручной запуск" android:id="@+id/button" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Очень важная опция" android:id="@+id/checkBox2" android:checked="true" android:layout_below="@+id/checkBox" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> </RelativeLayout>
Создадим новый IntentService, с именем UniversalService.
В MainActivity внесем изменения в onCreate:
onCreate
CheckBox checkBox, checkBox1; SharedPreferences sPref; SharedPreferences.Editor editor; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); checkBox = (CheckBox)findViewById(R.id.checkBox); sPref = getSharedPreferences("AlertTest",MODE_PRIVATE); editor = sPref.edit(); editor.putBoolean("chbAutomatic", checkBox.isChecked()); editor.putBoolean("success", checkBox.isChecked()); editor.commit(); checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { editor.putBoolean("chbAutomatic", checkBox.isChecked()); editor.commit(); setServiceAlarm(MainActivity.this); } }); checkBox1 = (CheckBox)findViewById(R.id.checkBox2); checkBox1.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { editor.putBoolean("success", checkBox1.isChecked()); editor.commit(); setServiceAlarm(MainActivity.this); } }); Button button = (Button)findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d("AlertTest", "Загрузка в ручном режиме"); Intent intent = new Intent(MainActivity.this, UniversalService.class); startService(intent); } }); }
И добавим метод:
public void setServiceAlarm(Context context){ SharedPreferences settings = getSharedPreferences("AlertTest", MODE_PRIVATE); Boolean automaticSynchronize = settings.getBoolean("chbAutomatic", false); Intent intent = new Intent(context, UniversalService.class); PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0); AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Integer period = 5; if(automaticSynchronize){ alarmManager.setRepeating(AlarmManager.RTC, System.currentTimeMillis(), period*1000, pendingIntent); Log.d("AlertTest", "Загрузка в автоматическом режиме, период: "+period*1000); } else { Log.d("AlertTest", "Загрузка отменена"); alarmManager.cancel(pendingIntent); pendingIntent.cancel(); } }
В данном коде (в методе onCreate), мы сохраняем в Общие Настройки, с именем «AlertTest», состояние чекбоксов, и (в методе setServiceAlarm) запускаем таймер с периодичностью 5 секунд, на выполнение службы UniversalService.
Также, мы хотим получать ответ от службы, об успешности выполнения операции.
Для этого добавим в MainActivity класс:
public class MyBroadRec extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Boolean result = intent.getBooleanExtra(UniversalService.EXTRA_KEY_OUT, false); Intent intentRec = new Intent(MainActivity.this, UniversalService.class); if(!result){ Log.d("AlertTest", "Новая попытка"); startService(intentRec); } } }
И в конец onCreate добавим:
MyBroadRec myBroadRec = new MyBroadRec(); IntentFilter intentFilter = new IntentFilter(UniversalService.ACTION_MYINTENTSERVICE); intentFilter.addCategory(Intent.CATEGORY_DEFAULT); registerReceiver(myBroadRec, intentFilter); setServiceAlarm(MainActivity.this);
А в UniversalService поменяем код на следующий:
UniversalService
package ru.alerttest; import android.app.IntentService; import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.util.Log; public class UniversalService extends IntentService { public static final String EXTRA_KEY_OUT = "EXTRA_OUT"; public static final String ACTION_MYINTENTSERVICE = "ru.timgor.alerttest.RESPONSE"; public UniversalService() { super("UniversalService"); } @Override protected void onHandleIntent(Intent intent) { Log.d("AlertTest", "Начало загрузки"); if(!verify()){ Intent responseIntent = new Intent(); responseIntent.setAction(ACTION_MYINTENTSERVICE); responseIntent.addCategory(Intent.CATEGORY_DEFAULT); responseIntent.putExtra(EXTRA_KEY_OUT, false); Log.d("AlertTest", "Загрузка не произошла"); sendBroadcast(responseIntent); } else{ Log.d("AlertTest", "Загрузка прошла успешно"); } } public boolean verify(){ SharedPreferences settings = getSharedPreferences("AlertTest", MODE_PRIVATE); Boolean success = settings.getBoolean("success", false); return success; } }
Как видите, метод verify, эмулирует нашу задачу. В том случае, если она выполнена неуспешно — создается Intent, запускающий MyBroadRec, который опять стартует нашу службу. Настроить мы можем через чекбокс «Очень важная опция». Если мы переведем чекбокс «Автоматический запуск» в неактивное состояние, то работа таймера будет прекращена. Как только задача будет выполнена — переходим в штатный режим.
Но у нас еще осталась задача автозапуска нашего приложения. Для этого создаем класс UniversalReceiver наследующий от BroadcastReceiver:
package ru.alerttest; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; import android.widget.Toast; public class UniversalReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.d("AlertTest", "Произошла смена статуса"); Intent intentNew = new Intent(context, MainActivity.class); intentNew.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intentNew); } }
И в манифесте добавляем следующие строки:
<receiver android:name=".UniversalReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter> </receiver>
Все, задача решена. Засим позвольте откланяться. Надеюсь кому-то это окажется полезным. Если у кого-то есть замечания и подсказки, с большим удовольствием их выслушаю.
P.S.: Классы полностью:
MainActivity
package ru.alerttest; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; public class MainActivity extends AppCompatActivity { CheckBox checkBox, checkBox1; SharedPreferences sPref; SharedPreferences.Editor editor; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); checkBox = (CheckBox)findViewById(R.id.checkBox); sPref = getSharedPreferences("AlertTest",MODE_PRIVATE); editor = sPref.edit(); editor.putBoolean("chbAutomatic", checkBox.isChecked()); editor.putBoolean("success", checkBox.isChecked()); editor.commit(); checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { editor.putBoolean("chbAutomatic", checkBox.isChecked()); editor.commit(); setServiceAlarm(MainActivity.this); } }); checkBox1 = (CheckBox)findViewById(R.id.checkBox2); checkBox1.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { editor.putBoolean("success", checkBox1.isChecked()); editor.commit(); setServiceAlarm(MainActivity.this); } }); Button button = (Button)findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d("AlertTest", "Загрузка в ручном режиме"); Intent intent = new Intent(MainActivity.this, UniversalService.class); startService(intent); } }); MyBroadRec myBroadRec = new MyBroadRec(); IntentFilter intentFilter = new IntentFilter(UniversalService.ACTION_MYINTENTSERVICE); intentFilter.addCategory(Intent.CATEGORY_DEFAULT); registerReceiver(myBroadRec, intentFilter); setServiceAlarm(MainActivity.this); } public class MyBroadRec extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Boolean result = intent.getBooleanExtra(UniversalService.EXTRA_KEY_OUT, false); Intent intentRec = new Intent(MainActivity.this, UniversalService.class); if(!result){ Log.d("AlertTest", "Новая попытка"); startService(intentRec); } } } public void setServiceAlarm(Context context){ SharedPreferences settings = getSharedPreferences("AlertTest", MODE_PRIVATE); Boolean automaticSynchronize = settings.getBoolean("chbAutomatic", false); Intent intent = new Intent(context, UniversalService.class); PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0); AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Integer period = 5; if(automaticSynchronize){ alarmManager.setRepeating(AlarmManager.RTC, System.currentTimeMillis(), period*1000, pendingIntent); Log.d("AlertTest", "Загрузка в автоматическом режиме, период: "+period*1000); } else { Log.d("AlertTest", "Загрузка отменена"); alarmManager.cancel(pendingIntent); pendingIntent.cancel(); } } }
UniversalService
package ru.alerttest; import android.app.IntentService; import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.util.Log; public class UniversalService extends IntentService { public static final String EXTRA_KEY_OUT = "EXTRA_OUT"; public static final String ACTION_MYINTENTSERVICE = "ru.timgor.alerttest.RESPONSE"; public UniversalService() { super("UniversalService"); } @Override protected void onHandleIntent(Intent intent) { Log.d("AlertTest", "Начало загрузки"); if(!verify()){ Intent responseIntent = new Intent(); responseIntent.setAction(ACTION_MYINTENTSERVICE); responseIntent.addCategory(Intent.CATEGORY_DEFAULT); responseIntent.putExtra(EXTRA_KEY_OUT, false); Log.d("AlertTest", "Загрузка не произошла"); sendBroadcast(responseIntent); } else{ Log.d("AlertTest", "Загрузка прошла успешно"); } } public boolean verify(){ SharedPreferences settings = getSharedPreferences("AlertTest", MODE_PRIVATE); Boolean success = settings.getBoolean("success", false); return success; } }
UniversalReceiver
package ru.alerttest; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; import android.widget.Toast; public class UniversalReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.d("AlertTest", "Произошла смена статуса"); Intent intentNew = new Intent(context, MainActivity.class); intentNew.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intentNew); } }
P.P.S.: Внес изменения в BroadcastReceiver, на ошибки, которые указали мне Handy и BlackStream.
Сейчас разбираюсь с SyncAdapter.