Android Service error to ui - android

i am getting errors/exceptions on my service which i use to stream a shoutcast.
I would like to show an error on ui thread if any exception is generated such as if music stream is not working.
How can this be done , Here is my current code.
UI Class :
private OnClickListener btnStopListener = new OnClickListener() {
public void onClick(View v) {
if (MyService.isRunning()) {
doUnbindService();
stopService(new Intent(MainActivity.this, MyService.class));
btnStop.setBackgroundResource(R.drawable.player_play);
barLoader.setVisibility(View.GONE);
} else {
Intent intent = new Intent(MainActivity.this, MyService.class);
intent.putExtra("streamUrl", strLinkUrl);
startService(intent);
doBindService();
}
}
};
void doBindService() {
bindService(new Intent(this, MyService.class), mConnection,
Context.BIND_AUTO_CREATE);
mIsBound = true;
}
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,
MyService.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.
unbindService(mConnection);
mIsBound = false;
}
}
Service Class :
public class MyService extends Service implements
OnPreparedListener,OnErrorListener{
public String txtBTitle, txtBChannel, txtBDescription, txtBTitleNow, txtBImageUrl ,txtBLinkUrl;private NotificationManager nm;
private static boolean isRunning = false;
private Timer timer1 = new Timer();
String strTitle,strArtist;
MediaPlayer mediaPlayer = new MediaPlayer();
ArrayList<Messenger> mClients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
int mValue = 0; // Holds last value set by a client.
static final int MSG_REGISTER_CLIENT = 1;
static final int MSG_UNREGISTER_CLIENT = 2;
static final int MSG_SET_INT_VALUE = 3;
static final int MSG_SET_STRING_VALUE = 4;
String strUrl;
final Messenger mMessenger = new Messenger(new IncomingHandler()); // Target we publish for clients to send messages to IncomingHandler.
#Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
class IncomingHandler extends Handler { // Handler of incoming messages from clients.
#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_INT_VALUE:
// incrementby = msg.arg1;
break;
default:
super.handleMessage(msg);
}
}
}
private void sendMessageToUI(int intvaluetosend) {
for (int i=mClients.size()-1; i>=0; i--) {
try {
// Send data as an Integer
mClients.get(i).send(Message.obtain(null, MSG_SET_INT_VALUE, intvaluetosend, 0));
//Send data as a String
Bundle b = new Bundle();
b.putString("str1",strTitle+"\n"+strArtist);
b.putString("strUrl", strUrl);
Message msg = Message.obtain(null, MSG_SET_STRING_VALUE);
msg.setData(b);
mClients.get(i).send(msg);
} catch (RemoteException 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);
}
}
}
#Override
public void onCreate() {
super.onCreate();
Log.i("MyService", "Service Started.");
showNotification();
isRunning = true;
}
private void showNotification() {
nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(R.string.service_started);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.icon, text, System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class).putExtra("fromWhere", "fromService"), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.service_label), text, contentIntent);
// Send the notification.
// We use a layout id because it is a unique number. We use it later to cancel.
nm.notify(R.string.service_started, notification);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
strUrl = intent.getExtras().getString("streamUrl");
Log.i("ZealDeveloper", strUrl);
if(!mediaPlayer.isPlaying()){
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(strUrl);
mediaPlayer.setOnPreparedListener(this);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mediaPlayer.prepareAsync(); // might take long! (for buffering, etc)
// mediaPlayer.start();
}
timer1.scheduleAtFixedRate(new TimerTask(){ public void run() {onMetaParser();}
}, 0, 100000L);
Log.i("MyService", "Received start id " + startId + ": " + intent);
return START_STICKY; // run until explicitly stopped.
}
public static boolean isRunning()
{
return isRunning;
}
//Meta Parser
private void onMetaParser() {
// TODO Auto-generated method stub
URL url;
try {
url = new URL(strUrl);
IcyStreamMeta icy = new IcyStreamMeta(url);
try{
strTitle = icy.getTitle();
}
catch (NullPointerException e) {
strTitle="";
}
try{
strArtist = icy.getArtist();
}
catch (NullPointerException e) {
strArtist = "";
}
System.out.println("Title : " + strTitle + strUrl);
System.gc();
// txtTitleNow.setText(T);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer.isPlaying()) {
mediaPlayer.release();
}
if (timer1 != null) {timer1.cancel();
Log.i("ZealDeveloper", "Timer 1 Cancled");}
nm.cancel(R.string.service_started); // Cancel the persistent notification.
Log.i("MyService", "Service Stopped.");
isRunning = false;
try {
timer1.cancel();
mediaPlayer.release();
mediaPlayer = null;
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onPrepared(MediaPlayer mp) {
// TODO Auto-generated method stub
mp.start();
sendMessageToUI(0);
}
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// TODO Auto-generated method stub
mediaPlayer.release();
timer1.cancel();
return false;}}
Stack Trace :
01-30 11:03:32.470: W/System.err(311): java.io.FileNotFoundException: http://xxxxxstream
01-30 11:03:32.480: E/MediaPlayer(311): error (1, -1004)
01-30 11:03:32.490: W/System.err(311): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:1162)
01-30 11:03:32.500: W/System.err(311): at com.sikhnetRadio.test.IcyStreamMeta.retreiveMetadata(IcyStreamMeta.java:91)
01-30 11:03:32.500: W/System.err(311): at com.sikhnetRadio.test.IcyStreamMeta.refreshMeta(IcyStreamMeta.java:79)
01-30 11:03:32.500: W/System.err(311): at com.sikhnetRadio.test.IcyStreamMeta.getMetadata(IcyStreamMeta.java:72)
01-30 11:03:32.500: W/System.err(311): at com.sikhnetRadio.test.IcyStreamMeta.getTitle(IcyStreamMeta.java:60)
01-30 11:03:32.500: W/System.err(311): at com.sikhnetRadio.test.MyService.onMetaParser(MyService.java:192)
01-30 11:03:32.510: W/System.err(311): at com.sikhnetRadio.test.MyService.access$0(MyService.java:182)
01-30 11:03:32.510: W/System.err(311): at com.sikhnetRadio.test.MyService$1.run(MyService.java:163)
01-30 11:03:32.510: W/System.err(311): at java.util.Timer$TimerImpl.run(Timer.java:289)
01-30 11:03:32.610: E/MediaPlayer(311): Error (1,-1004)

Two options
1) You can create traditional java type listener ( a interface that your Activity will override) and pass it to service through Ibinder interface .
2) Implement Broadcast receiver in your activity something similar to this (Here its been done for Intentservice i hope it won't be difficult for you to do the same for servie).

Timer is not thread-safe in UI. You sould use Handler instead.
As an example I would propose my utility class Clock. I use it myself in work. It is richly commented.
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import android.os.Handler;
import android.widget.TextView;
/**
* The class for creating and refreshing many different fields on different layouts,
* that can hold actual time and/or date in different formats
* The formats should be as in http://developer.android.com/reference/java/text/SimpleDateFormat.html.
* Only present and visible fields are being actualized, so there is no need to clean the clock list after closing an activity
*
* Examples of use:
*
* Clock.registerClock((TextView) findViewById(R.id.TimeField), "HH:mm");
* Clock.registerClock((TextView) findViewById(R.id.DateField), "d.M.yyyy EEE");
* Clock.start(10000L);
*
* #author Petr Gangnus
*/
public final class Clock {
/**
* the handler that works instead of timer and supports UI
*/
static private Handler handler = new Handler();
/**
* the interval of the time refreshing
*/
static private long refreshStep;
/**
* pairs TextView clockFace + time/date format
*/
private TextView clockFace;
private String format;
private Clock(TextView clockFace, String format){
this.clockFace=clockFace;
this.format=format;
}
// here is the list of views containing the visual timers that should be held actual
static private ArrayList<Clock> clocks=new ArrayList<Clock>();
/**
* fills all timer fields by actual time value, according to their formats.
*/
static private Runnable mUpdateTimeTask = new Runnable() {
public void run() {
for(Clock clock:clocks){
showActualTimeDate(clock);
}
handler.postDelayed(this,refreshStep);
}
};
//============================================ public members ====================================================================
/**
* add a clock to the list of updating clocks
* #param clockFace - the place where the time or date will be shown
* #param format - the format of the time/date
* #return
*/
public static boolean registerClock(TextView clockFace, String format){
if (clockFace==null) return false;
if(clocks.contains(clockFace)){
// old clockFace
clocks.get(clocks.indexOf(clockFace)).format=format;
} else {
// new clockFace
clocks.add(new Clock(clockFace, format));
}
return true;
}
/**
* remove a clock from the updating list
* #param clockFace
* #return
*/
public static boolean unRegisterClock(TextView clockFace){
if (clockFace==null) return false;
if(clocks.contains(clockFace)){
// found clockFace
clocks.remove(clocks.indexOf(clockFace));
} else {
// not found clockFace
return false;
}
return true;
}
/**
* put in the "place" the actual date/time in the appropriate "format"
* #param place
* #param format
*/
public static void showActualTimeDate(Clock clock){
if (clock.clockFace==null) return;
if (clock.clockFace.getVisibility()!=TextView.VISIBLE) return;
Date thisDate=new Date();
SimpleDateFormat df=new SimpleDateFormat(clock.format);
clock.clockFace.setText(df.format(thisDate));
}
/**
* start the ticking for all clocks
* #param step the tick interval
*/
public static void start(long step) {
refreshStep=step;
handler.removeCallbacks(mUpdateTimeTask);
handler.postDelayed(mUpdateTimeTask, 0);
}
/**
* Stopping ticking all clocks (not removing them)
* the calling could be put somewhere in onStop
*/
public static void stop() {
handler.removeCallbacks(mUpdateTimeTask);
}
}

Related

My service shutsdown when acticty exits and unbinds to it, would like it to keep running

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.

Android - how to run a socket in a long lives background thread

I'm building a simple chat that connects between an android client and a java server (running on my pc). The user can send and receive messages to/from the android app and the desktop server.
I'm dealing now with the question of how to run the client-socket in a different thread than the UI Thread.
I saw solutions using AsyncTask, but as the user may communicate using the app for a long sequential time, AsyncTask looks like a bad approach.
AsyncTasks should ideally be used for short operations (a few seconds at the most.) API
Because i need the client socket to consistently listen for messages from the desktop server, i thought of creating new Thread receiving a Runnable implementing class.
My questions
1. In which "thread mechanism" to place the client socket rows (Thread, IntentService)?
Socket client = new Socket(host, port);
InputStreamReader inputStreamReader = new InputStreamReader(client.getInputStream());
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
while ((messageFromServer = bufferedReader.readLine()) != null) { //... }
2. How can the client socket (running from a different thread than the main thread) post the messageFromServer to an TextView?
How will i send the user messages from the app to the server (using the client-socket ofcourse), upon user entering text and clicking a button?
Thanks!
I've created a similar app and I used a Service which runs in the background.
I've copied the code from the IntentService clas and updated handleMessage(Message msg) method and removed stopSelf(msg.arg1); line. In this way you have a service that runs in the background. After it I used a Thread for the connection.
You have two choices here. Store the data into the database and the GUI refreshes itself. Or use LocalBroadcastManager.
Here you can also store the data into the db or Start the service with a special intent.
Here is my implementation. I hope you will understand the code.
public class KeepAliveService extends Service {
/**
* The source of the log message.
*/
private static final String TAG = "KeepAliveService";
private static final long INTERVAL_KEEP_ALIVE = 1000 * 60 * 4;
private static final long INTERVAL_INITIAL_RETRY = 1000 * 10;
private static final long INTERVAL_MAXIMUM_RETRY = 1000 * 60 * 2;
private ConnectivityManager mConnMan;
protected NotificationManager mNotifMan;
protected AlarmManager mAlarmManager;
private boolean mStarted;
private boolean mLoggedIn;
protected static ConnectionThread mConnection;
protected static SharedPreferences mPrefs;
private final int maxSize = 212000;
private Handler mHandler;
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private final class ServiceHandler extends Handler {
public ServiceHandler(final Looper looper) {
super(looper);
}
#Override
public void handleMessage(final Message msg) {
onHandleIntent((Intent) msg.obj);
}
}
public static void actionStart(final Context context) {
context.startService(SystemHelper.createExplicitFromImplicitIntent(context, new Intent(IntentActions.KEEP_ALIVE_SERVICE_START)));
}
public static void actionStop(final Context context) {
context.startService(SystemHelper.createExplicitFromImplicitIntent(context, new Intent(IntentActions.KEEP_ALIVE_SERVICE_STOP)));
}
public static void actionPing(final Context context) {
context.startService(SystemHelper.createExplicitFromImplicitIntent(context, new Intent(IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER)));
}
#Override
public void onCreate() {
Log.i(TAG, "onCreate called.");
super.onCreate();
mPrefs = getSharedPreferences("KeepAliveService", MODE_PRIVATE);
mConnMan = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
mNotifMan = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
mHandler = new Handler();
final HandlerThread thread = new HandlerThread("IntentService[KeepAliveService]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
// If our process was reaped by the system for any reason we need to
// restore our state with merely a
// call to onCreate.
// We record the last "started" value and restore it here if necessary.
handleCrashedService();
}
#Override
public void onDestroy() {
Log.i(TAG, "Service destroyed (started=" + mStarted + ")");
if (mStarted) {
stop();
}
mServiceLooper.quit();
}
private void handleCrashedService() {
Log.i(TAG, "handleCrashedService called.");
if (isStarted()) {
// We probably didn't get a chance to clean up gracefully, so do it now.
stopKeepAlives();
// Formally start and attempt connection.
start();
}
}
/**
* Returns the last known value saved in the database.
*/
private boolean isStarted() {
return mStarted;
}
private void setStarted(final boolean started) {
Log.i(TAG, "setStarted called with value: " + started);
mStarted = started;
}
protected void setLoggedIn(final boolean value) {
Log.i(TAG, "setLoggedIn called with value: " + value);
mLoggedIn = value;
}
protected boolean isLoggedIn() {
return mLoggedIn;
}
public static boolean isConnected() {
return mConnection != null;
}
#Override
public void onStart(final Intent intent, final int startId) {
final Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
Log.i(TAG, "Service started with intent : " + intent);
onStart(intent, startId);
return START_NOT_STICKY;
}
private void onHandleIntent(final Intent intent) {
if (IntentActions.KEEP_ALIVE_SERVICE_STOP.equals(intent.getAction())) {
stop();
stopSelf();
} else if (IntentActions.KEEP_ALIVE_SERVICE_START.equals(intent.getAction())) {
start();
} else if (IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER.equals(intent.getAction())) {
keepAlive(false);
}
}
#Override
public IBinder onBind(final Intent intent) {
return null;
}
private synchronized void start() {
if (mStarted) {
Log.w(TAG, "Attempt to start connection that is already active");
setStarted(true);
return;
}
try {
registerReceiver(mConnectivityChanged, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
} catch (final Exception e) {
Log.e(TAG, "Exception occurred while trying to register the receiver.", e);
}
if (mConnection == null) {
Log.i(TAG, "Connecting...");
mConnection = new ConnectionThread(Config.PLUGIN_BASE_HOST, Config.PLUGIN_BASE_PORT);
mConnection.start();
}
}
private synchronized void stop() {
if (mConnection != null) {
mConnection.abort(true);
mConnection = null;
}
setStarted(false);
try {
unregisterReceiver(mConnectivityChanged);
} catch (final Exception e) {
Log.e(TAG, "Exception occurred while trying to unregister the receiver.", e);
}
cancelReconnect();
}
/**
* Sends the keep-alive message if the service is started and we have a
* connection with it.
*/
private synchronized void keepAlive(final Boolean forced) {
try {
if (mStarted && isConnected() && isLoggedIn()) {
mConnection.sendKeepAlive(forced);
}
} catch (final IOException e) {
Log.w(TAG, "Error occurred while sending the keep alive message.", e);
} catch (final JSONException e) {
Log.w(TAG, "JSON error occurred while sending the keep alive message.", e);
}
}
/**
* Uses the {#link android.app.AlarmManager} to start the keep alive service in every {#value #INTERVAL_KEEP_ALIVE} milliseconds.
*/
private void startKeepAlives() {
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + INTERVAL_KEEP_ALIVE, INTERVAL_KEEP_ALIVE, pi);
}
/**
* Removes the repeating alarm which was started by the {#link #startKeepAlives()} function.
*/
private void stopKeepAlives() {
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.cancel(pi);
}
public void scheduleReconnect(final long startTime) {
long interval = mPrefs.getLong("retryInterval", INTERVAL_INITIAL_RETRY);
final long now = System.currentTimeMillis();
final long elapsed = now - startTime;
if (elapsed < interval) {
interval = Math.min(interval * 4, INTERVAL_MAXIMUM_RETRY);
} else {
interval = INTERVAL_INITIAL_RETRY;
}
Log.i(TAG, "Rescheduling connection in " + interval + "ms.");
mPrefs.edit().putLong("retryInterval", interval).apply();
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_RECONNECT), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.set(AlarmManager.RTC_WAKEUP, now + interval, pi);
}
public void cancelReconnect() {
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_RECONNECT), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.cancel(pi);
}
private synchronized void reconnectIfNecessary() {
if (mStarted && !isConnected()) {
Log.i(TAG, "Reconnecting...");
mConnection = new ConnectionThread(Config.PLUGIN_BASE_HOST, Config.PLUGIN_BASE_PORT);
mConnection.start();
}
}
private final BroadcastReceiver mConnectivityChanged = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
final NetworkInfo info = mConnMan.getActiveNetworkInfo(); // (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
final boolean hasConnectivity = info != null && info.isConnected();
Log.i(TAG, "Connecting changed: connected=" + hasConnectivity);
if (hasConnectivity) {
reconnectIfNecessary();
} else if (mConnection != null) {
mConnection.abort(false);
mConnection = null;
}
}
};
protected class ConnectionThread extends Thread {
private final Socket mSocket;
private final String mHost;
private final int mPort;
private volatile boolean mAbort = false;
public ConnectionThread(final String host, final int port) {
mHost = host;
mPort = port;
mSocket = new Socket();
}
/**
* Returns whether we have an active internet connection or not.
*
* #return <code>true</code> if there is an active internet connection.
* <code>false</code> otherwise.
*/
private boolean isNetworkAvailable() {
final NetworkInfo info = mConnMan.getActiveNetworkInfo();
return info != null && info.isConnected();
}
#Override
public void run() {
final Socket s = mSocket;
final long startTime = System.currentTimeMillis();
try {
// Now we can say that the service is started.
setStarted(true);
// Connect to server.
s.connect(new InetSocketAddress(mHost, mPort), 20000);
Log.i(TAG, "Connection established to " + s.getInetAddress() + ":" + mPort);
// Start keep alive alarm.
startKeepAlives();
final DataOutputStream dos = new DataOutputStream(s.getOutputStream());
final BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
// Send the login data.
final JSONObject login = new JSONObject();
// Send the login message.
dos.write((login.toString() + "\r\n").getBytes());
// Wait until we receive something from the server.
String receivedMessage;
while ((receivedMessage = in.readLine()) != null) {
Log.i(TAG, "Received data: " + receivedMessage);
processMessagesFromServer(dos, receivedMessage);
}
if (!mAbort) {
Log.i(TAG, "Server closed connection unexpectedly.");
}
} catch (final IOException e) {
Log.e(TAG, "Unexpected I/O error.", e);
} catch (final Exception e) {
Log.e(TAG, "Exception occurred.", e);
} finally {
setLoggedIn(false);
stopKeepAlives();
if (mAbort) {
Log.i(TAG, "Connection aborted, shutting down.");
} else {
try {
s.close();
} catch (final IOException e) {
// Do nothing.
}
synchronized (KeepAliveService.this) {
mConnection = null;
}
if (isNetworkAvailable()) {
scheduleReconnect(startTime);
}
}
}
}
/**
* Sends the PING word to the server.
*
* #throws java.io.IOException if an error occurs while writing to this stream.
* #throws org.json.JSONException
*/
public void sendKeepAlive(final Boolean forced) throws IOException, JSONException {
final JSONObject ping = new JSONObject();
final Socket s = mSocket;
s.getOutputStream().write((ping.toString() + "\r\n").getBytes());
}
/**
* Aborts the connection with the server.
*/
public void abort(boolean manual) {
mAbort = manual;
try {
// Close the output stream.
mSocket.shutdownOutput();
} catch (final IOException e) {
// Do nothing.
}
try {
// Close the input stream.
mSocket.shutdownInput();
} catch (final IOException e) {
// Do nothing.
}
try {
// Close the socket.
mSocket.close();
} catch (final IOException e) {
// Do nothing.
}
while (true) {
try {
join();
break;
} catch (final InterruptedException e) {
// Do nothing.
}
}
}
}
public void processMessagesFromServer(final DataOutputStream dos, final String receivedMessage) throws IOException {
}
}
You can start the service by calling KeepAliveService.actionStart() and you can also define custom functions.
Please note that the service will be stopped only if you call KeepAliveService.actionStop(). Otherwise it will run forever. If you call e.g. KeepAliveService.actionSendMessage(String message) then the intent will be passed to the service and you can handle it easily.
EDIT:
The SystemHelper class is only a utility class which contains static methods.
public class SystemHelper {
/**
* Android Lollipop, API 21 introduced a new problem when trying to invoke implicit intent,
* "java.lang.IllegalArgumentException: Service Intent must be explicit"
*
* If you are using an implicit intent, and know only 1 target would answer this intent,
* This method will help you turn the implicit intent into the explicit form.
*
* Inspired from SO answer: http://stackoverflow.com/a/26318757/1446466
* #param context the application context
* #param implicitIntent - The original implicit intent
* #return Explicit Intent created from the implicit original intent
*/
public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
Log.i(TAG, "createExplicitFromImplicitIntent ... called with intent: " + implicitIntent);
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
Log.i(TAG, "createExplicitFromImplicitIntent ... resolveInfo is null or there are more than one element.");
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
Log.i(TAG, "createExplicitFromImplicitIntent ... found package name:" + packageName + ", class name: " + className + ".");
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
}
The Config class.
public class Config {
public static final String PACKAGE_NAME = "com.yourapp.package";
public static final String PLUGIN_BASE_HOST = "test.yoursite.com";
public static final int PLUGIN_BASE_PORT = 10000;
}
And the IntentActions class.
public class IntentActions {
public static final String KEEP_ALIVE_SERVICE_START = Config.PACKAGE_NAME + ".intent.action.KEEP_ALIVE_SERVICE_START";
public static final String KEEP_ALIVE_SERVICE_STOP = Config.PACKAGE_NAME + ".intent.action.KEEP_ALIVE_SERVICE_STOP";
public static final String KEEP_ALIVE_SERVICE_PING_SERVER = Config.PACKAGE_NAME + ".intent.action.KEEP_ALIVE_SERVICE_PING_SERVER";
}
In the AndroidManifest file the service is defined in the following way:
<service android:name="com.yourapp.package.services.KeepAliveService"
android:exported="false">
<intent-filter>
<action android:name="com.yourapp.package.intent.action.KEEP_ALIVE_SERVICE_START" />
<action android:name="com.yourapp.package.intent.action.KEEP_ALIVE_SERVICE_STOP" />
<action android:name="com.yourapp.package.intent.action.KEEP_ALIVE_SERVICE_PING_SERVER" />
</intent-filter>
</service>
I suggest you take a look at the Android documentation for background services. Personally I would use the IntentService as it's a well established pattern within Android.
http://developer.android.com/training/run-background-service/index.html

Android Mosquitto - Client connection state

I've a MQTT client runnning in Android, and a MQTT Broker in the server. My problem is where we will use the app we have some connections drop so my web app needs to know the current state of the client.
So what we are doing right now is:
1 - The server sends a random number to the clients (each client will receive a different random number)
2 - The android client receives the number and send to a web service
3 - The web service writes in SQL db
4 - The server wait 4 secs to the response from android client and if the random number sent by the server == to the number in the db , the client is connected.
But now the problem is when, multi-users sends the random number the only one that will be write in the db is the last one so it's huge design fault.
In order to fix the only good solution is to get a direct response from the MQTT client and have to be unique per client but i don't know if is possible or if is the best way to go.
Some draw to better understand:
Flow
Here is my android code:
public class MQTTService extends Service implements MqttCallback {
public static final String DEBUG_TAG = "MqttService"; // Debug TAG
private static final String MQTT_THREAD_NAME = "MqttService[" + DEBUG_TAG + "]"; // Handler
// Thread
// ID
private String MQTT_BROKER = ""; // Broker URL
// or IP
// Address
private static final int MQTT_PORT = 1883; // Broker Port
public static final int MQTT_QOS_0 = 0; // QOS Level 0 ( Delivery Once no
// confirmation )
public static final int MQTT_QOS_1 = 1; // QOS Level 1 ( Delivery at least
// Once with confirmation )
public static final int MQTT_QOS_2 = 2; // QOS Level 2 ( Delivery only once
// with confirmation with handshake
// )
private static final int MQTT_KEEP_ALIVE = 30000; // KeepAlive Interval in
// MS
private static final String MQTT_KEEP_ALIVE_TOPIC_FORMAT = "/users/%s/keepalive"; // Topic
// format
// for
// KeepAlives
private static final byte[] MQTT_KEEP_ALIVE_MESSAGE = { 0 }; // Keep Alive
// message
// to send
private static final int MQTT_KEEP_ALIVE_QOS = MQTT_QOS_2; // Default
// Keepalive QOS
private static final boolean MQTT_CLEAN_SESSION = false; // Start a clean
// session?
private static final String MQTT_URL_FORMAT = "tcp://%s:%d"; // URL Format
// normally
// don't
// change
public static final String ACTION_START = DEBUG_TAG + ".START"; // Action
// to
// start
public static final String ACTION_STOP = DEBUG_TAG + ".STOP"; // Action to
// stop
public static final String ACTION_KEEPALIVE = DEBUG_TAG + ".KEEPALIVE"; // Action
// to
// keep
// alive
// used
// by
// alarm
// manager
private static final String ACTION_RECONNECT = DEBUG_TAG + ".RECONNECT"; // Action
// to
// reconnect
// private final String DEVICE_ID_FORMAT = "andr_%s"; // Device ID
// Format, add
// any prefix
// you'd like
// Note: There
// is a 23
// character
// limit you
// will get
// An NPE if you
// go over that
// limit
private boolean mStarted = false; // Is the Client started?
private String user_ID; // Device ID, Secure.ANDROID_ID
private Handler mConnHandler; // Seperate Handler thread for networking
private MqttDefaultFilePersistence mDataStore; // Defaults to FileStore
private MemoryPersistence mMemStore; // On Fail reverts to MemoryStore
private MqttConnectOptions mOpts; // Connection Options
private MqttTopic mKeepAliveTopic; // Instance Variable for Keepalive topic
private MqttClient mClient; // Mqtt Client
private AlarmManager mAlarmManager; // Alarm manager to perform repeating
// tasks
private ConnectivityManager mConnectivityManager; // To check for
// connectivity changes
public static final String TAG_CONNECTED = "CONNECTED";
public static final String TAG_ASSIGNED = "ASSIGNED";
public static final String TAG_REFRESH = "REFRESH";
public String TOPIC_CONNECTED = null;
public String TOPIC_ASSIGNED = null;
public String TOPIC_REFRESH = null;
private Intent intent;
private PendingIntent alarmIntent;
private AppMaintenance appStatus;
/**
* Initializes the DeviceId and most instance variables Including the
* Connection Handler, Datastore, Alarm Manager and ConnectivityManager.
*/
#Override
public void onCreate() {
super.onCreate();
// mDeviceId = String.format(DEVICE_ID_FORMAT,
// Secure.getString(getContentResolver(), Secure.ANDROID_ID));
android.os.Debug.waitForDebugger(); // Debugger
appStatus = (AppMaintenance) getApplicationContext();
ExceptionHandler.register(this, appStatus.getException_URL());
HandlerThread thread = new HandlerThread(MQTT_THREAD_NAME);
thread.start();
mConnHandler = new Handler(thread.getLooper());
try {
mDataStore = new MqttDefaultFilePersistence(getCacheDir().getAbsolutePath());
} catch (Exception e) {
// writeToFile("Exception - onCreate()");
e.printStackTrace();
mDataStore = null;
mMemStore = new MemoryPersistence();
}
mOpts = new MqttConnectOptions();
mOpts.setCleanSession(MQTT_CLEAN_SESSION);
// Do not set keep alive interval on mOpts we keep track of it with
// alarm's
mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
mConnectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
registerReceiver(mConnectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
/**
* Start MQTT Client
*
* #param Context
* context to start the service with
* #return void
*/
public static void actionStart(Context ctx) {
Intent i = new Intent(ctx, MQTTService.class);
i.setAction(ACTION_START);
ctx.startService(i);
}
/**
* Stop MQTT Client
*
* #param Context
* context to start the service with
* #return void
*/
public static void actionStop(Context ctx) {
Intent i = new Intent(ctx, MQTTService.class);
i.setAction(ACTION_STOP);
ctx.startService(i);
}
/**
* Send a KeepAlive Message
*
* #param Context
* context to start the service with
* #return void
*/
public static void actionKeepalive(Context ctx) {
Intent i = new Intent(ctx, MQTTService.class);
i.setAction(ACTION_KEEPALIVE);
ctx.startService(i);
}
/**
* Service onStartCommand Handles the action passed via the Intent
*
* #return START_REDELIVER_INTENT
*/
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
this.intent = intent;
SharedPreferences myPrefs = getSharedPreferences("UserPreferences", MODE_PRIVATE);
MQTT_BROKER = myPrefs.getString("broker", "");
user_ID = myPrefs.getString("userID", "");
String action = intent.getAction();
TOPIC_CONNECTED = user_ID + "\\" + TAG_CONNECTED;
TOPIC_ASSIGNED = user_ID + "\\" + TAG_ASSIGNED;
TOPIC_REFRESH = user_ID + "\\" + TAG_REFRESH;
Log.i(DEBUG_TAG, "Received action of " + action);
// writeToFile("Received action of " + action);
if (user_ID.isEmpty() || user_ID == null)
action = null;
if (action == null) {
Log.i(DEBUG_TAG, "Starting service with no action\n Probably from a crash");
// writeToFile("Starting service with no action\n Probably from a crash");
Toast.makeText(getApplicationContext(), getString(R.string.mqtt_warning_userid), Toast.LENGTH_LONG).show();
action = null;
} else {
if (action.equals(ACTION_START)) {
Log.i(DEBUG_TAG, "Received ACTION_START");
// writeToFile("Received ACTION_START");
start();
} else if (action.equals(ACTION_STOP)) {
Log.i(DEBUG_TAG, "Received ACTION_STOP");
// writeToFile("Received ACTION_STOP");
stop();
} else if (action.equals(ACTION_KEEPALIVE)) {
Log.i(DEBUG_TAG, "Received ACTION_KEEPALIVE");
// writeToFile("Received ACTION_KEEPALIVE");
keepAlive();
} else if (action.equals(ACTION_RECONNECT)) {
Log.i(DEBUG_TAG, "Received ACTION_RECONNECT");
// writeToFile("Received ACTION_RECONNECT");
reconnectIfNecessary();
}
}
return START_NOT_STICKY;
}
/**
* Attempts connect to the Mqtt Broker and listen for Connectivity changes
* via ConnectivityManager.CONNECTVITIY_ACTION BroadcastReceiver
*/
private synchronized void start() {
if (mStarted) {
Log.i(DEBUG_TAG, "Attempt to start while already started");
// writeToFile("Attempt to start while already started");
return;
}
if (hasScheduledKeepAlives()) {
stopKeepAlives();
}
connect();
}
/**
* Attempts to stop the Mqtt client as well as halting all keep alive
* messages queued in the alarm manager
*/
private synchronized void stop() {
if (!mStarted) {
Log.i(DEBUG_TAG, "Attemt to stop connection that isn't running");
// writeToFile("Attemt to stop connection that isn't running");
return;
}
if (mClient != null) {
mConnHandler.post(new Runnable() {
#Override
public void run() {
try {
mClient.disconnect();
} catch (Exception ex) {
// writeToFile("Exception - stop() ");
ex.printStackTrace();
mClient = null;
mStarted = false;
} finally {
mClient = null;
mStarted = false;
stopKeepAlives();
}
}
});
}
}
/**
* Connects to the broker with the appropriate datastore
*/
private synchronized void connect() {
String url = String.format(Locale.US, MQTT_URL_FORMAT, MQTT_BROKER, MQTT_PORT);
Log.i(DEBUG_TAG, "Connecting with URL: " + url);
// writeToFile("Connecting with URL: " + url);
try {
if (mDataStore != null) {
Log.i(DEBUG_TAG, "Connecting with DataStore");
// writeToFile("Connecting with DataStore");
mClient = new MqttClient(url, user_ID, mDataStore);
} else {
Log.i(DEBUG_TAG, "Connecting with MemStore");
// writeToFile("Connecting with MemStore");
mClient = new MqttClient(url, user_ID, mMemStore);
}
} catch (Exception e) {
// writeToFile("Exception - connect L.343");
e.printStackTrace();
}
mConnHandler.post(new Runnable() {
#Override
public void run() {
try {
mClient.connect(mOpts);
mClient.subscribe(new String[] { TOPIC_CONNECTED, TOPIC_ASSIGNED, TOPIC_REFRESH }, new int[] { MQTT_QOS_0,
MQTT_KEEP_ALIVE_QOS, MQTT_KEEP_ALIVE_QOS });
mClient.setCallback(new MQTTPushCallback(MQTTService.this, intent, user_ID, TOPIC_CONNECTED, TOPIC_ASSIGNED,
TOPIC_REFRESH));
mStarted = true; // Service is now connected
Log.i(DEBUG_TAG, "Successfully connected and subscribed starting keep alives");
// writeToFile("Successfully connected and subscribed starting keep alives");
startKeepAlives();
} catch (Exception e) {
// writeToFile("Exception - connect L.366");
e.printStackTrace();
}
}
});
}
/**
* Schedules keep alives via a PendingIntent in the Alarm Manager
*/
private void startKeepAlives() {
Intent i = new Intent();
i.setClass(this, MQTTService.class);
i.setAction(ACTION_KEEPALIVE);
alarmIntent = PendingIntent.getService(this, 0, i, 0);
mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + MQTT_KEEP_ALIVE, MQTT_KEEP_ALIVE, alarmIntent);
Log.i(DEBUG_TAG, "Started keepAlives sucessfully");
// writeToFile("Started keepAlives sucessfully");
}
/**
* Cancels the Pending Intent in the alarm manager
*/
private void stopKeepAlives() {
if (mAlarmManager != null) {
mAlarmManager.cancel(alarmIntent);
}
}
/**
* Publishes a KeepALive to the topic in the broker
*/
private synchronized void keepAlive() {
// if (isForeground()) {
if (isConnected()) {
try {
sendKeepAlive();
return;
} catch (MqttConnectivityException ex) {
// writeToFile("Exception - KeepAlive() 1");
ex.printStackTrace();
reconnectIfNecessary();
} catch (MqttPersistenceException ex) {
// writeToFile("Exception - KeepAlive() 2");
ex.printStackTrace();
stop();
restartService();
} catch (MqttException ex) {
// writeToFile("Exception - KeepAlive() 3");
ex.printStackTrace();
stop();
restartService();
} catch (Exception ex) {
// writeToFile("Exception - KeepAlive() 4");
ex.printStackTrace();
stop();
restartService();
}
}
}
/**
* Checks the current connectivity and reconnects if it is required.
*/
private synchronized void reconnectIfNecessary() {
if (!mStarted && mClient == null)
start();
}
/**
* Query's the NetworkInfo via ConnectivityManager to return the current
* connected state
*
* #return boolean true if we are connected false otherwise
*/
private boolean isNetworkAvailable() {
NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
return info == null ? false : info.isConnected();
}
/**
* Verifies the client State with our local connected state
*
* #return true if its a match we are connected false if we aren't connected
*/
private boolean isConnected() {
if (mStarted && mClient != null && !mClient.isConnected()) {
Log.i(DEBUG_TAG, "Mismatch between what we think is connected and what is connected");
// writeToFile("Mismatch between what we think is connected and what is connected");
}
if (mClient != null) {
return mStarted && mClient.isConnected() ? true : false;
}
return false;
}
/**
* Receiver that listens for connectivity changes via ConnectivityManager
*/
private final BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// writeToFile("isNetworkAvailable = " + isNetworkAvailable());
if (isNetworkAvailable() && !mStarted) {
Log.i(DEBUG_TAG, "Connectivity Changed...");
// Intent i = new Intent(context, MQTTService.class);
// i.setAction(ACTION_RECONNECT);
// context.startService(i);
restartService();
} else if (!isNetworkAvailable()) {
stop();
}
}
};
/**
* Sends a Keep Alive message to the specified topic
*
* #see MQTT_KEEP_ALIVE_MESSAGE
* #see MQTT_KEEP_ALIVE_TOPIC_FORMAT
* #return MqttDeliveryToken specified token you can choose to wait for
* completion
*/
private synchronized MqttDeliveryToken sendKeepAlive() throws MqttConnectivityException, MqttPersistenceException, MqttException {
if (!isConnected())
throw new MqttConnectivityException();
if (mKeepAliveTopic == null) {
mKeepAliveTopic = mClient.getTopic(String.format(Locale.US, MQTT_KEEP_ALIVE_TOPIC_FORMAT, user_ID));
}
Log.i(DEBUG_TAG, "Sending Keepalive to " + MQTT_BROKER);
// writeToFile("Sending Keepalive to " + MQTT_BROKER);
MqttMessage message = new MqttMessage(MQTT_KEEP_ALIVE_MESSAGE);
message.setQos(MQTT_KEEP_ALIVE_QOS);
return mKeepAliveTopic.publish(message);
}
/**
* Query's the AlarmManager to check if there is a keep alive currently
* scheduled
*
* #return true if there is currently one scheduled false otherwise
*/
private synchronized boolean hasScheduledKeepAlives() {
Intent i = new Intent();
i.setClass(this, MQTTService.class);
i.setAction(ACTION_KEEPALIVE);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, PendingIntent.FLAG_NO_CREATE);
return pi != null ? true : false;
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
/**
* Connectivity Lost from broker
*/
#Override
public void connectionLost(Throwable arg0) {
stopKeepAlives();
mClient = null;
if (isNetworkAvailable()) {
reconnectIfNecessary();
}
}
/**
* Publish Message Completion
*/
#Override
public void deliveryComplete(IMqttDeliveryToken arg0) {
// TODO Auto-generated method stub
}
/**
* Received Message from broker
*/
#Override
public void messageArrived(String arg0, MqttMessage arg1) throws Exception {
// Log.i(DEBUG_TAG,
// " Topic:\t" + topic.getName() + " Message:\t"
// + new String(message.getPayload()) + " QoS:\t"
// + message.getQos());
}
/**
* MqttConnectivityException Exception class
*/
private class MqttConnectivityException extends Exception {
private static final long serialVersionUID = -7385866796799469420L;
}
#Override
public void onDestroy() {
try {
mClient.unsubscribe(new String[] { TOPIC_CONNECTED, TOPIC_ASSIGNED, TOPIC_REFRESH });
mClient.disconnect(0);
} catch (Exception e) {
// writeToFile("Exception - onDestroy() 1");
e.printStackTrace();
} finally {
new WS_LOGOUT(this).execute(user_ID);
}
}
public void restartService() {
mKeepAliveTopic = null;
actionStart(getApplicationContext()); // restart the service
}
}
What sort of latency can you live with when knowing if the client is disconnected?
You can use the Last Will and Testament feature to publish a value to a topic when the server detects that the MQTT keep alive time has expired with out receiving a ping from the client.
You can set the keep alive time at connection time. But depending on your requirements (battery/network usage) you need to work out what to set it to. If I remember correctly the default is 30 seconds (might be 60)
When your client connects it can set a flag on a persitent topic to say it's online, and the LWT can set this to 0.
e.g.
on connect publish "1" to client/[uniqueid]/online
set the LWT to publish "0" to client/[uniqueid]/online

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

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

Sending message to an unbound Service in onCreate() method

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.

Categories

Resources