part-2 persistent foreGround android service that

2019-02-07 09:25发布

问题:

Status: Really thankful to all who helped and guided here and in part-1! I have made code out of research and help given and have put that working code in EDIT-1. Criticisms are welcomed to make the code better.

Scenario:

I asked the question mention in Part-1, but for some reason I can persistently cannot make a design and correct strategy with code that has true integration and sanity.

It was a lengthy question and the question nor answer could have been concluded or finalized in just one episode, so I have made this second part as a synopsis.

May be I am incompetent or just disturbed reading so much scatered documentations and answers of different strategies, or answers are of different views/coding styles.

Part-1 part-1 persistent foreGround android service that starts by UI, works at sleep mode too, also starts at phone restart

Question:

Heres what I wanted and had finally concluded after observing different answers:

Need to run code every 15 minuts (even when phone is at sleep). Would need wake locks I think?

            //AT boot, check shared preferences to see boolean "serviceEnabled"?
                    //if true, set alarm manager to run a service every 15 minuts.
                    //if false, do nothing.

            //On "enable" button clicked.
                    //make "serviceEnabled" boolean true in shared preferences.
                    //start alarm manager to run a service every 15 minuts.

            //on "Disable" button clicked.
                    //make "serviceEnabled" boolean false in shared preferences.
                    //stop alarm manager and deregister it to run ever.

Can any one comprehensively tell... what code should I use...? I am humbly thanks full with a headache of research.

Requests:

Please only answer if you confidently and with experience know what you are doing.

EDIT-1-Start:

Here's what I have made so far. Please feel free to comment or criticize.

Booter class that runs on boot.

public class Booter extends BroadcastReceiver {

      public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
          Log.e("boot-status","BOOT_COMPLETED=================================================");
        //  SharedPreferences prefs = context.getSharedPreferences("$MYPACKAGE_preferences",0);
        //  if (prefs.getBoolean("startatboot",false)) {
        if(true){
        Intent updateIntent = new Intent();
        updateIntent.setClass(context, TheService.class);

        PendingIntent pendingIntent = PendingIntent.getService(context, 0, updateIntent, 0);
        AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, java.lang.System.currentTimeMillis()+5000,5000, pendingIntent);

        }
        }
      }


}

The service class

public class TheService extends Service{

    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return null;
    }
    PowerManager pm;
    PowerManager.WakeLock wl;
    @Override

      public int onStartCommand(Intent intent, int flags, int startId) {

        pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
        wl.acquire();


        startForeground(1, new Notification());

            ////// will do all my stuff here on in the method onStart() or onCreat()?

        Log.e("app-status","ping ====================================================");

        new Thread(new Runnable() {

            @Override
            public void run() {
                wl.release();
                stopSelf(); 
            }
        }).start();


        return START_STICKY;    
    }

    @Override
      public void onDestroy() {
        stop();
      }

    public void stop(){
        //if running
        // stop
        // make vars as false
        // do some stopping stuff
        stopForeground(true);


    }





}

GUI to start / stop

public class SettingsActivity extends Activity {
  // some code to initialize things

    buttonStop.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            Intent updateIntent = new Intent();
        updateIntent.setClass(SettingsActivity.this, TheService.class);

        PendingIntent pendingIntent = PendingIntent.getService(SettingsActivity.this, 0, updateIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        AlarmManager alarmManager = (AlarmManager)SettingsActivity.this.getSystemService(Context.ALARM_SERVICE);
        alarmManager.cancel(pendingIntent);
        //make sharepred boolean as false

        }
    });

    buttonStart.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            Intent updateIntent = new Intent();
        updateIntent.setClass(SettingsActivity.this, TheService.class);

        PendingIntent pendingIntent = PendingIntent.getService(SettingsActivity.this, 0, updateIntent, 0);
        AlarmManager alarmManager = (AlarmManager)SettingsActivity.this.getSystemService(Context.ALARM_SERVICE);
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, java.lang.System.currentTimeMillis()+5000,5000, pendingIntent);
        //make shared prefs boolean as true

        }
    });

Menifiest

  <?xml version="1.0" encoding="utf-8"?>
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.example.myapp"
      android:versionCode="1"
      android:versionName="1.0" >

      <uses-sdk
      android:minSdkVersion="10"
      android:targetSdkVersion="17" />

      <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> // irrelevent here
      <uses-permission android:name="android.permission.INTERNET" />        // my app uses these though in service class.
      <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
      <uses-permission android:name="android.permission.WAKE_LOCK" />

      <application

      android:allowBackup="true"
      android:debuggable="true"
      android:icon="@drawable/ic_launcher"
      android:label="@string/app_name"
      android:theme="@style/AppTheme" >
      <activity
          android:name="com.example.myapp.MainActivity"
          android:label="@string/app_name" >
          <intent-filter>
          <action android:name="android.intent.action.MAIN" />

          <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
      </activity>
      <activity
          android:name="com.example.myapp.SettingsActivity"
          android:label="@string/title_activity_settings" >
      </activity>

      <service android:name=".TheService" />
      <receiver android:name=".Booter" >
          <intent-filter>
          <action android:name="android.intent.action.BOOT_COMPLETED" />
          <category android:name="android.intent.category.HOME" />
          </intent-filter>
      </receiver>

      </application>

  </manifest>

EDIT-1-END.

回答1:

If you only need to run this code every 15 minutes. then you really don't need to keep the service running 24/7. Really, don't do it, it is bad idea. what you need to do is this:

  1. Use AlarmManager to schedule an alarm every 15 minutes. this alarm is then catched with a BroadcastReceiver. This alarm HAS TO BE RTC_WAKE_UP so that it wakes the phone up if it is in deep sleep and it has to be real time since it will use the deep sleep timer.

  2. Broadcast receiver will have to start a service. now this call to the service has to be done like this:

    • 2.1 get a wakelock in the BroadcastReceiver and acquire() it.
    • 2.2 start a IntenetService (This type of services start and end themselves after work is done)
    • 2.3 release the wakelock in the service

There is a good example of how to implement this here: commonsware's WakefulIntentService. You don't have to use it as is, you can make your own service and broadcast receiver. Just remember to acquire the lock before calling the service and releasing it when the service is done, otherwise the service might not get called.

  1. Your service does whatever you want to execute each 15 minutes. Then you can reschedule another call in another 15 minutes. You can also check if the service is enabled via the shared preferences before executing and rescheduling.

As for your activity controlling your service:

  1. When the button is pressed check the shared preference state and save the opposite.
  2. Then send a broadcast to your same receiver that starts your service if they have enabled it (or schedule it for later).
  3. If it was disabled, then cancel any schedules for the service.

As for your boot up requirement:

  1. Declare in your manifest that the same receiver that starts your service is called when the action android.intent.action.BOOT_COMPLETED is called.
  2. Declare the permission: android.permission.RECEIVE_BOOT_COMPLETED

An example manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.yourcompany.yourapp"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="7"
        android:targetSdkVersion="17" />

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:allowBackup="false"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <activity
            android:name="com.yourcompany.yourapp.activities.HomeActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name="com.yourcompany.yourapp.services.ActionHandlerService" />
        <receiver android:name="com.yourcompany.yourapp.receivers.BootReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.HOME" />
            </intent-filter>
        </receiver>

    </application>
</manifest>

Example of cancelling and scheduling alarms:

public synchronized static void disableTimers(final Context context)
{
  Log.i(TAG, "Canceling Alarms");
  final Intent in = new Intent(Constants.Actions.TIMER_ACTION);
  final PendingIntent pi = PendingIntent.getBroadcast(context, 0, in, PendingIntent.FLAG_UPDATE_CURRENT);
  ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).cancel(pi);
}

public synchronized static void enableTimer(final Context context)
{
  Log.i(TAG, "Enabling Alarm");
  final Intent in = new Intent(Constants.Actions.TIMER_ACTION);
  final PendingIntent pi = PendingIntent.getBroadcast(context, 0, in, PendingIntent.FLAG_UPDATE_CURRENT);
  ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + FIFTEEN_MINUTES, pi);
}

Example of starting the service on boot up:

@Override
public void onReceive(final Context context, final Intent intent)
{
  final Intent in = new Intent(context, MyService.class);
  in.setAction(Actions.BOOT_RECEIVER_ACTION);
  Log.i(TAG, "Boot completed. Starting service.");
  MyService.acquireLock();
  context.startService(in);
}

And on the service release the lock

private static volatile WakeLock mStaticWakeLock = null;

private synchronized static WakeLock getLock(final Context context)
{
  if (mStaticWakeLock == null)
  {
    final PowerManager mgr = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    mStaticWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_PARAMETER);
    mStaticWakeLock.setReferenceCounted(true);
  }
  return mStaticWakeLock;
}

@Override
protected final void onHandleIntent(final Intent intent)
{
  try
  {
    run(intent);
  }
  finally
  {
    final WakeLock lock = getLock(getApplicationContext());
    if (lock.isHeld())
    {
      lock.release();
      Log.i(TAG, "Releasing WakeLock");
    }
  }
}

That's it.