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.

Program letöltése