I have a background service that reads cpu usage and frequency and displays it on notification bar
In application settings(Preferences) i have a option to chose to display only frequency only load or both
But method for getting shared preferences wont get most recent SharedPreference
it get SharedPreference only first time service starts and if i chose diferent option in Preference screen it wont update in service
Here is the code
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Runnable runnable = new Runnable() {#Override
public void run() {
while (thread) {
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
items = sharedPrefs.getString("notif", "freq");
System.out.println(items); //this keeps displaying the same value even if i go to Preference screen and change to something else
if (items.equals("freq") || items.equals("both")) {
}
if (items.equals("load") || items.equals("both")) {
} //reading frequency and load depending on what is selected
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandler.post(new Runnable() {#Override
public void run() {
if (thread) {
createNotification(); //create notification
}
}
});
}
}
};
new Thread(runnable).start();
return START_STICKY;
}
SOLVED
Because my service was running in separate process i had to add this flag when accesing shared preference
private final static int PREFERENCES_MODE = Context.MODE_MULTI_PROCESS;
and change like this
sharedPrefs = this.getSharedPreferences("preference name", PREFERENCES_MODE);
Ensure you write your data to shared preferences correctly, specifically you commit() your changes, as docs say:
All changes you make in an editor are batched, and not copied back to
the original SharedPreferences until you call commit() or apply()
Here is example code:
SharedPreferences.Editor editor = mPrefs.edit();
editor.putBoolean( key, value );
editor.commit();
I think the error is on the line
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
where you are passing 'this' from inside a thread? Can you change it with the application context?
Related
Hello guys and Happy new year to all!
I'm having a weird trouble in my app which I can't seem to fix. It should be a logic error, but I'm not able to somehow catch it.
Here is my code
public String[] str={"Disabled","Sound Quality Prefered","Bass Prefered","Battery Prefered",};
public int ThemePresetValue = 0;
private int SelectedThemePresetValue = 0;
public void presets() {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
alertDialog.setTitle("Select Your Sound Preset");
alertDialog.setNegativeButton("Cancel", null);
alertDialog.setPositiveButton("Select", themePresetDialogPositiveListener);
alertDialog.setSingleChoiceItems(str, ThemePresetValue, PresetListListener);
alertDialog.show();}
DialogInterface.OnClickListener PresetListListener =
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
SelectedThemePresetValue = which;
}
};
DialogInterface.OnClickListener themePresetDialogPositiveListener =
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
mPreset = "";
ThemePresetValue = SelectedThemePresetValue;
if (ThemePresetValue == 0) {
mPreset = "Disabled";
} else if (ThemePresetValue == 1) {
mPreset = "Sound Quality Prefered";
} else if (ThemePresetValue == 2) {
mPreset = "Bass Prefered";
} else if (ThemePresetValue == 3) {
mPreset = "Battery Prefered";
}
if (mPreset.equals("Disabled")) {
disabler();
} else if (mPreset.equals("Sound Quality Prefered")) {
SoundQPreset();
} else if (mPreset.equals("Bass Prefered")) {
bassPreset();
} else if (mPreset.equals("Battery Prefered")) {
batteryPreset();
}
}
};
The problem is that after I choose one of the presets the choice sticks until the app is closed from multitasking (MainActivity gets restarted or killed). Then if I re-open the app, the choice of dialog is re-set onto 0 ("Disabled").
Why is this happening? Do you have a solution?
Yes, the field is created each time anew for the respective object and since this object (i.e. the activity) is destroyed the memory holding the field is freed up as well. So the field's lifespan is bounded by that of the object. To make it continuous, you better save the value in SharedPreferences, or in general to write it out to some storage, before destroying the activity, e.g. in onPause() and then fetch it from those preferences in onCreate() or onResume() callbacks. For example:
/*--- Saving ---*/
SharedPreferences prefs =
getApplicationContext().getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
prefs.edit().putInt(KEY_NAME, VALUE).apply();
/*--- Retrieving ---*/
int oldValue =
getApplicationContext().getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
.getInt(KEY_NAME, 0);
PREFERENCES_NAME is the file name of your shared preferences file. KEY_NAME is the key, under which you save and later retrieve the stored value. VALUE is simply the value to save.
Hope this helps!
As you are not persisting the user's choice, the choice remains in memory until activity finishes. You should save the user's choice locally using SharedPreferences or sqlite for instance!
When the activity restarts you can read the saved value and set the option as selected!
The question is how to communicate with an Android phone to a server, so that if the Activity is left and the call in the Activity was not successful to repeat the transaction once again automatically. Just now I use the AsyncTask of Android to communicate with the server:
new AsyncTask<String, Void, List<String>>() {
#Override
protected void onPreExecute(
showWaitDialog();
}
#Override
protected void onPostExecute(List<String> msgList) {
//here I put the handling after the POST ie. error and success handling
hideWaitDialog();
if (msgList.isEmpty() {
//success handling --> starting an new Activity
} else {
errorView.setText (...);
errorLayout.setVisibility (View.VISIBLE);
}
}
#Override
protected List<String> doInBackground(String... params) {
List<String> msgs = new ArrayList<String>();
try{
//for example submitting an JSONObject
JSONObject result = HttpUtils.sendHttpPost(
AppConstants.WEB_URL, jsonObject);
//error handling on the result
boolean hasErrors = JsonResult.isOk(result);
if (hasErrors) {
// adding errors to msgs list
String[] errorMessages = JsonResult.getErrorMessages (result,...);
fillList (msgs, errorMessages);
return msgs;
}
} catch (CommunicationError er) {
msgs.add (er...);
}
return msgs;
}
}
The problem with this approach is, that if I don't have a successful transmission of the data I must stay in the same Activity. Until now I show an error message to the user and he is in charge to submit by a button again the results to the server.
What I'm looking for is some Activity that remains persistent in the memory which runs later in the case that the transmission wasn't made.
As an application case I use this to dynamically upload pictures for a Waypoint in a map if I pressed that waypoint. In some case it can happens that the connection to the mobile service provider isn't available (mountains, forest, far apart from antenna). Then I want to leave the map Activity and switch to the detail view of this waypoint. In the success case I put the picture into my model classes and make an serialization. If the user clicks again on the same waypoint the picture is not loaded again. In the non success case I don't want to wait that the user clicks against on the waypoint to retrieve the image. In fact I need a background task, some sort of a queue that pictures of waypoints that are already visited on couldn't be retrieved are loaded until the communication part gives back a positive result and the image can be written into the model. The next time the user is pressing the Waypoint the picture will be then present.
Are there any best practices for making such a code implementation?
Is there any example around?
Is there a better way of doing this?
Yes, you need to Implement Intent Service for this requirement
According to the developers website
The IntentService class provides a straightforward structure for running an operation on a single background thread.
For complete details and working source code, Go through the Android Docs
Thanks to the answer of David.
I just read after the suggestion the tutorial at
[1] http://code.tutsplus.com/tutorials/android-fundamentals-intentservice-basics--mobile-6183
After my tests I prefered a Service (not an IntentService)
and created a service: SubmissionService
public class SubmissionIntentService extends Service {
private List<PendingMessage> pMsgList = new CopyOnWriteArrayList<PendingMessage>();
private Handler handler = new Handler();
private boolean hasAppStopped = false;
private Runnable runner;
public SubmissionIntentService() {
super();
Log.d (TAG, "Service created...");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
PendingMessage pMessage = (PendingMessage) intent.getParcelableExtra(AppConstants.MESSAGE_OBJECT);
synchronized (pMsgList) {
pMsgList.add(pMessage);
}
if (runner == null) {
handler.postDelayed(runner = initializeRunnable(), 500);
}
return Service.START_NOT_STICKY;
}
private void runAsLongAppIsActive (Runnable runner) {
if (!hasAppStopped) {
handler.postDelayed (runner, SOME_INTERVAL_CONSTANT);
}
}
private Runnable initializeRunnable() {
Runnable result;
result = new Runnable() {
#Override
public void run() {
if (pMsgList.isEmpty()) {
runAsLongAppIsActive (this);
return;
}
PendingMessage[] pMArray = null;
synchronized(pMsgList) {
pMArray = pMsgList.toArray (new PendingMessage[pMsgList.size()]);
}
if (pMArray==null || pMArray.length==0) {
runAsLongAppIsActive (this);
return;
}
Log.d (TAG, "Message List size is actually :"+pMArray.length);
for (PendingMessage pM: pMArray) {
try {
JSONObject jsonMess = JSONSendMessage.buildOutput (pM);
JSONObject result = HttupUtils.sendHttpPost (WEB_URL, jsonMess);
boolean hasErrors = JSONResult.isOk (result);
if (hasErrors) {
//TODO: error handling in case of transmission
//don't remove the message from the queue
runAsLongAppIsActive(this);
return;
}
//remove pending transmission of the queue if success
synchronized (pMsgList) {
pMsgList.remove (pM);
}
//inform over receiver if activity is shown
Intent broadcastIntent = new Intent();
//put data in intent
sendBroadcast (intent);
//more important
WayPointModel model = ModelInstance.getWayPointModel();
model.addToModel (pM, result);
model.store();
} catch (Exception e) {
continue; //try to send other messages
}
}
runAsLongAppIsActive (this);
}
};
return result;
}
}
#Override
public void onDestroy() {
hasAppStopped = true;
handler.removeCallbacks (runner);
super.onDestroy();
}
}
Further I added a ResponseReceiver:
public class ResponseReceiver extends BroadcastReceiver {
public static final String ACTION_RESP = "MESSAGE_PROCESSED";
#Override
public void onReceive(Context context, Intent intent) {
//work in progress...
}
}
and in the Activity where I want to be informed about events:
public class SomeActivity extends Activity {
private ResponseReceiver receiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentFilter filter = new IntentFilter(ResponseReceiver.ACTION_RESP);
filter.addCategory(Intent.CATEGORY_DEFAULT);
receiver = new ResponseReceiver();
registerReceiver(receiver, filter);
...
}
}
and finally to send messages over Http:
Intent msgIntent = new Intent(this, SubmissionIntentService.class);
msgIntent.putExtra(...);
startService(msgIntent);
don't forget to declare the service in your manifest:
<service android:name="ch.xxx.app.service.SubmissionIntentService" />
Observations:
- I called the method startService(...) from different Activities. The constructor is only called once.
==> I have just on instance of the service for all Activities (exactly what I need).
What I don't get until now:
- Putting back data to the Activity. What is if the Activity is at the moment no shown?
I am trying to restore audio to the position it was and the file it was when the user left the fragment. To do this I save the location of the audio file, and the seek position using personal prefs, along with a boolean for whether or not the audio was playing when the user left. I save this info first thing in onPause().
When I resume, I initialize the views etc. and the very last thing I do in onResume is read from personal prefs and play the audio stored there is appropriate.
However when I try to play from onResume, the media completion listener gets called immediately and the file gets skipped.
I have been running tests and I know that the media player is handed the right data, is prepared correctly and set to play.
The way I am trying to play the audio is the same way I do it if a user clicks manually to play audio, and that works flawlessly.
Only when trying to 'restore' the audio to where it was when a user left does the completion listener get called immediately.
Has anyone seen this before?
public void setAudioURLAndPLay(Context context, String url)
{
Log.d(TAG, "setAudioURLAndPLay");
CacheQueue.getInstance().addImmediateTaskToQueue(CacheQueue.AUDIO_TASK, context, url, 0, handler);
}
private void playCahcedFile(String location)
{
Log.d(TAG, "playCahcedFile");
try
{
this.reset();
this.setAudioStreamType(AudioManager.STREAM_MUSIC);
this.setDataSource(location);
this.setOnPreparedListener(new OnPreparedListener()
{
#Override
public void onPrepared(MediaPlayer mp)
{
setPlay();
}
});
this.prepareAsync();
}
catch (Exception e)
{
Log.d(TAG, "Exception", e);
}
}
public void setPlay()
{
Log.d(TAG, "setPlay");
this.start();
this.setProgressHandler(this.listener);
}
and where the calls are being made
public void initializeFromResume()
{
PersonalPrefs prefs = new PersonalPrefs(getActivity());
if (!prefs.isPLaying())
{
return;
}
else
{
playNewAudio(prefs.getURL());
// ((ActivityMain) getActivity()).getMediaManager().setSeek(prefs.getSeek());
}
}
private void playNewAudio(String url)
{
getMediaManager().setAudioURLAndPLay(getActivity(), url)
mediaState = MediaState.playing;
initializeSeekBar();
getMediaManager().setOnCompletionListener(this);
mediaController.togglePlayButton(mediaState);
}
I figured it out and will post the answer to anyone who has similar troubles in the future.
Just need to run a post delayed. Not exactly amazing, but it works.
h.postDelayed(new Runnable()
{
#Override
public void run()
{
PersonalPrefs prefs = new PersonalPrefs(getActivity());
playNewAudio(prefs.getURL());
}
}, 1000);
I have three images with me and i want them to appear on first layout xml like a splash view so that they can be viewed only once i.e that activity will be called only once when app get's installed or if app get's a new update otherwise app should always start from the Second activity, i don't know how should i begin with this :
Can any one tell me any idea how this can be done.
To show splash for only once.
Next part of this question is here
Coding will be much appreciated.
Save a flag in the Preferences when you start up the application, after you've done the welcome screen stuff. Check for this flag before you show the welcome screen. If the flag is present (in other words, if it's not the first time), don't show it.
In your activity:
SharedPreferences mPrefs;
final String welcomeScreenShownPref = "welcomeScreenShown";
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
// second argument is the default to use if the preference can't be found
Boolean welcomeScreenShown = mPrefs.getBoolean(welcomeScreenShownPref, false);
if (!welcomeScreenShown) {
// here you can launch another activity if you like
// the code below will display a popup
String whatsNewTitle = getResources().getString(R.string.whatsNewTitle);
String whatsNewText = getResources().getString(R.string.whatsNewText);
new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle(whatsNewTitle).setMessage(whatsNewText).setPositiveButton(
R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).show();
SharedPreferences.Editor editor = mPrefs.edit();
editor.putBoolean(welcomeScreenShownPref, true);
editor.commit(); // Very important to save the preference
}
}
Try this :
public class MainActivity extends Activity {
private Thread mSplashThread;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.splash);
final MainActivity sPlashScreen = this;
mSplashThread = new Thread() {
#Override
public void run() {
try {
synchronized (this) {
wait(4000);
}
} catch (InterruptedException ex) {
}
finish();
Intent intent = new Intent();
intent.setClass(sPlashScreen, StartNewActivity.class);// <-- Activity you want to start after Splash
startActivity(intent);
}
};
mSplashThread.start();
} catch (Exception e) {
}
}
#Override
public boolean onTouchEvent(MotionEvent evt) {
try {
if (evt.getAction() == MotionEvent.ACTION_DOWN) {
synchronized (mSplashThread) {
mSplashThread.notifyAll();
}
}
} catch (Exception e) {
}
return true;
}
}
you put an Image in splash.xml to show
to do this you have to detect the first launch of your application. To do so you can store a boolean value as #Nirav suggested.
And for the splash screen, You can consider using Fragments and ViewPager to create an activity which will only be shown for the first time
Only on old android devices (2.x) I've a crash caused by stackoverflow everytime I rotate emulator. If I comment "preferenze()" emulator does not crash but app does not keep new settings. Can this code create an infinite loop? Is a incorrect code? What should be to runs correctly? Thanks!
private boolean preferencesChanged;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
private void preferenze() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
CheckboxPreference = prefs.getBoolean("checkboxPref", true);
ListPreference = prefs.getString("listpref", "");
numeronotifiche = prefs.getString("notify", "");
Sound = prefs.getString("sound", "");
barranotifiche = prefs.getBoolean("keep", false);
natura = prefs.getBoolean("suoninaturasino", false);
snatura = prefs.getString("suoninaturascelta", "");
snaturaold = prefs.getString("snaturaoldvalue", "");
if (snaturaold != snatura){
stopService(new Intent(this, UnUsedService.class));
}
SharedPreferences prefs2 = getSharedPreferences(PRIVATE_PREF, 0);
Editor editor10 = prefs2.edit();
editor10.putString("snaturaoldvalue", snatura);
editor10.commit();
// suoni attivati (o no)
if (natura){
startService(new Intent(this, UnUsedService.class));
}
else {
stopService(new Intent(this, UnUsedService.class));
}
if (barranotifiche){
showNotification();
}
else {
cancelNotification();
}
GestioneAllarme alarm = new GestioneAllarme();
if (CheckboxPreference){
if (numeronotifiche.equals("3")){
alarm.CancelAlarm(this);
alarm.SetAlarm3(this);
}
else if (numeronotifiche.equals("1")){
alarm.CancelAlarm(this);
alarm.SetAlarm1(this);
}
else if (numeronotifiche.equals("2")){
alarm.CancelAlarm(this);
alarm.SetAlarm2(this);
}
else {
//
}
}
else {
//
GestioneAllarme alarm2 = new GestioneAllarme();
alarm2.CancelAlarm(this);
}
//
if (Sound.equals("")){
Sound = "2";
Editor editor = prefs.edit();
editor.putString("sound", "2");
editor.commit();
}
if (ListPreference.equals("")){
ListPreference = "1500";
Editor editor = prefs.edit();
editor.putString("listpref", "1500");
editor.putInt("indexfade", 1500);
editor.commit();
}
if (numeronotifiche.equals("")){
numeronotifiche = "2";
Editor editor = prefs.edit();
editor.putString("numeronotifiche", "2");
editor.commit();
}
fade = Integer.parseInt(ListPreference);
notify = Integer.parseInt(numeronotifiche);
if (fade == 500){
animazione = R.style.MyCustomTheme1;
fadein = R.anim.fadein500;
fadeout = R.anim.fadeout500;
}
else if (fade == 1000){
animazione = R.style.MyCustomTheme2;
fadein = R.anim.fadein1000;
fadeout = R.anim.fadeout1000;
}
else if (fade == 1500){
animazione = R.style.MyCustomTheme3;
fadein = R.anim.fadein1500;
fadeout = R.anim.fadeout1500;
}
else if (fade == 2000){
animazione = R.style.MyCustomTheme4;
fadein = R.anim.fadein2000;
fadeout = R.anim.fadeout2000;
#Override
protected void onResume() {
super.onResume();
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
preferencesChanged = true;
}
};
sp.registerOnSharedPreferenceChangeListener(listener);
protected void onStop(){
super.onStop();
if(preferencesChanged){
//Update the app
preferenze();
}
}
public class Preferences extends PreferenceActivity implements OnSharedPreferenceChangeListener{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.layout.preferences);
}
#Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String listpref) {
It seems that as soon as preferenze() always modify the shared preferences you will have an infinite loop.
Since you didn't post the complete code it's difficult to say. But I guess that your code is such that it always modify prefs only on android 2.x
you can try something like this to avoid infinite loop.
private boolean isPreferenzeRunning = false;
...
listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String listpref) {
if(!isPreferenzeRunning)preferenze();
}
};
...
private void preferenze()
isPreferenzeRunning = true;
try{
...
}finally{isPreferenzeRunning = false;}
}
That code wouldn't even compile.
The code in preferenze() will return the preference values (boolean, String, int, etc), not the Preference objects. By changing the values in that method, you will also cause a StackOverflowError.
What is the need for a OnSharedPreferenceChangeListener?
// here several if/if else to change value
Those sentences probably change the Shared Preferences, that in turn will fire your listener, which in turn will call preferenze, ..., and so on. If this continues for ever, a S.O will be thrown. Now depending on the conditions it could happen that the preferenze method only reads but does not modify anything. In this case the loop will end.
And about the error being observed in 2.X devices only, it could be due to 4.x devices being more recent and probably having more RAM memory.
UPDATE:
The code is still incomplete. Looks like there are two activities: the one you posted first and the new one. I guess (that's all I can do with the code you posted) you have a PreferenceActivity to show the settings and allow the user to change them, and the listener is there to update other parts of the application according to the new settings. The problem is that when the listener is called, it itself modifies the settings, and this in turn will call the listener again, which will modify the preferences again, and so on. This will throw an SOException once the heap runs out of memory.
A way of rearranging the code to solve this would be:
Register the OnSharedPreferenceChangeListener in your activity's onResume instead of onCreate, and deregister it in the onPause method (calling unregisterOnSharedPreferenceChangeListener). Deregistering is very important because we don't want to listen for changes once the user leaves the screen, or the activity is recreated by the system (for instance when the device rotates):
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//We have removed the listener registration from here
}
#Override
protected void onResume() {
super.onResume();
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String listpref) {
//I'll show what to do here in point 2.
}
};
sp.registerOnSharedPreferenceChangeListener(listener);
}
#Override
protected void onPause() {
super.onPause();
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
sp.unregisterOnSharedPreferenceChangeListener(listener);
}
With your current code, each time the user changes a single setting, the preferenze method is called to update the app. So if it changes 5 fields, the method is called 5 times. What we could do now is to check for changes just once. I assume you don't care how many fields the user has changed, since all you need is to know if there are changes or not. So in the listener, instead of calling preferenze, you could set a boolean flag to true:
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
preferencesChanged = true;
}
Ok, so now we have a way of telling whether the settings have changed or not. When the user is done and the activity is about to be closed, the methods onPause, onStop and onDestroy will be called in this order. You can use one of these methods to check the boolean flag and only if there are changes, update the app. This way, if the user changes 1, 3 or 20 fields, we will update the app just once at the end. You can do this in any of the 3 thethods, but it's very important to do this AFTER deregistering the listener (onPause), or else you'll run into problems again. Example:
protected void onStop(){
super.onStop();
...
if(preferencesChanged){
//Update the app
preferenze();
}
}
You might need to change some things but overall you get the idea.