我有一个相当完整的位置跟踪应用,但有一个问题仍然困扰着我。启动时,如果跟踪服务当前正在运行,应用程序需要显示第二个活动。
但是,如果应用程序正在跟踪且MainActivity已被终止,然后用户通过常规启动器图标打开应用程序,则他们将被带到LoginActivity。 LoginActivity是应用程序的典型入口点,如清单中所定义:
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
如果LocationService当前正在跟踪,如何将MainActivity用作备用入口点?它应该如何恢复以前的状态数据?
我认为需要采取的措施:
解决这些问题的最佳方法是什么?
答案 0 :(得分:1)
在LoginActivity.onCreate()
中,您应检查跟踪服务是否正在运行,如果是,请立即将用户转发至MainActivity
。您希望这样做,就像用户点击Notification
一样,这样您就可以使用PendingIntent
中存储的Notification
中的附加内容。没问题。
在LoginActivity.onCreate()
执行此操作:
// Find the PendingIntent that is stored in the Notification
Intent notificationIntent = new Intent(this, MainActivity.class);
// Add any ACTION or DATA or flags that you added when you created the
// Intent and PendingIntent when you created the Notification
// Now get the `PendingIntent` that is stored in the notification
// (make sure you use the same "requestCode" as you did when creating
// the PendingIntent that you stored in the Notification)
PendingIntent pendingIntent = PendingIntent.getActivity(this,
requestCode, notificationIntent, PendingIntent.FLAG_NO_CREATE);
// Now start MainActivity with the Intent wrapped in the PendingIntent
// (it contains the extras)
pendingIntent.send();
// Finish LoginActivity (if you usually do that when you launch MainActivity)
finish();
答案 1 :(得分:1)
由于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)。如果您的代码都在同一个过程中,那么这种方法可能适合您。