I would like to track battery information(is it charging, level etc.) even when my app is not turned on. I think maybe service would be good for it? Or maybe there are other solutions? I'm all ears up.
This is in fact a very old post but since the accepted answer seems like a stub service i think there is the need to go deeper.
You need to declare the service in the android manifest. If you have it in a certain package the declaration would be:
<service android:name="packagename.servicename" />
Then you would need to implement the service class. I have created one for one of my applications. Hope this helps you.
I have this in the context of sensors. sensors are something i want to monitor so i created an interface like the following:
public interface Sensor {
SensorType getSensorType();
SensorName getSensorName();
}
I have to ENUM classes that represent the types and names of sensors i have:
public enum SensorName {
ENVIRONMENTAL_SENSOR, AUDIO_SENSOR, SOUND_SENSOR, SOCIAL_SENSOR,
LOCATION_SENSOR, PHOTO_SENSOR, ACCELEROMETER_SENSOR, BATTERY_SENSOR;
}
public enum SensorType {
HARDWARE_SENSOR, SOFTWARE_SENSOR, HYBRID_SENSOR;
}
Since i have several sensors and i might want to have a listing of all the sensors i needed to implement this interface. Next i created a abstract class to implement behaviours that are similar to all my sensors.
public abstract class SensorElement extends Service implements Sensor{
protected SensorType type;
protected SensorName name;
#Override
public abstract IBinder onBind(Intent arg0);
#Override
public int onStartCommand(Intent intent, int flags, int startId){
return super.onStartCommand(intent, flags, startId);
}
#Override
public abstract void onCreate();
#Override
public void onDestroy(){
super.onDestroy();
}
}
My sensors need this methods to work properly. In my case i only absolutely need the binder to bind the service to my activity.
Now what you want, the battery sensor itself:
public class BatterySensor extends SensorElement {
// Binder given to clients
private final IBinder mBinder = new BatteryLocalBinder();
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class BatteryLocalBinder extends Binder {
public BatterySensor getService() {
// Return this instance of MotionLightOrientationSensor so clients
// can call
// public
// methods
return BatterySensor.this;
}
}
private static final String TAG = "BatterySensor";
public static final SensorType type = SensorType.SOFTWARE_SENSOR;
public static final SensorName name = SensorName.BATTERY_SENSOR;
private int status;
private boolean isCharging;
private int chargePlug;
private boolean usbCharge;
private boolean acCharge;
private int level;
private int scale;
private double batteryPct;
private Battery battery;
#Override
public SensorType getSensorType() {
return type;
}
#Override
public SensorName getSensorName() {
return name;
}
#Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
#Override
public void onCreate() {
Log.e(TAG, "onCreate");
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(powerconnectionreceiver, ifilter);
battery = new Battery();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public void onDestroy() {
Log.e(TAG, "onDestroy");
super.onDestroy();
unregisterReceiver(powerconnectionreceiver);
}
private BroadcastReceiver powerconnectionreceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Retrieves a map of extended data from the intent.
status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING
|| status == BatteryManager.BATTERY_STATUS_FULL;
chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;
level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
batteryPct = level / (float) scale;
battery.setStatus(status);
battery.setCharging(isCharging);
battery.setChargePlug(chargePlug);
battery.setUsbCharge(usbCharge);
battery.setAcCharge(acCharge);
battery.setLevel(level);
battery.setScale(scale);
battery.setBatteryPct(batteryPct);
}
};
public Battery getBatteryReading() {
return this.battery;
}
public int getStatus() {
return status;
}
public boolean isCharging() {
return isCharging;
}
public int getChargePlug() {
return chargePlug;
}
public boolean isUsbCharge() {
return usbCharge;
}
public boolean isAcCharge() {
return acCharge;
}
public int getLevel() {
return level;
}
public int getScale() {
return scale;
}
}
To access data from this sensor you only need to start it in you activity like this:
private BatterySensor batterySensor;
private boolean mBatteryBound = false;
private Intent intentBattery;
intentBattery = new Intent(this.context, BatterySensor.class);
then i start the service:
context.startService(intentBattery);
and bind it:
if(this.context.bindService(intentBattery, mBatteryConnection, Context.BIND_AUTO_CREATE)){
this.numBoundedSensors++;
}
to bind it you need to have this class in your activity:
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mBatteryConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
BatteryLocalBinder BatteryBinder = (BatteryLocalBinder) service;
batterySensor = BatteryBinder.getService();
mBatteryBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBatteryBound = false;
}
};
Then if it succeeded the bind you can call all the methods from the sensor, like this:
public Battery getBatteryData(){
return mBatteryBound ? batterySensor.getBatteryReading() : null;
}
A service needs to be declared in the AndroidManifest.xml and the implementing class must extend the Service class or one of its subclasses. The following code shows an example for a service declaration and its implementation.
<service
android:name="MyService"
android:icon="#drawable/icon"
android:label="#string/service_name"
>
</service>
public class MyService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//TODO do something useful
return Service.START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
//TODO for communication return IBinder implementation
return null;
}
}
Related
I am trying to test out a simple service using Android's accelerometer but I'm not sure where to go from here. What I want it to do is to detect movement using the service and for the main activity to detect every time there is a change in the boolean variable 'moving' that is in the service.
Not shown here is my manifest in which I already put in the code for the service.
Here is my main activity.
public class MainActivity extends AppCompatActivity {
private boolean mBound = false;
private accelService mService = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, accelService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
accelService.LocalBinder binder = (accelService.LocalBinder) service;
mService = binder.getService();
showMessage("Service Bound");
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
private void showMessage(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
}
Here is my Service.
public class accelService extends Service implements SensorEventListener {
private boolean moving = false;
private final IBinder mBinder = new LocalBinder();
private SensorManager sensorManager;
public int onStartCommand(Intent intent, int flags, int startId) {
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
sensorManager.registerListener(this,
sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION),
SensorManager.SENSOR_DELAY_NORMAL);
return START_STICKY;
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
#Override
public void onSensorChanged(SensorEvent event) {
setMoving(true);
}
public boolean isMoving() {
return moving;
}
public void setMoving(boolean moving) {
this.moving = moving;
}
class LocalBinder extends Binder {
accelService getService() {
return accelService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
You already have a "getService()" that the MainActivity can call, right? Ok.
You have to create a new Interface in the Service class that acts as a Callback:
class accelService extends Service {
interface myCallback {
void onCalled(int parameter);
}
}
Then in your "LocalBinder" you add a new method and a variable:
class LocalBinder extends Binder {
private myCallback mListener = null;
accelService getService() {....} //<!-- the one you already have
void addListener(myCallback listener) { //<!-- the new one that sets the listener/callback
myCallback = listener;
}
}
At this point from your MainActivity you can call:
binder.addListener(new myCallback(){
Toast.makeText(context, "Test", Toast.LONG).show()
});
as you do with "getService()".
Finally you have to call
mBinder.onCalled(123456789)
somewhere in the Service to execute Toast...show() procedure.
In this way you can pass variables from Service to MainActivity while this last one is bounded to the Service.
I am trying to create a service to keep running even when the application is not at foreground. when it is in foreground, all works well, but after switching away from it, the onLocationChanged is being called once every ~10 minutes (instead of 5 seconds when in foreground).
The goal is to keep scanning the location and send updates for processing even when the application is in background every 5 seconds (I want the service to keep running as long as the UI applications is not being totally closed).
I tried following many guides and posts in the net and none of them helped solving this. in the past it was working fine with android 6.0, but I upgraded to 8.0 and it stopped working.
This is the code I'm using:
public class MainActivity extends AppCompatActivity {
private Intent _scanServiceIntent;
#Override
public void onStart()
{
super.onStart();
_scanServiceIntent = new Intent(this,ScanService.class);
bindService(_scanServiceIntent, m_serviceConnection, BIND_AUTO_CREATE);
}
private ServiceConnection m_serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
_scanService = ((ScanService.ScanServiceBinder)service).getService();
}
public void onServiceDisconnected(ComponentName className) {
Log.v("lsx", "onServiceDisconnected");
_scanService = null;
}
};
}
This is the ScanService class:
public class ScanService extends Service {
private LocationListener _locListener;
private LocationManager _locManager;
private static final String TAG = "locationtrigger";
#Nullable
#Override
public IBinder onBind(Intent intent) {
Log.v(TAG, "onBind");
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v(TAG, "ScanServiceonStartCommand");
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#SuppressLint("MissingPermission")
#Override
public void onCreate() {
Log.v(TAG, "ScanServiceonCreate - start");
List<UserLocationManager> locationsManagers = GetLocationsManagers();
_locListener = new MyLocationListener(locationsManagers);
_locManager = (LocationManager)getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
_locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 0, _locListener);
Log.v(TAG, "ScanServiceonCreate - end");
}
#Override
public void onDestroy() {
_locManager.removeUpdates(_locListener);
}
public List<UserLocationManager> GetLocationsManagers(){
// returning some managers
}
public class ScanServiceBinder extends Binder {
public ScanService getService() {
Log.v(TAG, "ScanServiceBinder.getService");
return ScanService.this;
}
}
class MyLocationListener implements LocationListener {
private List<UserLocationManager> _locationManagers;
public MyLocationListener(List<UserLocationManager> locationManagers)
{
_locationManagers = locationManagers;
}
#Override
public void onLocationChanged(Location location)
{
if (location != null)
{
// call managers
}
}
}
}
I made a simple example in order to test an un-killable service. I'm trying to restart as soon as it is killed by OS.
Everything works well. But, I found that the service was sometimes killed by OS frequently, and re-started again in OS over 6.0. Sometimes, onStartCommand is called in 30 seconds after starting the service. I don't know why.
OS under 6.0 works perfectly well.
My code is simple.
public class DataCollectorService extends Service {
public static BroadcastReceiver broadcastReceiver;
private Intent intent_data_processor;
private PendingIntent sender_data_processor;
private AlarmManager alarmMgr;
private LocalBroadcastManager a_manager;
private String TAG = "DataCollectorService";
private DatabaseReference mFirebaseDatabaseReference;
private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
public DataCollectorService(Context applicationContext) {
super();
Log.i("HERE", "here I am!");
}
public DataCollectorService() {
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
super.onDestroy();
release();
}
#Override
protected void finalize() throws Throwable {
super.finalize();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
Log.e("TAG", "START");
Log.e("TAG", "DataCollectorService onStartCommand");
return START_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
#Override
public boolean onUnbind(Intent arg0) {
return true;
}
#Override
public void onRebind(Intent arg0) {
}
And then I also set up the service like following in Manifest.
android:enabled="true"
android:exported="false"
What is the reason that the service is restarted within a few seconds after the starting?
I am developing a simple hello world application in Google Glass using Service. In my application I used TimelineManager to display a LiveCard. I want to invoke a http call when the app is visible to user (I mean when user scrolled from other app to our app).
I know if we use Activity, onResume() invoked automatically, but I am doing it in Service.
Please let me know which method will be invoked when app is resumed to user.
public class MyGlassService extends Service {
private static final String TAG = "SocketService";
private static final String LIVE_CARD_ID = "livecard";
private TimelineManager mTimelineManager;
private LiveCard mLiveCard;
private TextToSpeech mSpeech;
private final IBinder mBinder = new MainBinder();
private TextView txtName, txtBalance;
private RemoteViews remoteView;
private WakeLock screenLock;
public class MainBinder extends Binder {
public void sayMessage() {
mSpeech.speak(getString(R.string.hello_world),
TextToSpeech.QUEUE_FLUSH, null);
}
}
#Override
public void onCreate() {
super.onCreate();
mTimelineManager = TimelineManager.from(this);
mSpeech = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
// do nothing
}
});
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
remoteView = new RemoteViews(this.getPackageName(), R.layout.activity_main);
if (mLiveCard == null) {
mLiveCard = mTimelineManager.createLiveCard(LIVE_CARD_ID);
remoteView.setTextViewText(R.id.name, getString(R.string.pre_screen_msg));
mLiveCard.setViews(remoteView);
}
return START_STICKY;
}
#Override
public void onDestroy() {
if (mLiveCard != null && mLiveCard.isPublished()) {
mLiveCard.unpublish();
mLiveCard = null;
}
mSpeech.shutdown();
mSpeech = null;
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public String toHexString(byte[] data) {
String s = new String(data);
return s;
}
}
Live cards do not provide precise lifecycle methods like activities do to let you know when a user has scrolled to or away from one.
If you would like to see this feature, please post a feature request describing your use case on our issue tracker.
New to android, trying to figure out Services. I'm trying to bind a service to an activity, I'm following the examples in the documentation, but I keep getting a NullPointerException on the line marked below(appService.playSong(title)). Checking it in the debugger reveals that appService is indeed null.
public class Song extends Activity implements OnClickListener,Runnable {
protected static int currentPosition;
private ProgressBar progress;
private TextView songTitle;
private MPService appService;
private ServiceConnection onService = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder rawBinder) {
appService = ((MPService.LocalBinder)rawBinder).getService();
}
public void onServiceDisconnected(ComponentName classname) {
appService = null;
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.song);
Intent bindIntent = new Intent(Song.this,MPService.class);
bindService(bindIntent,onService,
Context.BIND_AUTO_CREATE);
Bundle b = getIntent().getBundleExtra("songdata");
String title = b.getString("song title");
// ...
appService.playSong(title); // nullpointerexception
// ...
}
Here's the relevant part of the service:
package org.example.music;
// imports
public class MPService extends Service {
private MediaPlayer mp;
public static int currentPosition = 0;
public List<String> songs = new ArrayList<String>();
public static String songTitle;
private static final String MEDIA_PATH = new String("/mnt/sdcard/");
#Override
public void onCreate() {
super.onCreate();
mp = new MediaPlayer();
songs = Music.songs;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
public class LocalBinder extends Binder {
MPService getService() {
return MPService.this;
}
}
private final IBinder binder = new LocalBinder();
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public void playSong(String songPath) {
try {
mp.reset();
mp.setDataSource(songPath);
mp.prepare();
mp.start();
mp.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer arg0) {
nextSong();
}
});
songTitle = songPath.substring(12,songPath.length()-4);
} catch (IOException e) {
Log.v(getString(R.string.app_name),e.getMessage());
}
}
public void nextSong() {
if (++currentPosition >= songs.size()) {
currentPosition = 0;
}
String song = MEDIA_PATH+songs.get(currentPosition);
playSong(song);
}
public void prevSong() {
if (--currentPosition<0) {
currentPosition=songs.size()-1;
}
String song = Music.MEDIA_PATH+songs.get(currentPosition);
playSong(song);
}
public int getSongPosition() {
return mp.getCurrentPosition();
}
public MediaPlayer getMP() {
return mp;
}
}
I have registered the service in AndroidManifest.xml and set android:enabled="true". Do you see any obvious mistakes here?
There are two kinds of binds you can make local and remote. Local is only for use by your application and remote if for use by any application that implements certain interface.
You should start with local binding.
Local binding tutorial.
Remote binding tutorial.
My solution without bind:
public class MyActivity extends Activity{
#Override
public void onCreate(Bundle savedInstanceState){
...
Intent it = new Intent(MyService.ACTIVITY_START_APP);
it.setClass(getApplicationContext(), MyService.class);
startService(it);
}
...
#Override
protected void onResume() {
super.onResume();
registerBroadcastReceiver();
}
#Override
protected void onPause() {
super.onPause();
this.unregisterReceiver(this.receiver);
}
...
private BroadcastReceiver receiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(MyService.BROADCAST_INIT)) {
//do your stuff here after init
}
}
};
private void registerBroadcastReceiver(){
IntentFilter filter = new IntentFilter();
filter.addAction(HMyService.BROADCAST_INIT);
this.registerReceiver(receiver, filter);
}
}
Your service:
public class MyService extends Service{
public static final String BROADCAST_INITIAL_DATA = "org.myapp.BROADCAST_INIT";
public static final String ACTIVITY_START_APP = "org.myapp.ACTIVITY_START_APP";
#Override
public int onStartCommand (Intent intent, int flags, int startId){
super.onStartCommand(intent, flags, startId);
if(intent.getAction().equals(ACTIVITY_START_APP)){
//do your initialization
//inform the client/GUI
Intent i = new Intent();
i.setAction(BROADCAST_INIT);
sendBroadcast(i);
}else{
//some other stuff like handle buttons
}
}
}
good luck.
You are assuming that the bindService() will connect to the service synchronously, but the connection will be available only after onCreate() finshed.
The framework runs onCreate() on the UI thread and bindService() just makes a note to connect to the service later. Connecting to a service will always be done on the UI thread, so this can only happen after onCreate was executed. You can't even count on the connection being set up right after onCreate(). It will happen sometime after that :). Also, the framework might disconnect the service on it's will, though it should only happen in low memory conditions.
So, move the code which works with appService from onCreate() to onServiceConnected() and it's gonna work.
From a quick glance it looks like you are trying to access your service before the binding has completed. You have to make sure onServiceConnected has fired before trying to call any methods on your service.
Example:
Intent bindIntent = new Intent(Song.this,MPService.class);
bindService(bindIntent,onService, Context.BIND_AUTO_CREATE);
//Wait until service has bound
while(appService == null){
Thread.sleep(100);
}
appService.playSong(title);
This example isn't the best but it demonstrates that you have to wait until the binding has completed before trying to access the service.