My intention is to have download service created when the app first runs and checks for update every 24 hours. I originally had everything running my main activity but it seems to much to run everything on one thread and one class. So this is my attempt to move it to another class and into service. It suppose to run and check for an update ever 24 hours and if there is no internet try again in 4 hours. I specifically want to involve any recursive problems, having two or three same services checking update, just one every 24 hours. But having problem with integrating my code into service, what am I doing wrong?
public class DownloadService extends IntentService {
// TODO 0 - Define your Download Service as Android component in
// AndroidManifest.xml
private int result = Activity.RESULT_CANCELED;
public DownloadService() {
super("DownloadService");
}
// Will be called asynchronously be Android
#Override
protected void onHandleIntent(Intent intent) {
private final Runnable mUpdateUi = new Runnable(){
public void run(){
check();
}
};
private void start(){
new Thread(
new Runnable(){
public void run(){
Log.d(TAG, "inside start");
Looper.prepare();
mHandler = new Handler();
check();
Looper.loop();
}
}
).run();
}
private void check(){
if (isNetworkAvailable()== true){
try {
new checkupdate().execute();
delayTime = 86400000;
Toast.makeText(DownloadService.this, "Daily update check!", Toast.LENGTH_SHORT).show();
}
catch (IOException e) {
e.printStackTrace();
delayTime = 21600000;
}
}else{
delayTime = 21600000;
Toast.makeText(DownloadService.this, "No internet for Daily update check, try again in little!", Toast.LENGTH_SHORT).show();
}
reCheck();
}
private void reCheck(){
mHandler.postDelayed(mUpdateUi, delayTime);
}
}
IntentService already handles setting up a worker thread and queue, and termination when the queue is empty. Which makes it a very good candidate for something like a download service to manage the actual work of downloading data, but not really a great candidate for a time scheduler.
I'd suggest using an AlarmManager to schedule your work instead. What you want is to trigger an Intent to start your DownloadService, by sending it intent with an Action indicating what to do.
Note also that if you want to cancel an IntentService with an Action, you will need to implement onStartCommand in addition to the usual onHandleIntent, so that you can respond to the action immediately -- you cannot do this from onHandleIntent, since the intent won't be sent to that until the current task in the queue is completed. Here's a quick example:
public class DownloadService extends IntentService {
private static final String TAG = "DownloadService";
////////////////////////////////////////////////////////////////////////
// Actions
public static final String ACTION_CANCEL = "package.name.DownloadService.action.CANCEL";
public static final String ACTION_DOWNLOAD = "package.name.DownloadService.action.DOWNLOAD";
////////////////////////////////////////////////////////////////////////
// Broadcasts
public static final String BROADCAST_DOWNLOADED = "package.name.DownloadService.broadcast.DOWNLOADED";
public static final String BROADCAST_ERROR = "package.name.DownloadService.broadcast.ERROR";
////////////////////////////////////////////////////////////////////////
// Extras
public static final String MESSAGE = "package.name.DownloadService.extra.MESSAGE";
// etc.
private boolean isCancelled;
// usual stuff omitted
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(intent != null) {
String action = intent.getAction();
Log.v(TAG, "onStartCommand() - action: "+action);
if(ACTION_CANCEL.equals(action)) {
isCancelled = true;
// insert code here to signal any objects to cancel
// their work, etc.
stopSelf();
}
}
return super.onStartCommand(intent, flags, startId);
}
#Override
protected void onHandleIntent(Intent intent) {
if(intent != null) {
final String action = intent.getAction();
Log.v(TAG, "onHandleIntent() - action: "+action);
if(ACTION_DOWNLOAD.equals(action)) {
handleDownloading(intent);
}
else if(ACTION_CANCEL.equals(action)) {
// nothing to do here, handled in onStartCommand
}
}
}
////////////////////////////////////////////////////////////////////
private void handleDownloading(Intent intent) {
// get stuff you need from the intent using intent.getStringExtra(), etc.
if(!isCancelled) {
// do downloading, call broadcastDownloaded() when done
}
else {
// stop work, send broadcast to report cancellation, etc.
}
}
// send a broadcast to a BroadcastReceiver (e.g. in your activity)
// to report that the download completed
private void broadcastDownloaded() {
Log.v(TAG, "broadcastDownloaded()");
Intent broadcastIntent = new Intent();
if (broadcastIntent != null) {
broadcastIntent.setAction(BROADCAST_DOWNLOADED);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
sendBroadcast(broadcastIntent);
}
}
private void broadcastError(String message) {
Log.v(TAG, "broadcastError(), message: "+message);
Intent broadcastIntent = new Intent();
if (broadcastIntent != null) {
broadcastIntent.setAction(BROADCAST_ERROR);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
if(message != null) {
broadcastIntent.putExtra(MESSAGE, message);
}
sendBroadcast(broadcastIntent);
}
}
}
This is not how IntentService is meant to be used. As per the documentation, IntentService already creates its own worker threads. You should not be creating your own:
Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
Apart from the fact that your code as shown here won't compile (your start method is inside the onHandleIntent method), your general approach seems to be to start your own worker thread. What would happen in this approach is that you would start the thread, onHandleIntent would complete and then the service would be stopped. In addition to not actually working, this approach is also a bad idea because (at best if you're lucky) the service would be running continually 24/7.
What you should do instead is actually do your main work in onHandleIntent which IntentService will queue on a worker thread for you. Then instead of using postDelayed use AlarmManager to set an alarm to send an Intent to start the service again in 24 hours or 4 hours.
Related
I have an android activity and a corresponding service, where the activity is just a UI and the service calculates what to display. The service is bound to the activity.
First I establish a connection:
in the Activity.java file:
final Messenger _messenger = new Messenger(new IncomingHandler(new WeakReference<>(this)));
Messenger _serviceMessenger = null;
private ServiceConnection _connection = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName name, IBinder service)
{
_serviceMessenger = new Messenger(service);
try
{
// sending the initial welcome message
Message m = Message.obtain(null, ForegroundService.BIND_SERVICE);
m.replyTo = _messenger;
_serviceMessenger.send(m);
}
catch(RemoteException ex)
{
_serviceMessenger = null;
}
}
#Override
public void onServiceDisconnected(ComponentName name)
{
_serviceMessenger = null;
}
};
private static class IncomingHandler extends Handler
{
private WeakReference<MyActivity> _parent;
IncomingHandler(WeakReference<MyActivity> parent)
{
super();
_parent = parent;
}
#Override
public void handleMessage(Message msg)
{
Log.i(LOG_TAG, "Activity: Received message");
MyActivity ta = _parent.get();
switch(msg.what)
{
case ForegroundService.LOCATION_UPDATED:
if(msg.obj == null)
{
Log.e(LOG_TAG, "Activity: msg null");
ta.setTexts("", null, null);
}
else
{
Log.i(LOG_TAG, "Activity: msg ok");
LocWithName loc = (LocWithName)msg.obj;
ta.setTexts(loc.getName(), Double.toString(loc.getHossz()), Double.toString(loc.getSzel()));
Chronometer chronometer = ta.findViewById(R.id.chronom);
chronometer.setBase(SystemClock.elapsedRealtime());
chronometer.start();
}
break;
case ForegroundService.LOST_GPS:
ta.setTexts("", "unknown", "unknown");
break;
default:
super.handleMessage(msg);
break;
}
}
}
in activity onCreate:
Intent startIntent = new Intent(MyActivity.this, ForegroundService.class);
startIntent.setAction(Constants.ACTION.STARTFOREGROUND_ACTION);
bindService(startIntent, _connection, Context.BIND_AUTO_CREATE);
startService(startIntent);
and in the service java file:
final Messenger _messenger = new Messenger(new IncomingHandler(new WeakReference<>(this)));
Messenger _activityMessenger = null;
private static class IncomingHandler extends Handler
{
WeakReference<ForegroundService> _parent;
IncomingHandler(WeakReference<ForegroundService> parent)
{
super();
_parent = parent;
}
#Override
public void handleMessage(Message msg) {
// received welcome message, now we know who to reply to
if(msg.what == ForegroundService.BIND_SERVICE)
{
_parent.get()._activityMessenger = msg.replyTo;
Log.d(LOG_TAG, "received reply address for messenger"); // after 1st edit
}
else
{
super.handleMessage(msg);
}
}
}
private void sendMessageToUI(int message, LocWithName newLoc)
{
Log.i(LOG_TAG, "Service: sending message to UI");
if(_activityMessenger != null)
{
Log.d(LOG_TAG, "messenger not null"); // after 1st edit
try
{
_activityMessenger.send(Message.obtain(null, message, newLoc));
Log.i(LOG_TAG, "Service: message sent");
}
catch(RemoteException ex)
{
// activity is dead
_activityMessenger = null;
}
}
}
then I start sending messages via the sendMessageToUI() function periodically from the service, namely every 5 seconds. The service's onStartCommand runs the first UI update immediately, which reschedules itself for every other iteration.
What I know:
the first immediate "UI update" in the service does run as logcat shows me the "sending message to UI" text at the correct time
all other updates run
all other updates deliver their messages successfully (which means that the first was stopped by _serviceMessenger being null, not the RemoteException, because the catch block would stop all later messages)
the welcome message from the activity to the service arrives as it is a necessity for further replies from the service
What I have tried:
in the activity, first bind then start the service (example code is in this state, initially it was the other way around), so _activityMessenger isn't null by the time it has to send first message
send a "burner message" so that is the one that doesn't get delivered instead of actually important messages
search google for similar problems to no avail - where there are problems, it doesn't work at all, not just the first time around
search this site for similar problems, same result as with google
Since there are five seconds between the first and the second message, I suspect it is an issue with the speed of initializing something, but I couldn't get further than that. So what exactly happens here and why doesn't it work?
EDIT 1: at the suggestion of #pskink, I added Log.d()-s. It turns out the activity only sends the "welcome message" with reply address after the first run of the UI updater despite being called earlier than startService.
Also, the code sending the messages, after #pskink asking:
in service class:
final Handler handler = new Handler();
Runnable updateUI = new Runnable()
{
// do work to get the information to display
// in this code I set "int message" to one of the constants handled by the activity's IncomingHandler and "LocWithName newLoc" to a useful value or null
sendMessageToUI(message, newLoc);
handler.removeCallbacks(updateUI);
handler.postDelayed(updateUI, 5000);
}
in service onStartCommand:
handler.post(updateUI);
Your mistake is assuming that the bindService() and startService() calls block until the service has been "bound" or "started", respectively. The reality is that onServiceConnected() won't get called until sometime after onCreate() returns. Likewise, the order you call them in is basically meaningless; the OS doesn't guarantee that the service will handle the binding or the onStartCommand() first or second, in this case.
To fix, delete the call to startService() (as #pskink suggested); the service is started by virtue of the fact that you are binding to it. onStartCommand() will no longer be called. Instead, have the Service kick off the updateUI Runnable when it gets the ForegroundService.BIND_SERVICE message. This allows you to establish the appropriate "happens before" relationships -- namely, that the ServiceConnection binding "happens before" you start trying to use _activityMessenger to send messages.
I want to show local notification in an Android app that i am working. notification are based on network transaction completed in background.
I have tried service, intent service, jobservice etc but nothing is working when the app is closed.please share some working code for the reference.....
//Here is my service----
public class JobSyncFAQ extends Service {
private Message msg;
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
public JobSyncFAQ() {
}
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
// here is my stuff
stopSelf(msg.arg1);
}
}
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("JobSyncFAQ", 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) {
msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
mServiceHandler.removeCallbacks(mServiceLooper.getThread());
mServiceLooper.quit();
Intent intent = new Intent(getApplicationContext(),JobSyncFAQ.class);
sendBroadcast(intent);
}
//and in my receiver
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context, JobSyncFAQ.class));
}
please note - i am facing this issue only for N+ version
When you "close" your app (i. e. you swipe it away on the recent apps screen), you don't actually close the app, you only close the underlying activity. In most cases, this corresponds to the app being closed, but if you have a background service running, the service is not terminated. This is for a good reason: YOu still want to receive WhatsApp messages, even when WhatsApp is closed. Hence, WhatsApp starts a background service to check for new messages, even when the app is closed.
For that reason, you need to notify your background service about the fact that it should cancel its task.
I developed a media application that uses a MediaBrowserService which is connected to a MediaController. Therefore, I can do the following in my activities onDestroyMethod:
MediaControllerCompat mediaControllerCompat = MediaControllerCompat.getMediaController(getActivity());
if (mediaControllerCompat != null)
mediaControllerCompat.getTransportControls().stop();
This tells the MediaBrowserService to stop and quit.
Edit: Since you use a generic service, you need to call stopService:
stopService(new Intent(MyActivity.this, JobSyncFAQ.class));
I'm trying to create a periodical service where I can validate certain values stored on my mobile with the data I have on my API Server. If a user's password gets changed or if the user gets deleted the API Server should send a response back so the mobile app knows the user should be signed out. The request/response is not the problem. Just having issues getting the service periodically.
MyService.class:
public class MyService extends IntentService {
private static final String TAG = "MyService";
public MyService() {
super("MyService");
}
#Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
try {
Thread.sleep(5000);
Log.d(TAG,"loop service");
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent, flags, startId);
}
}
Inside the onCreate of another random class:
Context mContext = getApplicationContext();
Intent i= new Intent(mContext, MyService.class);
i.putExtra("KEY1", "Value to be used by the service");
mContext.startService(i);
The service does start (it shows me the Log.d), how do I continue from here to get it restarted or to make it start after certain time?
First things first: IntentService is designed only to execute a queue of tasks off the main thread. So you must create a new one every time you want to check for updates: for that see Alarm Manager
But this is not necessarily the best way to do it. Instead of you polling the server for changes, try the push notifications approach.
I am used to developing standalone applications, ones that you click on, it runs, and when you are done, you exit.
I am now interested in tackling a new type (not sure if that's the right word) of app, and was wondering how I should go about it. I am not sure what to research, and would appreciate your advice to help me get the ball rolling. I'll give you an idea about what I have in mind.
My app would need to perform a special action in the dialer. When the user dials a number and is in the middle of a call, I would like the user to be able to press the Menu key, and find an option to scroll through all their contacts (either the stock app, or my own list which I grab from the contacts stored in the phone), and select one. Upon selection, that contact's number is pasted into the dialer (keep in mind, in the middle of a call).
I certainly don't expect an answer telling me how to do this exactly, I just need some guidance as I have never written an app of this nature before. On top of that, is it even possible to do what I want to do?
Thank you.
You need to go through Android Service or IntentService. A Service is an application component that can perform long-running operations in the background and does not provide a user interface(UI).
The following example is taken from android blog which is an implementation of the Service class
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// 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.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
// 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("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
#Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
On the other hand, The same thing can be achieved using IntentService, which is a base class for Services that handle asynchronous requests on demand.
public class HelloIntentService extends IntentService {
/**
* A constructor is required, and must call the super IntentService(String)
* constructor with a name for the worker thread.
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
#Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}
You can also go through SO post https://stackoverflow.com/a/4353653/432903
If your app isn't mainly written in javascript/webview/phonegap, then all you have to do is look at the Service class. That class and the linked documents tell you everything you need to know.
maybe you can use an IntentFilter so you can get a system notify when the user uses a dialer.
and you should learn the Service component which can work in background in android.
I've got two services running. They do their work and then reschedule themselves via the AlarmManager. In the BroadcastReceiver the only thing that happens is the launching of the service via Context.startService(). Both services are IntentServices, which as far as I can tell shouldn't be causing timeout problems. I've tried IntentServices, threading, and AsyncTasks but am repeatedly bumping up against the timeout error in the receivers themselves.
The timeout message is:
01-18 11:29:04.200: WARN/ActivityManager(73): Timeout of broadcast BroadcastRecord{433a4168 my.package.action.a} - receiver=android.os.BinderProxy#43399978
01-18 11:29:04.210: WARN/ActivityManager(73): Receiver during timeout: ResolveInfo{43394a30 my.package.MyReceiverA p=0 o=0 m=0x108000}
The basic structure of the two receivers:
public class MyReceiverA extends BroadcastReceiver {
public static final String ACTION_TO_BROADCAST = "my.package.action.a";
public void onReceive(Context context, Intent intent) {
// start the service
Intent serviceIntent = new Intent().setClassName(context,
MyServiceA.class.getName());
context.startService(serviceIntent);
}
}
And the services:
public class MyServiceA extends IntentService {
public ActivityMonitorService() {
super(TAG);
}
public IBinder onBind(Intent intent) {
// We don't allow anyone to bind to us
return null;
}
public void onCreate() {
super.onCreate();
_context = getApplicationContext();
_config = new Config();
if (_handler == null) {
_handler = new Handler();
}
}
/**
* Schedules an alarm to run ourselves again after ALARM_INTERVAL has passed.
*/
private void reschedule() {
Intent intent = new Intent(MyReceiverA.ACTION_TO_BROADCAST);
PendingIntent pendingIntent = PendingIntent.getBroadcast(_context, 0, intent, 0);
AlarmManager manager = (AlarmManager) _context.getSystemService(Context.ALARM_SERVICE);
manager.set(AlarmManager.RTC, now + delay, pendingIntent);
}
private void doWork() {
// Do some work. This could take a while. It also accesses a database that the two
// services share through synchronized blocks of code in static accessor functions.
}
protected void onHandleIntent(Intent intent) {
try {
doWork();
} catch (Exception e) {
// log it
} finally {
reschedule();
}
}
}
I figured out what was going on. Changing the two services to be a single one fixed the problem, meaning there was some sort of deadlock or race going on in the two. I'm assuming it's with their database access but haven't had a chance to verify it yet.
When changing to a single service the problem wasn't that the alarm was firing late, it's that the loaded down phone was pausing my service to give the music player the resources it needed. Looks like my options are to live with it or run the service in the foreground.