Part-2:持久的前台Android服务,可以由UI启动,在睡眠模式下工作,并在手机重启时启动。

4

状态:非常感谢所有在第一部分和这里提供帮助和指导的人!我根据研究和得到的帮助编写了代码,并将工作代码放在EDIT-1中。欢迎批评以使代码更好。

情景:

我在第一部分中提出了问题,但由于某种原因,我无法坚持使用具有真正集成和健全性的代码来制定设计和正确策略。

这是一个冗长的问题,问题或答案不能在一个回合中得出结论或最终确定,因此我制作了这个第二部分作为概要。

也许我无能或只是阅读了太多分散的文档和不同策略的答案,或者答案具有不同的观点/编码风格。

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

问题:

这就是我想要并最终得出结论的内容,经过观察不同的答案:

需要每15分钟运行代码(即使手机处于睡眠状态)。我认为需要使用唤醒锁?

            //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.

请问有没有人能够全面地告诉我应该使用哪种代码?我正在做研究,非常感谢但也头痛不已。

请求:

只有您确信并且有经验时,请回答此问题。

编辑-1-开始:

这是我迄今为止制作的内容,请随意评论或批评。

启动时运行的引导程序类。

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);

        }
        }
      }


}

服务类
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)

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>
< p > 编辑-1-结束。

1个回答

4
如果您只需要每15分钟运行一次此代码,则真的不需要让服务在24/7运行。真的,不要这样做,这是个坏主意。您需要做的是:
1. 使用AlarmManager安排每15分钟的闹钟。然后使用BroadcastReceiver捕获此闹钟。此闹钟必须是RTC_WAKE_UP类型,以便在手机处于深度睡眠状态时唤醒它,并且必须是实时的,因为它将使用深度睡眠计时器。
2. BroadcastReceiver必须启动一个服务。现在必须像这样调用服务:
- 在BroadcastReceiver中获取wakelock并acquire()。 - 启动一个IntenetService(此类型的服务在工作完成后自行启动和结束)。 - 在服务中释放wakelock。

这里有一个很好的示例,展示了如何实现这个功能: commonsware's WakefulIntentService。你不必按照它的样子使用,你可以创建自己的服务和广播接收器。只需记住在调用服务之前获取锁定,在服务完成后释放它,否则服务可能无法被调用。

  1. 你的服务可以每15分钟执行一次你想要执行的任务。然后你可以在另外15分钟后重新安排另一个调用。你还可以在执行和重新安排之前通过共享首选项检查服务是否启用。

至于你的活动控制你的服务:

  1. 当按钮被按下时,检查共享首选项状态并保存相反的状态。
  2. 然后向同一接收器发送广播,如果他们已经启用,则启动你的服务(或将其安排到稍后执行)。
  3. 如果已禁用,则取消服务的任何计划。

至于你的开机要求:

  1. 在你的清单中声明,当调用android.intent.action.BOOT_COMPLETED操作时,会调用启动你的服务的同一个接收器。
  2. 声明权限:android.permission.RECEIVE_BOOT_COMPLETED

一个清单示例:

<?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>

取消和安排闹钟的示例:

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);
}

启动时开启服务的示例:

@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);
}

并在服务发布时释放锁定

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");
    }
  }
}

就是这样。


加上 +1 鼓励你的巨大努力!我会尝试编写代码并返回结果,感谢您的答案。 - Masood Ahmad
你好,非常感谢您提供的出色帮助,并接受了您的答案。仅作讨论,您对EDIT-1和顶部的状态有什么看法? - Masood Ahmad
不确定您是否需要前台服务。 您还需要检查您的唤醒锁定获取和释放。 如果您连续调用服务2次,则锁定将被获取2次,但是一旦第一个onStartCommand完成,它将调用释放,使第二个onStartCommand没有锁定。您可以将锁定设置为引用计数以避免此问题。 - superjugy
另外,由于您将在工作完成后终止服务,请考虑使用IntentService,它也提供了Android API(而不是第三方),它已经在自己的线程中执行此操作,并在完成上一个调用之前排队任何到达的调用。我不确定调用将终止您的服务的线程的意义何在,因为您可以立即调用wl.release和stopself而无需使用线程。这只会增加并发问题,没有任何收益。有特定的原因吗? - superjugy
再次感谢。如果您有任何其他建议,请告诉我。 - Masood Ahmad
好的,我从来没有安排过直接启动服务的RTC_Wakeup。我通常会启动一个广播接收器,然后再启动服务。这是因为我不知道服务是否可以以这种方式调用锁的获取。你需要测试一下。除此之外,我想你已经准备好了。 - superjugy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接