Android 服务丢失 ArrayList
编辑:这里是 PasteBin 上的源代码。我觉得我可能需要重新设计整个服务.. :(
我是 RingPack。基本思想是在后台启动一个服务,负责为用户切换铃声。我遇到了丢失对 ArrayList 的引用的问题 是每当用户从 Activity 中选择一个包时就启动它。
我认为我可能误解了生命周期的工作原理。我的意图 ; i.putExtra(RingService.ACTION, RingService.PACK_SET); i.putExtra(RingService.PASSED_PACK, currentPackId); RingActivity.this.startService(i);
我告诉服务将默认通知音设置为与“currentPackId”对应的包的第一个音。
当用户想要关闭 RingPack 时,禁用操作如下:
Intent i = new Intent(RingActivity.this, RingService.class); RingActivity.this.stopService(i);
Toast.makeText(RingActivity.this.getBaseContext(), RingActivity.this.getString(R.string.ringPackDisabled), Toast.LENGTH_SHORT).show();
因此,Service 的 onCreate 看起来像这样:
public void onCreate() {
db = new DbManager(this);
db.open();
vib = (Vibrator) getSystemService(VIBRATOR_SERVICE);
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_TICK);
registerReceiver(tReceiver, intentFilter);
timeTick = 0;
//figure out the widget's status
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
widgetEnabled = prefs.getBoolean(WIDGET_ALIVE, false);
//save the ringtone for playing for the widget
Uri u = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
r = RingtoneManager.getRingtone(this.getBaseContext(), u);
playAfterSet = false;
super.onCreate();}
然后它将它传递给 onStartCommand,后者返回 START_NOT_STICKY(因为我将手动创建和销毁 Service),后者将它传递给handleStart()。
@Override private void handleStart(Intent intent) {
final Intent i = intent;
if (isSdOk()) {
int action = i.getIntExtra(ACTION, -1);
if (action != -1) {
if (action == PACK_SET) {
playAfterSet = true;
Thread packSetThread = new Thread() {
@Override
public void run() {
int passedPackId = i.getIntExtra(PASSED_PACK, -1);
//we were passed the id
if (passedPackId != -1) {
checkPrefs();
if (!enabled)
initControl(passedPackId);
else
setPack(passedPackId);
packSetHandler.sendEmptyMessage(0);
}
}
};
packSetThread.start();
}
else if (action == NEXT_TONE) {
checkPrefs();
swapTone();
}
else if (action == PLAY_TONE) {
playCurrentTone();
}
else if (action == WIDGET_STATUS) {
widgetEnabled = intent.getBooleanExtra(WIDGET_ALIVE, false);
if (toneName != null)
RingWidget.update(getBaseContext(), toneName);
}
}
}}
isSdOk() 方法仅检查 SD 卡是否已安装,因为铃声存储在 SD 卡上。 initControl() 只是保存用户的默认铃声,以便当他们禁用我们时我们可以将其返回。 setPack() 方法如下所示:
private void setPack(int packId) {
//save the current pack id in the prefs for restarting
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(RingService.this.getBaseContext());
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(SAVED_PACKID, packId);
editor.commit();
//get the info we need to work with this pack
//it's path on the SD
//build the tones ArrayList to work from
grabPath(packId);
if (tones == null)
tones = new ArrayList<Integer>();
else
tones.clear();
mapTones(packId);
currIndex = 0;
setNotificationTone(tones.get(currIndex));}
音调 ArrayList 是我一直丢失的。这是它被初始化的地方。它保存了包内所有启用的铃声的 ID。我看到的 NullPointerException 是在 swapTone() 中:
private void swapTone() {
//locked
if (lockPref)
return;
//shuffle on
else if (shufflePref) {
int randIndex = currIndex;
while (randIndex == currIndex)
randIndex = (int) Math.floor(Math.random() * tones.size());
currIndex = randIndex;
}
//shuffle off
else {
if (currIndex != (tones.size() - 1))
currIndex++;
else
currIndex = 0;
}
setNotificationTone(tones.get(currIndex));}
我希望它的工作方式是,如果 setPack() 尚未调用,则永远不会调用 swapTone() 。同样,我的用户不断收到此错误,但我自己无法重现它。任何帮助将不胜感激。我为代码墙道歉,但我很困惑。也许我没有正确使用服务的概念?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好吧,尽管有“代码墙”,您的列表仍然不完整(例如,您说您的问题出在
tones
上,但从未显示它的定义位置)。我猜测它是您的Service
的数据成员。在这种情况下,如果服务在
PACK_SET
和NEXT_TONE
操作之间停止,则该值将为null
。如果 Android 因为运行时间过长而停止该服务,则很容易发生这种情况。即使使用START_NOT_STICKY
,如果 Android 停止服务后下一个startService()
调用是NEXT_TONE
,而不是PACK_SET
,你会遇到这个问题。IOW,您的数据模型(所选的铃声包)不会存储在持久位置,而是保存在 RAM (
tones
) 中。您有两个选择:尝试找出一种方法,使您不需要成为始终运行的服务,并根据需要从持久存储(例如数据库)中加载音调。这将是理想的,因为您正在消耗大量 RAM,而不是每微秒都为该 RAM 提供价值。例如,您可以将
AlarmManager
与IntentService
一起使用。使用
startForeground()
可以降低 Android 停止您的服务的可能性。代价是您需要在屏幕上放置一个通知
,以便用户知道您正在持续运行。您可以让Notification
引导用户进入可以配置或关闭服务的 Activity。Well, despite the "code wall", your listings are incomplete (e.g., you say your problem is with
tones
but never show where it is defined). I am going to take a guess that it is a data member of yourService
.In that case, it will be
null
if the service was stopped betweenPACK_SET
andNEXT_TONE
operations. This can easily occur, if Android stops the service because it has been running too long. Even withSTART_NOT_STICKY
, if the nextstartService()
call after Android stops the service isNEXT_TONE
, notPACK_SET
, you will have this problem.IOW, your data model (the chosen ring pack) is not stored in a persistent location, but rather is held in RAM (
tones
). You have two choices:Try to figure out a way that you do not need to be an always-running service, and load the tones out of a persistent store (e.g., database) as needed. This would be ideal, as you are chewing up a bunch of RAM while not delivering value for that RAM every microsecond. For example, you could use
AlarmManager
with anIntentService
.Use
startForeground()
to make it less likely that Android will stop your service. The trade-off is that you will need to place aNotification
on the screen, so the user knows you are constantly running. You might make thatNotification
lead the user to the activity where they can configure or shut down the service.