How to pass handler from an activity to service? I am trying to update the activity UI on the state of Bluetooth connection by using Handler as shown below from service class.
mHandler.obtainMessage(MenuActivity.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
In the activity, I implemented this:
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_STATE_CHANGE:
if (true)
Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
switch(msg.arg1){
case BluetoothService.STATE_CONNECTED:
showToast("Connected to " + mConnectedDeviceName, Toast.LENGTH_SHORT);
break;
I tried to use a constructor to pass the handler from the activity to service like this:
public BluetoothService(Handler handler, BluetoothAdapter mBluetoothAdapter) {
mAdapter = mBluetoothAdapter;
mState = STATE_NONE;
mHandler = handler;
}
But there was an error which shows Unable to instantiate service and found that the service needs to have a public no-args constructor. But after removing the constructor, the handler did not get passed into the service.
How to solve this problem?
You have to bind to the service from activity and establish a ServiceConnection and then get the instance of service and set your handler.
Here is the activity and service class which i use for one of my media player application.....
public class MainActivity extends Activity
{
private CustomService mService = null;
private boolean mIsBound;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
startService(new Intent(this.getBaseContext(), CustomService.class));
doBindService();
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder)
{
mService = ((CustomService.LocalBinder)iBinder).getInstance();
mService.setHandler(yourHandler);
}
#Override
public void onServiceDisconnected(ComponentName componentName)
{
mService = null;
}
};
private void doBindService()
{
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
bindService(new Intent(this,
CustomService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
private void doUnbindService()
{
if (mIsBound)
{
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
#Override
protected void onDestroy()
{
super.onDestroy();
doUnbindService();
}
}
CustomService Code ....
public class CustomService extends Service
{
private final IBinder mIBinder = new LocalBinder();
private Handler mHandler = null;
#Override
public void onCreate()
{
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flag, int startId)
{
return START_STICKY;
}
#Override
public void onDestroy()
{
if(mHandler != null)
{
mHandler = null;
}
}
#Override
public IBinder onBind(Intent intent)
{
return mIBinder;
}
public class LocalBinder extends Binder
{
public CustomService getInstance()
{
return CustomService.this;
}
}
public void setHandler(Handler handler)
{
mHandler = handler;
}
}
Related
I am trying to constantly update the UI based on a long-running service output.
Basically, I want to display and append a list of users one by one after they get processed by the bound service.
MainActivityViewModel
public class MainActivityViewModel extends ViewModel {
private MutableLiveData<User> user = new MutableLiveData<>();
private MutableLiveData<MyService.MyBinder> mBinder = new MutableLiveData<>();
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder iBinder) {
MyService.MyBinder binder = (MyService.MyBinder) iBinder;
mBinder.postValue(binder);
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBinder.postValue(null);
}
};
public ServiceConnection getServiceConnection(){
return serviceConnection;
}
public LiveData<User> getUser(){
return user;
}
public LiveData<MyService.MyBinder> getBinder(){
return mBinder;
}
}
My Service
public class MyService extends Service {
private final IBinder mBinder = new MyBinder();
private Handler mHandler;
#Override
public void onCreate() {
super.onCreate();
mHandler = new Handler();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public void starFetchingUsers(User obj){
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
/**********************************************************/
Here i will be sending POST method one by one for 100 times and will get
a response as a user object which needs to be a liveData. So once this object
is changed after next post method, i want it to get displayed or appended on UI.
How to bind this dynamic user object variable to the MutableLiveData user created in MainActivityViewModel class? so this get auto updated in UI.
/**********************************************************/
}
});
thread.start();
}
public class MyBinder extends Binder{
MyService getService(){
return MyService.this;
}
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
stopSelf();
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
Please let me know what I am missing in both classes.
I have a service listening for UDP packets that is bound to my MainActivity (which is the only activity in the app). The service runs on its own thread and I can see the UDP messages as well as the parsed messages in logcat. I created a setParsedMessage() and a public getParsedMessage() in order to get the parsed string and send it to my main activity in order to change a TextView and an ImageView depending on what the parsed message is, however it does not appear to be retrieving the String for some reason. I read about this method on the Developer.Android website, however I've also seen something about using Handler to do this instead. Here is my code:
MainActivity:
public class MainActivity extends Activity {
AlertAssignments mAlertAssignments;
Button startListeningButton;
boolean started;
int counter;
boolean mBound = false;
Context context;
ListenerService mListenerService;
TextView mTextView;
TextView mBlinkView;
ImageView mImageView;
private StartListening _StartListeningTask;
String messageFromService = "";
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//start listener service
Intent listenerServiceIntent = new Intent(MainActivity.this, ListenerService.class);
this.bindService(listenerServiceIntent, mConnection, Context.BIND_AUTO_CREATE);
mImageView = (ImageView) findViewById(R.id.image_view);
mTextView = (TextView) findViewById(R.id.alert_text);
mBlinkView = (TextView) findViewById(R.id.blinking_text);
Animation mAnimation = new AlphaAnimation(0.0f, 1.0f);
mAnimation.setDuration(50);
mAnimation.setStartOffset(20);
mAnimation.setRepeatCount(Animation.INFINITE);
mAnimation.setRepeatMode(Animation.REVERSE);
mBlinkView.startAnimation(mAnimation); //animation value
mAlertAssignments = new AlertAssignments();
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
ListenerService.LocalBinder binder = (ListenerService.LocalBinder) service;
mListenerService = binder.getService();
mBound = true;
if(mBound) {
Log.e("UDP", "Service has been bound successfully");
}
else {
Log.e("UDP", "Service has not been bound");
}
readFromService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
#Override
protected void onStart() {
super.onStart();
}
#Override
protected void onStop() {
super.onStop();
//unbind from service
if(mBound) {
this.unbindService(mConnection);
mBound = false;
}
}
private void readFromService() {
try {
Integer parsedMessage = Integer.valueOf(mListenerService.getParsedMessage());
mImageView.setImageResource(mAlertAssignments.alarmImages[parsedMessage]);
if(parsedMessage >= 10 && parsedMessage <= 19 && parsedMessage != 0) {
mTextView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
} else {
mBlinkView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
}
} catch(NumberFormatException e) {
e.printStackTrace();
}
}
}
I had read that using the public getter like this:
Integer parsedMessage = Integer.valueOf(mListenerService.getParsedMessage());
would allow me to access the string value of mListenerService.getParsedMessage, however I'm guessing that may only work for started services, not bound services.
AlertAssignments is a simple enumeration that uses ordinal arrays to bind images and Strings to values, so mImageView.setImageResource(mAlertAssignments.alarmImages[parsedMessage]) would set the ImageView to an image. Finally, here is the Service:
public class ListenerService extends Service{
public String the_alarm_S;
public String parsedMessage = "";
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
ListenerService getService() {
return ListenerService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
DatagramSocket socket;
Thread UDPBroadcastThread;
void startListenForUDPBroadcast() {
UDPBroadcastThread = new Thread(new Runnable() {
public void run() {
try {
while (shouldRestartSocketListen) {
try {
socket = new DatagramSocket(12001);
socket.setReuseAddress(true);
String message = "";
byte[] recvBuf = new byte[1024];
DatagramPacket packet = new DatagramPacket(recvBuf, 1024);
Log.e("UDP", "Waiting for UDP broadcast");
try {
socket.receive(packet);
Log.e("UDP", "Received Packet");
} catch (IOException e) {
e.printStackTrace();
}
message = new String(packet.getData());
Log.e("UDP", "Got UDB broadcast message: " + message);
setParsedMessage(message);
if(socket != null) {
socket.close();
}
} catch (SocketException e) {
e.printStackTrace();
}
}
//if (!shouldListenForUDPBroadcast) throw new ThreadDeath();
} catch (Exception e) {
Log.i("UDP", "no longer listening for UDP broadcasts cause of error " + e.getMessage());
}
}
});
UDPBroadcastThread.start();
}
private Boolean shouldRestartSocketListen = true;
private void setParsedMessage(String messageContents) {
the_alarm_S = messageContents;
String parseMessage[] = the_alarm_S.split("!!!");
Log.e("UDP", "Parsed message with value " + parseMessage[1]);
parsedMessage = parseMessage[1];
}
public String getParsedMessage() {
return parsedMessage;
}
private void stopListen() {
shouldRestartSocketListen = false;
if(socket != null) {
socket.close();
}
}
#Override
public void onCreate() {
startListenForUDPBroadcast();
}
#Override
public void onDestroy() {
stopListen();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
shouldRestartSocketListen = true;
startListenForUDPBroadcast();
Log.i("UDP", "Service started");
return START_STICKY;
}
}
Can someone give me the simplest method of getting the String from the service to the main activity, or if I already have it, where I am going wrong in using it? I would like to avoid having to rewrite my Service as an IntentService unless it's absolutely necessary to do so since this is a relatively simple object to pass to MainActivity
Thanks
You could try subscribing to the service. What I mean is pass some interface that the service calls to notify the activity about changes, here's an example I just tested:
A Subscriber interface
public interface ServiceSubscriber {
void messageCallback(String message);
}
Subscribe to the service using the Subscriber
public class TestService extends Service {
ArrayList<ServiceSubscriber> subscribers = new ArrayList<>();
private TestBinder testBinder = new TestBinder();
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(){
#Override
public void run() {
while(true){
//this is where you are receiving UDP packets
doStuff();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
return super.onStartCommand(intent, flags, startId);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return testBinder;
}
private void doStuff() {
System.out.println("Service is doing stuff!");
//loop through your subscribers and notify them of your changes
//a loop here isn't very costly, if there aren't many subscribers
for (ServiceSubscriber subscriber : subscribers) {
subscriber.messageCallback("I'm doing stuff");
}
}
public class TestBinder extends Binder {
public TestService getService() {
return TestService.this;
}
}
public void subscribeToMessages(ServiceSubscriber subscriber) {
subscribers.add(subscriber);
}
public void unSubscribeToMessages(ServiceSubscriber subscriber) {
subscribers.remove(subscriber);
}
}
Now for the usual Binding Activity, where you define what you need to do with the Message Callback:
public class MainActivity extends AppCompatActivity {
private TestService testService;
private Subscriber subscriber;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, TestService.class),serviceConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
testService = ((TestService.TestBinder)service).getService();
subscriber = new ServiceSubscriber() {
#Override
public void messageCallback(String message) {
//I'm just printing out the message received
//Be careful if you need to do UI stuff to use a
//Handler
System.out.println(message);
}
}
testService.subscribeToMessages(subscriber );
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
};
}
Of course don't forget to unsubscribe on destroy.
Updating UI often doesn't break your app if you do it by using a handler
//activity fields
Handler handler
//in activity constructor
handler = new Handler();
//update UI by calling
handler.post(new Runnable(){
#Override
public void run(){
//update the UI here
}
EDIT: I forgot to keep a reference of the subscriber, to unsubscribe later. Changed from anonymous instance to a field.
Make below method to your sevice class:
private void sendMessage() {
Intent intent = new Intent("message");
intent.putExtra("message", your_message);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
And put the below code in your activity class:
#Override
public void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this)
.registerReceiver(mMessageReceiver,
new IntentFilter("message"));
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String yourMessage = intent.getIntExtra("message",-1);
}
};
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(mMessageReceiver);
super.onPause();
}
Note: -1 is for default value
In my application I want to establish a TCP connection with a service and bind this service to every activity where it is needed. It gets started in my Login activity and everything is working like it should. Here is the code:
private TCPService mService;
private boolean mBound = false;
#Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, TCPService.class);
startService(intent);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
if (mBound) {
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
TCPService.LocalBinder binder = (TCPService.LocalBinder)service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
Now I want to bind the service in another activity by using almost the same code just without startService(intent); But the mBound is never set to true and I can't access the service functions like it is intented.
Here is the implementation of the service:
public class TCPService extends Service {
private final IBinder myBinder = new LocalBinder();
public class LocalBinder extends Binder {
public TCPService getService() {
return TCPService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return myBinder;
}
}
What am I missing out? Is there anything I should check that may cause this behaviour?
Thanks in advance
I'm developing an app, which gets data from web and writes it back to web. For that I was using AsyncTask, but those requests weren't optimized, so I had to authenticate user, on every request. Now I want to use Service for holding the connection to backend and to do requests without needing to authenticate the user on every requests.
I'm having trouble to send this service object to subactivity without creating an new instance and nulling all the values I need from the service. So how to do it without osing values ?
Here is my service:
public class LasteMVService extends Service {
private ArrayList<WorkSheet> workSheetArrayList;
private SpreadSheet selectedSpeadSheet;
private static String LOG_TAG = "LasteMVService";
private IBinder mBinder = new MyBinder();
#Override
public void onCreate() {
super.onCreate();
new Thread(new Task()).start();
Log.v(LOG_TAG, "in onCreate");
}
#Override
public IBinder onBind(Intent intent) {
Log.v(LOG_TAG, "in onBind");
return mBinder;
}
#Override
public void onRebind(Intent intent) {
Log.v(LOG_TAG, "in onRebind");
super.onRebind(intent);
}
public SpreadSheet getSelectedSpeadSheet() {
return selectedSpeadSheet;
}
public ArrayList<WorkSheet> getWorkSheetArrayList() {
return workSheetArrayList;
}
#Override
public void onDestroy() {
Log.v(LOG_TAG, "in onDestroy");
super.onDestroy();
}
public class MyBinder extends Binder {
public LasteMVService getService() {
return LasteMVService.this;
}
}
class Task implements Runnable {
#Override
public void run() {
synchronized (this){
AndroidAuthenticator androidAuthenticator = new AndroidAuthenticator(getApplicationContext());
SpreadSheetFactory spf = SpreadSheetFactory.getInstance(androidAuthenticator);
ArrayList<SpreadSheet> sheets = spf.getSpreadSheet("Stardiprotokoll", true);
if (sheets != null) {
final SpreadSheet spreadSheet = sheets.get(0);
selectedSpeadSheet = spreadSheet;
ArrayList<WorkSheet> workSheet = spreadSheet.getAllWorkSheets();
workSheetArrayList = workSheet;
}
}
}
}
}
And here is where I bind it with BaseActivity:
public class BaseActivity extends AppCompatActivity {
protected LasteMVService lasteMVService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lasteMVService = ((LasteMVApplication) getApplicationContext()).getLasteMVService();
if(lasteMVService == null){
((LasteMVApplication) getApplicationContext()).initApp();
lasteMVService = ((LasteMVApplication) getApplicationContext()).getLasteMVService();
Util.restartApp(this, true);
}
}
#Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(BaseActivity.this,LasteMVService.class);
startService(intent);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
}
protected ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
LasteMVService.MyBinder myBinder = (LasteMVService.MyBinder) service;
lasteMVService = myBinder.getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
};
My question is how to get the same instance of the service in some of the subactivities of BaseActivity?
Okay so you need a service object, Scratch getting the object's reference from base activity rather create a singleton class that is globally accessible throughout your application..
The source code to help you is below
public class Common extends Application {
private Service mService; //Service Object from your class that extends service.
private Context mContext;
public Service getService(){
return mService;
}
public void setService(Service service){
mService = service;
}
#Override
public void onCreate() {
super.onCreate();
//Gets the context associated with the whole application
mContext = getApplicationContext();
}
}
public class Service extends Service{
private Common mApp;
private Context mContext;
//Instantiate the Common Object from the singleton class within on create.
#Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
mApp = (Common) getApplicationContext();
mApp.setService(this);
}
}
//Now to use it in your base Activity
public class BaseActivity extends AppCompatActivity{
private Common mApp;
private Service mService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mApp = (Common) mContext.getApplicationContext();
//To use the service you would simply use:
mService = mApp.getService()./*Some public method defined in your service*/;
}
}
I have some programm, where I want to save the instance of service and use it in another activities. But service doesn't create.
Just see the code, please.
MainActivity.java:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainService.get(this);
}
}
MainService.java:
public class MainService extends Service {
public static Object sWait = new Object();
public static MainService instance;
#Override
public IBinder onBind(Intent intent) {
return null;
}
public static MainService get(Context mContext) {
if (instance == null) {
Intent intent = new Intent(mContext, MainService.class);
mContext.startService(intent);
}
while (true) {
if (instance != null) {
Log.v("myLogs", "all is good!");
break;
}
synchronized (sWait) {
try {
sWait.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return instance;
}
#Override
public void onCreate() {
Log.v("myLogs", "created!");
instance = this;
synchronized (sWait) {
sWait.notify();
}
}
}
When I don't use while in MainService.get(), service creates. I added this service to manifest file. I don't know where is there error. I need your help.
That's just not the way you should use Service in Android.
What you probably need is to use bindService() call from your Activity.
See more about bound service here http://developer.android.com/guide/components/bound-services.html
If you need the service to be the same instance each time you bind to it, call startService once when your application start.
Example code:
public class LocalBinder<T> extends Binder {
private WeakReference<T> mService;
public LocalBinder(T service) {
mService = new WeakReference<T>(service);
}
public T getService() {
return mService.get();
}
}
public class MyService extends Service {
private final LocalBinder<MyService> binder;
public MyService() {
binder = new LocalBinder<MyService>(this);
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
}
public class MyActivity extends Activity implements ServiceConnection {
#Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, MyService.class), this, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
unbindService(this);
}
#SuppressWarnings("unchecked")
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyService serv = ((LocalBinder<MyService>) service).getService();
// serv is your service instance now
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
}
To start your service use (call this from your activity!):
Intent serviceIntent = new Intent(this, MainService.class);
startService(serviceIntent);
see docs: https://developer.android.com/training/run-background-service/send-request.html
That is definitely not the way you are supposed to use services in android.
I would suggest you revisit the offical android docs.
To get a quick introduction visit this tutorial here.