I have written a Service that listens for UDP messages then changes a TextView and an ImageView based on the parsed message from the UDP messages. I'm getting an NPE when attemping to use a public getParsedMessage method from the service, which means that the service has not been started. It is declared as a service in the manifest exactly as it is spelled, so I know that is not the problem. Here is my MainActivity's code:
public class MainActivity extends Activity {
AlertAssignments mAlertAssignments;
Button startListeningButton;
boolean started;
int counter;
boolean mBound = false;
Context context;
ListenerService mListenerService;
TextView mTextView;
TextView mBlinkView;
ImageView mImageView;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = (ImageView) findViewById(R.id.image_view);
mTextView = (TextView) findViewById(R.id.alert_text);
mBlinkView = (TextView) findViewById(R.id.blinking_text);
Animation mAnimation = new AlphaAnimation(0.0f, 1.0f);
mAnimation.setDuration(50);
mAnimation.setStartOffset(20);
mAnimation.setRepeatCount(Animation.INFINITE);
mAnimation.setRepeatMode(Animation.REVERSE);
mBlinkView.startAnimation(mAnimation); //animation value
mAlertAssignments = new AlertAssignments();
Integer parsedMessage = Integer.valueOf(mListenerService.getParsedMessage()); //this is the cause of the NPE
mImageView.setImageResource(mAlertAssignments.alarmImages[parsedMessage]);
if(parsedMessage >= 10 && parsedMessage <= 19 && parsedMessage != 0) {
mTextView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
} else {
mBlinkView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
}
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
ListenerService.LocalBinder binder = (ListenerService.LocalBinder) service;
mListenerService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
#Override
protected void onStart() {
super.onStart();
//start listener service
Intent listenerServiceIntent = new Intent(MainActivity.this, ListenerService.class);
this.bindService(listenerServiceIntent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
//unbind from service
if(mBound) {
this.unbindService(mConnection);
mBound = false;
}
}
}
The error occurs on line 75, which is marked above (Integer parsedMessage = Integer.valueOf(mListenerService.getParsedMessage());). I have followed the developer.android documentation in setting up and starting my service, however i seem to see conflicting information in multiple locations. Here is my ListenerService:
public class ListenerService extends Service{
public String the_alarm_S;
public String parsedMessage = "";
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
ListenerService getService() {
return ListenerService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
static String UDP_BROADCAST = "UDPBroadcast";
//Boolean shouldListenForUDPBroadcast = false;
DatagramSocket socket;
private void listenAndWaitAndThrowIntent(InetAddress broadcastIP, Integer port) throws Exception {
byte[] recvBuf = new byte[15000];
if (socket == null || socket.isClosed()) {
socket = new DatagramSocket(port, broadcastIP);
socket.setBroadcast(true);
}
//socket.setSoTimeout(1000);
DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
Log.e("UDP", "Waiting for UDP broadcast");
socket.receive(packet);
String senderIP = packet.getAddress().getHostAddress();
String message = new String(packet.getData()).trim();
Log.e("UDP", "Got UDB broadcast from " + senderIP + ", message: " + message);
broadcastIntent(senderIP, message);
setParsedMessage(message);
socket.close();
}
private void broadcastIntent(String senderIP, String message) {
Intent intent = new Intent(ListenerService.UDP_BROADCAST);
intent.putExtra("sender", senderIP);
intent.putExtra("message", message);
Log.e("UDP", "Message received containing" +message);
sendBroadcast(intent);
}
Thread UDPBroadcastThread;
void startListenForUDPBroadcast() {
UDPBroadcastThread = new Thread(new Runnable() {
public void run() {
try {
InetAddress broadcastIP = InetAddress.getByName("172.16.238.255"); //172.16.238.42 //192.168.1.255
Integer port = 12001;
while (shouldRestartSocketListen) {
listenAndWaitAndThrowIntent(broadcastIP, port);
}
//if (!shouldListenForUDPBroadcast) throw new ThreadDeath();
} catch (Exception e) {
Log.i("UDP", "no longer listening for UDP broadcasts cause of error " + e.getMessage());
}
}
});
UDPBroadcastThread.start();
}
private Boolean shouldRestartSocketListen=true;
private void setParsedMessage(String messageContents) {
the_alarm_S = messageContents;
String parseMessage[] = the_alarm_S.split("!!!");
Log.e("UDP", "Parsed message with value " + parseMessage[1]);
parsedMessage = parseMessage[1];
}
public String getParsedMessage() {
return parsedMessage;
}
void stopListen() {
shouldRestartSocketListen = false;
socket.close();
}
#Override
public void onCreate() {
};
#Override
public void onDestroy() {
stopListen();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
shouldRestartSocketListen = true;
startListenForUDPBroadcast();
Log.i("UDP", "Service started");
return START_STICKY;
}
}
I had previously been using an AsyncTask to fetch this data, however, I need it to be constantly fetching the data and updating the TextView and ImageView objects, and due to the ping speed using a while loop to do this caused it to run out of memory due to i assume it running on the UI thread. AlertAssignments is simply an Enum that binds image files and Strings to ordinal array values so that I can easily change the TextView and ImageView based on the integer value of the parsed message (parsedMessage[1] of the original message xxx!!!n!!!xxx gives parsedMessage[1] = n)
Any advice on how to resolve what is probably an oversight on my part would be great, thanks
Take a look at the Android activity lifecycle:
One thing you'll notice is that onCreate() is called before onStart(). This means you're trying to access the service before it actually starts.
The way I would work around this is to start your service in onCreate() (so it will start as soon as your activity is first created) and then read the value inside onResume(), so that every time your activity comes back into the foreground it will update according to the service.
You may also notice that if resources are required, your app could be killed as early as onPause(). It would be a good idea to do your cleanup there, instead of onDestroy().
EDIT:
If the above option didn't work, I suspect there's a race condition going on. Just because you started the service before you read from it, doesn't mean it was fully configured. Thankfully, you have a listener to tell you when the service is bound.
You can write a separate method specifically for updating the UI, and have your activity only call it once the service has started:
public class MyActivity {
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
ListenerService.LocalBinder binder = (ListenerService.LocalBinder) service;
mListenerService = binder.getService();
mBound = true;
readFromService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
private void readFromService() {
Integer parsedMessage = Integer.valueOf(mListenerService.getParsedMessage()); //this is the cause of the NPE
mImageView.setImageResource(mAlertAssignments.alarmImages[parsedMessage]);
if(parsedMessage >= 10 && parsedMessage <= 19 && parsedMessage != 0) {
mTextView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
} else {
mBlinkView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
}
}
}
Related
I have created background service to connect my socket server, it works background while app is off, when user open the app MainActivity join to my service class and it looks fine, my service can change fragment in main activity, but when it get disconnected and want to change fragment in main activity then app crash
check my MainActivity
public class MainActivity extends AppCompatActivity {
private clientService mclientService;
private Intent mServiceIntent;
private final FragmentManager fm = getFragmentManager();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mclientService = new clientService(this);
mServiceIntent = new Intent(this, mclientService.getClass());
if (!isMyServiceRunning(mclientService.getClass())) {
startService(mServiceIntent);
}
}
public Fragment changeFragment (Fragment cls) {
FragmentTransaction ft = this.fm.beginTransaction();
ft.replace(R.id.bodyFrame, cls);
ft.commit();
return cls;
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
Log.i ("isMyServiceRunning?", true+"");
return true;
}
}
Log.i ("isMyServiceRunning?", false+"");
return false;
}
}
and the clientService:
public class clientService extends Service {
private Socket mSocket;
{
try {
IO.Options opts = new IO.Options();
opts.query = "_d=jakistakiid";
mSocket = IO.socket("http://10.0.2.2:3000", opts);
} catch (URISyntaxException e) {}
}
public clientService(MainActivity main) {
super();
mainAttach(main);
}
public clientService() {
}
public MainActivity mMain;
public void startIt(){
Log.i("eroapp", "Service Started");
if(mSocket != null) {
mSocket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.i("eroapp", "connected");
}
}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.i("eroapp", "dc:"+mMain);
onDC();
}
});
mSocket.connect();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startIt();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Intent broadcastIntent = new Intent(this, restartReceiver.class);
sendBroadcast(broadcastIntent);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
// CLIENT FUNCTIONS //
private void onDC(){
Log.i("eroapp", "DC:"+mMain);
}
private void mainAttach(MainActivity m) {
mMain = m;
Log.i("eroapp","Main created!");
if(!mSocket.connected()) {
mMain.changeFragment(new offlineFragment());
}else{
mMain.changeFragment(new singFragment());
}
}
}
looks fine, when i close app activity my service restarts and run in background, it says in logcat:
2019-11-17 02:41:22.686 17458-17458/com.example.secmsg I/eroapp: Service Started
2019-11-17 02:41:22.803 17458-17483/com.example.secmsg I/eroapp: connected
when i open app again, my service is already running, so its run only function mainAttach in clientService, and then service run changeFragment function in main activity, works great, but
when I get disconnected from server it says mMain is null ; < and logcat output:
2019-11-17 02:41:31.654 17458-17499/com.example.secmsg I/eroapp: dc:null
2019-11-17 02:41:31.654 17458-17499/com.example.secmsg I/eroapp: DCnull
The mclientService you created on the MainActivity is not the same as the clientService that was started by the Android system when you called startService(intent). All services created and started by the Android system by using the service class' empty constructor, therefore, clientService#mainAttach will never be called. Take a look at this answer for more information.
If you want to directly interact with the service on the MainActivity, you might want to bind the service to your activity. Check out the documentation about bound services here.
Another thing... It looks like you're planning to directly control the MainActivity from the service using the reference to the activity. Please never do that and use broadcasts instead. Good luck!
I have activity in which is started service with startService() and in onStart() method binded to the same service. I'm sending some text using Messenger to service, then service open socket to the server in which this text is send to it, and server respond with some text (this working fine). The problem occurs when I'm trying to send response from service to activity using messenger. When application is started first time even that works fine, but after app is killed (so service restart) and I'm trying to send response from service to activity I get DeadObjectException. I commented line in service in which error occurse.
Here is my activity:
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
private GoogleMap mMap;
TextView tvFromServer;
EditText etSend;
Button bSend, bStopSocket, bBound, bStopService;
boolean mRun;
Intent mIntent;
/** Messenger for communicating with the service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mBound;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
//SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
// .findFragmentById(R.id.map);
//mapFragment.getMapAsync(this);
initialize();
mIntent = new Intent(this, SocketService.class);
startService(mIntent);
}
#Override
protected void onStart() {
super.onStart();
Intent iBind = new Intent(this, SocketService.class);
iBind.putExtra("messenger", new Messenger(mHandler));
bindService(iBind, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
mConnection = null;
mHandler = null;
}
}
Handler mHandler = new Handler(Looper.getMainLooper()){
#Override
public void handleMessage(Message msg) {
//Log.v("MapsActivity", (String) msg.obj );(String) msg.obj
tvFromServer.setText(msg.getData().getString("text"));
}
};
/**
* 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;
}
};
private void initialize(){
tvFromServer = (TextView) findViewById(R.id.tv_from_server);
bStopSocket = (Button) findViewById(R.id.b_stop);
mRun = false;
bStopSocket.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mRun = false;
}
});
etSend = (EditText) findViewById(R.id.et_send);
bSend = (Button) findViewById(R.id.b_send);
bSend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!mBound) return;
Message msg = Message.obtain(null, 1, etSend.getText().toString());
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
bBound = (Button) findViewById(R.id.b_bound);
bBound.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!mBound) return;
Message msg = Message.obtain(null, 1, "Hello from Activity");
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
bStopService = (Button) findViewById(R.id.b_stop_service);
bStopService.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mBound) {
unbindService(mConnection);
mBound = false;
}
stopService(mIntent);
}
});
}
/**
* Manipulates the map once available.
* This callback is triggered when the map is ready to be used.
* This is where we can add markers or lines, add listeners or move the camera. In this case,
* we just add a marker near Sydney, Australia.
* If Google Play services is not installed on the device, the user will be prompted to install
* it inside the SupportMapFragment. This method will only be triggered once the user has
* installed Google Play services and returned to the app.
*/
#Override
public void onMapReady(GoogleMap googleMap) {
//mMap = googleMap;
// Add a marker in Sydney and move the camera
//LatLng sydney = new LatLng(-34, 151);
//mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
//mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
}
}
Service:
public class SocketService extends Service {
public SocketService() {
}
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
public Messenger mMessenger, mUiMessenger;
PrintWriter out;
Socket mSocket;
boolean mRun;
Thread threa;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
if(msg.what == 1){
out.println((String) msg.obj);
}
}
}
#Override
public void onCreate() {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("SocketService");
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
mMessenger = new Messenger(new ServiceHandler(mServiceLooper));
mRun = false;
threa = new Thread(new Runnable() {
#Override
public void run() {
socketConnection();
}
});
threa.start();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
Bundle extras = intent.getExtras();
mUiMessenger = (Messenger) extras.get("messenger");
return mMessenger.getBinder();
}
#Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
private void socketConnection() {
mRun = true;
try{
mSocket = new Socket("xxx.xxx.xxx.xxx", xxxxx);
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream())), true);
BufferedReader in = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
while(mRun){
String s = in.readLine();
Log.v("SocketServiceFromServer", s);
Bundle bundle = new Bundle();
bundle.putString("text", s);
Message msg = Message.obtain(null, 1);
msg.setData(bundle);
try {
mUiMessenger.send(msg); //DeadObjectException is thrown here after service is restarted
} catch (RemoteException e) {
e.printStackTrace();
}
}
mSocket.close();
}catch(Exception e){
Log.e("SocketService ", e.toString());
}
}
}
logcat:
My question is, why this error is thrown? Looks like activity do not send new messenger object to service when binding to it after restart.
I had exactly the same problem and I found solution. I lost 4 hours on debugging code, search Internet and try millions of different approaches. Solution is very easy. Remove calling startService in your onCreate. Use only bindService and unbindService. Yes it is so simple...
I have read about Thread sleep, Handler, Runnable, AsyncTask and TimerTask, tried Thread sleep but could not figure out how can I accomplish the task.
I have implemented socket.io.client-java as service and using Volley as well as multiple activities & fragments. I need as follow:
Check if socket is connected before making Volley request.
If socket is connected then process Volley request otherwise re-check socket connectivity (a maximum of 5 times with 1 second interval).
In case of failure in 5 attempts, show a Toast message and continue the Volley request.
I 'm using a common class to perform Volley requests and others.
Socket Service
public class SocketClient extends Service {
private Socket mSocket = null;
private static SocketClient iSocket;
public SocketClient() {
super();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onCreate(){
super.onCreate();
iSocket = this;
startSocket();
}
public static synchronized SocketClient getInstance(){return iSocket;}
private void startSocket(){
try {
mSocket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
//...
}
}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
//...
}
}).on(...{...});
mSocket.connect();
} catch (URISyntaxException e){
e.printStackTrace();
}
}
public boolean isConnected(){ return (mSocket != null && mSocket.connected()); }
#Override
public void onDestroy(){
super.onDestroy();
mSocket.disconnect().close();
mSocket = null;
}
}
Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
//...
Helpers.startServices(getApplicationContext(), 1);
}
#Override
protected void onResume() {
Helpers.getNotifed(); //A Volley Request
}
Helpers Class
public static void makeReq(Map<String, Object> params, String api, final Context context, final VolleyListener listener)
{
manager = new SessionManager(context);
final String url = baseURL + api;
int i = 0;
boolean ss = SocketClient.getInstance().isConnected();
Log.i(TAG, "Socket status" + ss);
while(i < 5 && !ss) {
try {
Log.i(TAG, "Socket status" + ss);
Log.i(TAG, "sleeping " + i);
Thread.sleep(1000);
ss = SocketClient.getInstance().isConnected();
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(!ss)
Toast.makeText(context, "Operation interrupt!", Toast.LENGTH_LONG).show();
//Volley JSON Object Request
}
public static void startServices(Context context, int st) {
switch (st) {
case 1: //Socket
if (!isServiceRunning(context, SocketClient.class))
context.startService(new Intent(context, SocketClient.class));
break;
}
}
Issues:
Getting NullPointerException at boolean ss = SocketClient.getInstance().isConnected(); as class might not initialized yet.
ANR (Application Not Responding) because the main Thread is halted in while loop.
P.S.: I'm just a beginner in Android/Java so reply with clear code would be appreciable.
I need to execute some tasks when my application is closed.
I have made a service for do this and tried many things, but i don't have the good result.
If someone have a tutorial or some path to follow, it would be great
This is my service:
public class TrackersImporter extends Service {
private static TrackersImporter instance;
private static long refreshDelay = 1; // Minutes
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private boolean isInit = false;
public ArrayList<Tracker> trackers = new ArrayList<>();
public static TrackersImporter getInstance(){
if (instance == null)
instance = new TrackersImporter();
return instance;
}
#Override
public void onCreate() {
HandlerThread thread = new HandlerThread("TrackersImporter",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
Message message = mServiceHandler.obtainMessage();
message.arg1 = startId;
mServiceHandler.sendMessage(message);
} catch (Exception e) {
Log.w("TrackersImporter", e.getMessage());
}
return START_STICKY;
}
public void addTracker(Tracker tracker) {
trackers.add(tracker);
}
protected void showToast(final String msg){
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
// Object responsible for
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
addTracker(Runkeeper.getInstance(MainActivity.getActivity()));
addTracker(Strava.getInstance(MainActivity.getActivity()));
startImport(MainActivity.getActivity().getBaseContext(), MainActivity.getActivity().getAppUser(), trackers);
stopSelf(msg.arg1);
}
/**
* Perform data imports.
* Imports are performed only 1 time.
* Additional calls to this method are equivalent to no-op.
* Call init() then performImport() for each TrackerImportable
* #param user user receiving the datas
*/
public void startImport(Context context, User user, ArrayList<Tracker> trackers) {
Context ctx = MainActivity.getActivity().getApplicationContext();
LocalDateTime now = new LocalDateTime();
if (Preferences.getPref(ctx, "tracker_import_date") == "")
Preferences.setPref(ctx, "tracker_import_date", now.toString());
LocalDateTime past = LocalDateTime.parse(Preferences.getPref(ctx, "tracker_import_date"));
long duration = new Duration(past.toDateTime(), now.toDateTime()).getStandardMinutes();
if (isInit)
return;
if (duration > refreshDelay) {
Preferences.setPref(ctx, "tracker_import_date", now.toString());
for (Tracker tracker : trackers) {
if (tracker.isEnabled() && Tracker.isUserEnabled(context, tracker.getName())) {
tracker.init();
tracker.performImport(user);
}
}
}
isInit = true;
}
}
}
This is my mainActivity
public class MainActivity extends BaseActivity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
if (ConnectivityUtil.isConnected(this.getApplicationContext())) {
initGoogleFit();
initTrackers(appUser);
}
}
private void initTrackers(User user) {
Intent trackersIntentService = new Intent(this, TrackersImporter.class);
trackersIntentService.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startService(trackersIntentService);
}
#Override
protected void onResume() {
...
if (ConnectivityUtil.isConnected(this.getApplicationContext())) {
initTrackers(appUser);
}
}
}
First Create one launcher Activity which is like your Main Activity.
In Activity "onCreate" Method you need to start Service and Do Some thing if you wont in Service "onStartCommand" Method.
public class MainActivity extends Activity {
ArrayList<Integer> list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(MainActivity.this,TrackersImporter.class);
}
public class TrackersImporter extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// do something
Log.v(TAG ,"Service is started");
}
}
And also Register this Service at manifest.xml like this.
<service android:name=".TrackersImporter"></service>
if you like stop service
stopService(new Intent(MainActivity.this,TrackersImporter.class);
I'm developing an Android chat app in Java. Now I finally got my service to work but as soon as I fully kill the app the connection in my service dies.
I am using asmack as library for the XMPP connection. The goal is to receive messages even if the app is killed by the user (so it's not in the background).
It does work when I use a foreground service, but I don't want to use a foreground service because of high memory useage and because I don't want the foreground message in the notification center.
My service class
public class MessagingService extends Service {
private final String TAG = "MessagingService";
private final IBinder mBinder = new MessagingBinder();
public Context context;
public XMPPConnection Connection;
public static Handler mHandler = new Handler();
private final int ONGOING_NOTIFICATION_ID = 2344;
#Override
public void onCreate() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
return mBinder;
}
#Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind");
return true;
}
#Override
public void onRebind(Intent intent) {
super.onRebind(intent);
Log.d(TAG, "onRebind");
}
#Override
public void onDestroy() {
}
public class MessagingBinder extends Binder {
MessagingService getService() {
Log.d(TAG + " - MessagingBinder", "getService");
return MessagingService.this;
}
}
public Boolean isConnected() {
return (Connection != null);
}
public void Connect(final AuthorizeActivity authorize, final String username, final String password) {
Thread XMPPConnect = new Thread(new Runnable() {
public final String TAG = "XMPPConnect Thread";
#Override
public void run() {
AndroidConnectionConfiguration connConfig = new AndroidConnectionConfiguration(Configuration.HOST, Configuration.PORT, Configuration.SERVICE);
SmackConfiguration.setDefaultPingInterval(100);
connConfig.setReconnectionAllowed(true);
connConfig.setSASLAuthenticationEnabled(true);
connConfig.setRosterLoadedAtLogin(true);
Connection = new XMPPConnection(connConfig);
try {
Connection.connect();
Log.i(TAG, "Connected to " + Connection.getHost());
} catch (XMPPException ex) {
Log.e(TAG, "Failed to connect to " + Connection.getHost());
Log.e(TAG, ex.toString());
Connection = null;
}
if(authorize != null)
authorize.mServiceConnectCallback();
if(username != null && password != null)
Login(username, password, null);
}
});
XMPPConnect.start();
}
public void Login(final String username, final String password, final AuthorizeActivity authorize) {
Thread XMPPLogin = new Thread(new Runnable() {
public final String TAG = "XMPPConnect Thread";
#Override
public void run() {
try {
Connection.login(username, password);
Log.i(TAG, "Logged in as " + Connection.getUser());
Presence presence = new Presence(Presence.Type.available);
Connection.sendPacket(presence);
PacketFilter filter = new MessageTypeFilter(Message.Type.chat);
Connection.addPacketListener(new PacketListener() {
#Override
public void processPacket(Packet packet) {
final Message message = (Message) packet;
if (message.getBody() != null) {
final String fromName = StringUtils.parseName(message.getFrom());
Log.i(TAG, "Text Recieved " + message.getBody() + " from " + fromName );
mHandler.post(new Runnable() {
public void run() {
Receiver.recieveMessage(fromName, message.getBody());
if(!VisibilityHelper.IsVisible()) {
showNotification(fromName, message.getBody());
}
}
});
}
}
}, filter);
} catch (XMPPException ex) {
Log.e(TAG, "Failed to log in as " + "test");
Log.e(TAG, ex.toString());
Connection = null;
}
if(authorize != null)
authorize.mServiceLoginCallback();
}
});
XMPPLogin.start();
}
public void showNotification(String from, String message) {
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
CharSequence notiText = message;
long meow = System.currentTimeMillis();
Notification notification = new Notification(R.drawable.ic_launcher, notiText, meow);
Context context = getApplicationContext();
CharSequence contentTitle = from;
CharSequence contentText = message;
Intent notificationIntent = new Intent(context, MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
notification.flags = Notification.DEFAULT_LIGHTS | Notification.FLAG_AUTO_CANCEL;
int SERVER_DATA_RECEIVED = 1;
notificationManager.notify(SERVER_DATA_RECEIVED, notification);
}
public void Logout() {
if(Connection.isConnected()) {
Log.i(TAG, "Logout");
Connection.disconnect();
}
}
public HashMap<String, String> getVCard(String user) {
Log.d(TAG, "getVCard");
//String email = user + "#" + Configuration.HOST;
String email = user;
VCard card = new VCard();
ProviderManager.getInstance().addIQProvider("vCard", "vcard-temp", new VCardProvider());
try {
card.load(MainActivity.mService.Connection, email);
String jabber_id = card.getJabberId();
String firstname = card.getFirstName();
String middlename = card.getMiddleName();
String lastname = card.getLastName();
HashMap<String, String> vcard = new HashMap<String, String>();
vcard.put("jabber_id", jabber_id);
vcard.put("firstname", firstname);
vcard.put("middlename", middlename);
vcard.put("lastname", lastname);
return vcard;
} catch (XMPPException e) {
e.printStackTrace();
}
return null;
}
public void retrieveContactsFromList() {
if(this.isConnected()) {
Roster roster = Connection.getRoster();
Collection<RosterEntry> entries = roster.getEntries();
for(RosterEntry entry : entries) {
Receiver.onRetrieveContactFromList(entry);
}
}
}
}
My activity to start the service
public class ConnectionBinder extends FragmentActivity {
private final String TAG = "ConnectionBinder";
public static MessagingService mService;
public boolean mBound = false;
public Database DB;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!this.messagingServiceIsRunning())
{
startService(new Intent(this, MessagingService.class));
}
}
private boolean messagingServiceIsRunning() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (MessagingService.class.getName().equals( service.service.getClassName())) {
return true;
}
}
return false;
}
#Override
protected void onResume() {
super.onResume();
doBindService();
}
#Override
protected void onPause() {
super.onPause();
doUnbindService();
}
private void doBindService() {
Intent intent = new Intent(this, MessagingService.class);
bindService(intent, mMessagingService, Context.BIND_AUTO_CREATE);
}
private void doUnbindService() {
if (mBound) {
unbindService(mMessagingService);
}
}
private void doXMPPLogin() {
HashMap<String, String> user = DB.getUser();
mService.Connect(null, user.get("username"), user.get("password"));
}
private ServiceConnection mMessagingService = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(TAG, "mMessagingService.onServiceConnected()");
MessagingBinder binder = (MessagingBinder) service;
mService = binder.getService();
mBound = true;
if(!mService.isConnected()) {
doXMPPLogin();
}
mService.retrieveContactsFromList();
}
public void onServiceDisconnected(ComponentName arg0) {
Log.d(TAG, "mMessagingService.onServiceDisconnected()");
mBound = false;
}
};
}
Traditional XMPP implementations (and XMPP RFCs) does not define a way to maintain persistent user "sessions" when client disconnects - they all closing user session when underlying TCP/IP or HTTP connection is lost.
On the other hand, typical Android enviroment have "always-connected" Google Cloud Services, which can deliver messages for your application even if it is not connected. In fact, most chat and social networking applications are using GCM to inform user about new messages.
So, depend on your needs, you need to make some changes on the server side of your chat application:
Most XMPP server implementations are able to store messages which was received when user was "offline" and delivers it when user connects again. You can "hook" offline message receiving and inform user via Google Cloud Messaging about availability of new messages, user will receive it when open your application again and your XMPPConnection will established.
Use XMPP Stream Management extension - if you need to share same session across multiple user reconnections - and "resume" previous session when user open your app again. And you still should inform user about new events in his "session" via GCM.
Your server-side XMPP software should keep GCM registration ids for every user device, so when user device is registered in GCM - you need to inform your server about newly registered id - it can be achieved by sending custom <iq> packet to server with your GCM id.
Some commercial XMPP products already implement steps above and will sell you "Push-enabled XMPP service" which is in fact XMPP server with GCM backend, as I describe.