Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to run service in android platform continuously even after locking the device?

We have been working on developing service for android platform.

In our service we need to send GPS data (Lat and Long) of device to some external REST service after every one minute.

It is running fine for almost 15 minutes after locking of device. But after that it does not send any data.

After unlocking the device, it start again to send data over REST service.

My Code So far

public class MainActivity extends AppCompatActivity {

private PendingIntent pendingIntent;
private PowerManager.WakeLock wakeLock;

public static final String USER_NAME = "USERNAME";

String username;
String password;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Intent alarm = new Intent(this, AlarmReceiver.class);
    boolean alarmRunning = (PendingIntent.getBroadcast(this, 0, alarm, PendingIntent.FLAG_NO_CREATE) != null);
    if(alarmRunning == false) {
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, alarm, 0);
        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), 30000, pendingIntent);
    }

    PowerManager mgr = (PowerManager)this.getSystemService(Context.POWER_SERVICE);
    wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakeLock");
    wakeLock.acquire();
 }

public class BackgroundService extends Service  {

private boolean isRunning;
private Context context;
private Thread backgroundThread;

@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onCreate() {
    this.context = this;
    this.isRunning = false;
    this.backgroundThread = new Thread(myTask);
}

private Runnable myTask = new Runnable() {
    public void run() {
        // Do something here
        login("admin","admin");
        stopSelf();
    }
};

@Override
public void onDestroy() {
    this.isRunning = false;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if(!this.isRunning) {
        this.isRunning = true;
        this.backgroundThread.start();
    }
    return START_STICKY;
}

private void login(final String strLatitude, final String strLongitude) {


    class LoginAsync extends AsyncTask<String, Void, String> {

        String charset = "UTF-8";
        HttpURLConnection conn;
        DataOutputStream wr;
        StringBuilder result = new StringBuilder();
        URL urlObj;
        JSONObject jObj = null;
        StringBuilder sbParams;
        String paramsString;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // loadingDialog = ProgressDialog.show(MainActivity.this, "Please wait", "Loading...");
        }

        @Override
        protected String doInBackground(String... params) {
            String uname = params[0];
            String pass = params[1];

            sbParams = new StringBuilder();

            try {
                sbParams.append("name").append("=")
                        .append(URLEncoder.encode(uname, charset));
                sbParams.append("&");
                sbParams.append("password").append("=")
                        .append(URLEncoder.encode(pass, charset));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

            try {

                String url="http://192.168.0.122:1234/YegoService.svc/AddVehicleMovement";
                URL object=new URL(url);

                HttpURLConnection con = (HttpURLConnection) object.openConnection();
                con.setDoOutput(true);
                con.setDoInput(true);
                con.setRequestProperty("Content-Type", "application/json");
                con.setRequestProperty("Accept", "application/json");
                con.setRequestMethod("POST");

                JSONObject parent = new JSONObject();

                parent.put("strValidatorID","111");
                parent.put("TXT_LAT", "28.25252525");

                parent.put("TXT_LONG", "77.7777777");
                parent.put("DAT_DATE", "");
                con.connect();

                OutputStreamWriter wr = new OutputStreamWriter(con.getOutputStream());
                wr.write(parent.toString());
                wr.flush();
                wr.close();

                InputStream input = con.getInputStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(input));
                String line;

                while ((line = reader.readLine()) != null) {
                    result.append(line);
                }

                con.disconnect();

            } catch (IOException e) {
                e.printStackTrace();
            }
            catch (Exception ex) {
                ex.printStackTrace();
            }

            return result.toString();
        }

        @Override
        protected void onPostExecute(String result){
            String s = result.trim();
        }
    }

    LoginAsync la = new LoginAsync();
    la.execute("admin", "admin");

}

}

public class AlarmReceiver extends BroadcastReceiver {
String strLatitude;
String strLongitude;

@Override
public void onReceive(Context context, Intent intent) {
    Intent background = new Intent(context, BackgroundService.class);
    context.startService(background);
}
}

What to do?

like image 681
Rajeev Kumar Avatar asked Apr 04 '16 09:04

Rajeev Kumar


2 Answers

One approach could be for you to rely on the AlarmManager : once you subscribe to an AlarmManager the system itself runs your code at the interval you setup, even if your app is not active. Each time it runs you can decide to process some code... So you completely avoid the need to keep a service alive.

What you need is an Alarm class that will handle the AlarmManager intent.


Create your Alarm :

public class Alarm extends BroadcastReceiver 
{

    private static final String TAG = "Alarm";

    @Override
    public void onReceive(Context context, Intent intent) 
    {   
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "");
        wl.acquire();

        /***************
        Here you can do your stuff...
        This will be triggered every second. 
        Send data from here, or better: call an IntentService
        that will take care of it.
        ****************/

        wl.release();
    }

    public void SetAlarm(Context context)
    {
        Intent i = new Intent(context, Alarm.class);

        boolean alarmUp = (PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_NO_CREATE) != null);

        if (alarmUp)
        {
            // The alarm is already running, do not set it twice
        }
        else
        {
            AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
            PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
            am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000, pi); // 1000 Millisec means it will trigger it every second... and RTC_WAKEUP means that it will wake up your device if needed.
        }
    }

    // later on, use this method if you want to manually cancel the AlarmManager :

    public void CancelAlarm(Context context)
    {
        Intent intent = new Intent(context, Alarm.class);
        PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0);
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        alarmManager.cancel(sender);
    }
}

In your Manifest declare this Alarm BroadcastReceiver

<receiver 
    android:name=".utils.Alarm"
    android:process=":remote" >
</receiver>

And from where you want in your Activity call this AlarmManager !

Alarm alarm = new Alarm();

@Override
protected void onCreate(Bundle savedInstanceState) 
{

    alarm.SetAlarm(this);

}

// or if elsewhere you need to stop the Alarm :
alarm.CancelAlarm(this);

This is the main idea. Now you need to deal with screen on or off. For this 2 solutions : you can register for the device screen state intent and manage the AlarmManager on/off... or you can let the AlarmManager always running but checking if the device is on/off before sending data...

Hope this will help !

like image 149
JBA Avatar answered Oct 22 '22 09:10

JBA


You are acquiring the wake lock in your Activity. The problem here is that when the device is locked, your Activity gets pushed to the background. After 15 minutes of inactivity, Android is simply killing the process. This releases the wake lock. The device goes to sleep.

Now, the next time your alarm goes off, the device wakes up, your BroadcastReceiver is triggered, onReceive() is called, it starts your Service, but then the device goes back to sleep because there is no wake lock, so the `Service doesn't do anything.


Another approach, if you want to prevent the phone from going to sleep while your app is running, would be to acquire the wake lock in the Service. In this case, you don't want to call stopSelf() every time your Runnable runs. You would want to keep your Service running until you want to stop it, at which time you would call stopService(). This way, the Service would always be active (even though it isn't doing anything) and it would prevent the device from sleeping through the wake lock. This may, however, put an unacceptable drain on the battery (you'll have to test it).

You need to acquire the wake lock in the BroadcastReceiver and make sure that the Service gets started and acquires a wake lock before the device goes back to sleep. Have a look at WakefulBroadcastReceiver, which you can use to implement this behaviour.

like image 4
David Wasser Avatar answered Oct 22 '22 10:10

David Wasser