Stabil Service készítése
Androidban olyan Service-t írni ami gond nélkül túléli az applikáció kilövését, az nem egyszerű. Az alap felállás az, hogy applikáció kilő, service többé nem kötődik az őt létrehozó osztályhoz és az android őt is kilövi. Van a Service-nek több típúsa, van amelyik ekkor újraindul, de ha az applikációt megnyitom majd újra bezárom, akkor megint bezáródik, majd újraindul, és persze minden változója törlődik, köszönöm szépen...
Megoldás persze van, de nem egyszerű. Ennek lényege, hogy előtérbe kell tenni az applikációt (foreground), és mikor a programot bezárják vagy kilövik, akkor az applikációnk a foreground-ban tovább él. Ez a foreground azt jelenti a gyakorlatban, hogy ki kell tenni egy applikációs ikont az értesítési sávba azért, hogy bezárás után ne maradhasson észrevétlenül futó része a programnak. Az user aztán letilthatja az adott program értesítési sávjának használatát a beállítások menüben, ekkor már tud a service teljesen észrevétlenül is futni, de az már a felhasználó tudtával történik.
A lenti programban elindítjuk a Service-t, indításkor átadunk neki egy számot. A service ezt a számot másodpercenként növeli és visszaküldi az Activity-be ahol az megjelenik. A service nézi hogy fut-e az Activity, ha fut akkor így csipog, ha nem fut akkor úgy csipog. Továbbá az Activity-ből a harmadik gombbal megváltoztathatjuk a már futó Service-ben a számláló értékét
MainActivity.java
package com.project.domel.peldaegy; import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private PeldaegyService mPeldaegyService; Intent PeldaegyServiceIntent; Intent uzenetKuldo = new Intent(); private uzenetFogado receiver; //ez azért kell, hogy le tudjuk kérdezni a MainActivity osztályát a Service-ből public MainActivity(Context dapplicationContext) { dapplicationContext= this; } //az eredeti is kell, mert különben fagyás public MainActivity() {} //megnézi hogy fut-e a Service private boolean isMyServiceRunning(Class<?> serviceClass) { ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { if (serviceClass.getName().equals(service.service.getClassName())) { return true; } } return false; } //service-ből jövő üzenet fogadása public class uzenetFogado extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Bundle extras = intent.getExtras(); if (extras!=null){ //------------------------------------------------------------------------------------------- if (extras.containsKey("szoveg")){ String st = intent.getStringExtra("szoveg"); TextView tw = (TextView)findViewById(R.id.textView); tw.setText(st); } } }} @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPeldaegyService = new PeldaegyService(this); PeldaegyServiceIntent = new Intent(this, mPeldaegyService.getClass()); //fogadó broadcast beregisztrálása IntentFilter filter = new IntentFilter("mainAct"); filter.addCategory(Intent.CATEGORY_DEFAULT); receiver = new uzenetFogado(); registerReceiver(receiver, filter); //küldő broadcast beregisztrálása uzenetKuldo.setAction("serviceAct"); uzenetKuldo.addCategory(Intent.CATEGORY_DEFAULT); } //indul button public void indul(View view) { if (!isMyServiceRunning(mPeldaegyService.getClass())) { PeldaegyServiceIntent.putExtra("szambacsi",155); startService(PeldaegyServiceIntent); PeldaegyServiceIntent.replaceExtras(new Bundle()); } } //leáll button public void megall(View view) { if (isMyServiceRunning(mPeldaegyService.getClass())) { stopService(PeldaegyServiceIntent); } } //érték átadás a futó Service-be public void ertekatadas(View view) { if (isMyServiceRunning(mPeldaegyService.getClass())) { uzenetKuldo.putExtra("szambacsi", 817); //küldendő szöveg sendBroadcast(uzenetKuldo); //küldés uzenetKuldo.replaceExtras(new Bundle()); //törlés } } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(receiver); } }
PeldaegyService.java
package com.project.domel.peldaegy; import android.app.ActivityManager; import android.app.AlarmManager; import android.app.Notification; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.MediaPlayer; import android.os.Bundle; import android.os.IBinder; import android.os.SystemClock; import java.util.List; public class PeldaegyService extends Service { MediaPlayer mp1, mp2; Integer szamlalo = 0; Intent uzenetKuldo = new Intent(); private uzenetFogado receiver; Notification ikari; Notification.Builder noti; PendingIntent timer_pi; BroadcastReceiver timer_br; AlarmManager timer_am; private MainActivity mMainActivity; //ez azért kell, hogy le tudjuk kérdezni a Service osztályát a MainActivity-ből public PeldaegyService(Context applicationContext) { applicationContext= this; } //az eredeti is kell, mert különben fagyás public PeldaegyService() { } @Override //szemét, de meg kell hagyni public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); } //ezzel kérdezzük le hogy fut-e a MainActivity protected Boolean isActivityRunning(Class activityClass){ ActivityManager activityManager = (ActivityManager) getBaseContext().getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(Integer.MAX_VALUE); for (ActivityManager.RunningTaskInfo task : tasks) { if (activityClass.getCanonicalName().equalsIgnoreCase(task.baseActivity.getClassName())) return true; } return false; } //activity-ből jövő üzenet fogadása public class uzenetFogado extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Bundle extras = intent.getExtras(); if (extras!=null){ //------------------------------------------------------------------------------------------- if (extras.containsKey("szambacsi")){ szamlalo = intent.getIntExtra("szambacsi",0); } } }} //indul a Service @Override public int onStartCommand(Intent intent, int flags, int startId) { super.onStartCommand(intent, flags, startId); Bundle extras = intent.getExtras(); if (extras!=null)if (extras.containsKey("szambacsi"))szamlalo = extras.getInt("szambacsi"); mp1 = MediaPlayer.create(this,R.raw.sound1); mp2 = MediaPlayer.create(this,R.raw.sound2); //fogadó broadcast beregisztrálása IntentFilter filter = new IntentFilter("serviceAct"); filter.addCategory(Intent.CATEGORY_DEFAULT); receiver = new uzenetFogado(); registerReceiver(receiver, filter); //átveszi a MainActivity osztályát mMainActivity = new MainActivity(this); //küldő broadcast beregisztrálása uzenetKuldo.setAction("mainAct"); uzenetKuldo.addCategory(Intent.CATEGORY_DEFAULT); //értesítési sávban a MainActicity indíthatósága Intent intente = new Intent(this, MainActivity.class); intente.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); intente.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP); final PendingIntent pendingIntent = PendingIntent.getActivity(PeldaegyService.this, 0, intente, PendingIntent.FLAG_CANCEL_CURRENT); //értesítési sávban ikon, szöveg, indítható Activity létrehozása //azért van két részre osztva, hogy menetközben rugalmasabban lehessen a szöveget megváltoztatni //máshol így írhatod át a szöveget //noti.setContentText("valami3"); //ikari = noti.build(); //startForeground(4456, ikari); //az értesítési sávnak az az értelme, hogy előtérbe hozzuk az applikációt, különben //újraindul a Service, ha bezárjuk a programot noti = new Notification.Builder(this.getApplicationContext()); noti.setContentTitle("szoveg1"); noti.setContentText("szoveg2"); noti.setSmallIcon(R.mipmap.ic_launcher); noti.setContentIntent(pendingIntent); noti.setPriority(Notification.PRIORITY_DEFAULT); ikari = noti.build(); startForeground(4456,ikari); //ezt hívja meg az AlarmManager a beállított időpontban timer_br = new BroadcastReceiver() { @Override public void onReceive(Context c, Intent i) { szamlalo= szamlalo +1; uzenetKuldo.putExtra("szoveg", Integer.toString(szamlalo)); //küldendő szöveg sendBroadcast(uzenetKuldo); //küldés uzenetKuldo.replaceExtras(new Bundle()); //törlés //ha a MainActivity aktív akkor így csipog, ha nem aktív akkor úgy csipog if (isActivityRunning(mMainActivity.getClass())) {mp1.start();}else {mp2.start();} //beállítjuk az új meghívást 1000 millisec múlva timer_am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 1000, timer_pi); } }; //az AlarmManager-t is előtérbe kell regisztrálni, mert különben újraindul a Service a timer meghívásakor timer_am = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE); Intent kati = new Intent("azonosito1234"); kati.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); timer_pi = PendingIntent.getBroadcast(PeldaegyService.this, 1, kati, 0); registerReceiver(timer_br, new IntentFilter("azonosito1234") ); timer_am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 1, timer_pi); return START_STICKY; } @Override public void onDestroy() { timer_am.cancel(timer_pi); unregisterReceiver(timer_br); } }
Az applikáció kilövése telefononként változó, a Huawei telefonoknál a beállítások menüben van egy olyan hogy Védett alkalmazások, ha itt az applikációnkat bejelöljük, akkor az alkalmazásválasztóból teljesen kilőhetjük, akkor is tovább fut a service, ellenkező esetben a bezárt/halott activity-nek ott kell lenni az alkalmazásválasztó menüben. A többi telefontípúst nem ismerem.