Accessing a variable of a Service - android

I have an Android Activity called Main that calls a Service called MainService as follows:
Intent intent = new Intent(this, MainService.class);
if(MainService.getInstance() == null){
Log.d(TAG, "Calling MainService");
startService(intent);
}
MainService maintains a variable during its lifetime that I wish to access in Main later on. How do I do this?
Thanks.

You can bind the service and can have the service instance forever. Below sample code will help you:-
Service Class
public class MusicService extends Service {
MyBinder binder=new MyBinder();
MusicService services;
static Context context;
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return binder;
}
#Override
public void onCreate() {
super.onCreate();
context=getApplicationContext();
MediaPlayer mPlayer = MediaPlayer.create(getApplicationContext(), R.raw.yaar);
mPlayer.start();
}
public class MyBinder extends Binder
{
public MusicService getServiceSystem()
{
return MusicService.this;
}
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
Activity
public class MainActivity extends AppCompatActivity {
MusicService services;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ServiceConnection connection=new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.MyBinder binderr=(MusicService.MyBinder)service;
services=binderr.getServiceSystem();
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
};
Intent intent= new Intent(this, MusicService.class);
startService(intent);
}
}
You can then use service anywhere you need in activity. Hope it helps.

Yes, you can access variables inside service, but for that you have to bind to this service first. After that, use accessors for getting or setting variables or call any other method of the service.
See https://developer.android.com/guide/components/bound-services.html

Related

I want the instance of the Background service which is running

In this, the service is been create repeated times but I need to get the instance of running service so that I can start the timer task. The object of this service can get from any activity. The basic aim is to start the timer task after the particular button click.
public class MainActivity extends Activity {
MyService mService;
boolean mBound = false;
int b=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_start_timer).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mBound) {
mService.runTimerTask(++b);
}
}
});
}
#Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, MyService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
unbindService(mConnection);
mBound = false;
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
MyService.LocalBinder binder = (MyService.LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
You could create a ServiceManager class and route all methods through that and store it in the application:
public class YourApplication extends Application {
...
private ServiceManager serviceManager = ...;
public ServiceManager getServiceManager() {
return serviceManager;
}
}
In MainActivity:
ServiceManager serviceManager = ((YourApplication)getApplication()).getServiceManager();
MyService runningService = serviceManager.getRunningService();
The ServiceManager would handle unbinding etc and all methods that involve the state of the service. So you just ask the ServiceManager for the running service. Where you have ServiceConnection::onServiceConnected doing the work, delegate that work to the ServiceManager so it can keep state and share it with the rest of the application:
public void onServiceConnected(ComponentName className,
IBinder service) {
((YourApplication)getApplication()).getServiceManager().bind(service);
}
It's a general technique for sharing object state across an application but you'd need to tailor it to fit.

Pass Service From one Activity to Another

how do I pass a service from one activity to another? I have a music player (Activity1) that displays a list of songs, and when you click on it, it starts the service within Activity1. I have a button that users can click that will open Activity2.
So what is the best way for me to pass the service from Activity1 to Activity2. If the service is started in Activity1, then Activity2 should continue playing. If the service is not started in Activity1, then Activity2 should start the service before using it.
Thanks.
Here's some sample code, the MusicService is a class that extends the service class.
public class Activity1 extends AppCompatActivity {
private MusicService serviceMusic;
private ServiceConnection musicConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.PlayerBinder binder = (MusicService.PlayerBinder) service;
//get service
serviceMusic = binder.getService();
serviceMusic.setSongList(songList);
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
};
}
I think it would be better to bind service instead of pass a static global service. Since you have multiple activities which will use the same service, a base activity should be created like this:
BasicServiceActivity.java
public abstract class BasicServiceActivity extends AppCompatActivity {
protected DvrService mDvrService;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_basic);
attachService();
}
#Override
protected void onDestroy() {
detachService();
super.onDestroy();
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
DvrService.DvrServiceBinder serviceBinder = (DvrService.DvrServiceBinder) binder;
mDvrService = serviceBinder.getService();
onServiceAttached(mDvrService);
}
#Override
public void onServiceDisconnected(ComponentName name) {
mDvrService = null;
}
};
private void attachService() {
Intent service = new Intent(this, DvrService.class);
bindService(service, mServiceConnection, Service.BIND_AUTO_CREATE);
}
private void detachService() {
unbindService(mServiceConnection);
}
/** Callback when service attached. */
protected void onServiceAttached(DvrService service) {
// do something necessary by its subclass.
}
}
Then you can implement subclass activity like this:
public class ServiceActivity extends BasicServiceActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startService(new Intent(this, DvrService.class));
}
#Override
protected void onDestroy() {
if (mDvrService != null) {
mDvrService.removeListener1(mListener1);
}
super.onDestroy();
}
#Override
protected void onServiceAttached(DvrService service) {
// do your stuff, for example add a listener.
service.addListener1(mListener1);
}
}
you can define Application extend class and use public static variable in this class and access this variable in all activity .
like this :
public class G extends Aplication{
public static MusicService serviceMusic;
}
and in manifest :
<application ...
android:name=".G">
Now you can access G.serviceMusic any where.

Calling activity class method from Service class

I have seen many posts in SO regarding this but could not get the exact and most easy way to call an activity method from service class. Is broadcast receiver only the option? No easy way out ? I just need to call the following method in Activity class after the media player is prepared in Service class .
Activity class:
public void updateProgress() {
// set Progress bar values
songProgressBar.setProgress(0);
songProgressBar.setMax(100);
// Updating progress bar
updateProgressBar();
}
Service class:
#Override
public IBinder onBind(Intent intent) {
Log.d(this.getClass().getName(), "BIND");
return musicBind;
}
#Override
public boolean onUnbind(Intent intent) {
return false;
}
#Override
public void onPrepared(MediaPlayer mp) {
try {
mp.start();
} catch (IllegalStateException e) {
e.printStackTrace();
}
// updateProgress();// Need to call the Activity method here
}
Define an interface your Service will use to communicate events:
public interface ServiceCallbacks {
void doSomething();
}
Write your Service class. Your Activity will bind to this service, so follow the sample shown here. In addition, we will add a method to set the ServiceCallbacks.
public class MyService extends Service {
// Binder given to clients
private final IBinder binder = new LocalBinder();
// Registered callbacks
private ServiceCallbacks serviceCallbacks;
// Class used for the client Binder.
public class LocalBinder extends Binder {
MyService getService() {
// Return this instance of MyService so clients can call public methods
return MyService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public void setCallbacks(ServiceCallbacks callbacks) {
serviceCallbacks = callbacks;
}
}
Write your Activity class following the same guide, but also make it implement your ServiceCallbacks interface. When you bind/unbind from the Service, you will register/unregister it by calling setCallbacks on the Service.
public class MyActivity extends Activity implements ServiceCallbacks {
private MyService myService;
private boolean bound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(...);
}
#Override
protected void onStart() {
super.onStart();
// bind to Service
Intent intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
// Unbind from service
if (bound) {
myService.setCallbacks(null); // unregister
unbindService(serviceConnection);
bound = false;
}
}
/** Callbacks for service binding, passed to bindService() */
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
// cast the IBinder and get MyService instance
LocalBinder binder = (LocalBinder) service;
myService = binder.getService();
bound = true;
myService.setCallbacks(MyActivity.this); // register
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
bound = false;
}
};
/* Defined by ServiceCallbacks interface */
#Override
public void doSomething() {
...
}
}
Now when your service wants to communicate back to the activity, just call one of the interface methods from earlier. Inside your service:
if (serviceCallbacks != null) {
serviceCallbacks.doSomething();
}
Use Broadcast receiver with service for updating your view from the service class.
For example:
In my activity class
public class ServiceDemoActivity extends Activity {
Intent intent;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final TextView notification = (TextView) findViewById(R.id.notification);
if (CheckIfServiceIsRunning()) {
} else {
startService(new Intent(this, MyService.class));
}
}
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
updateDate(intent);
}
};
private void updateDate(Intent intent) {
String time = intent.getStringExtra("time");
Toast.makeText(getApplicationContext(), "Yea!!! Service called", Toast.LENGTH_SHORT).show();
TextView date = (TextView) findViewById(R.id.date);
date.setText(time);
}
#Override
public void onResume() {
super.onResume();
registerReceiver(broadcastReceiver, new IntentFilter(
MyService.BROADCAST_ACTION));
}
}
And in my service class I am calling my update ui after a few interval of time which updates my UI.
public class MyService extends Service {
public static final String
BROADCAST_ACTION = "com.mukesh.service";
private final Handler handler = new Handler();
#Override
public void onCreate() {
intent = new Intent(BROADCAST_ACTION);
}
#Override
public void onDestroy() {
stopService(intent);
}
#Override
public void onStart(Intent intent, int startid) {
int i = 0;
while (i <= 2) {
if (i > 1) {
i++;
this.onDestroy();
} else {
counter = i;
i++;
handler.removeCallbacks(sendUpdatesToUI);
handler.postDelayed(sendUpdatesToUI, 1 * 1000); // 1 sec
}
}
}
private Runnable sendUpdatesToUI = new Runnable() {
public void run() {
DisplayLoggingInfo();
handler.postDelayed(this, 7 * 1000); // 7 sec
}
};
private void DisplayLoggingInfo() {
intent.putExtra("time", new Date().toLocaleString());
intent.putExtra("counter", String.valueOf(counter));
sendBroadcast(intent);
stopService(intent);
}
}
For complete code check this link
I created a general class called Delegate (it's not a special name, you can name it John) and passed MainActivity class into it as a static field. Then I can access it from the service since its global now. I am not sure if it is cost-effective but it solved the problem for me simple.
My service:
package com.some.package;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
public class FirebaseInstanceIDService extends FirebaseInstanceIdService {
#Override
public void onTokenRefresh() {
String token = FirebaseInstanceId.getInstance().getToken();
Delegate.theMainActivity.onDeviceTokenChange(token);
}
}
Delegate class:
package com.some.package;
public class Delegate {
static MainActivity theMainActivity;
}
What I did in MainActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Delegate.theMainActivity = this;
//rest of the code...
}
public void onDeviceTokenChange(String token){
Log.e("updated token:", token);
}
You can't call your sevices method direcly from your activity or vise versa. There are 3 ways to communicate with a service; using broadcasters and receivers, using Messenger or binding to the service. For further information look at http://developer.android.com/guide/components/bound-services.html
You can call from your service
getContentResolver().notifyChange(uri, null);
and in your activity you set up a
getContentResolver().registerContentObserver(uri, false, new ContentObserver(getHandler())
{
public void onChange(boolean selfChange)
{
updateProgress()
}
};
the onChange method will ba called on the UI thread
You can call a method of activity from service by implementing your own listener like this
https://stackoverflow.com/a/18585247/5361964
You might consider running your activity method in runOnUiThread like this:
// method will be called from service
override fun callback(activity: Activity, result: String) {
runOnUiThread{
Toast.makeText(activity, result, Toast.LENGTH_LONG).show()
}
}
I would prefer to use some very easy and cleaner solution provided by
EventBus

How to access 'Activity' from a Service class via Intent?

I am new to Android programming - so I do not have very clear understanding of the 'Context' and the 'Intent'.
I want to know is there a way to access Activity from a Service class?
i.e. Let's say I have 2 classes - one extends from "Activity" and other extends from "Service" and I have created an intent in my Activity class to initiate the service.
Or, how to access the 'Service' class instance from my 'Activity' class - because in such workflow Service class is not directly instantiated by my Activity-code.
public class MainActivity extends Activity {
.
.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this, CommunicationService.class));
.
.
}
public class CommunicationService extends Service implements ..... {
.
.
#Override
public int onStartCommand(final Intent intent, int flags, final int startId) {
super.onStartCommand(intent, flags, startId);
....
}
}
You can use bindService(Intent intent, ServiceConnection conn, int flags) instead of startService to initiate the service. And the conn will be a inner class just like:
private ServiceConnection conn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMyService = ((CommunicationService.MyBinder) service).getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
};
mMyService is the instance of your CommunicationService.
In your CommunicationService, just override:
public IBinder onBind(Intent intent) {
return new MyBinder();
}
and the following class in your CommunicationService:
public class MyBinder extends Binder {
public CommunicationService getService() {
return CommunicationService.this;
}
}
So you can use mMyService to access any public methods and fields in your activity.
In addition, you can use callback interface to access activity in your service.
First write a interface like:
public interface OnChangeListener {
public void onChanged(int progress);
}
and in your service, please add a public method:
public void setOnChangeListener(OnChangeListener onChangeListener) {
this.mOnChangeListener = onChangeListener;
}
you can use the onChanged in your service anywhere, and the implement just in your activity:
public void onServiceConnected(ComponentName name, IBinder service) {
mMyService = ((CommunicationService.MyBinder) service).getService();
mMyService.setOnChangeListener(new OnChangeListener() {
#Override
public void onChanged(int progress) {
// anything you want to do, for example update the progressBar
// mProgressBar.setProgress(progress);
}
});
}
ps: bindService will be like this:
this.bindService(intent, conn, Context.BIND_AUTO_CREATE);
and do not forget
protected void onDestroy() {
this.unbindService(conn);
super.onDestroy();
}
Hope it helps.

android bind service to activity

I have seen several similar examples here but can't seem to get my service to bind with activity.
I am getting the error
"android.os.binderproxy cannot be cast to IC_CommissaryService".
My service looks like this:
public class IC_CommissaryService extends Service
{
#Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder
{
IC_CommissaryService getService()
{
return IC_CommissaryService.this;
}
}
public int onStartCommand(Intent intent, int flags, int startId)
{
}
private boolean SendOrderToServer(int orderID)
{
/* do stuff*/
}
}
and my activity looks like this:
public class SubmitOrders extends Activity
{
IC_CommissaryService ICservice;
#Override
public void onCreate(Bundle savedInstanceState)
{
Intent serviceintent = new Intent(this, IC_CommissaryService.class);
serviceintent.putExtra("binded", true);
bindService(serviceintent, mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName className, IBinder service)
{
Log.e("TEST", "SERVICE CONNECTED");
try
{
ICservice =(IC_CommissaryService.LocalBinder)service).getService();
for(int i = 0; i < Submitorders.size(); i++)
{
ICservice.SendOrderToServer(Submitorders.get(i).intValue());
}
}
catch(Exception ex)
{
Log.e("Error", "Error connecting service: " + ex.getMessage());
}
}
#Override
public void onServiceDisconnected(ComponentName className)
{
}
};
}
I am getting the error in my activity on the line ICservice =(IC_CommissaryService.LocalBinder)service).getService();
I think I have done the same as people already suggested in other posts so any help please?
thanks
I had the same kind of problem. I just figured it out today. Please look at the parts annotated with <<===== below. I hope it helps.
public class PracticeServiceBindingActivity extends ListActivity {
private MyService.MyBinder service; <<====
....
private ServiceConnection connection = new ServiceConnection( ){
public void onServiceConnected (ComponentName name, IBinder service) {
setService(MyService.MyBinder) service; <<====
....
}
}
public void onCreate(....) {
...
public MyService.MyBinder getService(){ <<=====
return service;
}
public void setService(MyService.MyBinder service) { <<=====
this.service = service;
}
}
}
Quote from the Bound Services documentation:
If your service is private to your own application and runs in the
same process as the client (which is common), you should create your
interface by extending the Binder class and returning an instance of
it from onBind().
Remove the android:process attribute in in AndroidManifest.ml to make the service run in the same process. I had the same problem today it did the trick.
These are the abstract classes that I use to solve this problem:
https://gist.github.com/frenchie4111/6086c6e4327d7936364a
Just extend both these classes with your service and activity (You can change the fragment from a fragment to an activity with ease). And make sure that in your service/fragment onCreate you set the serviceClass like so:
public void onCreate( Bundle savedInstance ) {
super.onCreate( savedInstance );
this.serviceClass = IC_CommissaryService.class;
}

Categories

Resources