Optimal way for a daily network operation - java

I am kinda confused at the moment. What is the "correct" / "optimal" way for a daily network operation in an android app?
Pseudocode:
If newDay
HTTP Request to server
If responseOfRequest equals something
Do something
If HTTP Request is unsuccessfull (no internet, server down, ...)
Try again in 1 hour
How can I achieve that? I thought about a JobService but my minSDK is below Android 5.
Cheers,
DDerTyp

What you need is a service to run the logic in the background and an alarm.
A little bit of theory first:
https://developer.android.com/training/scheduling/alarms.html#tradeoffs
A repeating alarm is a relatively simple mechanism with limited flexibility. It may not be the best choice for your app, particularly if you need to trigger network operations. A poorly designed alarm can cause battery drain and put a significant load on servers.
If you own the server that is hosting your app's data, using Google Cloud Messaging (GCM) in conjunction with sync adapter is a better solution than AlarmManager.
https://developer.android.com/training/sync-adapters/running-sync-adapter.html
By default, all alarms are canceled when a device shuts down.
You will need to set up the alarm somewhere in your app, at the beginning, but saving a flag because you don't want to set up this alarm every time the user opens the app
if (!appSettings.isAlarmSetUp()) {
final AlarmManager am = (AlarmManager) context.getSystemService(ALARM_SERVICE);
final Intent i = new Intent(context, CustomService.class);
final Intent intentNotRepeat = new Intent(context, CustomService.class);
final PendingIntent pi = PendingIntent.getService(context, 0, i, 0);
am.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + AlarmManager.INTERVAL_HALF_HOUR, AlarmManager.INTERVAL_DAY, pi);
appSettings.setAlarmSetUp(true);
}
Here more info about alarms:
https://developer.android.com/training/scheduling/alarms.html#type
As you can see, this alarm is waking up a CustomService, where you will do all your logic
public class CustomService extends IntentService {
public CustomService(String name) {
super(name);
}
#Override
protected void onHandleIntent(Intent intent) {
// Request to server
client.requestToServer()
.subscribe(response -> {
// Successful response
doSomething(response);
}
},
error -> {
// Error
createAlarmInOneHour();
});
}
}

Related

doWork() method of workmanager is not getting called from alarm manager receiver

I am using work manager Onetime Request to perform the task. First the alarm manager gets call in given frequency and from alarm manager receiver class, work manager class gets called. All code was working fine, but suddenly the work manager doWork is not getting called. Flow is coming till alarm manager receiver but not proceeding further.
Alarm Manager Receiver class
public class WorkAlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
final String TAG = "WorkAlarmReceiver";
if (BuildConfig.DEBUG) {
Log.i(TAG, "onReceive: intent=" + intent);
}
OneTimeWorkRequest startWork = new OneTimeWorkRequest.Builder(WorkManagerForTools.class).build();
WorkManager.getInstance(context).enqueueUniqueWork("Validate Tool", ExistingWorkPolicy.APPEND_OR_REPLACE , startWork);
}
This Receiver is calling fine in given time interval
//checking if alarm is working with pendingIntent
Intent workIntent = new Intent(mContext,
WorkAlarmReceiver.class)
.setAction(Long.toString(System.currentTimeMillis()
PendingIntent workPendingIntent = PendingIntent.getBroadcast(mContext, 1001,
workIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Log.e(TAG, "New data of pending intent: " + workSpecId + " local work id, " + localWorkId +
" tools id list " + updatedMobileAppToolIds);
if(BuildConfig.DEBUG) {
Log.d(TAG, "New data of pending intent: " + workSpecId + " local work id, " + localWorkId +
" tools id list " + updatedMobileAppToolIds);
}
boolean isWorking = (PendingIntent.getBroadcast(mContext, 1001, workIntent, PendingIntent.FLAG_NO_CREATE) != null);//just changed the flag
if (isWorking) {
alarmManager.cancel(workPendingIntent);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
10 * (minFrequency / 10), workPendingIntent);
} else {
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
10 * (minFrequency / 10), workPendingIntent);
}
The alarm manager receiver class then call workmanager class. Here is the work manager class
#SuppressLint("WrongThread")
#NonNull
#Override
public Result doWork() {
// Code to be execute
}
Any help on this Appreciated.
You can achieve scheduled tasks using periodic work requests. Here's how you can do it:
Define constraints like if device should be connected or should have sufficient battery.
Now create a periodic request with constraints & if required define a tag name. Also set a delay this which will set a initial delay for your work to start.
And create a enqueueUniquePeriodicWork by providing unique work name & the ExistingPeriodicWorkPolicy which you can read here ExistingPeriodicWorkPolicy
Enum values
By default periodic work will take 15 minutes to execute the work request.
Constraints constraints = new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).setRequiresBatteryNotLow(true).build();
PeriodicWorkRequest periodicRequest = new PeriodicWorkRequest.Builder(MyWorkerClass.class, 15, TimeUnit.MINUTES)
.addTag("myPeriodicRequest")
.setConstraints(constraints)
.setInitialDelay(5, TimeUnit.MINUTES)
.build();
WorkManager.getInstance(context).enqueueUniquePeriodicWork("myPeriodicRequest", ExistingPeriodicWorkPolicy.APPEND_OR_REPLACE, periodicRequest);
You are missing the point on how the WorkManager should be used. You can be exact with AalarmManager, but then you pass your execution to something inexact - the WorkManager. So no point in using the AlarmManager at all. Just use the WorkManager directly with either Periodic Request or maybe some Trigger. You can check more here in my answer comparing the different APIs.
Background processing in modern Android

How to send local notification everyday at specific time in Android (API > 26)

I searched Stackoverflow and Google but no answers.
I can't find how to send notifications at specific time that occur everyday. Below API level of 26, it's not a problem. How can I do it in API>26?
I know that i need to create a channel to create notifications in API>26 but how to set it to repeat everyday?
From API 19 onwards, alarm delivery is inexact (the OS will shift alarms in order to minimize wakeups and battery use). These are new APIs which provide strict delivery guarantees:
see setWindow(int, long, long, android.app.PendingIntent)
setExact(int, long, android.app.PendingIntent)
So, we can use setExact:
public void setExact (int type,
long triggerAtMillis,
PendingIntent operation)
setExact can Schedule an alarm to be delivered precisely at the stated time.
This method is like set(int, long, android.app.PendingIntent), but does not permit the OS to adjust the delivery time. The alarm will be delivered as nearly as possible to the requested trigger time.
First, use setExact like this:
void scheduleAlarm(Context context) {
AlarmManager alarmmanager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent yourIntent = new Intent();
// configure your intent here
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, MyApplication.ALARM_REQUEST_CODE, yourIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmmanager.setExact(AlarmManager.RTC_WAKEUP, timeToWakeUp, alarmIntent);
}
Now, schedule the next occurrence (for making repeat) in the
onReceive of your BroadcastReceiver like below:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// process alarm here
scheduleAlarm(context);
}
}

Fix the 'background service' problem on Android 8+

I have code running a service behind the scenes. It is set to run when we copy the text to the phone.
This code works fine on Android 8 below
But the problem is when I run the app on Android 8 and above
In my searches, I realized that I had to use FOREGROUND_SERVICEs and give specific access to the project.
What solutions do you suggest now?
Service Class:
public class AutoDownloadService extends Service {
private ClipboardManager mClipboardManager;
public static final String CHANNEL_ID = "ForegroundServiceChannel";
#Override
public void onCreate() {
super.onCreate();
mClipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
mClipboardManager.addPrimaryClipChangedListener(mOnPrimaryClipChangedListener);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Intent notificationIntent = new Intent(this, SettingsActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
// stopSelf();
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
if (mClipboardManager != null) {
mClipboardManager.removePrimaryClipChangedListener(mOnPrimaryClipChangedListener);
}
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
private ClipboardManager.OnPrimaryClipChangedListener mOnPrimaryClipChangedListener =
new ClipboardManager.OnPrimaryClipChangedListener() {
#Override
public void onPrimaryClipChanged() {
ClipData clip = mClipboardManager.getPrimaryClip();
String textClipBoard = clip.getItemAt(0).getText().toString();
Toast.makeText(AutoDownloadService.this, textClipBoard, Toast.LENGTH_SHORT).show();
}
};
}
Manifest
<service
android:name=".services.AutoDownloadService"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" />
and add finally uses permission
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
I think you should use Intent Service instead of service.
what you can do is if system shutdown your service you can again trigger it after sometime using alarm manger.
As stated in documentation
While an app is in the foreground, it can create and run both
foreground and background services freely. When an app goes into the
background, it has a window of several minutes in which it is still
allowed to create and use services. At the end of that window, the app
is considered to be idle. At this time, the system stops the app's
background services, just as if the app had called the services'
Service.stopSelf() methods.
So, you solution is to run foreground service on Android >= 8.0 and do something like this
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(Intent(this, AutoDownloadService.class))
} else {
startService(Intent(this, AutoDownloadService.class))
}
}
First of all, you should not do this.
Monitoring clipboard in background is not something right.
Android 8 added some protection on this, so you should run as foreground services, to let the end user aware your app is monitoring the clipboard.
Anyway clipboard access only available to default IME from Android 10. So, your apps will not work in Android 10.
This example of my code is currently working fine but I have problems with Chinese mobile
Tested on mobile: Xiaomi 7
public class AutoDownloadService extends IntentService {
private ClipboardManager mClipboardManager;
public AutoDownloadService() {
super("AutoDownloadService");
}
#Override
public void onCreate() {
super.onCreate();
mClipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
mClipboardManager.addPrimaryClipChangedListener(mOnPrimaryClipChangedListener);
}
#Override
protected void onHandleIntent(Intent intent) {
Toast.makeText(this, " service started.", Toast.LENGTH_SHORT).show();
}
private final ClipboardManager.OnPrimaryClipChangedListener mOnPrimaryClipChangedListener =
new ClipboardManager.OnPrimaryClipChangedListener() {
#Override
public void onPrimaryClipChanged() {
ClipData clip = mClipboardManager.getPrimaryClip();
String textClipBoard = clip.getItemAt(0).getText().toString();
if (textClipBoard.startsWith("https://www.instagram.com/")) {
Toast.makeText(AutoDownloadService.this, textClipBoard, Toast.LENGTH_SHORT).show();
}
}
};
}
And in the file Manifast.xml This is how it is defined
<service android:name="com.amirhf.inatasave.services.AutoDownloadService" />
When you swipe the app from recent apps, few Chinese OEMs force stop the apps.
Once the App is force stopped, you can't post notifications, start services, receive broadcasts etc.
The only work around is : Your app should be whitelisted, added in
auto start list. Apps like WhatsApp, Facebook etc are already added by
the OEMs in the list.
This blog talks about similar problem of not receiving notifications when App is force stopped. https://hackernoon.com/notifications-in-android-are-horribly-broken-b8dbec63f48a
You can take a similar approach where you can have a "Troubleshoot" section where you can educate the user to add your app in auto start list.
If by any chance your app is very popular you can get in touch with
Chinese Manufacturer and request them to get your app whitelisted but
they do it for very popular apps. For example in my experience
Microsoft and Hike Messenger got it done for their apps.
I didn't quite understand if you're messing with file download or anything else. But I guess you're not going the right way. So here's what I may share.
From https://developer.android.com/training/scheduling/wakelock
If your app is performing long-running HTTP downloads, consider using DownloadManager.
If your app is synchronizing data from an external server, consider creating a sync adapter.
If your app relies on background services, consider using JobScheduler or Firebase Cloud Messaging to trigger these services
at specific intervals.
Also note that if you just have a task that has to done often, use JobIntentService. Its compatible with Oreo and versions below it:
Helper for processing work that has been enqueued for a job/service.
When running on Android O or later, the work will be dispatched as a
job via JobScheduler.enqueue. When running on older versions of the
platform, it will use Context.startService.
On Oreo and above versions there are limitations helping the device save resources (battery, ram...) and even when using JobIntentService you must consider them; otherwise your app may be recognized as Battery Draining App.
If what you're about to do is heavy and is important enough to be shown in notification bar, do it using ForegroundService. So that it will be taken more seriously by android system and chances of it being killed gets fewer.
Try using WorkManager it's a JetPack Library.
Advantages:
Ensures task execution, even if the app or device restarts (Guaranteed Execution)
You don’t need to write device logic to figure out what capabilities the device has and choose an appropriate API; instead, you can just hand your task off to WorkManager and let it choose the best option. It is a wrapper on all the above concepts.
Uses JobScheduler on devices with API 23+
Uses a combination of BroadcastReceiver + AlarmManager on devices with API 14-22
Ref : WorkManager Docs
Ref : Medium Article
Ref : Medium Article(1)
[Update] - stable version is out WorkManager

System.exit(0) in Android Wear Watchface?

I've read that using System.exit(0) is frowned upon when it comes to Java and Android, but so far I can find no alternative for what I'm trying to accomplish. To be clear, this is for a Watchface, which is simply a service extending CanvasWatchFaceService. I cannot call finish() in this case. I've also tried stopService and startService with no success.
The issue I'm trying to solve: It's well known that changing timezones on your device will not be reflected on a watchface unless it is restarted. In my testing, I found that System.currentTimeMillis() quite literally does not respond to timezone changes in Android Wear. The watchface must be restarted in order for it to show the correct time after a timezone change.
So I built a system with the following code:
private final BroadcastReceiver timeChangeReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (!restarting) {
if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
if (upSeconds >= 15) {
System.exit(0);
} else {
restarting = true;
int delay = ((15 - upSeconds) * 1000);
new CountDownTimer(delay, delay) {
#Override
public void onTick(long millisUntilFinished) { }
#Override
public void onFinish() {
System.exit(0);
}
}.start();
}
}
}
}
};
The delay is in case a user triggers a time zone change more frequently than 15 seconds at a time. Android Wear seems to detect system exits that are too frequent and replace the watchface with the "Simple" watchface.
It seems to work great, Android Wear automatically boots the watchface back up on exit. But I would eventually like to put this app on the Google Play Store, so I thought I should make sure I'm not playing with fire here.
I can't believe I went through all that work when the proper solution was so simple. Thanks ianhanniballake for the link!
After looking at the Analog Watchface Sample, I found that all I needed to do was use mCalendar.setTimeZone(TimeZone.getDefault());. In many places I was directly comparing the time in milliseconds fetched with long now = System.currentTimeMillis();, so I simply did a now = mCalendar.getTimeInMillis() to take care of that.
Now the watchface changes time properly when the timezone is changed. I guess the other watchfaces I downloaded did not properly handle this!

getExtra crashing the app in Service

I search a lot and I tried several ways but I couldn't find anything that avoid my error.
I'm working on app for my learning, where is an String in my MainActivity and then I call it in my Service. I tried something like this:
This next one goes in my myService.class
//In my myService.class that extends Service
public class myService extends Service{
AlarmManager manager;
PendingIntent pintent;
String te;
#Override
public void onCreate()
{
manager = (AlarmManager)(this.getSystemService(Context.ALARM_SERVICE));
pintent = PendingIntent.getBroadcast( this, 0, new Intent("blahblah"), 0 );}
#Override
public int onStartCommand(Intent intent, int flags, int startid)
{
super.onStartCommand(intent, flags, startid);
te = intent.getStringExtra("tst"); //if I change this line to te = "something", everything goes fine
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive( Context context, Intent intent )
{
Toast.makeText(getApplicationContext(),te, Toast.LENGTH_SHORT).show();
}
};
this.registerReceiver(receiver, new IntentFilter("blahblah") );
// set alarm to fire 30 min and waits 2 min
manager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 1000*30*60, 1000*2*60, pintent);
return START_STICKY;}
public IBinder onBind(Intent p1)
{
// TODO: Implement this method
return null;
}
}
Here the code runs perfectly, but when I exit my App, it crashes. After 1 minute, my device shows again that my App crashes, confirming my app "successfully" ran into background. What is wrong with it?
I also learned I could use IntentService instead of Service, wich one is better for long tasks and what is the difference between them ?
EDIT***
I received the following error: java.lang.NullPointerExeption.
So I change this:
te = intent.getStringExtra("tst");
To this:
try
{
te = intent.getStringExtra("tst");
}
catch(NullPointerException e)
{}
When I changed it my app works with any error, but The problem is: I need to retrieve my String from my MainActivity, when I close my app my service runs without errors but my "te" String takes null valor, what can I do to "save" my String in my service, to be able to use it and keeping showing the "working" message after I close my App ? Should I use SharedPreferences ?
Hope I was clear.
IntentService is different from Service.
IntentService Self kills the service as it finishes the task. Whereas Service runs for ever unless you kill it.
For my coding experience, I would use IntentService only for small tasks that run for a couple of seconds and use Service for long running one and call StopSelf() as needed.
Kindly post the log to answer your problem

Categories

Resources