如何在广播接收器中的单例处理程序上获取实例?

How to get instance on my singleton handler in Broadcast Receiver?

我在尝试使我的服务正常工作时陷入困境,我相信我刚刚进行了合并。我正在尝试制作一个计步器服务,它使用一个处理程序,该处理程序在其上实现了一个计步器传感器。每 6 小时我想用步数信息更新我的服务器,在接近一天结束时重置步数并在我的处理程序上设置另一个警报以每 6 小时执行相同的操作。处理程序还定期将数据发送到 activity 以读取其记录的步数。

然而,一切正常,但我无法在我的广播接收器中获得我的处理程序的实例。因此永远不会设置另一个警报并且永远不会重置步骤。谁能帮我解决这个问题?

具体来说,广播接收器中的第一行代码 (StepCounterHandler handler = StepCounterHandler.getInstance();) 在我的测试中总是 return null。

这是我的代码:

Android Manifest.xml:

    <receiver android:name=".Account.StepCounterService.StepCountUpdaterAlarm"
        android:exported="true">
    </receiver>

    <service
        android:enabled="true"
        android:process=":stepCounterService"
        android:name=".Account.StepCounterService.StepCounterService" />

StepCounterHandler.java:

public class StepCounterHandler extends Handler implements SensorEventListener {
private boolean onSensorChangedFirstCall = true;
private int previousSteps;
private int cumulativeSteps;

private List<String> debugInfo = new ArrayList<String>() {};

private static StepCounterHandler stepCounterHandler;

public static StepCounterHandler getInstance(Context callingContext, Looper looper) {
    if(stepCounterHandler == null) stepCounterHandler = new StepCounterHandler(callingContext, looper);
    Log.d("MyService", "setting handler instance:" + (stepCounterHandler == null));
    return stepCounterHandler;
}

public static StepCounterHandler getInstance()
{
    if(stepCounterHandler == null) return null;
    return stepCounterHandler;
}

private StepCounterHandler(Context callingContext, Looper looper) {
    super(looper);

    SensorManager sensorManager = (SensorManager) callingContext.getSystemService(Context.SENSOR_SERVICE);
    Sensor stepSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);

    setAlarm(callingContext);

    if (stepSensor != null) {
        sensorManager.registerListener(this, stepSensor, SensorManager.SENSOR_DELAY_UI);
    } else {
        Toast.makeText(callingContext, "No step sensor has been detected on this device.", Toast.LENGTH_SHORT).show();
    }
}

public void setAlarm(Context context)
{
    AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);

    Intent intent = new Intent(context, katibh.project.walkersquest.Account.StepCounterService.StepCountUpdaterAlarm.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,    intent, 0);

    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.DAY_OF_YEAR, -1);
    calendar.set(Calendar.HOUR_OF_DAY, 23);
    calendar.set(Calendar.MINUTE, 50);
    calendar.set(Calendar.SECOND, 00);


    SimpleDateFormat formatter= new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z");
    Date date = new Date(System.currentTimeMillis());
    debugInfo.add("Attempted update at:" + formatter.format(date));

    while(calendar.getTimeInMillis() < System.currentTimeMillis())
    {
        calendar.add(Calendar.MINUTE, 1);
    }

    debugInfo.add("Attempting next update at:" + calendar.getTime());

    // CANNOT RUN ALARM EVERY MINUTE IN setAndAllowWhileIdle, ONE MINUTE ALARM IS FOR TESTING PURPOSES
    //        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    //            alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
    //        } else {
                   alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
    //        }
}

@Override
public void handleMessage(@NonNull Message msg) {
    super.handleMessage(msg);
}

@Override
public void onSensorChanged(SensorEvent event) {
    int totalSteps = (int) event.values[0];
    if(onSensorChangedFirstCall)
    {
        previousSteps = totalSteps;
        onSensorChangedFirstCall = false;
    }

    cumulativeSteps = totalSteps - previousSteps;
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}

public synchronized int getCumulativeSteps()
{
    cumulativeSteps = (int)(5000.0 * new Random().nextDouble());
    return cumulativeSteps;
}

public synchronized void resetStepsCounter() {
    cumulativeSteps = 0;
    previousSteps = 0;
    onSensorChangedFirstCall = true;
}
}

广播接收器:

public class StepCountUpdaterAlarm extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
    //Handler is null here when i the code below is executed.
    StepCounterHandler handler = StepCounterHandler.getInstance();
              
    //Do something...
    sendToServer(handler.getCumultiveSteps)
    handler.setAlarm(context);

    if(isTimeToReset())
       handler.resetStepsCounter();
}

private boolean isTimeToReset()
{
    Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.HOUR_OF_DAY, 23);
    calendar.set(Calendar.MINUTE, 50);
    calendar.set(Calendar.SECOND, 00);

    return calendar.getTimeInMillis() < System.currentTimeMillis();
}
}

上面的示例设置并应每分钟触发一次警报(出于测试目的),因此我使用了 set 方法。我知道 setAndAllowWhileIdle 只能每 15 分钟发送一次,但主要问题是我无法在接收器中获取处理程序的实例。它总是 return null,我不确定为什么。

如果 zero-parameter getInstance() 返回 null,则您没有单例。

你有两个进程。 StepCounterService:stepCounterService 进程中。 StepCountUpdaterAlarm 处于默认进程中。这些进程不共享任何对象,因此它们将具有单独的 StepCounterHandler 个单例实例。如果您之前没有在默认进程中设置 StepCounterHandler,它将是 null.

如果您希望在 StepCounterServiceStepCountUpdaterAlarm 之间共享单例,则它们需要在同一进程中。将 StepCountUpdaterAlarm 移动到 :stepCounterService 进程或将 StepCounterService 移动到默认进程。

感谢 @CommonsWare,在我的例子中,我不得不将广播接收器移动到服务进程中。

AndroidManifest.xml(之前):

<receiver android:name=".Account.StepCounterService.StepCountUpdaterAlarm"
    android:exported="true">
</receiver>

<service
    android:enabled="true"
    android:process=":stepCounterService"
    android:name=".Account.StepCounterService.StepCounterService" />

AndroidManifest.xml(之后):

<receiver android:name=".Account.StepCounterService.StepCountUpdaterAlarm"
    android:process=":stepCounterService" 
    android:exported="true">
</receiver>

<service
    android:enabled="true"
    android:process=":stepCounterService"
    android:name=".Account.StepCounterService.StepCounterService" />