感谢David Wasser 的帮助,我解决了我的问题。我已在此答案中包含了所有需要的内容,以帮助遇到此问题的其他人。
LoginActivity 创建后,它(间接)检查我们是否正在跟踪:
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// It's possible that we're already tracking. If so, we want to skip LoginActivity and start MainActivity.
if(Utilities.doesTrackingPendingIntentExist(this))
{
if(Utilities.isLocationServiceRunning(this))
{
recreateMainActivity();
}
else
{
Log.e(TAG, "TRACKING? PendingIntent exists, but LocationService isn't running.");
Utilities.deleteTrackingPendingIntent(this);
}
}
// LocationService wasn't running, so we can display the login screen and proceed as normal
setContentView(R.layout.activity__login);
...
如果是这样,它会获取 LocationService 为通知创建的 PendingIntent,并使用它来启动 MainActivity。
private void recreateMainActivity()
{
// This intent is an abstract description of what we want to accomplish: starting MainActivity
Intent intentToStartMainActivity = new Intent(this, MainActivity.class);
// Get the PendingIntent that's stored in the notification (using the "requestCode" that LocationService used
// when it created the PendingIntent)
PendingIntent pendingIntent = PendingIntent.getActivity
(
this, LocationService.NOTIFICATION_ID, intentToStartMainActivity, PendingIntent.FLAG_NO_CREATE
);
try
{
Log.i(TAG, "LocationService is running. Attempting to recreate MainActivity!");
// Now start MainActivity with the Intent wrapped in the PendingIntent (which also contains the required data in extras)
pendingIntent.send();
finish();
}
catch(PendingIntent.CanceledException e)
{
Log.e(TAG, "It seems that our PendingIntent was cancelled. Hmmm....", e);
}
}
这是我们用来确定是否正在跟踪的实用程序函数。它根据 ID 和 Intent 检查是否已经存在匹配的 PendingIntent。如果 PendingIntent 为 null,则表示未找到匹配项,因此我们认为这意味着通知不存在并且我们不跟踪。在 API 23+ 中,您可以直接检查通知是否存在,这会比这稍微安全一些(因为如果服务意外终止,通知消失后 PendingNotification 可能会继续存在)。
public static boolean doesTrackingPendingIntentExist(Context context)
{
Intent intentToStartMainActivity = new Intent(context, MainActivity.class);
// Get the PendingIntent that's stored in the notification (using the "requestCode" that LocationService used
// when it created the PendingIntent)
PendingIntent pendingIntent = PendingIntent.getActivity
(
context, LocationService.NOTIFICATION_ID, intentToStartMainActivity, PendingIntent.FLAG_NO_CREATE
);
if(pendingIntent == null)
{
Log.i(TAG, "TRACKING? No matching PendingIntent found. LocationService probably isn't running.");
return false;
}
else
{
Log.i(TAG, "TRACKING? A matching PendingIntent was found. LocationService seems to be running.");
return true;
}
}
另一种方法,通过遍历所有正在运行的服务以查找名称匹配来检查服务是否正在运行。由于我的 LocationService 并不总是在 onDestroy() 之后立即死亡,因此仅此一项并不是检查我们是否正在跟踪的完全可靠的方法。可以与其他方法结合使用,更加确定跟踪状态。
public static boolean isLocationServiceRunning(Context context)
{
Log.i(TAG, "TRACKING? Reviewing all services to see if LocationService is running.");
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
// Go through every service until we find LocationService
for(ActivityManager.RunningServiceInfo service : activityManager.getRunningServices(Integer.MAX_VALUE))
{
Log.v(TAG, "TRACKING? service.getClassName() = " + service.service.getClassName());
if(LocationService.class.getName().equals(service.service.getClassName()))
{
Log.i(TAG, "TRACKING? LocationService is running!");
return true;
}
}
Log.i(TAG, "TRACKING? LocationService is NOT running.");
return false;
}
注意:
LocationService 在完成跟踪时取消 PendingIntent 非常重要,否则这将不起作用。不幸的是,不能保证 LocationService.onDestroy() 会被操作系统调用。 Android可以在不调用它的情况下杀死它。它以前台优先级运行,因此不太可能被意外杀死,但可能会导致 PendingIntent 在您不跟踪时存在。
结合这两个实用功能是确定我们是否正在跟踪的最安全方法。
旁注:
我尝试使用静态 volatile 布尔值来跟踪 LocationService 中的跟踪状态,但不同的进程似乎使用不同的 ClassLoaders 有自己的内存空间(感谢 David)。如果您的代码都在同一个进程中,那么该方法可能适合您。