Привет. Данная статья адресована к постигающим искусство Android-разработки, как и я.
Недавно мне нужно было сделать таймер, запускающий некую задачу, через определенный промежуток времени.
Но отличия от стандартного решения заключались, в следующем:
- Во-первых, если задача не смогла выполниться, тут же необходимо повторить её. И так, до победного конца. После этого, возвращаемся в штатный режим.
- Во-вторых, может возникнуть необходимость запустить задачу вручную, не дожидаясь наступления следующего срабатывания таймера.
- И в-третьих, таймер должен запуститься в том случае, если устройство перезагружено, или была восстановлена связь с интернетом.
Задача была решена следующим образом.
Напишем тестовое приложение.Создайте проект, с пустой активностью.
Форма:
<?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:
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 поменяем код на следующий:
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", "Произошла смена статуса");
MainActivity mainActivity = new MainActivity();
mainActivity.setServiceAlarm(context);
}
}
И в манифесте добавляем следующие строки:
<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.: Классы полностью:
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();
}
}
}
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;
}
}
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", "Произошла смена статуса");
MainActivity mainActivity = new MainActivity();
mainActivity.setServiceAlarm(context);
}
}
Автор: Snakecatcher