I have been reading the Android documentation and I am wondering if anyone can shed some light on what happens to a service instance when a service started with START_STICKY has it's process killed. I am assuming that the local state data (instance variables) are also lost. Does Android do anything to help re-populate the local state when it recreates the service?
I had some data that was sent to the service in an intent. In onStateCommand(), I would populate the service's instance data based on what was in the intent. From what I have read in the Android docs, the intent passed to onStartCommand() will be null when the service has been killed and restarted (with START_STICKY). Does this mean that I lose both the intent and the service's member data when the service is recreated?
When a process is killed and recreated, it goes through the entire lifecycle again (starting at onCreate). Depending on how it was killed and how you save data it may or may not be available to you.
As for getting the intent back, there's a flag for START_REDELIVER_INTENT that will redeliver the intent.
I recently came across this same problem. Service provides no built in means of saving state and the last intent may not be enough to get the service back to its previous state. My solution was to have the activity persist state and pass that state to the service via startService(). The service then just fires events at the activity, like:
here's an update
something died and here's the exception
I've been killed, please restart me with any necessary state
This approach cleaned up my design a lot, and both service and activity are resilient to being killed.
Android will not re-populate 'lost' data values when it re-starts your service so your code needs to cater for this eventuality.
My approach is to use all non-primitive state variables and to leave them as null. That way I can test for null and take appropriate steps to initialise them as and when.
I have taken to storing lightweight data that I want to persist across application restarts in the application's preferences.
use Internal Storage for Saving object or its field individually.
public void writeToInternalStorage(String fileName,String userName)
{
try{
String endOfLine = System.getProperty("line.separator");
StringBuffer buffer = new StringBuffer();
FileOutputStream fos = openFileOutput(fileName, Context.MODE_PRIVATE); //// MODE_PRIVATE will create the file (or replace a file of the same name) and make it private to your application. Other modes available are: MODE_APPEND, MODE_WORLD_READABLE, and MODE_WORLD_WRITEABLE.
buffer.append(userName.toString() + endOfLine);
fos.write(buffer.toString().getBytes());
Log.v(TAG, "writeFileToInternalStorage complete.. " + buffer.toString());
// writer.write(userName);
fos.close();
}
catch(Exception e)
{
Log.v(TAG, "Error: " + e.getMessage());
ExceptionNotificationMessage("writeToInternalStorage() Error: " + e.getMessage());
}
}
public String readFromInternalStorage(String fileName)
{
try{
File file = this.getFileStreamPath(fileName);
if(file.exists() == true)
{
Log.v(TAG, "readFileFromInternalStorage File found...");
FileInputStream fis = openFileInput(fileName);
StringBuilder buffer = new StringBuilder();
int ch;
while( (ch = fis.read()) != -1){
buffer.append((char)ch);
}
Log.v(TAG, "readFileFromInternalStorage complete.. " + buffer.toString());
fis.close();
return buffer.toString();
}
}
catch(Exception e)
{
Log.v(TAG, "Error: " + e.getMessage());
ExceptionNotificationMessage("readFromInternalStorage() Error: " + e.getMessage());
}
return "";
}
Related
Im starting several background services that take a while to configure due to web services calls, etc...
However Im starting these services via AsyncTask in order to avoid locking the main thread & GUI, however the GUI stills becomes locked.
Im using AsyncTask to call start a BluetoothService in my Activity onCreate():
I only included relevant lines of code:
//asynchronously - start the bluetooth service
new BluetoothServiceStart().execute();
Then in the BluetoothServiceStart service class, Im using Callable & Future task to get bytes from a web service:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// stop the service when the notification bar is pressed
if (intent != null && ACTION_STOP_SERVICE.equals(intent.getAction())) {
Log.d(TAG, "Stopping bluetooth service...");
broadcastServiceState(false);
stopSelf();
return START_NOT_STICKY;
}
// in case of attempting to restart while already running
clearSubscriptions();
Util.logToast(this, TAG, "Bluetooth service starting", Toast.LENGTH_SHORT, Util.DEBUG);
setupNotification();
// find and load JSON config file
loadDevices();
}
/**
* Gets the UUIDs of devices to connect to from the bluetooth JSON file.
*/
private void loadDevices() {
devicesLoaded = false;
Byte [] bytesFromWebService = null;
InputStream is = null;
URL url = null;
try {
if (ConnectivityMonitoring.hasNetwork()) {
//lets get the path of rest service that has the config file
String address = NgfrApp.getContext().getResources().getString(R.string.address);
String configuration_restful_port = NgfrApp.getContext().getResources().getString(R.string.rest_port);
String client_name = NgfrApp.getContext().getResources().getString(R.string.client_name);
String protocol = NgfrApp.getContext().getResources().getString(R.string.protocol);
//construct bluetooth config path
String bluetooth_config_path = NgfrApp.getContext().getResources().getString(R.string.bluetooth_path);
url = new URL(protocol + "://" + address + ":" + configuration_restful_port + bluetooth_config_path + client_name);
//lets execute an FutureTask (async task with a result, that blocks until result is returned).
ExecutorService exService = Executors.newSingleThreadExecutor();
Log.i(TAG, "making call to URL:" + url.toString());
Future<byte []> future = exService.submit(new CallWebServiceAndGetBytes(url));
bytesFromWebService = Util.toObjects(future.get());
}
if (bytesFromWebService != null) {
devices = readDeviceConfigFromWebService(bytesFromWebService);
Log.i(TAG, "Loaded configuration from URL:" + url.toString());
} else {
// read in the device UUIDs from the file
is = Util.scanForJson(getString(R.string.file_path), getString(R.string.bt_config_file));
devices = Util.readJsonStream(is, localConfigReadFunc);
Log.i(TAG, "Read config file from PATH:" + getString(R.string.file_path)+getString(R.string.bt_config_file));
}
if (devices != null) {
if (devices.size() < 1)
Log.w(TAG, "No devices to load!");
devicesLoaded = true;
}
// devices successfully loaded
if (devices != null && devicesLoaded) {
Log.d(TAG, "" + devices.size() + " BLE device IDs retrieved");
Log.d(TAG, "Devices: " + devices.toString());
}
// failed to load devices or find the JSON file
else {
Log.e(TAG, "Unable to load devices! Creating empty list...");
devices = new ArrayList<>();
}
}
catch (MalformedURLException e1) {
e1.printStackTrace();
}
catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
catch (FileNotFoundException e) {
Log.d(TAG, "Unable to locate bluetooth config file: " + getString(R.string.bt_config_file));
} catch (IOException e) {
Log.d(TAG, "Error reading json file: " + e.getMessage());
}
}//end loadDevices
Im getting an ANR & later crash.
Android Thread dump:
"main#4817" prio=5 tid=0x2 nid=NA waiting
java.lang.Thread.State: WAITING
blocks main#4817
at java.lang.Object.wait(Object.java:-1)
at java.lang.Thread.parkFor$(Thread.java:2135)
- locked <0x1a72> (a java.lang.Object)
at sun.misc.Unsafe.park(Unsafe.java:358)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:190)
at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:450)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at ngfr.wams.controller.core.services.RuleEngineService.loadRules(RuleEngineService.java:358)
at ngfr.wams.controller.core.services.RuleEngineService.updateRules(RuleEngineService.java:462)
at ngfr.wams.controller.core.services.RuleEngineService.onCreate(RuleEngineService.java:131)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3542)
at android.app.ActivityThread.-wrap4(ActivityThread.java:-1)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1786)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6938)
at java.lang.reflect.Method.invoke(Method.java:-1)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
The error line points to future.get().
I understand that the future.get() blocks, which is intended behavior in order to wait for the web service to return the bytes otherwise in low network connectivity/bandwidth situations the code will continue to execute and miss the network response & data.
The future.get() blocks the Service, however since the BluetoothService is started using BluetoothServiceStart AsyncTask, then why is the UI blocked???
Thanks
It's a common mistake to assume, that a service is running on another thread than the starting activity. It will run on the main thread as well as stated here: Services
Caution: A service runs in the main thread of its hosting process; the service does not create its own thread and does not run in a separate process unless you specify otherwise. If your service is going to perform any CPU-intensive work or blocking operations, such as MP3 playback or networking, you should create a new thread within the service to complete that work. By using a separate thread, you can reduce the risk of Application Not Responding (ANR) errors, and the application's main thread can remain dedicated to user interaction with your activities.
The startService call will not change this behaviour, even if you call it in an AsyncTask. So if you want to reanimate your app, you should create a thread inside of your service, which is not blocking the service, thus not blocking the main thread.
Note that IntentServices offload the task to a worker thread but automatically stop when there is no further work to do.
Clients send requests through Context.startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
Maybe this is not what you want with a bluetooth service.
Lifecycle methods always run on the man application loop, since the system creates the Service object for you. So, it's onStartCommand won't run on a background thread like you intend. If you want the Service to run on a background thread, use an IntentService.
I just started to learn developping android and I have a (probably) basic questions, but I didn't find anything clear.
I'm trying to store data in a JSON file, well, I've understood the logic to store it, my way is:
public boolean writeFileJson(JSONObject jobj) {
try {
FileOutputStream fOut = openFileOutput(file, Context.MODE_PRIVATE);
fOut.write(jobj.toString().getBytes());
fOut.close();
Toast.makeText(getBaseContext(), "file saved", Toast.LENGTH_SHORT).show();
} catch (Exception e1) {
e1.printStackTrace();
}
return true;
}
But my problem is to read, and concretely for the first time, because the way I do it is:
public String readFileJson() {
int c;
String temp = "";
try {
FileInputStream fin = openFileInput(file);
while ((c = fin.read()) != -1) {
temp = temp + Character.toString((char) c);
}
Toast.makeText(getBaseContext(), "file read", Toast.LENGTH_SHORT).show();
} catch (Exception e2) {
}
return temp;
}
So wen I read it for the first time and I want to acces to a parameter of my JSON is obvious that any JSON Object already exist in the file.
So I try to save a first JSON Object with my parameters in onCreate() method and save it in the file, but wen I run the app, and I stop it, it returns again to execute onCreate() and deletes all data stored during the run time.
So my question is: There is any way to init only for one time the parameters of the JSON file to could access for the first time unlike it's empty???
I hope that I'd explained well!!
Thanxxxx!!!!
You can create your own flag boolean and check when you start.
Well I don't understand well why you can use a flag if the flag is set to init value in onCreate(), but I've tried a basic method: check each time if the json file is null. But it's like so basic no? Is there any ther way, or trying to understand how to use flags without reset their values?
msgjson = readFileJson();
if(msgjson == "") {
json.put("ARRAY", jsonArray);
}else{
json = new JSONObject(msgjson);
}
Thanx!!
hi friend i am trying to make game and i need to create demo version of my game
i need a button that when users click on it the game run but just 3 time.
after 3 time users clicked on button the app show message "you should buy the game"
i used sharedpreferences . after 3 click on button the game show message but when user delete app and install it again , game not show message and run game. are there any ways to save sharedpreferences after delete app ?
this is my code :
Button frist = ((Button)findViewById(R.id.test));
final SharedPreferences check = getSharedPreferences("check",MODE_PRIVATE);
final Editor edit = check.edit();
frist.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
if(check.getInt("check1",0)<3){
Intent intent = new Intent(MainActivity.this,choose_name.class
);
startActivity(intent);
edit.putInt("check1", check.getInt("check1",0)+1).commit();
}
}
});
edit : friend im trying to write a file and save my setting into it , can i save my file in any directory that users cant see it and my file dont delete after user delete app and i dont need to use permission?
Try Backing up and restoring a user’s SharedPreferences. This will backup your preference to any External Storage.
Before user uninstall app, you can take back up of it. Add some logic that if file is present in External Storage, restore it back.
Edit
Use following code to back your preference.
Preferences stored in the SharedPreferences class get saved into /data/data//shared_prefs/_preferences.xml - backing up is easy, you just need to copy the contents of this file to external storage:
public static void backupUserPrefs(Context context) {
final File prefsFile = new File(context.getFilesDir(), "../shared_prefs/" + context.getPackageName() + "_preferences.xml");
final File backupFile = new File(context.getExternalFilesDir(null),
"preferenceBackup.xml");
String error = "";
try {
FileChannel src = new FileInputStream(prefsFile).getChannel();
FileChannel dst = new FileOutputStream(backupFile).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
Toast toast = Toast.makeText(context, "Backed up user prefs to " + backupFile.getAbsolutePath(), Toast.LENGTH_SHORT);
toast.show();
return;
} catch (FileNotFoundException e) {
error = e.getMessage();
e.printStackTrace();
} catch (IOException e) {
error = e.getMessage();
e.printStackTrace();
}
Use following code to restore preference
But restoring provides more of a challenge, since the shared_prefs file isn’t directly writable by the app, and the SharedPrefs class doesn’t directly expose its functionality to serialise from xml. Instead you have to parse the XML yourself and push the values back in. Thankfully the XML file has a straightforward structure, so it’s easy to loop over the elements and turn these back into preferences.
public static boolean restoreUserPrefs(Context context) {
final File backupFile = new File(context.getExternalFilesDir(null),
"preferenceBackup.xml");
String error = "";
try {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
Editor editor = sharedPreferences.edit();
InputStream inputStream = new FileInputStream(backupFile);
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.parse(inputStream);
Element root = doc.getDocumentElement();
Node child = root.getFirstChild();
while (child != null) {
if (child.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) child;
String type = element.getNodeName();
String name = element.getAttribute("name");
// In my app, all prefs seem to get serialized as either "string" or
// "boolean" - this will need expanding if yours uses any other types!
if (type.equals("string")) {
String value = element.getTextContent();
editor.putString(name, value);
} else if (type.equals("boolean")) {
String value = element.getAttribute("value");
editor.putBoolean(name, value.equals("true"));
}
}
child = child.getNextSibling();
}
editor.commit();
Toast toast = Toast.makeText(context, "Restored user prefs from " + backupFile.getAbsolutePath(), Toast.LENGTH_SHORT);
toast.show();
return true;
} catch (FileNotFoundException e) {
error = e.getMessage();
e.printStackTrace();
} catch (ParserConfigurationException e) {
error = e.getMessage();
e.printStackTrace();
} catch (SAXException e) {
error = e.getMessage();
e.printStackTrace();
} catch (IOException e) {
error = e.getMessage();
e.printStackTrace();
}
Toast toast = Toast.makeText(context, "Failed to restore user prefs from " + backupFile.getAbsolutePath() + " - " + error, Toast.LENGTH_SHORT);
toast.show();
return false;
}
Toast toast = Toast.makeText(context, "Failed to Back up user prefs to " + backupFile.getAbsolutePath() + " - " + error, Toast.LENGTH_SHORT);
toast.show();
}
Finally, you need to restart your app before these preferences will take hold:
if (restoreUserPrefs(context)) {
// Restart
AlarmManager alm = (AlarmManager) context.getSystemService(
Context.ALARM_SERVICE);
alm.set(AlarmManager.RTC,
System.currentTimeMillis() + 1000, PendingIntent.getActivity(context, 0,
new Intent(context, MainActivityName.class), 0));
android.os.Process.sendSignal(android.os.Process.myPid(),
android.os.Process.SIGNAL_KILL);
}
if user uninstall/delete app or clean data through setting then all the data in shared preference will get vanished
your problem is when user do such kind of things with your application shared preference value get initialized from 0/starting value
you can use webservice for that for ex. you can take users IMEI number through code and against of that you can save its sharedpreference value of " number of play counter"
and then through smart logic check your requirement
Android provides several options for you to save persistent application data. The solution you choose depends on your specific needs, such as whether the data should be private to your application or accessible to other applications (and the user) and how much space your data requires.
Your data storage options are the following:
Shared Preferences Store private primitive data in key-value pairs.
Internal Storage Store private data on the device memory.
External Storage Store public data on the shared external storage.
SQLite Databases Store structured data in a private database.
Network Connection Store data on the web with your own network server.
Android provides a way for you to expose even your private data to other applications — with a content provider. A content provider is an optional component that exposes read/write access to your application data, subject to whatever restrictions you want to impose. For more information about using content providers, see the Content Providers documentation.
SharedPreferences are deleted when you uninstall an app, so no: SharedPreferences aren't the best option here. I would recommend using some kind of remote database to track the usage of your app.
Shared Preferences are always cleared along with uninstalling app. But since android-21 Backup task stores preferences by default to cloud. Later when you uninstall then install newer version .You are probably going to use restored preferences. You just have to add this to your manifest ( or at least manifest for debug). -
<application ...
android:allowBackup="true">
...
</application>
Read this:http://developer.android.com/guide/topics/data/backup.html
It's Important to mention here that process of backup is blackbox .. you don't know when it starts, and period between checks ... so better for developing to disable it.
I'm trying to set up a log handler to output the Android log to file to external storage. The code below creates the log file, but no output is sent to the file, so something is obviously wrong with how the handler is configured. Or, perhaps this arrangement cannot be expected to work at all?
The function is called in onCreate() from the main activity.
private void logToFile(String path) {
try {
// Get package name
String packageName = MainActivity.class.getPackage().getName();
String logfileName = path + "/" + packageName + ".log";
Logger logger = Logger.getLogger(packageName);
logger.setLevel(Level.FINE);
FileHandler fileTxt = new FileHandler(logfileName);
SimpleFormatter formatterTxt = new SimpleFormatter();
fileTxt.setFormatter(formatterTxt);
logger.addHandler(fileTxt);
Toast.makeText(this, "Logging to " + logfileName, Toast.LENGTH_LONG).show();
} catch (IOException e) {
Log.d(TAG, e.getMessage());
}
Log.i(TAG, "logging to filesystem enabled");
}
To write to the logger declared above (and, thus, the attached handler which writes to a file), the following should be used instead of Log.i(TAG, "message")
private static final Logger logger = Logger.getLogger(TAG);
public void someFunction() {
logger.info("message")
}
These log messages will also appear in logCat/debugger, with the supplied TAG.
P.S. Java logging makes my head hurt...
I was frustrated at having to use Logger instead of standard Logcat Log.d(), Log.e(), etc. so I started using this Frankenstein's monster solution of reading from Logcat into a LogRecord and saving that using FileHandler.
This means you can limit the log file size easily, and retain your detailed Android logs.
But this isn't going to give you continuous output to file. If you don't mind pressing a button or calling it once a session though, then it shouldn't really matter since Logcat is constantly updated anyway.
(I strongly recommend calling from a non-UI thread.)
FileHandler fh=null;
String name;
if ( 0 == Environment.getExternalStorageState().compareTo(Environment.MEDIA_MOUNTED))
name = Environment.getExternalStorageDirectory().getAbsolutePath();
else
name = Environment.getDataDirectory().getAbsolutePath();
name += "/yourapp/yourapp";
try {
fh = new FileHandler(name, 1024*1024, 7, true); //Limit to 7 x 1MB files.
fh.setFormatter(new SimpleFormatter());
//Try to read Logcat.
try {
//Dumps the entire logcat to std output.
Process processD = Runtime.getRuntime().exec("logcat -v long -d");
BufferedReader bufferedReaderD = new BufferedReader(new InputStreamReader(processD.getInputStream()));
String lineD;
while ((lineD = bufferedReaderD.readLine()) != null){
//Send to the file handler.
fh.publish(new LogRecord(Level.ALL, lineD));
}
//Clear the logcat storage. Don't feel like rewriting old records.
Process processC = Runtime.getRuntime().exec("logcat -c");
} catch (IOException e) {
Log.e(TAG, "Could not get Logcat logs.");
e.printStackTrace();
}
} catch (Exception e) {
Log.e("MyLog", "FileHandler exception", e);
} finally {
if (fh != null)
fh.close();
}
I'm developing an Android real-time-data app that sends data (floats and ints) to a server on the local subnet via a TCP socket. The problem I'm facing is that after sending some data simultaneously the socket doesn't send anymore data at all. I debugged the app and it shows that data is being sent but doesn't show up on the server. After this happens if I close the connection the server doesn't even get the notification that the connection has been terminated which it should according to my design model. Meanwhile I get an exception on the app saying it can not write to a broken pipe. This tells me that the problem is with the app because I also did test using a desktop app and I can send huge amounts of data to the server and it gets delivered.
And please keep in mind that the data size I'm talking about here is 252 bytes per packet.
Here's my class I'm using. (This runs in an AsyncTask object )
public class Network
{
private Socket handle;
public static enum TASK
{
TASK_CONNECT, TASK_SEND, TASK_CLOSE
}
public Network()
{
}
public String lastError = "";
public boolean Connect(String host, int port)
{
try
{
lastError = "Connecting to server.";
handle = new Socket(host, port);
handle.setTcpNoDelay(true); //
handle.setSendBufferSize(SIZE_OF_PACKET); ///==> These don't seem to help at all
handle.setKeepAlive(true); ///
return true;
}catch(IOException e)
{
lastError += e.getMessage() != null ? " "+ e.getMessage() : "";
return false;
}
}
private void err(String e){
System.err.println(e);
}
private boolean SendPacket(byte buffer[])
{
OutputStream oStream = null;
err("sending: " + buffer.length + " bytes");
try
{
lastError = "Obtaining output stream.";
oStream = handle.getOutputStream();
lastError = "Error sending data.";
oStream.write(buffer);
oStream.flush();
return true;
}catch(Exception e)
{
lastError += e.getMessage() != null ? " "+ e.getMessage() : "";
}
return false;
}
public void Close()
{
try{ handle.close(); handle = null; }catch(Exception e){} // swallow exception
}
}
I send my data in a loop depending on how many numbers I have. I tried a Google search but didn't find anything relevant. Has anyone experienced this before? It's making me mad now.
EDIT: Wireshark shows incoming "red" packets that don't reach the desktop app (server)
Look at this picture.
You can see the first few have Len > 0 the red ones have 0.
I think it's time Google interfaced the USB so we can use it. At least that'd would have been my first option.
Should you not be calling oStream.close() after you flush the stream, given that you never use it again?
Also, you say that this is being run in an AsyncTask object. Is it possible that multiple threads could be attempting to send packets at the same time? If so, you might need some form of synchronisation around the SendPacket method.
Ok. I solved the issue by using UDP instead. Thank you all.
But I still didn't find the source of the problem.