I have implemented Socket.io client on android, I have managed to initialize the socket in an Unbounded service but the problem is it got disconnected after 5 minutes when the app is in background and phone is locked. If the app is in foreground the connection remains alive endless.
Situation on Disconnection:
Service is still Running
Socket is disconnected due to unknown reason
Socket instance is create in Singelton class and the instance remain same
always in the lifecycle, i have checked the hashcode for socket instance.
I have implemented the Ping Pong solution as well but it is not working.
Checked the server logs and found the socket is disconnected for the user.
I have even tried to restart the service but didn't got connect after restart.
The only way to connect the socket again to kill the app and restart the app.
MySingelton.java
public class Singelton implements Serializable {
private static Singelton instance;
private static final String SERVER_ADDRESS = "xyz";
private Socket mSocket;
private Context context;
public Singelton(Context context) {
this.context = context;
this.mSocket = getServerSocket();
}
public static Singelton get(Context context){
if(instance == null){
instance = getSync(context);
}
instance.context = context;
return instance;
}
private static synchronized Singelton getSync(Context context) {
if(instance == null){
instance = new Singelton(context);
}
return instance;
}
public Socket getSocket(){
return this.mSocket;
}
public Socket getServerSocket() {
try {
IO.Options opts = new IO.Options();
opts.forceNew = true;
opts.reconnection = true;
mSocket = IO.socket(SERVER_ADDRESS, opts);
return mSocket;
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
}
MyService
public class MessageService extends Service {
Socket mSocket;
public MessageService() {
mSocket = Singelton.get(this).getSocket();
Log.d("chat", "Constructor"+String.valueOf(mSocket.hashCode()));
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("chat", "onStartCommand"+String.valueOf(mSocket.hashCode()));
initSocket();
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
wl.acquire();
//Log.e(TAG, "onStartCommand");
return START_STICKY;
}
#Override
public void onDestroy() {
Log.d("chat", "onDestroyed");
Calendar cal = Calendar.getInstance();
Intent intent = new Intent(getApplicationContext(), MessageService.class);
PendingIntent pintent = PendingIntent.getService(MessageService.this, 0, intent, 0);
AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
alarm.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis()+(1*1000), pintent);
super.onDestroy();
}
#Override
public void onCreate() {
Log.d("chat", "onCreate");
super.onCreate();
}
private void initSocket(){
mSocket.on(Socket.EVENT_CONNECT,onConnect);
mSocket.on(Socket.EVENT_DISCONNECT,onDisconnect);
mSocket.on(Socket.EVENT_CONNECT_ERROR, onConnectError);
mSocket.on(Socket.EVENT_CONNECT_TIMEOUT, onConnectError);
mSocket.on(Socket.EVENT_RECONNECT, onReconnect);
mSocket.on("pingy", ping);
mSocket.connect();
mSocket.emit("pongy", "Hi");
}
int i=0;
Emitter.Listener ping = new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.d("chat", String.valueOf(i) + " " + mSocket.hashCode());
i++;
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
new CountDownTimer(2000,1000) {
#Override
public void onTick(long millisUntilFinished) {
}
#Override
public void onFinish() {
mSocket.emit("pongy", "Hi");
}
}.start();
}
});
}
};
Emitter.Listener onConnect = new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.d("chat", "connected");
}
};
Emitter.Listener onReconnect = new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.d("chat", "onReconnect");
}
};
Emitter.Listener onDisconnect = new Emitter.Listener() {
#Override
public void call(Object... args) {
//mSocket.disconnect();
Log.d("chat", "onDisconnect");
stopSelf();
}
};
Emitter.Listener onConnectError = new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.d("chat", args[0].toString());
}
};
}
Note: I want to make the service running in the background all the time and it should not be killed by the system or with the app
Do i need to implement Remote service for that?
If its a remote service should it be bounded or Unbounded?
Related
I am using this source app to chat with other devices. But how to make it to start like a Service so I can to start foreground service.
Do I need MainFragment and LoginActivity rewrite in Service?
socket.io app socket.io-android-chat
I have tried something like that in class SocketService, what other I need to include in Service for App to get notification messages even if app is closed.
public class SocketService extends Service {
private Socket mSocket;
public static final String TAG = SocketService.class.getSimpleName();
private static final String NOTIFICATION_CHANNEL_ID_DEFAULT = "App running in background";
String GROUP_KEY_WORK_EMAIL = "com.android.example.WORK_EMAIL";
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw null;
}
#Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "on created", Toast.LENGTH_SHORT).show();
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setGroup(GROUP_KEY_WORK_EMAIL);
Notification notification = builder.build();
NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle();
// Set big text style.
builder.setStyle(bigTextStyle);
startForeground(3, notification);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "start command", Toast.LENGTH_SHORT).show();
try {
mSocket = IO.socket(Constants.CHAT_SERVER_URL);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
mSocket.on("newMessageReceived", onNewMessage);
mSocket.connect();
return START_STICKY;
}
private Emitter.Listener onNewMessage = new Emitter.Listener() {
#Override
public void call(Object... args) {
JSONObject data = (JSONObject) args[0];
String username;
String message;
try {
username = data.getString("username");
message = data.getString("message");
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
return;
}
Log.d(TAG, "call: new message ");
setNotificationMessage(message, username);
}
};
public void setNotificationMessage(CharSequence message, CharSequence title) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentTitle(title);
builder.setContentText(message);
NotificationManagerCompat nm = NotificationManagerCompat.from(this);
nm.notify(3, builder.build());
}
}
you shouldn't use a foreground service in order to get notification messages when your app is in the background
instead you should use firebase push notification
however if you still need to use the socket connection in your foreground service
just simply create a singleton class to handle all socket connection and use it your foreground service as shown below
public class SocketManger {
private static SocketManger socketManger;
Socket socket;
Callback<Boolean> onConnect;
public void init(Callback<Boolean> onConnect){
this.onConnect = onConnect;
connectToSocket();
listenToPublicEvents();
}
private void connectToSocket(){
try{
IO.Options opts = new IO.Options();
//optional parameter for authentication
opts.query = "token=" + YOUR_TOKEN;
opts.forceNew = true;
opts.reconnection = true;
opts.reconnectionDelay = 1000;
socket = IO.socket(YOUR_URL, opts);
socket.connect();
}
catch(URISyntaxException e){
throw new RuntimeException(e);
}
}
private void listenToPublicEvents(){
socket.on(Socket.EVENT_CONNECT, args -> {
if(onConnect!=null)
onConnect.onResult(true);
} );
socket.on(Socket.EVENT_DISCONNECT, args ->{
if(onConnect!=null)
onConnect.onResult(false);
});
}
public void emit(String event, JSONObject data, Ack ack){
socket.emit(event, new JSONObject[]{data}, ack);
}
public void on(String event, Emitter.Listener em){
socket.on(event, em);
}
public static SocketManger getSocketManger() {
if(socketManger == null){
socketManger = new SocketManger();
}
return socketManger;
}
public boolean isConnected(){
return socket!=null && socket.connected();
}
public void onDestroy() {
onConnect = null;
socket.disconnect();
}
public interface Callback<T> {
void onResult(T t);
}
}
and add this code to your foreground service
SocketManager socketManger = SocketManger.getSocketManger();
#Override
public void onCreate() {
socketManger.init(this::onSocketEvent);
}
public void onSocketEvent(boolean connect){
//your code when the socket connection connect or disconnect
}
and make sure to disconnect the socket when the service is destroyed
#Override
public void onDestroy() {
socketManger.onDestroy()
super.onDestroy();
}
You can start it from a custom Application class in onCreate method if you want the service to be started immediately after the app is launched.
Or you you can start it from any Activity eg. from onCreate method in case you want to start the service from certain activity.
Or you can start from BroadcastReceiver when device is booted. In this case use BOOT_COMPLETED action:
To start your service just use this code, anywhere you want to start your service:
Intent intent = new Intent(context, SocketService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
I'm creating an application where I want a Handler to send an http request every 15 second. The problem is, that while my device (huawei watch 2) is on chare, the Handler works as supposed to, but when I take the watch off the charger, the 15 second is changes between 15 and 40 seconds. Is there a problem with my implementation? I'm not passing any Runnable to the Handler, since there is only a little work to do. I have a SensorHelper class, which just gets the heart rate value. In the request I'm sending a custom message object as JSON.
MainActivity:
public class MainActivity extends WearableActivity {
private static ConnectionService mService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.BODY_SENSORS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.BODY_SENSORS},
1);
}
Intent intent = new Intent(this, ConnectionService.class);
this.startService(intent);
this.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
setContentView(R.layout.activity_main);
// Enables Always-on
setAmbientEnabled();
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
ConnectionService.LocalBinder binder = (ConnectionService.LocalBinder) service;
mService = binder.getServiceInstance();
try {
sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
}
};
public static void sendMessage(String message) {
mService.sendMessage(message);
}
}
ConnectionService in order to avoid my application to go into DOZE mode:
public class ConnectionService extends Service {
private final IBinder mBinder = new LocalBinder();
private static SensorHelper sensorHelper;
private static OkHttpClient client = new OkHttpClient();
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
public class LocalBinder extends Binder {
public ConnectionService getServiceInstance() {
return ConnectionService.this;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public void onCreate() {
if (sensorHelper == null) {
sensorHelper = new SensorHelper(this);
}
super.onCreate();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("ASD")
.setContentText("ASD")
.setContentIntent(pendingIntent).build();
startForeground(1337, notification);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_REDELIVER_INTENT;
}
public void sendMessage(String message) {
Message msg = new Message("HR", message);
RequestBody requestBody = RequestBody.create(JSON, msg.toJson());
Request request = new Request.Builder()
.url("http://104.248.32.100/")
.post(requestBody)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
}
});
}
}
SensorHelper in order to get the heart rate value, and post it:
public class SensorHelper implements SensorEventListener {
private String sensorValue = "normal";
private static Handler handler = new Handler();
private int delay = 15000;
public SensorHelper(Context context) {
SensorManager sensorManager = (SensorManager) context.getSystemService(SENSOR_SERVICE);
Sensor heartRateSensor = sensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE);
sensorManager.registerListener(this, heartRateSensor, SensorManager.SENSOR_DELAY_NORMAL);
handler.postDelayed(new Runnable(){
public void run() {
sendMessage();
handler.postDelayed(this, delay);
}
}, delay);
}
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_HEART_RATE) {
sensorValue = String.valueOf(event.values[0]);
}
}
#Override
public void onAccuracyChanged (Sensor sensor,int i){
}
private String getSensorValue() {
return sensorValue;
}
private void sendMessage() {
MainActivity.sendMessage(getSensorValue());
}
}
Is there any mistakes I'm making, while the Handler is not working correctly? Is passing a Runnable necessary to create a new Thread? As far as is read, the Handler is creating a new Thread
I had the same exact problem, with a lot of applications that run on the background while the device is not charging. The problem is Android's battery optimization.
The solution is pretty simple, you have to disable your app's battery optimization by going to Settings > Battery > Battery optimization (The path varies by manufacturer).
Some manufacturers also add extra measures to optimize battery by making timed tasks proc less often, so check out for extra settings that might affect this.
Hope this helps!
I created a service class in my application to run some task in the background. When I close the application the service still running and it's ok. But the problem is, When I run the application again, a new service running and every time when I restart the application a new service create. Here's my code
public class myService extends Service {
Socket socket;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
connectSocket();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
socket.disconnect();
}
private void connectSocket() {
try {
socket = IO.socket("http://192.168.1.52:2500");
socket.on(Socket.EVENT_CONNECT, onConnected);
socket.on(Socket.EVENT_CONNECT_ERROR, onConnectionError);
socket.on(Socket.EVENT_CONNECT_TIMEOUT, onConnectionTimout);
socket.on(Socket.EVENT_DISCONNECT, onDisconnect);
socket.on("test_callback", eventCallback );
socket.connect();
} catch (URISyntaxException e) {
Log.d(getClass().getSimpleName(), "Socket Connection Exception");
e.printStackTrace();
}
}
private Emitter.Listener onConnected = new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.d("SocketIO","onConnected");
socket.connect();
}
};
private Emitter.Listener onConnectionError = new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.d("SocketIO","onConnectionError");
socket.connect();
}
};
private Emitter.Listener onConnectionTimout = new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.d("SocketIO","onConnectionTimout");
socket.connect();
}
};
private Emitter.Listener onDisconnect = new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.d("SocketIO","onDisconnect");
socket.disconnect();
}
};
int notno = 0;
private Emitter.Listener eventCallback = new Emitter.Listener() {
#Override
public void call(Object... args) {
JSONObject data = (JSONObject) args[0];
String message;
try {
message = data.getString("message").toString();
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(myService.this)
.setSmallIcon(R.drawable.head)
.setContentTitle("Message")
.setContentText(message);
NotificationManager nfm = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
nfm.notify(notno ,mBuilder.build());
notno++;
} catch (JSONException ignored) {
}
}
};
}
and in onCreate method in main class run this code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
startService(new Intent(getBaseContext(), myService.class));
}
This is by design. Every time startService is called, onStartCommand will be called. This allows it to be started wth different intents. However it will only be created once (unless stopped for resources, of course). If you want to only have a single instance of a socket or thread, its your job to make sure you only instantiate them the first time onStartCommand is called.
Also, your code as is won't work. Services do not have their own threads or processes by default. That mean you need to launch a new Thread to do your socket work, or you'll get a NetworkOnMainThreadErrror.
I am creating bound service for socket connection.Which means it is creating a long polling connection and listens my server.If user closes the app in task manager my service is killing i have no problem with this.But when user presses the back button I am calling activity.finish() method for close app.But with this method my service doesn't kill,it is still connected to socket server.
Is this normal ? And Could be this drain the battery ?
My service:
public class SocketService extends Service {
//you need constants to tell servise and activity what you are sending a message for
public static final int REGISTER_CHAT_ACTIVITY = 1;
public static final int MESSAGE_RECEIVED = 2;
final Messenger mMessenger = new Messenger(new IncomingHandler());
Messenger chat;
private Socket socket;
#Override
public void onCreate() {
try {
socket = IO.socket("ip");
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
}
}).on("connected", new Emitter.Listener() {
#Override
public void call(Object... args) {
}
}).on("message", new Emitter.Listener() {
#Override
public void call(Object... args) {
try {
chat.send(android.os.Message.obtain(null, MESSAGE_RECEIVED, args[0]));
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
//and add all the other on listeners here
socket.connect();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (socket != null) {
socket.disconnect();
socket.connect();
} else {
try {
socket = IO.socket("ip");
socket.connect();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
class IncomingHandler extends Handler {
#Override
public void handleMessage(android.os.Message msg) {
switch(msg.what){
case REGISTER_CHAT_ACTIVITY:
chat = msg.replyTo;
break;
}
}
}
public class LocalBinder extends Binder {
SocketService getService() {
return SocketService.this;
}
}
}
I had something similar a while ago i solved the issue by using shared preferences.(Note: I dont think it's the best answer but it solved my problem)
I saved in preferences a boolean to register when i dont need the service anymore but lost reference of it.
public class YourService extends Service {
private YourService serv;
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
serv = this;
Then Somehwere on your code that the service does frequently.
if(!sharedPref.getBoolean("TurnOffService", false)){
serv.stopSelf();
}
Hope it helps.
First I will explain the current situation.
I've 2 different threads in 2 services(read from usb port service and make web requests service). I'm starting them in onCreate of my activity like:
serialServiceIntent = new Intent(NDKSerialActivity.this, SerialService.class);
startService(serialServiceIntent);
webServiceIntent = new Intent(NDKSerialActivity.this, RecordWebService.class);
startService(webServiceIntent);
There is nothing wrong with serial service but in RecordWebService when I make a request my gui stops until response comes.
The code is like that:
public class RecordWebService extends Service
{
public static final String SERVER_ADDRESS = "http://192.168.1.100:8080/MobilHM/rest";
private static final String TAG = RecordWebService.class.getSimpleName();
private RecordWebThread recordWebThread;
#Override
public void onStart(Intent intent, int startId)
{
super.onStart(intent, startId);
recordWebThread = new RecordWebThread(true);
recordWebThread.start();
}
#Override
public void onDestroy()
{
super.onDestroy();
Log.i(TAG, "RecordWebService Destroyed");
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
}
and
public class RecordWebThread extends Thread
{
private static final String TAG = RecordWebThread.class.getSimpleName();
public boolean always;
public RecordWebThread(boolean always)
{
this.always = always;
}
#Override
public void run()
{
PatientRecord patientRecord = new PatientRecord();
while (always)
{
RestClient restClient = new RestClient(RecordWebService.SERVER_ADDRESS + "/hello");
try
{
restClient.execute(RequestMethod.GET);
}
catch (Exception e1)
{
Log.e(TAG, "", e1);
}
Log.i(TAG, "Server Response Code:->" + restClient.getResponseCode());
Log.i(TAG, "Server Response:->" + restClient.getResponse());
try
{
sleep(4 * 1000);
}
catch (InterruptedException e)
{
Log.e(TAG, "Web service interrupted", e);
}
}
}
}
Also I've tried to remove sleep part and make the thread to run with timer and timer task like:
public void sendRecord()
{
scanTask = new TimerTask()
{
public void run()
{
handler.post(new Runnable()
{
public void run()
{
RestClient restClient = new RestClient(RecordWebService.SERVER_ADDRESS + "/hello");
try
{
restClient.execute(RequestMethod.GET);
}
catch (Exception e1)
{
Log.e(TAG, "", e1);
}
Log.i(TAG, "Server Response Code:->" + restClient.getResponseCode());
Log.i(TAG, "Server Response:->" + restClient.getResponse());
}
});
}
};
t.schedule(scanTask, 1000, 4000);
}
but no luck, my gui hangs when it comes to restClient.execute .
You can find RestClient.java # http://www.giantflyingsaucer.com/blog/?p=1462
How can I make my requests not block my gui thread?
Edit:
public void sendRecord()
{
scanTask = new TimerTask()
{
public void run()
{
RestClient restClient = new RestClient(RecordWebService.SERVER_ADDRESS + "/hello");
try
{
restClient.execute(RequestMethod.GET);
}
catch (Exception e1)
{
Log.e(TAG, "", e1);
}
Log.i(TAG, "Server Response Code:->" + restClient.getResponseCode());
Log.i(TAG, "Server Response:->" + restClient.getResponse());
}
};
t.schedule(scanTask, 1000, 4000);
}
Without handler, I call this in onCreate of my activity but still ui hanging.
Or you can use an IntentService which will handle the thread issues for you.
This is an example class:
public class MyService extends IntentService {
public MyService() {
super("MyService");
}
public MyService(String name) {
super(name);
}
#Override
protected void onHandleIntent(Intent arg0) {
//Do what you want
}
}
Then you just call:
Intent intent = new Intent(getApplicationContext(),MyService.class);
startService(intent);
Edit:
To repeat the same thing every 4 seconds you should do something like this:
PendingIntent serviceIntent= PendingIntent.getService(context,
0, new Intent(context, MyService.class), 0);
long firstTime = SystemClock.elapsedRealtime();
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
long intervalInSec = 4;
am.setRepeating(AlarmManager.ELAPSED_REALTIME, firstTime, intervalInSec*1000, serviceIntent)
;
In your code (2d version) happens next: You create thread, and it asks UI thread to do some net interaction. Just exclude handler.post(...) while executing request. Later you can use this for simple runnable for updating your UI with results of request.