編集:ここに PasteBin のソースがあります。サービス全体を再設計する必要があるかもしれない.. :(
私はRingPackの開発者です。基本的な考え方は、バックグラウンドで起動されたサービスがあり、ユーザーの着信音を切り替えることです。サービス内の ArrayList への参照が失われるという問題があります。ライフサイクルの仕組みを誤解している可能性があると思います。私の意図は、ユーザーがアクティビティからパックを選択するたびに開始されることでした。
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 に渡します。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 カードに保存されているため、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() が呼び出されることはありません。繰り返しますが、私のユーザーはこのエラーを受け取り続けていますが、自分で再現することはできません。どんな助けでも大歓迎です。コードウォールで申し訳ありませんが、非常に混乱しています。おそらく、サービスの概念を正しく使用していないのでしょうか?