Таймер с ручным запуском (Работа над ошибками)
«Здравствуй, милый дедушка Хабр Хабрович! — писал он. — Пишу тебе письмо. Поздравляю вас со светлой Пятницей, и желаю тебе всего на выходных.»
Ванька, покосился на Скайп, и живо вообразил себе Хабра Хабровича. Образ получился впечатляющий, но слишком объёмный.
Ванька, вздохнул продолжил писать.
«А вчерась мне была выволочка. Надумал я написать свой таймер, с автоматическим запуском и ручным управлением. Написал, и любовался им долго. Но дядьки, сурово отчитали меня. Ругали, но за чуб не таскали. Дали книжек умных, и советов пользительных.
Дядька Dimezis, ругался сильно за неакуратные имена переменных, да за ключи переменных захардкоженные. Кодстайл ругал тож. Сказал переписать и не позориться.
Дядьки ivazhnov и Alex837 ругали, за неаккуратное использование батареи. В морду, мордой ейной не сували, но хмурились сильно. Сказали переписать и не позориться.
Дядька MetAmfetamin, утешил, но поддержал других. Сказал переписать и не позориться.»
Ванька почесал за ухом, и продолжил стучать по клавишам.
«Сказали, что нельзя для отлова изменения состояния сети использовать BroadcastReceiver, для которого в манифесте прописано:
. Нельзя так писать, ибо батарейку выносит не по детски: 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);
}
}
Сказали смотреть в сторону JobScheduler или GcmNetworkManager или SyncAdapter.
Долго я думал, и решил остановиться на GcmNetworkManager, потому как он для старых версий Android подходит, и универсальней мне кажется.»
Ванька покосился, на гору документации прочитанной вчера, и зевнул.
» Удалил я для начала все упоминания, о UniversalReceiver. Ликвидировал, так сказать, как класс. И из манифеста потёр.
Далее, создал класс служебный, в который вынес все теги.
public class Utils {
public static final String EXTRA_KEY_OUT = "EXTRA_OUT";
public static final String ACTION_MYINTENTSERVICE = "ru.timgor.alerttest.RESPONSE";
public static String TAG = "AlertTest";
public static String SUCCESS = "success";
public static String AUTOMATIC = "chbAutomatic";
}
Далее в зависимости gradle, добавил строчку:
compile 'com.google.android.gms:play-services-gcm:8.1.0'
В MainActivity, добавил изменения:
Объявил:
private GcmNetworkManager mGcmNetworkManager;
В onCreate:
mGcmNetworkManager = GcmNetworkManager.getInstance(this);
setAutoStart(true);
И метод добавил:
public void setAutoStart(boolean isOn){
if(isOn){
Log.d(Utils.TAG, "Автозапуск задачи");
Random myRandom = new Random();
Bundle bundle = new Bundle();
bundle.putInt("randomNum", myRandom.nextInt(10));
PeriodicTask periodicTask = new PeriodicTask.Builder()
.setService(AutomaticService.class)
.setTag("PeriodicTask")
.setPeriod(30)
.setPersisted(true)
.setExtras(bundle)
.setRequiredNetwork(Task.NETWORK_STATE_CONNECTED)
.setRequiresCharging(false)
.build();
mGcmNetworkManager.schedule(periodicTask);
} else {
Log.d(Utils.TAG, "Остановка автозапуска задачи");
mGcmNetworkManager.cancelAllTasks(AutomaticService.class);
}
Тут, дорогой Хабр Хабрович, коль в метод, правда-истина придёт, объявляю я об намерении создать задачу, которая будет периодически запускать службу AutomaticService (setService), носить тег PeriodicTask (setTag), вызываться раз в 30 секунд (setPeriod), работать после перезапуска (setPersisted), передавать случайное число, не работать пока сеть не подключится (setRequiredNetwork) и не требовать подключения к зарядке (setRequiresCharging).
А коли, в метод, кто-то соврамши передаёт, то автоматическая работа прекращается.
Далее создал я службу AutomaticService, да не простую, а наследуемую от GcmTaskService:
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
import com.google.android.gms.gcm.GcmNetworkManager;
import com.google.android.gms.gcm.GcmTaskService;
import com.google.android.gms.gcm.TaskParams;
public class AutomaticService extends GcmTaskService {
@Override
public int onRunTask(TaskParams taskParams) {
Log.d(Utils.TAG, "Автоматический запуск. Начало работы");
Log.d(Utils.TAG, "Переданное число: "+ taskParams.getExtras().getInt("randomNum"));
if (!verify()) {
Log.d(Utils.TAG, "AUTO. Задача не отработала");
Intent responseIntent = new Intent();
responseIntent.setAction(Utils.ACTION_MYINTENTSERVICE);
responseIntent.addCategory(Intent.CATEGORY_DEFAULT);
responseIntent.putExtra(Utils.EXTRA_KEY_OUT, false);
Log.d(Utils.TAG, "Загрузка не произошла");
sendBroadcast(responseIntent);
} else {
Log.d(Utils.TAG, "AUTO. Задача отработала успешно");
}
return GcmNetworkManager.RESULT_SUCCESS;
}
public boolean verify(){
SharedPreferences settings = getSharedPreferences(Utils.TAG, MODE_PRIVATE);
boolean success = settings.getBoolean("success", false);
return success;
}
}
Если служба хорошо отработала, то мы радуемся, а коли не судьба ей исполниться, то запускает BroadcastReceiver, который прописан в MainActivity. Дважды по десять пробует подключиться, с перерывом в 0.1 секунду, да плюнув в сердцах бросает это дело, до следующего тика.
В приложении тестовом, я добавил галочку. Коли она нажата, то благополучно задача отрабатывает.
public class MyBroadRec extends BroadcastReceiver {
public int qnt = 0;
@Override
public void onReceive(Context context, Intent intent) {
Boolean result = intent.getBooleanExtra(Utils.EXTRA_KEY_OUT, false);
Intent intentRec = new Intent(MainActivity.this, ManualService.class);
if(!result && qnt<20){
Log.d(Utils.TAG, "Новая попытка № "+qnt);
qnt++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
startService(intentRec);
}
else {
qnt=0;
}
}
}
И в onCreate:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
chbAuto = (CheckBox)findViewById(R.id.chb_Auto);
chbVIP = (CheckBox)findViewById(R.id.chb_VIP);
btnManual = (Button)findViewById(R.id.btn_Manual);
mGcmNetworkManager = GcmNetworkManager.getInstance(this);
sPref = getSharedPreferences(Utils.TAG, MODE_PRIVATE);
editor = sPref.edit();
editor.putBoolean(Utils.AUTOMATIC, chbAuto.isChecked());
editor.putBoolean(Utils.SUCCESS, chbVIP.isChecked());
editor.commit();
chbAuto.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
editor.putBoolean(Utils.AUTOMATIC, isChecked);
editor.commit();
setAutoStart(isChecked);
}
});
chbVIP.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
editor.putBoolean(Utils.SUCCESS, isChecked);
editor.commit();
}
});
btnManual.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(Utils.TAG, "Загрузка в ручном режиме");
Intent intent = new Intent(MainActivity.this, ManualService.class);
startService(intent);
}
});
MyBroadRec myBroadRec = new MyBroadRec();
IntentFilter intentFilter = new IntentFilter(Utils.ACTION_MYINTENTSERVICE);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(myBroadRec, intentFilter);
setAutoStart(true);
}
Регистрирую этот ресивер.»
Ванька оттер пот, покосился на заманчиво запотевший пузырёк с тёмным чаем, решительно тряхнул головой, и продолжил.
«А ещё, хочу запускать я вручную задачу, не дожидаясь тика таймера. Для этого написал я службу ManualService наследующуюся от IntentService.
import android.app.IntentService;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
public class ManualService extends IntentService {
public ManualService() {
super("ManualService");
}
@Override
protected void onHandleIntent(Intent intent) {
if (!verify()) {
Intent responseIntent = new Intent();
responseIntent.setAction(Utils.ACTION_MYINTENTSERVICE);
responseIntent.addCategory(Intent.CATEGORY_DEFAULT);
responseIntent.putExtra(Utils.EXTRA_KEY_OUT, false);
sendBroadcast(responseIntent);
Log.d(Utils.TAG, "MANUAL. Задача не отработала");
} else {
Log.d(Utils.TAG, "MANUAL. Задача отработала успешно");
}
}
public boolean verify(){
SharedPreferences settings = getSharedPreferences(Utils.TAG, MODE_PRIVATE);
boolean success = settings.getBoolean(Utils.SUCCESS, false);
return success;
}
}
Запустил я приложение, и радовался очень. Если в режим полёта перейти, да выключить интернеты — то автоматический запуск и не думает запускаться.
А вот дорогой дедушка и все классы:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
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;
import com.google.android.gms.gcm.GcmNetworkManager;
import com.google.android.gms.gcm.PeriodicTask;
import com.google.android.gms.gcm.Task;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
CheckBox chbAuto, chbVIP;
Button btnManual;
SharedPreferences sPref;
SharedPreferences.Editor editor;
private GcmNetworkManager mGcmNetworkManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
chbAuto = (CheckBox)findViewById(R.id.chb_Auto);
chbVIP = (CheckBox)findViewById(R.id.chb_VIP);
btnManual = (Button)findViewById(R.id.btn_Manual);
mGcmNetworkManager = GcmNetworkManager.getInstance(this);
sPref = getSharedPreferences(Utils.TAG, MODE_PRIVATE);
editor = sPref.edit();
editor.putBoolean(Utils.AUTOMATIC, chbAuto.isChecked());
editor.putBoolean(Utils.SUCCESS, chbVIP.isChecked());
editor.commit();
chbAuto.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
editor.putBoolean(Utils.AUTOMATIC, isChecked);
editor.commit();
setAutoStart(isChecked);
}
});
chbVIP.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
editor.putBoolean(Utils.SUCCESS, isChecked);
editor.commit();
}
});
btnManual.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(Utils.TAG, "Загрузка в ручном режиме");
Intent intent = new Intent(MainActivity.this, ManualService.class);
startService(intent);
}
});
MyBroadRec myBroadRec = new MyBroadRec();
IntentFilter intentFilter = new IntentFilter(Utils.ACTION_MYINTENTSERVICE);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(myBroadRec, intentFilter);
setAutoStart(true);
}
public class MyBroadRec extends BroadcastReceiver {
public int qnt = 0;
@Override
public void onReceive(Context context, Intent intent) {
Boolean result = intent.getBooleanExtra(Utils.EXTRA_KEY_OUT, false);
Intent intentRec = new Intent(MainActivity.this, ManualService.class);
if(!result && qnt<20){
Log.d(Utils.TAG, "Новая попытка № "+qnt);
qnt++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
startService(intentRec);
}
else {
qnt=0;
}
}
}
public void setAutoStart(boolean isOn){
if(isOn){
Log.d(Utils.TAG, "Автозапуск задачи");
Random myRandom = new Random();
Bundle bundle = new Bundle();
bundle.putInt("randomNum", myRandom.nextInt(10));
PeriodicTask periodicTask = new PeriodicTask.Builder()
.setService(AutomaticService.class)
.setTag("PeriodicTask")
.setPeriod(30)
.setPersisted(true)
.setExtras(bundle)
.setRequiresCharging(false)
.setRequiredNetwork(Task.NETWORK_STATE_CONNECTED)
.build();
mGcmNetworkManager.schedule(periodicTask);
} else {
Log.d(Utils.TAG, "Остановка автозапуска задачи");
mGcmNetworkManager.cancelAllTasks(AutomaticService.class);
}
}
}
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
import com.google.android.gms.gcm.GcmNetworkManager;
import com.google.android.gms.gcm.GcmTaskService;
import com.google.android.gms.gcm.TaskParams;
public class AutomaticService extends GcmTaskService {
@Override
public int onRunTask(TaskParams taskParams) {
Log.d(Utils.TAG, "Автоматический запуск. Начало работы");
Log.d(Utils.TAG, "Переданное число: "+ taskParams.getExtras().getInt("randomNum"));
if (!verify()) {
Log.d(Utils.TAG, "AUTO. Задача не отработала");
Intent responseIntent = new Intent();
responseIntent.setAction(Utils.ACTION_MYINTENTSERVICE);
responseIntent.addCategory(Intent.CATEGORY_DEFAULT);
responseIntent.putExtra(Utils.EXTRA_KEY_OUT, false);
Log.d(Utils.TAG, "Загрузка не произошла");
sendBroadcast(responseIntent);
} else {
Log.d(Utils.TAG, "AUTO. Задача отработала успешно");
}
return GcmNetworkManager.RESULT_SUCCESS;
}
public boolean verify(){
SharedPreferences settings = getSharedPreferences(Utils.TAG, MODE_PRIVATE);
boolean success = settings.getBoolean("success", false);
return success;
}
}
import android.app.IntentService;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
public class ManualService extends IntentService {
public ManualService() {
super("ManualService");
}
@Override
protected void onHandleIntent(Intent intent) {
if (!verify()) {
Intent responseIntent = new Intent();
responseIntent.setAction(Utils.ACTION_MYINTENTSERVICE);
responseIntent.addCategory(Intent.CATEGORY_DEFAULT);
responseIntent.putExtra(Utils.EXTRA_KEY_OUT, false);
sendBroadcast(responseIntent);
Log.d(Utils.TAG, "MANUAL. Задача не отработала");
} else {
Log.d(Utils.TAG, "MANUAL. Задача отработала успешно");
}
}
public boolean verify(){
SharedPreferences settings = getSharedPreferences(Utils.TAG, MODE_PRIVATE);
boolean success = settings.getBoolean(Utils.SUCCESS, false);
return success;
}
}
public class Utils {
public static final String EXTRA_KEY_OUT = "EXTRA_OUT";
public static final String ACTION_MYINTENTSERVICE = "ru.timgor.alerttest.RESPONSE";
public static String TAG = "AlertTest";
public static String SUCCESS = "success";
public static String AUTOMATIC = "chbAutomatic";
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.google.android.gms:play-services-gcm:8.1.0'
}
В общем, дорогой дедушка, получился таймер, просто загляденье. Буду ждать, что дядьки скажут.
А за меня не волнуйся. Хочу стать я разработчиком умным, и стараться буду впредь.
Засим желаю тебе здоровья крепкого, и выходных увлекательных.»
Ванька, подвинул клавиатуру, набулькал из запотевшего пузырька чаю, и потянулся к кнопке «Опубликовать». Подумав немного, набрал в теге «На деревню дедушке».
Почесал мышку за ухом, и добавил «Хабр Хабровичу».
Профессор из института рассказывал, что публикации разносятся по проводам оптоволоконным и медным, по всему интернету, управляемые веселыми админами. Ванька собрался с духом, и нажал большую зелёную кнопку.
Убаюканный сладкими надеждами, он час спустя крепко спал… Ему снился Half-Life 3…