1

多くのコンポーネントで構成されるアプリに取り組んでいます。
アプリはAlarmManager、サーバーからのポーリングを行うために使用しています。データを表示する通常のアクティビティもあります ( と に保存されますSqlite) SharedPreferences

デバイスが boot( BOOT_COMPLETED) を終了しSharedPreferencesContextときにポーリングを開始する機能を追加しようとするまで、すべてがうまくonReceive(Context context, Intent intent)機能していましたBroadcastReceiver

もう 1 つのことは、Singleton を使用してすべての機能を処理していることSharedPreferencesですDB。この Singleton は、アプリContext最初のランチ アクティビティ( LoginActivity) を保持します。アプリ全体と Polling で使用しますBroadcastReciver

だから私は理解しています(信じています...)デバイスがブートを終了すると、私は Contextのものになり(私が取得していたLoginActivityコンテキストではありません)、これが問題の原因です(それは???)

このすべての前文の後、私が本当に必要としているのは、そのようなタスクのベストプラクティスアプローチです-アプリ上SharedPreferencesおよびDBアプリ全体でデータを保存および取得する方法:

  1. ユーザーがそれを実行するとき
  2. 経由でバックグラウンドタスクを実行するときAlarmManager
  3. BOOT_COMPLETEDブロードキャスト経由で自動起動するとき

Contextこの問題に苦しむことなく。例は素晴らしいでしょう。

編集: ここに私のコードスニペットがあります:

ConnectionManager.java- このクラスは REST リクエストの実装を保持し、SharedPreferences にデータを保存します:

public class ConnectionManager {

    //There are many more variables here - irrelevant for the example

    private CookieStore _cookieStore;
    private static ConnectionManager _instance;
    private SharedPreferences _sharedPref;
    private Context _context;
    private DataPollingBroadcastReceiver _dataPoller;

    private ConnectionManager(Context caller) {
        _context = caller;
        _sharedPref = PreferenceManager.getDefaultSharedPreferences(_context);
    }

    public static ConnectionManager getInstance(Context caller) {
        if (_instance == null) {
            _instance = new ConnectionManager(caller);
        }
        return _instance;
    }

public void setPollingActive(boolean active) {
    if (active) {
        SharedPreferences.Editor editor = _sharedPref.edit();
        editor.putString("myapp.polling", "true");
        editor.commit();
        startRepeatingTimer();
    } else {
        SharedPreferences.Editor editor = _sharedPref.edit();
        editor.putString("myapp.polling", "false");
        editor.commit();
        cancelRepeatingTimer();
    }
}

private void startRepeatingTimer() {
    if (_dataPoller!= null) {
        _dataPoller.SetAlarm(_context);
    } else {
        Toast.makeText(_context, "_dataPoller object is null",
                Toast.LENGTH_SHORT).show();
    }
}

private void cancelRepeatingTimer() {
    if (_dataPoller!= null) {
        _dataPoller.CancelAlarm(_context);
    } else {
        Toast.makeText(_context, "_dataPoller object is null",
                Toast.LENGTH_SHORT).show();
    }
}

    //There are many more methods here - irrelevant for the example

} 

MainBootListener.java: このクラスは、ポーリング メカニズムをアクティブにすることを想定しています - ConnectionManager に例外があるため、機能していません。

public class MainBootListener extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Activated by boot event",
                Toast.LENGTH_LONG).show();
        ConnectionManager cm = ConnectionManager.getInstance(context);
        cm.setPollingActive(true);
    }
}

DataPollingBroadcastReceiver.java: サーバーからデータをポーリングするこのクラス

public class DataPollingBroadcastReceiver extends BroadcastReceiver {

    private ConnectionManager _mngr;

    @Override
    public void onReceive(Context context, Intent intent) {
        if (_mngr == null) {
            _mngr = ConnectionManager.getInstance(context);
        }
        PowerManager pm = (PowerManager) context
            .getSystemService(Context.POWER_SERVICE);
        PowerManager.WakeLock wl = pm.newWakeLock(
            PowerManager.PARTIAL_WAKE_LOCK, TAG);
        // Acquire the lock
        wl.acquire();
        // You can do the processing here update the widget/remote views.
        Bundle extras = intent.getExtras();
        StringBuilder msgStr = new StringBuilder();
        Format formatter = new SimpleDateFormat("hh:mm:ss");
        msgStr.append(formatter.format(new Date()));
        // /////
        _mngr.updateDataFromServer();
        msgStr.append(" [Updated AccessControlTable]");
        Log.i(TAG, msgStr.toString());
        Toast.makeText(context, msgStr, Toast.LENGTH_SHORT).show();
        // ////
        // Release the lock
        wl.release();
    }

    public void SetAlarm(Context context) {
        if (_mngr == null) {
            _mngr = ConnectionManager.getInstance(context);
        }
        AlarmManager am = (AlarmManager) context
            .getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(context, DataPollingBroadcastReceiver.class);
        intent.putExtra(ONE_TIME, Boolean.TRUE);
        PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
        am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
            1000 * _mngr.getPollingIntervalInSeconds(), pi);
    }

    public void CancelAlarm(Context context) {
        if (_mngr == null) {
            _mngr = ConnectionManager.getInstance(context);
        }
        Intent intent = new Intent(context, DataPollingBroadcastReceiver.class);
        PendingIntent sender = PendingIntent
            .getBroadcast(context, 0, intent, 0);
        AlarmManager alarmManager = (AlarmManager) context
            .getSystemService(Context.ALARM_SERVICE);
        alarmManager.cancel(sender);
    }
}

もちろん、もっと多くのクラスがあります-私は必要最小限のものを持ってくるようにしています.

編集私が受け取った例外2:アプリケーションからポーリングメカニズムがアクティブであり(シングルトーンがLoginActivityをコンテキストとして保持している)、タスクマネージャーからアプリケーションを閉じると、ポーリングが停止し、この例外が表示されました:

12-29 14:02:03.061: E/AndroidRuntime(9402): FATAL EXCEPTION: main
12-29 14:02:03.061: E/AndroidRuntime(9402): java.lang.RuntimeException: Unable to start receiver my.app.DataPollingBroadcastReceiver : java.lang.NullPointerException
12-29 14:02:03.061: E/AndroidRuntime(9402):     at android.app.ActivityThread.handleReceiver(ActivityThread.java:2277)
12-29 14:02:03.061: E/AndroidRuntime(9402):     at android.app.ActivityThread.access$1500(ActivityThread.java:140)
12-29 14:02:03.061: E/AndroidRuntime(9402):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
12-29 14:02:03.061: E/AndroidRuntime(9402):     at android.os.Handler.dispatchMessage(Handler.java:99)
12-29 14:02:03.061: E/AndroidRuntime(9402):     at android.os.Looper.loop(Looper.java:137)
12-29 14:02:03.061: E/AndroidRuntime(9402):     at android.app.ActivityThread.main(ActivityThread.java:4898)
12-29 14:02:03.061: E/AndroidRuntime(9402):     at java.lang.reflect.Method.invokeNative(Native Method)
12-29 14:02:03.061: E/AndroidRuntime(9402):     at java.lang.reflect.Method.invoke(Method.java:511)
12-29 14:02:03.061: E/AndroidRuntime(9402):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
12-29 14:02:03.061: E/AndroidRuntime(9402):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
12-29 14:02:03.061: E/AndroidRuntime(9402):     at dalvik.system.NativeStart.main(Native Method)
12-29 14:02:03.061: E/AndroidRuntime(9402): Caused by: java.lang.NullPointerException
12-29 14:02:03.061: E/AndroidRuntime(9402):     at my.app.ConnectionManager.<init>(ConnectionManager.java:172)
12-29 14:02:03.061: E/AndroidRuntime(9402):     at my.app.ConnectionManager.getInstance(ConnectionManager.java:196)
12-29 14:02:03.061: E/AndroidRuntime(9402):     at my.app.DataPollingBroadcastReceiver .onReceive(DataPollingBroadcastReceiver .java:27)
12-29 14:02:03.061: E/AndroidRuntime(9402):     at android.app.ActivityThread.handleReceiver(ActivityThread.java:2270)
12-29 14:02:03.061: E/AndroidRuntime(9402):     ... 10 more

アプリが実行されていないときに2番目の例外が発生し、シングルトーンが初期化しようとしたよりも、adbからBOOT_COMPLETEDを送信しました。BroadcastReciver コンテキストを使用します。これは例外でした:

12-26 11:54:58.556: E/AndroidRuntime(12373): FATAL EXCEPTION: main
12-26 11:54:58.556: E/AndroidRuntime(12373): java.lang.RuntimeException: Unable to start receiver my.app.MainBootListener : java.lang.NullPointerException
12-26 11:54:58.556: E/AndroidRuntime(12373):    at android.app.ActivityThread.handleReceiver(ActivityThread.java:2277)
12-26 11:54:58.556: E/AndroidRuntime(12373):    at android.app.ActivityThread.access$1500(ActivityThread.java:140)
12-26 11:54:58.556: E/AndroidRuntime(12373):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
12-26 11:54:58.556: E/AndroidRuntime(12373):    at android.os.Handler.dispatchMessage(Handler.java:99)
12-26 11:54:58.556: E/AndroidRuntime(12373):    at android.os.Looper.loop(Looper.java:137)
12-26 11:54:58.556: E/AndroidRuntime(12373):    at android.app.ActivityThread.main(ActivityThread.java:4898)
12-26 11:54:58.556: E/AndroidRuntime(12373):    at java.lang.reflect.Method.invokeNative(Native Method)
12-26 11:54:58.556: E/AndroidRuntime(12373):    at java.lang.reflect.Method.invoke(Method.java:511)
12-26 11:54:58.556: E/AndroidRuntime(12373):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
12-26 11:54:58.556: E/AndroidRuntime(12373):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
12-26 11:54:58.556: E/AndroidRuntime(12373):    at dalvik.system.NativeStart.main(Native Method)
12-26 11:54:58.556: E/AndroidRuntime(12373): Caused by: java.lang.NullPointerException
12-26 11:54:58.556: E/AndroidRuntime(12373):    at my.app.ConnectionManager.<init>(ConnectionManager.java:172)
12-26 11:54:58.556: E/AndroidRuntime(12373):    at my.app.ConnectionManager.getInstance(ConnectionManager.java:196)
12-26 11:54:58.556: E/AndroidRuntime(12373):    at my.app.MainBootListener .onReceive(MainBootListener .java:14)
12-26 11:54:58.556: E/AndroidRuntime(12373):    at android.app.ActivityThread.handleReceiver(ActivityThread.java:2270)
12-26 11:54:58.556: E/AndroidRuntime(12373):    ... 10 more
4

3 に答える 3

1

あなたの問題は、BR で受け取るコンテキストが - であるという事実に関連しているはずです。ReceiverRestrictedContext例外が発生するに違いありませんReceiverCallNotAllowedException。例外がある場合は、常に投稿する必要があります。何が起こっているのかを正確に理解できるように、投稿してください。そうは言っても、受信機でやりすぎです!

そして、コードを単純化してください例 :

public void setPollingActive(boolean active) {
    _sharedPref.edit().putBoolean("myapp.polling", active).commit();
    (active) ? startRepeatingTimer() : cancelRepeatingTimer();
}

また、アラーム マネージャーによって起こされた場合、レシーバーのウェイクロックは必要ありません。アラーム マネージャはウェイクロックを保持しています。レシーバーで多くのことを行う場合は、WakefulIntentService が必要です。
最後に、シングルトンが必要な場合は、それを正しく行い、 enum を使用します。あなたの実装は間違っています - そもそもスレッドセーフではありません。

編集:投稿された例外によると、問題はコンテキストに関連していませんでした-ある時点で静的フィールドがnullになるため、NPEでした

于 2013-12-29T15:53:24.940 に答える