I search a lot and I tried several ways but I couldn't find anything that avoid my error.
I'm working on app for my learning, where is an String in my MainActivity and then I call it in my Service. I tried something like this:
This next one goes in my myService.class
//In my myService.class that extends Service
public class myService extends Service{
AlarmManager manager;
PendingIntent pintent;
String te;
#Override
public void onCreate()
{
manager = (AlarmManager)(this.getSystemService(Context.ALARM_SERVICE));
pintent = PendingIntent.getBroadcast( this, 0, new Intent("blahblah"), 0 );}
#Override
public int onStartCommand(Intent intent, int flags, int startid)
{
super.onStartCommand(intent, flags, startid);
te = intent.getStringExtra("tst"); //if I change this line to te = "something", everything goes fine
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive( Context context, Intent intent )
{
Toast.makeText(getApplicationContext(),te, Toast.LENGTH_SHORT).show();
}
};
this.registerReceiver(receiver, new IntentFilter("blahblah") );
// set alarm to fire 30 min and waits 2 min
manager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 1000*30*60, 1000*2*60, pintent);
return START_STICKY;}
public IBinder onBind(Intent p1)
{
// TODO: Implement this method
return null;
}
}
Here the code runs perfectly, but when I exit my App, it crashes. After 1 minute, my device shows again that my App crashes, confirming my app "successfully" ran into background. What is wrong with it?
I also learned I could use IntentService instead of Service, wich one is better for long tasks and what is the difference between them ?
EDIT***
I received the following error: java.lang.NullPointerExeption.
So I change this:
te = intent.getStringExtra("tst");
To this:
try
{
te = intent.getStringExtra("tst");
}
catch(NullPointerException e)
{}
When I changed it my app works with any error, but The problem is: I need to retrieve my String from my MainActivity, when I close my app my service runs without errors but my "te" String takes null valor, what can I do to "save" my String in my service, to be able to use it and keeping showing the "working" message after I close my App ? Should I use SharedPreferences ?
Hope I was clear.
IntentService is different from Service.
IntentService Self kills the service as it finishes the task. Whereas Service runs for ever unless you kill it.
For my coding experience, I would use IntentService only for small tasks that run for a couple of seconds and use Service for long running one and call StopSelf() as needed.
Kindly post the log to answer your problem
Related
I am kinda confused at the moment. What is the "correct" / "optimal" way for a daily network operation in an android app?
Pseudocode:
If newDay
HTTP Request to server
If responseOfRequest equals something
Do something
If HTTP Request is unsuccessfull (no internet, server down, ...)
Try again in 1 hour
How can I achieve that? I thought about a JobService but my minSDK is below Android 5.
Cheers,
DDerTyp
What you need is a service to run the logic in the background and an alarm.
A little bit of theory first:
https://developer.android.com/training/scheduling/alarms.html#tradeoffs
A repeating alarm is a relatively simple mechanism with limited flexibility. It may not be the best choice for your app, particularly if you need to trigger network operations. A poorly designed alarm can cause battery drain and put a significant load on servers.
If you own the server that is hosting your app's data, using Google Cloud Messaging (GCM) in conjunction with sync adapter is a better solution than AlarmManager.
https://developer.android.com/training/sync-adapters/running-sync-adapter.html
By default, all alarms are canceled when a device shuts down.
You will need to set up the alarm somewhere in your app, at the beginning, but saving a flag because you don't want to set up this alarm every time the user opens the app
if (!appSettings.isAlarmSetUp()) {
final AlarmManager am = (AlarmManager) context.getSystemService(ALARM_SERVICE);
final Intent i = new Intent(context, CustomService.class);
final Intent intentNotRepeat = new Intent(context, CustomService.class);
final PendingIntent pi = PendingIntent.getService(context, 0, i, 0);
am.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + AlarmManager.INTERVAL_HALF_HOUR, AlarmManager.INTERVAL_DAY, pi);
appSettings.setAlarmSetUp(true);
}
Here more info about alarms:
https://developer.android.com/training/scheduling/alarms.html#type
As you can see, this alarm is waking up a CustomService, where you will do all your logic
public class CustomService extends IntentService {
public CustomService(String name) {
super(name);
}
#Override
protected void onHandleIntent(Intent intent) {
// Request to server
client.requestToServer()
.subscribe(response -> {
// Successful response
doSomething(response);
}
},
error -> {
// Error
createAlarmInOneHour();
});
}
}
i have a class(location2.java) that finds location for me,I use this code in my class :
What is the simplest and most robust way to get the user's current location on Android?
and I have a service that override that abstract "locationResult";Now i want my service after running its codes,service doesn't finish and stay alive for receiving location from location2.java.
appreciating any help for this.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Location2.LocationResult locate = new Location2.LocationResult() {
#Override
public void gotLocation(Location location1, Boolean Gps, Boolean Net) {
if (location1 != null) {
Log.e("Loc", String.valueOf(location1.getLatitude()));
}
try {
//this is a method that i want to be run after receiving location from location2.java
json_maker(location1, speed_computation(location1), Gps, Net);
} catch (JSONException e) {
e.printStackTrace();
}
}
};
Location2 location = new Location2();
location.getLocation(context, locate);
return Service.START_FLAG_REDELIVERY;
}
The most successful way is to use return START_STICKY.
"and if service wants to restart, multiple constants for example START_STICKY can be used.doesn't it?" - Yes, we can use.
START_STICKY
Constant to return from onStartCommand(Intent, int, int) if this service's process is killed while it is started (after returning from onStartCommand(Intent, int, int)), then leave it in the started state but don't retain this delivered intent. Developer guide: Android
My app has one service already that handles notifications, but I need a second one that runs persistent in the background listening for incoming data from the Pebble smartwatch.
However, for some reason, even though the service is declared in the Android manifest and will launch with the app, it closes immediately and permanently.
I don't really want to use a foreground service, because I don't feel like I should have to. There are plenty of service that run quietly in the background in a persistent fashion like Facebook and Music Boss.
The service is being started in the main activity's onCreate, so why is my service being killed immediately?
From PebbleService.java:
package net.thevgc.quotes;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import com.getpebble.android.kit.PebbleKit;
import com.getpebble.android.kit.util.PebbleDictionary;
import java.util.UUID;
public class PebbleService extends Service {
private PebbleKit.PebbleDataReceiver appMessageReciever;
private static final int KEY_AUTHOR = 1;
private static final int KEY_QUOTE = 0;
private static final UUID WATCHAPP_UUID = UUID.fromString("18451441-8451-4418-4514-418451441845");
public void onCreate() {
super.onCreate();
}
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
String[] extras = (String[]) intent.getSerializableExtra("data");
final String quote = extras[0];
final String author = extras[1];
// Define AppMessage behavior
if (appMessageReciever == null) {
appMessageReciever = new PebbleKit.PebbleDataReceiver(WATCHAPP_UUID) {
#Override
public void receiveData(Context context, int transactionId, PebbleDictionary data) {
// Always ACK
PebbleKit.sendAckToPebble(context, transactionId);
// Send KEY_QUOTE to Pebble
PebbleDictionary out = new PebbleDictionary();
out.addString(KEY_QUOTE, mainActivity.quote[0]);
out.addString(KEY_AUTHOR, mainActivity.quote[1]);
PebbleKit.sendDataToPebble(getApplicationContext(), WATCHAPP_UUID, out);
}
};
// Add AppMessage capabilities
PebbleKit.registerReceivedDataHandler(this, appMessageReciever);
}
return START_STICKY;
}
public IBinder onBind(Intent intent) {
return null;
}
}
From AndroidManifest.xml:
<service
android:enabled="true"
android:name="PebbleService" />
UPDATE: Apparently the service is running somewhere, because I fiddled with the code some and now I'm getting a null pointer, but only when I close the main activity. I'm pretty sure it's getting relaunched and can't find the extra data it needs from the main activity because that's not the intent that started it. Which means I still have to use MainActivity mainActivity = new MainActivity(); to get the string data I need.
UPDATE 2: Okay, I feel really bad for causing all this confusion. The service is running, but it's not showing up in my Settings > Apps > Running list, even under the parent activity. I know it's running, though, because it finally ticked what it was supposed to do. Guessing a weak Bluetooth connection. That being said, I'm still throwing a NullPointerException with the current code trying to receive the intent extras. I've opened a new thread for that issue, though.
Since you are returning START_NOT_STICKY, Android will stop your Service as soon as it returns from onStartCommand(). If you want your Service to stay alive then you need to return START_STICKY from onStartCommand().
Also, your Service doesn't automatically launch with the app. It needs to be started by calling startService().
Also, as someone noted in a comment, do not create Android components using new, like this:
MainActivity mainActivity = new MainActivity();
Only Android can properly instantiate components Activity, Service, BroadcastReceiver and Provider, because they also need to have their Context set up.
I have created a plugin that starts a service and this is working fine. However I wish to be able to send variables to the running service from the plugin, and to get variables out of the service. I have researched broadcast/receivers and binding but haven't be able to to get any examples working with the code structure I am using below. Does anyone have any tips? I'm new to android development and pretty new to Java (but not programming) so there is a conceptual leap that I haven't quite got yet.
Plugin
public class IOIOconnect extends CordovaPlugin {
private Context thiscontext;
private Intent ioioService;
// Handle calls from Javascript
#Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
// Call from javascript to startup the IOIO service
if (action.equals("ioioStartup")) {
this.ioioStartup(callbackContext);
return true;
}
}
// Initialise IOIO service (Called from Javascript)
private void ioioStartup(CallbackContext callbackContext) {
// Initialise the service variables and start it it up
thiscontext = this.cordova.getActivity().getApplicationContext();
ioioService = new Intent(thiscontext, HelloIOIOService.class);
ioioService.putExtra("loadinterval", 800); // Set LED flash interval
thiscontext.startService(ioioService);
}
}
Service
public class HelloIOIOService extends IOIOService {
private int interval = 100;
#Override
public IBinder onBind(Intent intent) {
return null;
}
// USUAL IOIO SERVICE STUFF
#Override
public void onStart(Intent intent, int startId) {
// Service has been started
super.onStart(intent, startId);
// IOIO When service is started load external vars (if set)
int loadinterval = intent.getIntExtra("loadinterval", -1);
if(loadinterval>=0){ interval = loadinterval; }
// Native IOIO stuff
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (intent != null && intent.getAction() != null && intent.getAction().equals("stop")) {
// User clicked the notification. Need to stop the service.
nm.cancel(0);
stopSelf();
} else {
// Service starting. Create a notification.
Notification notification = new Notification(
R.drawable.ic_launcher, "IOIO service running",
System.currentTimeMillis());
notification
.setLatestEventInfo(this, "IOIO Service", "Click to stop",
PendingIntent.getService(this, 0, new Intent(
"stop", null, this, this.getClass()), 0));
notification.flags |= Notification.FLAG_ONGOING_EVENT;
nm.notify(0, notification);
}
}
}
I had exact same problem: writing plugin for Salesforce Mobile SDK (based on Cordova 2.3.0).
My case: plugin and app is different android projects.
The solutions, is that you have to publish the Service in AndroidManifest.xml of the main (app) project. Remember to sign it with full qualified path and as exported, like this:
<service
android:name="full.qualified.path.to.Service"
android:exported="true">
</service>
I managed to create a working plugin which is used in this project:
https://github.com/opensystemsassociation/southendtransportresearch/tree/master/phonegap
The code's not tidy as its still in development, but the 'hacky' approach works fine so hopefully it will help someone along the line a bit
I've been wondering if it is possible to use an IntentService to do some networking while keeping the queue of pending intents prioritized. My goal to be able to download some images in the background, add more if needed (send another Intent) and be able to reset the queue if necessary (preferably using a specific Intent). That is all possible with an IntentServie but when I send that 'stop' Intent it needs to be processed as the next item in the queue, not the last where it is right now.
EDIT
For those interested I have taken the AOSP code for IntentService and modified it to meet my needs. The reason I cannot just subclass IntentHandler is because of the private ServiceHandler class inside of IntentHandler.
Inside of the ServiceHandler I have a new method:
public final boolean sendPriorityMessage(Message msg)
{
int priority = msg.arg2;
//Log.i(GenericList.TAG,"recieved message priority: "+priority);
if(priority>PRIORITY_NORMAL){
return sendMessageAtFrontOfQueue(msg);
}else{
return sendMessage(msg);
}
}
This method is called from onStart instead of just sendMessage
#Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
try{
msg.arg2 = intent.getExtras().getInt(KEY_PRIORITY);
}catch(Exception e){
msg.arg2 = PRIORITY_NORMAL;
}
mServiceHandler.sendPriorityMessage(msg);
}
Overall the code is still limited but I am able to fast track some messages to the front of the queue, which is what I was after anyway.
you could implement/extend your own PriorityQueue that simply checks every new intent added to the queue. if it's the stop intent, it moves it straight to the front of the line.