Solution 1 :
Android has newer and better solution which perfectly suits your need
The WorkManager API is a suitable and recommended replacement for all previous Android background scheduling APIs
you can check official topics here : work manager
Solution 2 :
You have two options:
-
Using WorkManager as stated in the above answers, but they may not be good if you want your task to be done at a very exact time. In some devices they work well, but in custom OS devices with battery optimizations they don’t work as you expect them to.
-
if you want to do a task one a specific time use an alarm manager + broadcast recevier + intent service, just remember that you have to start the service as foreground service.
Solution 3 :
Thanks to @Farid for the solution.
Help links:
Here is just the final piece of code:
-
Add dependency in build.gradle(app):
dependencies { ... def work_version = "2.4.0" implementation "androidx.work:work-runtime:$work_version" implementation "androidx.work:work-rxjava2:$work_version" androidTestImplementation "androidx.work:work-testing:$work_version" }
-
Write the worker (NewNewsNotification.java)
public class NewNewsNotification extends Worker { private static final String TAG="NewNewsNotification"; private static Context rCtx; private RequestQueue reQueue; private String NEWS_TYPE= "post"; private static final int EXEC_MIN= 15; AsyncJsonFetch mVolleyService; AsyncJsonData pResultCallback = null; //Context rCtx; public NewNewsNotification( @NonNull Context context, @NonNull WorkerParameters params) { super(context, params); rCtx= context; } public static void scheduleReminder() { Log.d(TAG, "queueing req"); //WorkRequest notificationWork = new PeriodicWorkRequest.Builder(NewNewsNotification.class, 24, TimeUnit.HOURS).build(); Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .setRequiresStorageNotLow(true) .build(); PeriodicWorkRequest notificationWork = new PeriodicWorkRequest.Builder(NewNewsNotification.class, EXEC_MIN, TimeUnit.MINUTES).addTag(TAG).setConstraints(constraints).build(); WorkManager instance = WorkManager.getInstance(rCtx); instance.enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.KEEP, notificationWork); } @NonNull @Override public Result doWork() { try { Log.d(TAG, "fetch_last_update called"); fetch_last_update(); return Result.success(); }catch (Throwable e) { e.printStackTrace(); Log.e(TAG, "Error fetching data", e); return Result.failure(); } } public void fetch_last_update() { postDetVolleyCallback(); mVolleyService = new AsyncJsonFetch(pResultCallback, rCtx); try { JSONObject sendObj = new JSONObject(); sendObj.put("lastdate", 1); sendObj.put("NEWS_TYPE", NEWS_TYPE); mVolleyService.newsDataVolley("POSTCALL", "news", sendObj); } catch (JSONException e) { e.printStackTrace(); } } public void postDetVolleyCallback() { pResultCallback = new AsyncJsonData() { @RequiresApi(api = Build.VERSION_CODES.KITKAT) @Override public void notifySuccess(String requestType, JSONObject response) throws JSONException { int stat = (int) response.get("status"); if (stat == 1) { /***********************************************************/ JSONObject msgJSON = (JSONObject) response.get("msg"); int ldate= Integer.parseInt(msgJSON.get("date").toString()); JSONArray tparray= (JSONArray) msgJSON.get("news"); JSONObject tnews= (JSONObject) tparray.get(0); String title= tnews.get("news_title").toString(); String excerpt= tnews.get("news_excerpt").toString(); int id= Integer.parseInt(tnews.get("ID").toString()); sendNotification(title, excerpt, id); } } @Override public void notifyError(String requestType, VolleyError error) { Log.d(TAG, "Volley requester " + requestType); Log.d(TAG, "Volley JSON post" + "That didn't work!"); } }; } @RequiresApi(api = Build.VERSION_CODES.KITKAT) private void sendNotification(String title, String text, int id) { String cl_url= rCtx.getString(R.string.client_link); Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(cl_url+"?p="+id)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0); NotificationManager notificationManager = (NotificationManager)getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel("default", "Default", NotificationManager.IMPORTANCE_DEFAULT); Objects.requireNonNull(notificationManager).createNotificationChannel(channel); } NotificationCompat.Builder notification = new NotificationCompat.Builder(getApplicationContext(), "default") .setContentTitle(title) .setContentText(text) .setContentIntent(pendingIntent) .setSmallIcon(R.mipmap.ic_launcher) .setAutoCancel(true); Objects.requireNonNull(notificationManager).notify(id, notification.build()); }
}
-
Call the Worker init in onCreate of MainActivity.java
NewPostNotification.scheduleReminder();
Thats it.
Problem :
As stated in the question, I want a background process to run from an app (daily at 21:30) which makes a volley request to the server and display a notification depending upon the result. On clicking the notification, a specific link is opened (handled by the app).
The server request and response from the class (through async Volley) is working perfectly. The link-handler is also set up.
I did a research and is confused about the class to use. It seems, I can use:
- Service OR
- BroadcastReceiver (with AlarmManager)
Using AlarmManager (with the receiver
tag added in the manifest), I have setup the following method called in the onCreate of MainActivity.java:
private void setAlarms()
{
AlarmManager alarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(this, NewNewsNotification.class);
PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 0, i, 0);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 30);
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
1000 * 60 * 60, alarmIntent);
if (alarmMgr!= null) {
alarmMgr.cancel(alarmIntent);
}
}
NewNewsNotification.java
@Override
public void onReceive(Context context, Intent intent) {
rCtx= context;
fetch_last_update();
}
public void fetch_last_update()
{
VolleyCallback();
VolleyService = new AsyncJsonFetch(ResultCallback, rCtx);
try {
JSONObject sendObj = new JSONObject();
mVolleyService.postDataVolley("POSTCALL", "news", sendObj);
} catch (JSONException e) {
e.printStackTrace();
}
}
public void VolleyCallback()
{
pResultCallback = new AsyncJsonData() {
@Override
public void notifySuccess(String requestType, JSONObject response) throws JSONException {
int stat = (int) response.get("status");
if (stat == 1) {
JSONObject msgJSON = (JSONObject) response.get("msg");
Log.d(TAG, "msgJSON: "+msgJSON);
/*The above log is working correctly. PROCESS THE JSON HERE AND GENERATE THE NOTIFICATION*/
}
}
@Override
public void notifyError(String requestType, VolleyError error) {
Log.d(TAG, "Volley requester " + requestType);
Log.d(TAG, "Volley JSON post" + "That didn't work!");
}
};
}
What is the correct way and how to implement it? How to initiate the clickable notification?
Comments
Comment posted by Martin S
Yes the WorkManager API is definitely what you are looking for. I’ve also implemented it in one of my projects and it works like a charm.
Comment posted by sariDon
Thank to you all. Starting at once and will keep you updated…
Comment posted by sariDon
Thank you. Can I set weekly 21:30 with alarm manager?
Comment posted by Keivan.k
yes you can just set it’s time for a week later. As simple as that. Just remember to have BOOT_COMPLETED_RECEIVER because when you restart your device, alarms are all cleared.
Comment posted by sariDon
Ok. Thanks. Will give it a try.
Comment posted by sariDon
The ‘alarm manager’ is not working when the app is closed or phone is locked. It works fine when the app is open.
Comment posted by Keivan.k
are you opening the app via android studio?