I am trying to work around the gcm time out issue, there are many threads on this subject, here is one for reference.
A proposed workaround is to broadcast a pair of intents at an interval shorter than the tcp timeout.
My implementation is to create a class that extends the CountDownTimer class and hold an instance of that class in an existing service. This derived class restarts itself when it is finished and the service is marked as STICKY_START, so once started, I would think it should just keep broadcasting the intents every 4 minutes, but for some reason there are gaps, when the counter does not broadcast the intents and I still loose contact with the GCM server.
The two relevant classes are below. Can anyone explain and offer a solution as to why this strategy does not work?
I created a class that extends CounDownTimer that should broadcast the intents every 4 minutes.
public class GcmKeepAlive extends CountDownTimer {
protected CountDownTimer timer;
protected Context mContext;
protected Intent gTalkHeartBeatIntent;
protected Intent mcsHeartBeatIntent;
public GcmKeepAlive(Context context) {
super(4*60* 1000,4*60*1000);
mContext = context;
gTalkHeartBeatIntent = new Intent("com.google.android.intent.action.GTALK_HEARTBEAT");
mcsHeartBeatIntent = new Intent("com.google.android.intent.action.MCS_HEARTBEAT");
System.out.println("stariing heartbeat countdown timer");
this.start();
}
#Override
public void onTick(long millisUntilFinished) {
}
#Override
public void onFinish() {
System.out.println("sending heart beat to keep gcm alive");
mContext.sendBroadcast(gTalkHeartBeatIntent);
mContext.sendBroadcast(mcsHeartBeatIntent);
this.start();
}
}
here is the service in my app that holds an instance of the GcmKeepAlive class
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class LocationMonitorService extends Service {
private DeviceLocationClient deviceLocationClient;
private GcmKeepAlive gcmKeepAlive;
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
System.out.println("creating the LocationMonitorService");
deviceLocationClient = new DeviceLocationClient(this);
gcmKeepAlive = new GcmKeepAlive(this);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
System.out.println("inside service making request for location updates");
deviceLocationClient.requestLLocationUpdates();
gcmKeepAlive.start();
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
Here is an example of a gap as seen in logcat.
07-13 14:59:05.583 I/System.out(21651): sending heart beat to keep gcm alive
07-13 15:03:05.640 I/System.out(21651): sending heart beat to keep gcm alive
07-13 15:07:05.776 I/System.out(21651): sending heart beat to keep gcm alive
07-13 15:11:05.922 I/System.out(21651): sending heart beat to keep gcm alive
07-13 15:27:31.994 I/System.out(21651): sending heart beat to keep gcm alive
I actually solved this some time ago, Erik Z's recent comment remained me to post my solution.
I solved this by creating a recurring alarm that triggers a broadcast, which creates and broadcasts the intents. The gaps were caused by the original service being killed and then restarted as a result of the START_STICKY flag.
Here are the various parts (pulled from various files)
This was needed pre-kitkat at the very least, I don't know if it is still needed, I assume it is. I have not turned it off to confirm however.
The alarm manager, intent , and pending intent.
AlarmManager alarmManager = (AlarmManager) Context.getSystemService(Context.ALARM_SERVICE);
Intent gcmKeepAliveIntent = new Intent("com.gmail.npnster.ourlatitude.gcmKeepAlive");
PendingIntent gcmKeepAlivePendingIntent = PendingIntent.getBroadcast(mContext, 0, gcmKeepAliveIntent, PendingIntent.FLAG_CANCEL_CURRENT);
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, 4*60*1000, gcmKeepAlivePendingIntent);
The broadcast receiver:
public class GcmKeepAliveBroadcastReceiver extends BroadcastReceiver {
private GcmKeepAlive gcmKeepAlive;
#Override
public void onReceive(Context context, Intent intent) {
MyLog.p(this,"inside gcm keep alive receiver");
gcmKeepAlive = new GcmKeepAlive(context);
gcmKeepAlive.broadcastIntents();
}
}
The keep alive class that sends the keep alive broadcasts.
public class GcmKeepAlive {
protected Context mContext;
protected Intent gTalkHeartBeatIntent;
protected Intent mcsHeartBeatIntent;
public GcmKeepAlive(Context context) {
mContext = context;
gTalkHeartBeatIntent = new Intent(
"com.google.android.intent.action.GTALK_HEARTBEAT");
mcsHeartBeatIntent = new Intent(
"com.google.android.intent.action.MCS_HEARTBEAT");
}
public void broadcastIntents() {
MyLog.p(this,"sending heart beat to keep gcm alive");
mContext.sendBroadcast(gTalkHeartBeatIntent);
mContext.sendBroadcast(mcsHeartBeatIntent);
}
}
Related
How to run this in background , I mean even I move to other app or go to home screen of my android or close the screen , the button will still clicking itself
please help me
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
button1.performClick();
}
}, 5000);
Things to know
I will try to elaborate as much as I can in a layman terms so that you have a better grasp the Idea of Threads and async tasks
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//business logic
}
}, 5000);
is an Blocking method, which runs on the UI thread (I am supposing you are new to programming/android)[please read about Threads to understand what I am saying in deapth],
which means, in short, your application is executing some logic on the thread ("A worker" which is responsible for the rendering the UI on-screen),
By using Threads you can achieve efficiency in your application by dividing multiple tasks to multiple workers "Threads" but you can't run your application in the background.
How to make your application work in the background?
Google introduced some background limitations in Android Oreo. so to keep your application alive you need
foreground service by showing an ongoing notification.
1. The way you should implement service is like
public class YourService extends Service {
private static final int NOTIF_ID = 1;
private static final String NOTIF_CHANNEL_ID = "Channel_Id";
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId){
// do your jobs here
startForeground();
return super.onStartCommand(intent, flags, startId);
}
private void startForeground() {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
startForeground(NOTIF_ID, new NotificationCompat.Builder(this,
NOTIF_CHANNEL_ID) // don't forget create a notification channel first
.setOngoing(true)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(getString(R.string.app_name))
.setContentText("Service is running background")
.setContentIntent(pendingIntent)
.build());
}
}
2. Also you need to start the service
public class App extends Application {
#Override
public void onCreate() {
super.onCreate();
startService(new Intent(this, YourService.class));
}
}
3. Add your service in the "application" tag of your AndroidManifest.xml
<service android:name=".YourService"/>
4. And also this permission request in the "manifest" tag (if API level 28 or higher)
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
In this way, you can keep your service in the background. I suggest you read articles and see GitHub repositories, and also practice practice practice a lot to be good at Android :)
I am working on a wallpaper application in which i am setting a gallery of images on wallpaper with shuffle effect for 5 min, 10 min etc. I am using service for this task. My service works well when app remains in background, but service get stopped when app get stopped.This is my code for service class:
public class WallpaperService extends Service {
ArrayList<String> arrayList;int counter = 0;
boolean serviceStopped;
private IBinder binder = new WallpaperServiceBinder();
public WallpaperService() {
}
private Handler mHandler;
private Runnable updateRunnable = new Runnable() {
#Override
public void run() {
if (serviceStopped == false)
{
createNotificationIcon();
}
queueRunnable();
}
};
public class WallpaperServiceBinder extends Binder {
public WallpaperService getService() {
return WallpaperService.this;
}
}
private void queueRunnable() {
// 600000 : cada 10 minutos, comprueba si hay nuevas notificaciones y actualiza la
// notification BAR
mHandler.postDelayed(updateRunnable, 5000);
}
#Override
public int onStartCommand(Intent intent,int flag, int start_id){
super.onStartCommand(intent,flag,start_id);
arrayList = intent.getStringArrayListExtra("image_url");
return START_STICKY;
}
#Override
public void onRebind(Intent intent) {
Log.v("Service","in onRebind");
super.onRebind(intent);
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
#Override
public void onCreate() {
serviceStopped = false;
mHandler = new Handler();
queueRunnable();
}
#Override
public void onStart(Intent intent, int startid) {
}
public void createNotificationIcon()
{
counter += 1;
Toast.makeText(this, "Hello", Toast.LENGTH_SHORT).show();
Picasso.with(getApplicationContext()).load(arrayList.get(counter)).into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
try {
final WallpaperManager wallpaperManager =
WallpaperManager.getInstance(getApplicationContext());
wallpaperManager.setBitmap(bitmap);
wallpaperManager.suggestDesiredDimensions(1080, 1920);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
//Here you should place a loading gif in the ImageView to
//while image is being obtained.
}
});
}}
This is the code i am using to start service:
Intent intent = new Intent(CategoryActivity.this,WallpaperService.class);
intent.putExtra("image_url",img_urls);
intent.setAction(Constants.ACTION.STARTFOREGROUND_ACTION);
startService(intent);
bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);
Have you added these lines in manifest file
<application> <service android:name=".ExampleService" /></application>
Important Fact about the bindService
If a component calls bindService() to create the service and onStartCommand() is not called, the service runs only as long as the component is bound to it. After the service is unbound from all of its clients, the system destroys it.
Try using Started Service
A started service is one that another component starts by calling
startService(), which results in a call to the service's
onStartCommand() method.
When a service is started, it has a lifecycle that's independent of
the component that started it. The service can run in the background
indefinitely, even if the component that started it is destroyed. As
such, the service should stop itself when its job is complete by
calling stopSelf(), or another component can stop it by calling
stopService().
An application component such as an activity can start the service by
calling startService() and passing an Intent that specifies the
service and includes any data for the service to use. The service
receives this Intent in the onStartCommand() method.
Handling onStartCommand
Notice that the onStartCommand() method must return an integer. The
integer is a value that describes how the system should continue the
service in the event that the system kills it. The default
implementation for IntentService handles this for you, but you are
able to modify it. The return value from onStartCommand() must be one
of the following constants:
START_NOT_STICKY If the system kills the service after onStartCommand() returns, do not recreate the service unless there are
pending intents to deliver. This is the safest option to avoid running
your service when not necessary and when your application can simply
restart any unfinished jobs.
START_STICKY If the system kills the service after onStartCommand() returns, recreate the service and call
onStartCommand(), but do not redeliver the last intent. Instead, the
system calls onStartCommand() with a null intent unless there are
pending intents to start the service. In that case, those intents are
delivered. This is suitable for media players (or similar services)
that are not executing commands but are running indefinitely and
waiting for a job.
START_REDELIVER_INTENT If the system kills the service after onStartCommand() returns, recreate the service and call
onStartCommand() with the last intent that was delivered to the
service. Any pending intents are delivered in turn. This is suitable
for services that are actively performing a job that should be
immediately resumed, such as downloading a file.
Note: In your case you should use Started Service and return START_STICKY or START_REDELIVER_INTENT (based on your requirement) in onStartCommand()
Check Official Documentation for detailed description of the Services.
I am developing android application, so I am starting a service with alarm:
public void scheduleLocationCheckerAlarm() {
Intent intent = new Intent(getApplicationContext(), LocationCheckerReceiver.class);
final PendingIntent pIntent = PendingIntent.getBroadcast(this, LocationCheckerReceiver.REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
long firstMillis = System.currentTimeMillis();
AlarmManager alarm = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
alarm.setInexactRepeating(AlarmManager.RTC_WAKEUP, firstMillis, 600000, pIntent);
}
LocationCheckerReceiver:
public class LocationCheckerReceiver extends BroadcastReceiver {
public static final int REQUEST_CODE = 12345;
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, LocationNotificator.class);
context.startService(i);
}
Service:
public class LocationNotificator extends Service {
public LocationNotificator() {
}
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("Location checker", "Service running");
//My code is here
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("Location checker", "Service destroyed");
}
So I want this service to be checking for something every 1 minute and to be running all the time, even when the application is closed by the user.
You must call startForeground(FOREGROUND_ID, buildForegroundNotification(filename)); in order to ensure that your service running continuously. Also, this will post a notification from your app to show the user about the service state. Please follow the reference.
Here is the code :
public class LocationNotificator extends Service {
private static int FOREGROUND_ID=1338;
public LocationNotificator() {
}
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("Location checker", "Service running");
//My code is here
startForeground(FOREGROUND_ID,
buildForegroundNotification(filename));
stopForeground(true);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("Location checker", "Service destroyed");
}
private Notification buildForegroundNotification(String filename) {
NotificationCompat.Builder b=new NotificationCompat.Builder(this);
b.setOngoing(true);
b.setContentTitle("Some Title")
.setContentText("some File name")
.setSmallIcon(android.R.drawable.stat_sys_download)
.setTicker("downloading");
return(b.build());
}
You need to read this first. https://developer.android.com/guide/components/services.html
In a nutshell, start your service as a foreground service so there's lesser chance of Android killing your service. As a foreground service, you need to display an on-going notification in the status bar.
There's no direct way of making sure your service is never killed by the Android system. A workaround is to send a broadcast in onDestroy() of your service, and have a Receiver in your Android application start the service upon receiving the broadcast.
By the way, it seems that your service is sending location updates periodically to your backend server. This might be better implemented using Firebase Job Dispatcher library or Evernote's Android-Job library.
i just use service to make something like chathead and this is my result
but only i have two problem
first some time my service killed i notice that's on weak device so how can i prevent it from killed in same time the facebook messenger never killed
second my class animation not smooth i think i must run the class on new thread
xxx = new classanimation (mContext);
i tried this
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
xxx = new classanimation (mContext);
}
});
but i see it same no different
and this is my service code
public class MyCustomService extends Service {
private volatile HandlerThread mHandlerThread;
private ServiceHandler mServiceHandler;
private static Context mContext;
Handler mHandler;
IBinder mBinder = new LocalBinder();
public static Socket client;
public classanimation xxx;
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class LocalBinder extends Binder {
public MyCustomService getServerInstance() {
return MyCustomService.this;
}
}
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
// Define how to handle any incoming messages here
#Override
public void handleMessage(Message message) {
// ...
// When needed, stop the service with
// stopSelf();
}
}
public void onCreate() {
super.onCreate();
this.mContext = this;
xxx= new classanimation(mContext); //class i run it but its not smooth
mHandler = new Handler(Looper.getMainLooper());
// An Android handler thread internally operates on a looper.
mHandlerThread = new HandlerThread("MyCustomService.HandlerThread");
mHandlerThread.start();
// An Android service handler is a handler running on a specific background thread.
mServiceHandler = new ServiceHandler(mHandlerThread.getLooper());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Send empty message to background thread
mServiceHandler.sendEmptyMessageDelayed(0, 500);
// or run code in background
mServiceHandler.post(new Runnable() {
#Override
public void run() {
// Do something here in background!
SessionManager session = new SessionManager(mContext);
// If desired, stop the service
//stopSelf();
}
});
// Keep service around "sticky"
return START_STICKY;
}
#Override
public void onDestroy() {
// Cleanup service before destruction
mHandlerThread.quit();
}
}
To ensure that your service is never killed, you can make your service a foreground service by using a notifiaction. This means that there will always be a notification icon when the service is running, but it will never be killed. For example :
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text), System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title), getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
You can't prevent from killing,Yes but you can minimize probability of killing by starting service in different process than your app process.
I want to send a heartbeat from my application to the GCM server, so the connection will stay alive.
How can I do that, and how can I know the URL of my GCM server??
Thanks in advance!!
How to send the heartbeat
This class can sent the proper intents
public class GcmKeepAlive {
protected CountDownTimer timer;
protected Context mContext;
protected Intent gTalkHeartBeatIntent;
protected Intent mcsHeartBeatIntent;
public GcmKeepAlive(Context context) {
mContext = context;
gTalkHeartBeatIntent = new Intent(
"com.google.android.intent.action.GTALK_HEARTBEAT");
mcsHeartBeatIntent = new Intent(
"com.google.android.intent.action.MCS_HEARTBEAT");
}
public void broadcastIntents() {
System.out.println("sending heart beat to keep gcm alive");
mContext.sendBroadcast(gTalkHeartBeatIntent);
mContext.sendBroadcast(mcsHeartBeatIntent);
}
}
if you just want to send the heartbeat you can do the following in an Activity
GcmKeepAlive gcmKeepAlive = new GcmKeepAlive(this);
gcmKeepAlive.broadcastIntents();
I don't think you need to set any additional permissions for this but here are the gcm related permissions I have in my manifest
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission
android:name=your_package_name.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="your_package_name.permission.C2D_MESSAGE" />
One way to send the heartbeats on a regular basis
If you want to send them on a regular basis, here is how I am doing that:
public class GcmKeepAliveBroadcastReceiver extends BroadcastReceiver {
private GcmKeepAlive gcmKeepAlive;
#Override
public void onReceive(Context context, Intent intent) {
System.out.println("inside gcm keep alive receiver");
gcmKeepAlive = new GcmKeepAlive(context);
gcmKeepAlive.broadcastIntents();
}
}
I also have a service that has an Dagger injected alarmmanger and pendingintent
#Inject AlarmManager alarmManager;
#Inject PendingIntent gcmKeepAlivePendingIntent;
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, 4*60*1000, gcmKeepAlivePendingIntent);
Here is the section of the Dagger module that provides the alarm manager and pending intent.
There are several ways to have an alarm manager periodically call a method, so assuming you don't use Dagger, you should still be able to pull out the relevant parts. Your question was how to send the heartbeat, not how to use an alarm manager. There are lots of answers to that already so search on that.
#Provides PendingIntent provideGcmKeepAlivePendingIntent() {
System.out.println("pending intent provider");
Intent gcmKeepAliveIntent = new Intent("com.gmail.npnster.first_project.gcmKeepAlive");
return PendingIntent.getBroadcast(mContext, 0, gcmKeepAliveIntent, PendingIntent.FLAG_CANCEL_CURRENT);
}
#Provides AlarmManager provideGcmKeepAliveAlarmManager() {
return (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
}