2

编辑:这是PasteBin 的来源。我觉得我可能需要重新设计整个服务.. :(

我是RingPack的开发者。基本思想是在后台启动一个服务,负责为用户切换铃声。我在丢失对服务中的 ArrayList 的引用时遇到问题。我想我可能误解了生命周期是如何工作的。我的意图是在用户从 Activity 中选择一个包时启动它。

Intent i = new Intent(RingActivity.this, RingService.class); 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();

所以服务的 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(因为我将手动创建和销毁服务),后者将其传递给 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 卡是否已安装,因为铃声存储在其上。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() ,则永远不会被调用。同样,我的用户不断收到此错误,但我自己无法重现它。任何帮助将不胜感激。我为代码墙道歉,但我很困惑。也许我没有正确使用服务的概念?

4

1 回答 1

1

好吧,尽管有“代码墙”,但您的列表是不完整的(例如,您说问题tones出在但从不显示它的定义位置)。我猜测它是您的Service.

在这种情况下,null如果服务在PACK_SETNEXT_TONE操作之间停止。如果 Android 由于运行时间过长而停止服务,则很容易发生这种情况。即使使用START_NOT_STICKY, 如果startService()Android 停止服务后的下一个调用是NEXT_TONE, not PACK_SET,你也会遇到这个问题。

IOW,您的数据模型(选择的环包)没有存储在持久位置,而是保存在 RAM ( tones) 中。你有两个选择:

  1. 尝试找出一种不需要始终运行的服务的方法,并根据需要从持久存储(例如数据库)中加载音调。这将是理想的,因为您正在咀嚼一堆 RAM,而不是每微秒为该 RAM 提供价值。例如,您可以AlarmManager使用IntentService.

  2. 用于startForeground()降低 Android 停止服务的可能性。权衡是您需要Notification在屏幕上放置一个,以便用户知道您一直在运行。您可能会Notification引导用户进入他们可以配置或关闭服务的活动。

于 2011-02-21T20:03:23.607 回答