I am developing an application and I need that when I leave the application (onPause ()) send a message to the service, using a bundle to save some data of my application. It works great. My problem is that when the application starts, it executes the method onCreate (), in this method I use the following code to establish the connection between the application and the service:
MyActivity.java
// This takes care of make the connection
this.getApplicationContext().startService(intent);
this.bindService(intent, mConnectionCallback, Context.BIND_AUTO_CREATE);
/** I need restore the bundle that is in the service, thus i'm sending a message */
Message msg = Message.obtain();
try {
Bundle bundle = new Bundle();
msg.setData(bundle);
msg.what = MyService.REQUEST_BUNDLE;
msg.replyTo = mMessageDispatcher;
mServiceConnection.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
The problem is that when you send the message, the connection will still not be established and thus this will throw a NullPointerException. And i dont know how manage this. To be clear my app is just a simple time-tracker and when the user exits the application, I want the time to be saved in a bundle in the service. Is there a way to send the message, right after establishing the connection?
Some of you will say: - "Just send the message after establishing the connection in method onServiceConnected (ComponentName className, IBinder service) of your callback ServiceConnection". But the problem is that I have separated the activity from the API implementation of the service. Here is the complete code of my classes:
ServiceManager.java
public class ServiceManager extends Service {
private NotificationManager mNM;
protected HashMap<Integer, Method> magicSwitch = new HashMap<Integer, Method>();
public ServiceManager() {
try {
for (Method method : Class
.forName(getClass().getCanonicalName())
.getMethods()) {
if (method.isAnnotationPresent(ExecutesWhen.class)) {
try {
ExecutesWhen a = method.getAnnotation(ExecutesWhen.class);
magicSwitch.put(a.what(), method);
Log.d("AkrasiaService","AkrasiaService now knows how handle a "+method.getName()+" with id="+a.what());
} catch (Throwable ex) {
ex.printStackTrace();
}
}
}
} catch (SecurityException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
class ServiceHandler extends Handler {
#Override
public void handleMessage(Message msg) {
Log.d("AkrasiaService","The service is going to manage a message from the client with what="+msg.what);
try {
Method met = magicSwitch.get(msg.what);
if (met == null) {
throw new NonExistingWhatException();
} else {
met.invoke(ServiceManager.this, msg);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessageInBox = new Messenger(new ServiceHandler());
/**
* Sends a message to the replyTo client.
* #param replyTo: The <code>Messenger</code> to reply the message.
* #param what: The what (subject).
* #param bundle: A data bundle that will go attached to the message.
*/
protected void sendMessageToClient(Messenger replyTo, int what, Bundle bundle) {
try {
Message msg = Message.obtain(null,
what, 0, 0);
msg.setData(bundle);
replyTo.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
#Override
public IBinder onBind(Intent intent) {
return mMessageInBox.getBinder();
}
#Override
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
showNotification();
}
/**
* Show a notification while this service is running.
*/
private void showNotification() {
//Here we immplement the notification
}
}
AkrasiaService.java (my concrete service)
public class AkrasiaService extends ServiceManager {
public final static int TRACKER_APP_BACKUP_REFRESH = 0;
public final static int TRACKER_APP_BACKUP_RETRIVE = 1;
public Bundle mTrackerBackupBundle;
public AkrasiaService() {
super();
}
/** This are the handlers of the request from the client application. The
* annotation ExecuteWhen specifies which method must handle one determined request.
* It uses the "what" annotation attribute like discriminative. */
#ExecutesWhen(what = AkrasiaService.TRACKER_APP_BACKUP_REFRESH)
public void trackerBundleRefresh(Message msg) {
mTrackerBackupBundle = msg.getData();
}
#ExecutesWhen(what = AkrasiaService.TRACKER_APP_BACKUP_RETRIVE)
public void trackerBundleRetrive(Message msg) {
sendMessageToClient(msg.replyTo, AkrasiaService.TRACKER_APP_BACKUP_RETRIVE, mTrackerBackupBundle);
}
//Test
#ExecutesWhen(what = AkrasiaService.FOO)
public void fooResponse(Message msg) {
Bundle bundle = new Bundle();
bundle.putString("test", "Test value");
sendMessageToClient(msg.replyTo, AkrasiaService.FOO, bundle);
}
}
AnnotatedHandler.java
public class AnnotatedHandler extends Handler {
protected HashMap<Integer, Method> magicSwitch = new HashMap<Integer, Method>();
public AnnotatedHandler() {
try {
for (Method method : this.getClass().getMethods() ) {
if (method.isAnnotationPresent(ExecutesWhen.class)) {
try {
ExecutesWhen a = method
.getAnnotation(ExecutesWhen.class);
magicSwitch.put(a.what(), method);
Log.d("AnnotatedHandler","AnnotatedHandler now knows how handle a "+method.getName()+" and id"+a.what());
} catch (Throwable ex) {
ex.printStackTrace();
}
}
}
} catch (SecurityException e) {
e.printStackTrace();
}
}
#Override
public void handleMessage(Message msg) {
try {
Log.d("AnnotatedHandler","The service is going to manage a message from the client with what="+msg.what);
Method met = magicSwitch.get(msg.what);
if (met == null) {
throw new NonExistingWhatException();
} else {
met.invoke(AnnotatedHandler.this, msg);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
ServiceClient.java (My service api implementation)
public class ServiceClient {
/** Messenger for communicating with service. */
Messenger mServiceConnection = null;
/** Flag indicating whether we have called bind on the service. */
private boolean mIsBound = false;
private Messenger mMessageDispatcher;
/**
* Class for interacting with the main interface of the service. This callback
* takes care of setup <code>mServiceConnection</code> and therefore, start to
* talk with the service.
*/
private ServiceConnection mConnectionCallback = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
mServiceConnection = new Messenger(service);
Log.d("AkrasiaService","The application is connected to the service now!");
mIsBound = true;
}
public void onServiceDisconnected(ComponentName className) {
mServiceConnection = null;
}
};
/**
* This method makes the binding between the service and the client application.
* #param intent: An intent of the concrete implementation of the <code>ServiceManager</code>
* #param activity: The Activity thats want communicate with the service.
*/
public void doBindService(Intent intent, Activity activity) {
Log.d("AkrasiaService","The application is trying to bind to the service...");
activity.getApplicationContext().startService(intent);
activity.bindService(intent, mConnectionCallback, Context.BIND_AUTO_CREATE);
}
public void doUnbindService(Activity activity) {
if (mIsBound) {
activity.unbindService(mConnectionCallback);
mIsBound = false;
}
}
/**
* This method sends a single message to the service.
* #param what: The what (subject) of the message.
*/
public void sendMessage(int what) {
Message msg = Message.obtain();
try {
Bundle bundle = new Bundle();
msg.setData(bundle);
msg.what = what;
msg.replyTo = mMessageDispatcher;
Log.d("AkrasiaService","The application is going to send a message to the service");
mServiceConnection.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* This method sends a message to the service with a bundle attached.
* #param what: The what (subject) of the message.
* #param bundle: The data bundle attached to the message.
*/
public void sendMessage(int what, Bundle bundle) {
Message msg = Message.obtain();
try {
msg.setData(bundle);
msg.what = what;
msg.replyTo = mMessageDispatcher;
mServiceConnection.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void setIncomingHandler(Handler handler) {
mMessageDispatcher = new Messenger(handler);
}
public boolean isConnected() {
return mIsBound;
}
}
MainActivity.java (the Activity of my Tracker app)
public class MainActivity extends SherlockFragmentActivity {
private ActionBar mActionBar;
private Tab mStatTab;
private Tab mTrackerTab;
private ServiceClient mClientServiceAPI;
/**
* Handler of incoming messages from service.
*/
public class MyHandler extends AnnotatedHandler {
#ExecutesWhen(what = AkrasiaService.TRACKER_APP_BACKUP_RETRIVE)
public void handleBackupRestore(Message msg) {
Log.d("Client","Handling a message");
Bundle bundle = msg.getData();
if ((bundle != null) && (!bundle.isEmpty())) {
Long timeStamp = bundle.getLong("last-time");
Long chrometerTime = bundle.getLong("chrometer-time");
Chronometer chrometer = (Chronometer) findViewById(R.id.chronometer);
//We add the time between the timestamp and now to the chorometer base
Long now = Calendar.getInstance().getTimeInMillis();
chrometerTime = now - timeStamp + chrometerTime;
chrometer.setBase(chrometerTime);
}
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
setTheme(com.actionbarsherlock.R.style.Sherlock___Theme);
super.onCreate(savedInstanceState);
// Notice that setContentView() is not used, because we use the root
// android.R.id.content as the container for each fragment
// try {
// DatabaseFixture.populateDatabase();
// } catch (NumberFormatException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// } catch (ParseException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
ApplicationContext.getInstance().setMainActivity(this);
ApplicationContext.getInstance().setupPreferences();
sherlockActionBarSetup();
}
private void sherlockActionBarSetup() {
mActionBar = getSupportActionBar();
mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
mActionBar.setDisplayShowTitleEnabled(false);
mActionBar.setDisplayShowHomeEnabled(false);
TabListener<TrackerFragment> trackerTabListener = new TabListener<TrackerFragment>(this,
"tracker", TrackerFragment.class);
mTrackerTab = mActionBar.newTab().setText("Track").setTabListener(trackerTabListener);
mActionBar.addTab(mTrackerTab);
TabListener<StatFragment> statTabListener = new TabListener<StatFragment>(this, "stats",
StatFragment.class);
mStatTab = mActionBar.newTab().setText("Stats").setTabListener(statTabListener);
mActionBar.addTab(mStatTab);
}
public void sendBackupToTheService() {
Log.d("Client","We are going to make a backup of the chromnometer");
Chronometer chrometer = (Chronometer) findViewById(R.id.chronometer);
Bundle bundle = new Bundle();
bundle.putLong("last-time", Calendar.getInstance().getTimeInMillis());
bundle.putLong("chrometer-time", chrometer.getBase());
bundle.putBoolean("deprecated", false);
mClientServiceAPI.sendMessage(AkrasiaService.TRACKER_APP_BACKUP_REFRESH, bundle);
}
#Override
protected void onPause() {
super.onPause();
sendBackupToTheService();
mClientServiceAPI.doUnbindService(this);
}
#Override
protected void onStop() {
super.onStop();
mClientServiceAPI.doUnbindService(this);
}
public void restarBackupFromService(View view) {
mClientServiceAPI.sendMessage(AkrasiaService.TRACKER_APP_BACKUP_RETRIVE);
}
#Override
protected void onDestroy() {
super.onDestroy();
mClientServiceAPI.doUnbindService(this);
}
#Override
public void onResume() {
super.onResume();
//Sadly this behavior can't be exported to ServiceClient.
//This from below used to be in onResume method
Log.d("Client","We are going to connect to the service");
mClientServiceAPI = new ServiceClient();
mClientServiceAPI.setIncomingHandler(new MyHandler());
Intent intent = new Intent(AkrasiaService.class.getName());
mClientServiceAPI.doBindService(intent,this);
}
/*
* Apparently you can't just tie the callback to the fragment from:
* http://stackoverflow.com/a/6271637/147072
*/
public void triggerClick(View view) {
TrackerFragment fragment = (TrackerFragment)getSupportFragmentManager().findFragmentByTag(
"tracker");
fragment.triggerClick(view);
}
public void saveTimeClick(View view) {
TrackerFragment fragment = (TrackerFragment)getSupportFragmentManager().findFragmentByTag(
"tracker");
try {
fragment.saveTimeClick(view);
} catch (ParseException e) {
e.printStackTrace();
}
// We reload the StatFragment this is to refresh the Graph of the
// StatFragment
mActionBar.removeTab(mStatTab);
TabListener<StatFragment> statTabListener = new TabListener<StatFragment>(this, "stats",
StatFragment.class);
mStatTab = mActionBar.newTab().setText("Stats").setTabListener(statTabListener);
mActionBar.addTab(mStatTab);
}
public void discardTimeClick(View view) {
TrackerFragment fragment = (TrackerFragment)getSupportFragmentManager().findFragmentByTag(
"tracker");
fragment.discardTimeClick(view);
}
How you can see in the MainActivity.java in the onResume method i'm doing the binding between the app and the service, this calls the doBindService(Intent,Activity) method of the ServiceClient.java, and this one do the real binding.
I've been thinking about saving the block of code that sends the message to the service within an instance of the class Method, and send it to the api for this run it on your onServiceConnected method, but I think there must be a better way.
Any suggestions will be appreciated.
Related
I'm trying to creat a bacckground service that will have a thread that adds one to a counter every minute.
I want other apps to be able to call a methed call getBot that will retunt the value.
Right now I have a test app that has a activty that bindss to the app so it can talk to it to get the counter. When it exits it unbounds to it.
It has a button called botGert that gets the current counter value from the service.
When it unbounds to it, the service shuts down. How can I get it so it does not shut down????
service class
public class MessengerService extends Service {
/** Command to the service to display a message */
static final int MSG_SAY_HELLO = 1;
static final int MSG_BOT_GET = 2;
static final int MSG_BOT_SET = 3;
static final int MSG_BOT_STOP = 4;
static final int MSG_BOT_PAUSE = 5;
static final int MSG_GET_ORDERS = 6;
static final int MSG_GET_POINT = 7;
static final int MSG_BOT_GET_RES = 2;
static final int PRICE_STATIC =1;
static final int PRICE_PERCENTAGE =2;
static final int PRICE_ALL = 3;
static final int VOL_STATIC = 1;
static final int VOL_PERCENTAGE = 2;
static final int VOL_ALL = 3;
int counter;
boolean isStop=false; // put bot in pause ste on next 1 min update
boolean isUYpdateNextMinute=false;
/**
This is called by a client to exicute code in this service
*/
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_BOT_GET:
Message resp = Message.obtain(null, MSG_BOT_GET_RES);
Bundle bResp = new Bundle();
bResp.putString("respData", Integer.toString(counter ));
resp.setData(bResp);
try {
msg.replyTo.send(resp);
} catch (Exception e)
{
}
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
final Messenger mMessenger = new Messenger(new IncomingHandler());
/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
#Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
//////////////////////////////////////////////////////////////////////////////
// emplanent callbacks
MessengerService parent;
#Override
public void onCreate() {
Toast.makeText(this, "Service Created", Toast.LENGTH_LONG).show();
parent=this;
}
#Override
public void onStart(Intent intent, int startid) {
Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
ThreadDemo T1 = new ThreadDemo( "Thread-1");
T1.start();
// sgart 1 minute update thread
}
#Override
public void onDestroy() {
Toast.makeText(this, "Service Stopped", Toast.LENGTH_LONG).show();
}
///////////////////////////////////////////////////////////////
// thread code that runbs counter
class ThreadDemo extends Thread {
private String threadName;
ThreadDemo( String name) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
try {
do {
counter++;
Thread.sleep(1000 *60 );
counter++;
} while (true);
} catch(Exception e)
{
Toast.makeText(parent, "thread stopped", Toast.LENGTH_LONG).show();
}
} // end inner thread class
} // end class
}
/////////////////////////////////////////////////////////////////////
activity class
public class ActivityBoundMessenger extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bound_messenger);
}
/** Messenger for communicating with the service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mBound;
/**
* Class for interacting with the main interface of the service.
*/
/////////////////////////////////////////////////////////////////////////////////////////
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mBound = false;
}
};
////////////////////////////////////////////////////////////////////////////////
public void sayHello(View v) {
if (!mBound) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void butSet(View v) {
if (!mBound) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_BOT_SET, 0, 0);
Bundle b = new Bundle();
b.putString("data", "json object");
msg.setData(b);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void butGet(View v) {
if (!mBound) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message
.obtain(null, MessengerService.MSG_BOT_GET);
msg.replyTo = new Messenger(new ResponseHandler());
// We pass the value
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
////////////////////////////////////////////////////////////////////////////////////////
// these metheds get called whenb this acty starts and endsa
#Override
protected void onStart() {
super.onStart();
// Bind to the service
////////////////////////////////////////////
Intent ser = new Intent(this, MessengerService.class);
startService(ser);
bindService(ser, mConnection,
Context.BIND_AUTO_CREATE);
///////////////////////////////////////////
}
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
// This class handles the Service response
int ted=0;
String nextUpdate=null;
class ResponseHandler extends Handler {
#Override
public void handleMessage(Message msg) {
int respCode = msg.what;
switch (respCode) {
case MessengerService.MSG_BOT_GET_RES:
String nextUpdate= msg.getData().getString("respData");
ted++;
}
}
}
}
From Android Documentation:
When the last client unbinds from the service, the system destroys the service, unless the service was also started by startService().
So, all you have to do is call this in your onCreate() method in the Service:
startService(new Intent(this, MessengerService.class))
Of course make sure to call stopSelf() when you are done with the service.
I am creating bound service for socket connection.Which means it is creating a long polling connection and listens my server.If user closes the app in task manager my service is killing i have no problem with this.But when user presses the back button I am calling activity.finish() method for close app.But with this method my service doesn't kill,it is still connected to socket server.
Is this normal ? And Could be this drain the battery ?
My service:
public class SocketService extends Service {
//you need constants to tell servise and activity what you are sending a message for
public static final int REGISTER_CHAT_ACTIVITY = 1;
public static final int MESSAGE_RECEIVED = 2;
final Messenger mMessenger = new Messenger(new IncomingHandler());
Messenger chat;
private Socket socket;
#Override
public void onCreate() {
try {
socket = IO.socket("ip");
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
}
}).on("connected", new Emitter.Listener() {
#Override
public void call(Object... args) {
}
}).on("message", new Emitter.Listener() {
#Override
public void call(Object... args) {
try {
chat.send(android.os.Message.obtain(null, MESSAGE_RECEIVED, args[0]));
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
//and add all the other on listeners here
socket.connect();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (socket != null) {
socket.disconnect();
socket.connect();
} else {
try {
socket = IO.socket("ip");
socket.connect();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
class IncomingHandler extends Handler {
#Override
public void handleMessage(android.os.Message msg) {
switch(msg.what){
case REGISTER_CHAT_ACTIVITY:
chat = msg.replyTo;
break;
}
}
}
public class LocalBinder extends Binder {
SocketService getService() {
return SocketService.this;
}
}
}
I had something similar a while ago i solved the issue by using shared preferences.(Note: I dont think it's the best answer but it solved my problem)
I saved in preferences a boolean to register when i dont need the service anymore but lost reference of it.
public class YourService extends Service {
private YourService serv;
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
serv = this;
Then Somehwere on your code that the service does frequently.
if(!sharedPref.getBoolean("TurnOffService", false)){
serv.stopSelf();
}
Hope it helps.
I've got problem and I don't know how to resolve it. I wanted create service which will sent sensor changes to client (thread).
I've got thread where inside I start services, and client thread receive answers and sent them through bluetooth. The problem is I can't handle service.
public class SensorMsgService extends Service implements SensorEventListener{
public static final int MSG_SAY_HELLO = 1;
public static final int MSG_REGISTER_CLIENT = 1;
public static final int MSG_UNREGISTER_CLIENT = 2;
public static final int MSG_SET_VALUE = 3;
static final String TAG = "Sensor Msg Service";
ArrayList<Messenger> mClients = new ArrayList<Messenger>();
private SensorManager mSensorManager;
private Sensor mSensor;
private ArrayList<Float> temp;
private Looper mServiceLooper;
public class IncomingHandler extends Handler{
public IncomingHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
break;
case MSG_SET_VALUE:
//mValue = msg.arg1;
for (int i = mClients.size() - 1; i >= 0; i--) {
try {
/*mClients.get(i).send(
Message.obtain(null, MSG_SET_VALUE, 1, 0));*/
Log.d(TAG, "Message from client");
}
//catch (RemoteException e) {
catch (Exception e) {
// The client is dead. Remove it from the list;
// we are going through the list from back to front
// so this is safe to do inside the loop.
mClients.remove(i);
}
}
break;
default:
super.handleMessage(msg);
}
}
//Toast.makeText(getApplicationContext(), "Hello service test", Toast.LENGTH_SHORT).show();
}
final Messenger mMessenger = new Messenger(new IncomingHandler(mServiceLooper));
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
Log.d(TAG, "binding");
//return null;
return mMessenger.getBinder();
}
#Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
// TODO Auto-generated method stub
super.bindService(service, conn, flags);
// mServiceLooper.prepare();
temp = new ArrayList<Float>();
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
return true;
}
#Override
public void unbindService(ServiceConnection conn) {
// TODO Auto-generated method stub
super.unbindService(conn);
mSensorManager.unregisterListener(this);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
#Override
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
temp.add(event.values[0]);
temp.add(event.values[1]);
temp.add(event.values[2]);
for (int i = mClients.size() - 1; i >= 0; i--) {
try {
mClients.get(i).send(
Message.obtain(null, MSG_SET_VALUE, temp));
Log.d(TAG, "Message service to client sending");
}
//catch (RemoteException e) {
catch (Exception e) {
// The client is dead. Remove it from the list;
// we are going through the list from back to front
// so this is safe to do inside the loop.
mClients.remove(i);
}
}
}
}
I've initiated this service as I said from thread:
public class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private Context mContext;
/** Messenger for communicating with service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mIsBound;
ArrayList<Float> temp;
private Looper mServiceLooper;
public ConnectedThread(BluetoothSocket socket, Context ctxx) {
Log.d("ConnectedThread", "constructor ConnectedThread");
mContext = ctxx;
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
temp = new ArrayList<Float>();
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e("ConnectedThread", "it was trying create input and output sockets", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
Log.i("ConnectedThread", "run mConnectedThread");
byte[] buffer = new byte[1024];
/*int i = 0;
while(true)
{
Log.i("ConnectedThread", "sending int test value");
write(i++);
}*/
doBindService();
}
/**
* Write to the connected OutStream.
* #param buffer The bytes to write
*/
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
} catch (IOException e) {
Log.e("ConnectedThread", "Exception during write", e);
}
}
public void write(int out) {
try {
mmOutStream.write(out);
} catch (IOException e) {
Log.e("ConnectedThread", "Exception during write", e);
}
}
public void cancel() {
try {
//mmOutStream.write(EXIT_CMD);
mmSocket.close();
} catch (IOException e) {
Log.e("ConnectedThread", "close() of connect socket failed", e);
}
}
///------------------------------
/**
* Handler of incoming messages from service.
*/
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SensorMsgService.MSG_SET_VALUE:
temp = (ArrayList<Float>) msg.obj;
//mCallbackText.setText("Received from service: " + msg.arg1);
Log.d("Handle message from service", temp.get(0) + " " + temp.get(1) + " " + temp.get(2) + "\n");
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = new Messenger(service);
//mCallbackText.setText("Attached.");
// We want to monitor the service for as long as we are
// connected to it.
try {
Message msg = Message.obtain(null,
SensorMsgService.MSG_REGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
// Give it some value as an example.
msg = Message.obtain(null, SensorMsgService.MSG_SET_VALUE, this
.hashCode(), 0);
mService.send(msg);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
// As part of the sample, tell the user what happened.
Toast.makeText(mContext, "Service connected",
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
//mCallbackText.setText("Disconnected.");
// As part of the sample, tell the user what happened.
Toast.makeText(mContext, "Service disconnedcted",
Toast.LENGTH_SHORT).show();
}
};
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
// do Bind
void doBindService() {
// Establish a connection with the service. We use an explicit
// class name because there is no reason to be able to let other
// applications replace our component.
mContext.bindService(new Intent(mContext, SensorMsgService.class),
mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
Log.d("ConnectedThread", "doBindService");
}
void doUnbindService() {
if (mIsBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mService != null) {
try {
Message msg = Message.obtain(null,
SensorMsgService.MSG_UNREGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
// There is nothing special we need to do if the service
// has crashed.
}
}
// Detach our existing connection.
mContext.unbindService(mConnection);
mIsBound = false;
//mCallbackText.setText("Unbinding.");
}
}
}
The problem is when I wanted run this thread:
04-04 01:36:26.853: W/dalvikvm(29341): threadid=12: thread exiting with uncaught exception (group=0x420372a0)
04-04 01:36:26.853: E/AndroidRuntime(29341): FATAL EXCEPTION: Thread-11596
04-04 01:36:26.853: E/AndroidRuntime(29341): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
04-04 01:36:26.853: E/AndroidRuntime(29341): at android.os.Handler.<init> (Handler.java:121)
04-04 01:36:26.853: E/AndroidRuntime(29341): at com.nauka.bluetooth.ConnectedThread$IncomingHandler.<init>(ConnectedThread.java:127)
04-04 01:36:26.853: E/AndroidRuntime(29341): at com.nauka.bluetooth.ConnectedThread.<init>(ConnectedThread.java:200)
Could you show me how to handle this issue? thx
I am building a music player that uses a service for playback. I have an Activity UI that controls (play, pause, next, ...) the service.
I want to update the UI from my service when the next or previous button is pressed. I thought of passing an integer value. Here is my code:
I have used a messenger for communication. The code in my service looks like:
enum Event {
NextSongChanged, CurrentPlayingSong
};
public synchronized void registerHandler(Messenger m) {
clients.add(m);
}
private void emit(Event e) {
for (Messenger m : clients) {
try {
m.send(Message.obtain(null, e.ordinal()));
} catch (RemoteException exception) {
/* The client must've died */
clients.remove(m);
}
}
}
My method that I am calling on click of next button:
public synchronized void playnext() {
reset();
if(songIndex < (songsList.size() - 1)) {
songIndex += 1;
playSong(songIndex);
} else {
songIndex = 0;
playSong(songIndex);
}
emit(Event.NextSongChanged);
}
As soon as I fire the NextSongChanged event I want to pass the "songIndex" variable into my activity. Any idea on how to achieve this?
My Activity code to handle the event:
private Messenger playerMessenger = new Messenger(new Handler() {
#Override
public void handleMessage(Message msg) {
switch (MyService.Event.values()[msg.what]) {
case NextSongChanged:
//String songTitle = songsList.get(currentSongIndex+1).get("songTitle");
//currentSongIndex += 1;
//songTitleLabel.setText(songTitle);
//updatePlaying();
break;
case CurrentPlayingSong:
break;
}
}
});
Sample code hope will help others
1. Set Handler (to receive message) and Messenger (to communicate) MainActivity.java
Messenger msgService;
boolean isBound;
ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) { isBound = false; }
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
isBound = true;
msgService = new Messenger(service);
}
};
public void sendMessage(View view) {
if (isBound) {
try {
Message message = Message.obtain(null, MessangerService.MESSAGE, 1, 1);
message.replyTo = replyMessenger;
Bundle bundle = new Bundle();
bundle.putString("rec", "Hi, you hear me");
message.setData(bundle);
msgService.send(message); //sending message to service
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
//setting reply messenger and handler
Messenger replyMessenger = new Messenger(new HandlerReplyMsg());
class HandlerReplyMsg extends Handler {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String recdMessage = msg.obj.toString(); //msg received from service
toast(recdMessage);
}
}
2. Setup service class MessengerService.java
Messenger replyMessanger;
final static int MESSAGE = 1;
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
if (msg.what == MESSAGE) {
Bundle bundle = msg.getData();
toast(bundle.getString("rec"));//message received
replyMessanger = msg.replyTo; //init reply messenger
doSomething();
}
}
}
private void doSomething() {
// do stuff
if (replyMessanger != null)
try {
Message message = new Message();
message.obj = "Yes loud and clear";
replyMessanger.send(message);//replying / sending msg to activity
} catch (RemoteException e) {
e.printStackTrace();
}
}
Messenger messenger = new Messenger(new IncomingHandler());
#Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
Make sure you declared Service in manifest and binding/unbinding Service in activity.
I am facing a grave problem. Inside a service I am opening Wifi connection and closing it after my task completes. Since, a service exits at any point i face a problem wherein the connection opens and remains open.
Is there a way i can handle this as i am using START_STICKY or i will have to handle it programmatically only?
EDIT : Can i share my intent information across couple of receivers (BroadcastReceiver). For example, I will write another receiver for action android.net.wifi.wifi_state_changed and my existing receiver is for android.intent.action.PHONE_STATE.
IF that can be achieved i can do something about it.
EDIT2 : My code is as follows:
public class CallReceiver extends BroadcastReceiver {
private static final String LOG_TAG = "CallReceiver";
private static final String CALL_ACTION = "android.intent.action.PHONE_STATE";
#Override
public void onReceive(Context context, Intent callIntent)
{
Log.d(LOG_TAG, "----------------Inside onReceive of CallReceiver----------------");
if (callIntent.getAction().equals(CALL_ACTION))
{
try
{
Intent myIntent = new Intent(context, MyService.class);
context.startService(myIntent);
}
catch (Exception e)
{
e.printStackTrace();
Log.d(LOG_TAG,"----------------Exception occured while starting service----------------");
}
}
}
}
public class MyService extends Service {
private Context context;
private static final String LOG_TAG = "MyService";
private Thread thread = null;
public MyService()
{
super();
Log.d(LOG_TAG, "----------------Inside Email Service constructor----------------");
}
public int onStartCommand(Intent myIntent, int flags, int startId)
{
Log.d(LOG_TAG, "----------------Email Service Command Started----------------");
try
{
context = getApplicationContext();
if(thread == null || !thread.isAlive())
{
thread = new Thread(new MyRunnable("Email Sender", myIntent));
thread.start();
}
}
catch (Exception e)
{
e.printStackTrace();
Log.d(LOG_TAG,
"----------------Exception occured in Email Service onStartCommand----------------");
}
return START_REDELIVER_INTENT;
}
class MyRunnable implements Runnable {
String name;
Intent myIntent;
public MyRunnable(String name, Intent myIntent) {
this.name = name;
this.myIntent = myIntent;
}
#Override
public void run()
{
try
{
doStuff(emailIntent);
}
catch (NumberFormatException e)
{
e.printStackTrace();
Log.d(LOG_TAG, e.getMessage());
}
catch (InterruptedException e)
{
e.printStackTrace();
Log.d(LOG_TAG, e.getMessage());
}
catch (Exception e)
{
e.printStackTrace();
Log.d(LOG_TAG, e.getMessage());
}
finally
{
stopSelf();
}
}
}
private void doStuff(Intent emailIntent) throws InterruptedException, Exception
{
if (context != null)
{
boolean isWifiConnection = false;
try
{
// Check if WiFi connection is available ,if yes try opening it;
// Attempt to open WiFi connection
isWifiConnection = Utility.isEnableWifiSuccessful(getApplicationContext());
Log.d(LOG_TAG, "----------------Wifi conn enabled = " + isWifiConnection
+ "----------------");
if (isWifiConnection)
{
// Do more stuff
}
}
catch (Exception e)
{
e.printStackTrace();
throw e;
}
finally
{
// Code never reaches here !! Somehow, the service stops and by
// the time the service stops,
// WiFi has been enabled
try
{
if (isWifiConnection)
{
Utility.isDisableWifiSuccessful(getApplicationContext());
}
}
catch (Exception e)
{
e.printStackTrace();
Log.d(LOG_TAG,
"----------------Error occured while closing network connections----------------");
}
}
}
else
{
Log.d(LOG_TAG, "----------------Context is null----------------");
}
}
#Override
public IBinder onBind(Intent intent)
{
// TODO Auto-generated method stub
return null;
}
}
Now, if i have another receiver as NetworkReceiver
public class NetworkReceiver extends BroadcastReceiver {
private static final String ACTION = "android.net.wifi.WIFI_STATE_CHANGED";
private static final String LOG_TAG = "NetworkReceiver";
#Override
public void onReceive(Context context, Intent networkIntent)
{
if(networkIntent.getAction().equals(ACTION))
{
Log.d(LOG_TAG, "----------------Inside Network Receiver----------------");
//Do something which will keep track who has opened the WiFi connection
}
}
}
then can myIntent and networkIntent share information and can MySerivce read that information.
Any help would be really grateful.
Service exits when the memory is too low, since you are already using START_STICKY, the service will be restarted once the memory resources are available. I beleive you might need to check if the connection is opened and you are done with the task, then you have stop the service by using stopSelf().
Hope this helps.
Thanks,
Ramesh