timoschloesser
timoschloesser

Reputation: 2802

CalledFromWrongThreadException in SharedPreferenceListener

I have an IntentService which updates a preference like this:

SharedPreferences.Editor editor = userPrefs.edit();
editor.putInt("COUNT", intCount);
editor.commit();

In my main activity I am listening for preference changes and update a TextView

userPrefsListener = new SharedPreferences.OnSharedPreferenceChangeListener() {

@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {

  if(key.equals("COUNT")) {

    final TextView txvCounter = (TextView) findViewById(R.id.TXV_COUNTER);

    if(txvCounter != null) {

    SharedPreferences userPrefs = getSharedPreferences("USER_SCORE", 0);
    int intCount = userPrefs.getInt("COUNT", 0);
    txvFishcounter.setText(String.format("%03d",intCount));
    }
  }
}
};

userPrefs.registerOnSharedPreferenceChangeListener(userPrefsListener);

For Android 2.3 everything works fine, but for 2.2 I'm getting CalledFromWrongThreadException everytime the OnSharedListener is triggered.

Thanks for your Help!

Upvotes: 1

Views: 289

Answers (2)

Force
Force

Reputation: 6382

The reason why the CalledFromWrongThreadException is raised, is because the OnChangeListener is not supposed to be called from the IntentService.

What you could do though, is sending a Broadcast (where you can actually include the value as well).

In case you are only using the SharedPreference for communication, you can replace it entirely (and I would recommend this, as SharedPreferences are wasting Write Cycles).

You could use a code like this for sending a Broadcast:

/**
 * Send an Intent with the Broadcast, a permission and a Bundle
 * 
 * @param context
 *            A context to use
 * @param broadcast
 *            String to use, eg. "de.bulling.smstalk.ENABLE"
 * @param permission
 *            Permission needed for receiving
 * @param bundle
 *            Extras to attach
 */
public static void send_broadcast(Context context, String broadcast, String permission, Bundle bundle) {
    //SettingsClass.log_me(tag, "Sending broadcast " + broadcast);
    Intent i = new Intent();
    i.setAction(broadcast);
    if (bundle != null) {
        i.putExtras(bundle);
    }
    if (permission != null) {
        context.sendBroadcast(i, permission);
    } else {
        context.sendBroadcast(i);
    }
}

You should include a custom permission as well, so other apps don't get the Broadcast, but it is not necessarily needed.

To receive the Broadcast, register a Receiver in your Activity, eg

private final BroadcastReceiver receiver = new BroadcastReceiver() {

    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle b = intent.getExtras();
        doSomething();
    }
};



@Override
public void onCreate(Bundle bundle) {
    super.onCreate(bundle);
    IntentFilter filter = new IntentFilter();
    filter.addAction(YOURBROADCAST);
    registerReceiver(receiver, filter);
    [...]
}

@Override
public void onDestroy() {
    unregisterReceiver(receiver);
    super.onDestroy();
}

Also you should consider using Handlers, so your methods in the OnChangeListener call are run by the MainUI thread.

Example:

Handler mHandler = new Handler();
Runnable myCode = new Runnable(){
     @Override
     protected void onRun() {
          yourCode();
          }
     };
mHandler.run(myCode);

This has also the advantage of running the code delayed with runDelayed(), so you don't have to use sleep, and you UI will be still responding.

Upvotes: 1

Free Wildebeest
Free Wildebeest

Reputation: 7562

The thread running the onSharedPreferenceChanged() callback is not the main UI thread in the 2.2 device which give you CalledFromWrongThreadException (therefore violating the second of the Android UI thread access rules of only calling UI toolkit from the UI thread). A straightforward way to get code to run on the UI thread is to use Activity.runOnUiThread(). This could be done simply by wrapping the body of your code in a new Runnable:

activity.runOnUiThread(new Runnable() {
    public void run() {
        // Code which updates UI controls goes here.
    }
});

A short discussion of the change between 2.2 and 2.3 and code snippets of why it occurred can be found here.

Upvotes: 3

Related Questions