My app is targeting Android 7, with minimum SDK Android 4.
Hence, listening to CONNECTIVITY_CHANGE (Even when the app is killed) no longer work anymore. What I wish to do is
Even when my main app is killed, when the internet connectivity change from "not available" to "available", I would like to start an alarm broadcast receiver.
I try to achieve with the following code
MainActivity.java
#Override
public void onPause() {
super.onPause();
installJobService();
}
private void installJobService() {
// Create a new dispatcher using the Google Play driver.
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(this));
Job myJob = dispatcher.newJobBuilder()
// the JobService that will be called
.setService(MyJobService.class)
// uniquely identifies the job
.setTag("my-unique-tag")
// one-off job
.setRecurring(true)
// persist forever
.setLifetime(Lifetime.FOREVER)
// start between 0 and 60 seconds from now
.setTrigger(Trigger.executionWindow(0, 60))
// overwrite an existing job with the same tag
.setReplaceCurrent(true)
// retry with exponential backoff
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
// constraints that need to be satisfied for the job to run
.setConstraints(
// only run on any network
Constraint.ON_ANY_NETWORK
)
.build();
dispatcher.mustSchedule(myJob);
}
However,
MyJobService.java
import android.content.Context;
import com.firebase.jobdispatcher.JobParameters;
import com.firebase.jobdispatcher.JobService;
import org.yccheok.jstock.gui.JStockApplication;
/**
* Created by yccheok on 21/5/2017.
*/
public class MyJobService extends JobService {
#Override
public boolean onStartJob(JobParameters jobParameters) {
Context context = this.getApplicationContext();
android.util.Log.i("CHEOK", "Internet -> " + Utils.isInternetAvailable(context));
// Answers the question: "Is there still work going on?"
return false;
}
#Override
public boolean onStopJob(JobParameters jobParameters) {
// Answers the question: "Should this job be retried?"
return true;
}
}
However, the above code isn't reliable. How I test is
Quit my app.
Kill my app explicitly via Settings using "Force stop".
Turn off internet.
Turn on internet.
Wait for few minutes. MyJobService is never executed.
Is there any reliable way, to use FirebaseJobDispatcher to replace CONNECTIVITY_CHANGE reliably?
I had gone through Firebase JobDispatcher - how does it work compared to previous APIs (JobScheduler and GcmTaskService)? , but I still can't find a way to make it work reliably.
Not sure how you are detecting CONNECTIVITY_CHANGE through FirebaseJobDispatcher but for same situation I had used broadcast
public class ConnectivityStateReceiver extends BroadcastReceiver {
String TAG = "MyApp";
#Override
public void onReceive(Context context, Intent intent) {
Intent serviceIntent = new Intent(context, NetworkService.class);
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm == null) {
return;
} else if (cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected()) {
Log.e(TAG, "Connected!");
context.startService(serviceIntent);
} else {
Log.e(TAG, "Not Connected!");
context.stopService(serviceIntent);
}
}
}
Related
I have a firebase job dispatcher which is scheduled to run whenever network changes ,the job is working perfectly on a marshmallow device (23 API level) but the same code when run is not scheduling jobs on a oreo device(26 API level)
Here is my Job Service code:
public class MyJobService extends JobService {
#Override
public boolean onStartJob(JobParameters job) {
Log.d("Executing","Job");
Realm realm = Realm.getDefaultInstance();
RealmResults<JsontoSend> realmResults = realm.where(JsontoSend.class).findAll();
if(!realmResults.isEmpty()) {
for (JsontoSend jsontoSend : realmResults) {
final Intent intent = new Intent(getApplicationContext(), PostUploadIntentService.class);
intent.putExtra("object", jsontoSend.getJson());
new Thread(new Runnable() {
#Override
public void run() {
startService(intent);
}
}).run();
}
}
return true;
}
#Override
public boolean onStopJob(JobParameters job) {
Log.d("instopjob","cancelled");
return true;
}
}
This is my code where i have created the job :
synchronized public static void schedule(#NonNull final Context context){
if(sInitialized)
return;
Driver driver=new GooglePlayDriver(context);
FirebaseJobDispatcher dispatcher=new FirebaseJobDispatcher(driver);
Job myJob = dispatcher.newJobBuilder()
.setService(MyJobService.class)
.setTag(JOB_TAG)
.setRecurring(true)
.setTrigger(Trigger.executionWindow(5, 60))
.setLifetime(Lifetime.FOREVER)
.setConstraints(Constraint.ON_ANY_NETWORK)
.setReplaceCurrent(false)
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.build();
dispatcher.schedule(myJob);
sInitialized=true;
}
What i am trying to do is if i do not have internet connection then storing the data in local database and then running a job whenever i connect to internet and sync the data with server .The above code is working perfectly on marshmallow device but the job is never scheduled on oreo devices.
well the problem here is probably because you use GooglePlayDriver which relies on google play services to be presented on the devices. If the services are not available the job will not be scheduled.
So if you target on devices higher than 5.0 (which is true in most cases) you have to use android's JobScheduler build in since lollipop.
Possible Duplicate
I have created a JobDispatcher service to keep getting user location in background and post user location to server. For that i have created a Job as follow:
private void startLocationJobService() {
// Check if location job service already running
if(!isMyServiceRunning(LocationJobService.class)) {
Context context = config.getActivity();
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
Job job = dispatcher.newJobBuilder()
.setLifetime(Lifetime.FOREVER)
.setService(LocationJobService.class)
.setTag(JOB_SERVICE_TAG)
.setReplaceCurrent(true)
.build();
//creating new job and adding it with dispatcher
dispatcher.mustSchedule(job);
}
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) config.getActivity().getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
In service's onStartJob i am configuring location api to get user location
public boolean onStartJob(final JobParameters job) {
Log.i("TAG", "Service Run onStartJob called");
configureFusedLocation(LocationJobService.this);
return true;
}
Service starts and keep running as expected. But it does not stop. I have used both ways (dispatcher.cancel and stopService) to stop service as follow:
public void stopService() {
if(isMyServiceRunning(LocationJobService.class)) {
config.getActivity().stopService(new Intent(config.getActivity(), LocationJobService.class));
cancelLocationJobService();
}
}
private void cancelLocationJobService(){
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(config.getActivity()));
// Cancel the job for this tag
dispatcher.cancel(JOB_SERVICE_TAG);
//Cancel all the jobs for this package
dispatcher.cancelAll();
}
you need to cancel job in itself by unregistering location listener and calling jobFinished() inside LocationJobService.
As dispatcher.cancelAll() works only to cancel scheduled jobs which are yet to be run.
You may use tools like Eventbus to inform service when to stop.
Refer https://medium.com/google-developers/scheduling-jobs-like-a-pro-with-jobscheduler-286ef8510129 for more info.
I have a web service on my server that needs to be pinged every hour. For this, I am using an Android app to ping it every hour. I have tried using Alarm manager but it stops working after few hours and if I swipe exit it. I have tried using service but for some reason, that doesn't seem to work and my app keeps crashing. I have am thinking about using Firebase Job dispatcher. My requirement is that the app needs to ping the web service on my server every hour. This should go on for at least next 3-4 months. Is there a way to accomplish this ? Thanks in advance!
EDIT: I have tried broadcast receiver with Alarm Manager but have not been able to sustain the firing for more then 4 hours.
I second Anantha's answer but seems like job parameters are little off for your needs.
You can go over this article to learn about the subtle differences between various Job schedulers.
As a matter of fact, even Google recommends using Firebase Job Schedular if the app needs to do a network communication due to various reasons. Please watch the attached video on the Github page for more info on the same. This also gives you basic code to kickstart your application. You can just change the job parameters to suit your needs
Hopefully, this below code should suit your requirement of triggering every one hour with a tolerance of 15 minutes
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(getContext()));
final int periodicity = (int)TimeUnit.HOURS.toSeconds(1); // Every 1 hour periodicity expressed as seconds
final int toleranceInterval = (int)TimeUnit.MINUTES.toSeconds(15); // a small(ish) window of time when triggering is OK
Job myJob = dispatcher.newJobBuilder()
// the JobService that will be called
.setService(yourJobService.class)
// uniquely identifies the job
.setTag("my-unique-tag")
// recurring job
.setRecurring(true)
// persist past a device reboot
.setLifetime(Lifetime.FOREVER)
// start between 0 and 60 seconds from now
.setTrigger(Trigger.executionWindow(periodicity, toleranceInterval))
// overwrite an existing job with the same tag
.setReplaceCurrent(true)
// retry with exponential backoff
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
// constraints that need to be satisfied for the job to run
.setConstraints(
// only run on an unmetered network
Constraint.ON_ANY_NETWORK
)
.setExtras(schedulerextras)
.build();
dispatcher.mustSchedule(myJob);
Jhon you can use firebase jobdispatcher. because it will support from api level 9. you can see below how to create job dispatcher and how to call it.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
scheduleJob(this);
}
public static void scheduleJob(Context context) {
//creating new firebase job dispatcher
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
//creating new job and adding it with dispatcher
Job job = createJob(dispatcher);
dispatcher.mustSchedule(job);
}
public static Job createJob(FirebaseJobDispatcher dispatcher){
Job job = dispatcher.newJobBuilder()
//persist the task across boots
.setLifetime(Lifetime.FOREVER)
//.setLifetime(Lifetime.UNTIL_NEXT_BOOT)
//call this service when the criteria are met.
.setService(ScheduledJobService.class)
//unique id of the task
.setTag("UniqueTagForYourJob")
//don't overwrite an existing job with the same tag
.setReplaceCurrent(false)
// We are mentioning that the job is periodic.
.setRecurring(true)
// Run between 30 - 60 seconds from now.
.setTrigger(Trigger.executionWindow(30, 60))
// retry with exponential backoff
.setRetryStrategy(RetryStrategy.DEFAULT_LINEAR)
//.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
//Run this job only when the network is available.
.setConstraints(Constraint.ON_ANY_NETWORK, Constraint.DEVICE_CHARGING)
.build();
return job;
}
public static Job updateJob(FirebaseJobDispatcher dispatcher) {
Job newJob = dispatcher.newJobBuilder()
//update if any task with the given tag exists.
.setReplaceCurrent(true)
//Integrate the job you want to start.
.setService(ScheduledJobService.class)
.setTag("UniqueTagForYourJob")
// Run between 30 - 60 seconds from now.
.setTrigger(Trigger.executionWindow(30, 60))
.build();
return newJob;
}
public void cancelJob(Context context){
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
//Cancel all the jobs for this package
dispatcher.cancelAll();
// Cancel the job for this tag
dispatcher.cancel("UniqueTagForYourJob");
}}
ScheduledJobService.java
public class ScheduledJobService extends JobService {
private static final String TAG = ScheduledJobService.class.getSimpleName();
#Override
public boolean onStartJob(final JobParameters params) {
//Offloading work to a new thread.
new Thread(new Runnable() {
#Override
public void run() {
codeYouWantToRun(params);
}
}).start();
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
return false;
}
public void codeYouWantToRun(final JobParameters parameters) {
try {
Log.d(TAG, "completeJob: " + "jobStarted");
//This task takes 2 seconds to complete.
Thread.sleep(2000);
Log.d(TAG, "completeJob: " + "jobFinished");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//Tell the framework that the job has completed and doesnot needs to be reschedule
jobFinished(parameters, true);
}
}}
You will need to use JobScheduler(api >21 ) and GcmNetworkManager (api<21) depending on the api level of android. Check out this library from evernote which takes care of it.
Do you try broadcast receiver? I use Broadcast Receiver with Alarm Manager to vibrate every minute and it work fine. The only problem is that when device turn off or restarted, it not vibrate till I enter my application.
My test code.
public void setAlarm() {
alarmMgr =(AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(AlarmManagerActivity.this, AlarmManagerBroadcastReceiver.class);
intent.setAction("a.b.c.d");
PendingIntent pi = PendingIntent.getBroadcast( getApplicationContext(), 0, intent, 0);
//After after 5 seconds
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 13);
calendar.set(Calendar.MINUTE, 40);
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis()
, (1000) * (60)
, pi);
}
My receiver
#Override
public void onReceive(Context context, Intent intent) {
Log.d(getClass().getSimpleName(), Intent.ACTION_BOOT_COMPLETED);
if ( intent.getAction().equals("a.b.c.d")) {
Log.d(getClass().getSimpleName(), "Custom Broadcast01");
Vibrator vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(10000);
}
else
Log.d(getClass().getSimpleName(), "no this action for intent!");
}
Broadcast receiver to start Alarm when device restart
<receiver
android:name=".OnBootBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class OnBootBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
setAlarm();
}
}
First off: I know that ConnectivityManager.CONNECTIVITY_ACTION has been deprecated and I know how to use connectivityManager.registerNetworkCallback. Furthermore if read about JobScheduler, but I am not entirely sure whether I got it right.
My problem is that I want to execute some code when the phone is connected / disconnected to/from a network. This should happen when the app is in the background as well. Starting with Android O I would have to show a notification if I want to run a service in the background what I want to avoid. I tried getting info on when the phone connects / disconnects using the JobScheduler/JobService APIs, but it only gets executed the time I schedule it. For me it seems like I can't run code when an event like this happens. Is there any way to achieve this? Do I maybe just have to adjust my code a bit?
My JobService:
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class ConnectivityBackgroundServiceAPI21 extends JobService {
#Override
public boolean onStartJob(JobParameters jobParameters) {
LogFactory.writeMessage(this, LOG_TAG, "Job was started");
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
if (activeNetwork == null) {
LogFactory.writeMessage(this, LOG_TAG, "No active network.");
}else{
// Here is some logic consuming whether the device is connected to a network (and to which type)
}
LogFactory.writeMessage(this, LOG_TAG, "Job is done. ");
return false;
}
}
#Override
public boolean onStopJob(JobParameters jobParameters) {
LogFactory.writeMessage(this, LOG_TAG, "Job was stopped");
return true;
}
I start the service like so:
JobScheduler jobScheduler = (JobScheduler)context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
ComponentName service = new ComponentName(context, ConnectivityBackgroundServiceAPI21.class);
JobInfo.Builder builder = new JobInfo.Builder(1, service).setPersisted(true)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).setRequiresCharging(false);
jobScheduler.schedule(builder.build());
jobScheduler.schedule(builder.build()); //It runs when I call this - but doesn't re-run if the network changes
Manifest (Abstract):
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="android.permission.VIBRATE" />
<application>
<service
android:name=".services.ConnectivityBackgroundServiceAPI21"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
</application>
</manifest>
I guess there has to be an easy solution to this but I am not able to find it.
Second edit: I'm now using firebase's JobDispatcher and it works perfect across all platforms (thanks #cutiko). This is the basic structure:
public class ConnectivityJob extends JobService{
#Override
public boolean onStartJob(JobParameters job) {
LogFactory.writeMessage(this, LOG_TAG, "Job created");
connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
connectivityManager.registerNetworkCallback(new NetworkRequest.Builder().build(), networkCallback = new ConnectivityManager.NetworkCallback(){
// -Snip-
});
}else{
registerReceiver(connectivityChange = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
handleConnectivityChange(!intent.hasExtra("noConnectivity"), intent.getIntExtra("networkType", -1));
}
}, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
if (activeNetwork == null) {
LogFactory.writeMessage(this, LOG_TAG, "No active network.");
}else{
// Some logic..
}
LogFactory.writeMessage(this, LOG_TAG, "Done with onStartJob");
return true;
}
#Override
public boolean onStopJob(JobParameters job) {
if(networkCallback != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)connectivityManager.unregisterNetworkCallback(networkCallback);
else if(connectivityChange != null)unregisterReceiver(connectivityChange);
return true;
}
private void handleConnectivityChange(NetworkInfo networkInfo){
// Calls handleConnectivityChange(boolean connected, int type)
}
private void handleConnectivityChange(boolean connected, int type){
// Calls handleConnectivityChange(boolean connected, ConnectionType connectionType)
}
private void handleConnectivityChange(boolean connected, ConnectionType connectionType){
// Logic based on the new connection
}
private enum ConnectionType{
MOBILE,WIFI,VPN,OTHER;
}
}
I call it like so (in my boot receiver):
Job job = dispatcher.newJobBuilder()
.setService(ConnectivityJob.class)
.setTag("connectivity-job")
.setLifetime(Lifetime.FOREVER)
.setRetryStrategy(RetryStrategy.DEFAULT_LINEAR)
.setRecurring(true)
.setReplaceCurrent(true)
.setTrigger(Trigger.executionWindow(0, 0))
.build();
Edit: I found a hacky way. Really hacky to say. It works but I wouldn't use it:
Start a foreground service, go into foreground mode using startForeground(id, notification) and use stopForeground after that, the user won't see the notification but Android registers it as having been in the foreground
Start a second service, using startService
Stop the first service
Result: Congratulations, you have a service running in the background (the one you started second).
onTaskRemoved get's called on the second service when you open the app and it is cleared from RAM, but not when the first service terminates. If you have an repeating action like a handler and don't unregister it in onTaskRemoved it continues to run.
Effectively this starts a foreground service, which starts a background service and then terminates. The second service outlives the first one. I'm not sure whether this is intended behavior or not (maybe a bug report should be filed?) but it's a workaround (again, a bad one!).
Looks like it's not possible to get notified when the connection changes:
With Android 7.0 CONNECTIVITY_ACTION receivers declared in the manifest won't receive broadcasts. Additionally receivers declared programmatically only receive the broadcasts if the receiver was registered on the main thread (so using a service won't work). If you still want to receive updates in the background you can use connectivityManager.registerNetworkCallback
With Android 8.0 the same restrictions are in place but in addition you can't launch services from the background unless it's a foreground service.
All of this allows these solutions:
Start a foreground service and show a notification
This most likely bothers users
Use JobService and schedule it to run periodically
Depending on your setting it takes some time until the service get's called thus a few seconds could have passed since the connection changes. All in all this delays the action which should happen on connection change
This is not possible:
connectivityManager.registerNetworkCallback(NetworkInfo, PendingIntent) cannot be used because the PendingIntent executes it's action instantly if the condition is met; it's only called once
Trying to start a foreground service this way which goes to the foreground for 1 ms and re-registers the call results in something comparable to infinite recursion
Since I already have a VPNService which runs in foreground mode (and thus shows a notification) I implemented that the Service to check connectivity runs in foreground if the VPNService doesn't and vice-versa. This always shows a notification, but only one. All in all I find the 8.0 update extremely unsatisfying, it seems like I either have to use unreliable solutions (repeating JobService) or interrupt my users with a permanent notification. Users should be able to whitelist apps (The fact alone that there are apps which hide the "XX is running in background" notification should say enough). So much for off-topic
Feel free to expand my solution or show me any errors, but these are my findings.
i've have to add some modifications to Ch4t4r jobservice and work for me
public class JobServicio extends JobService {
String LOG_TAG ="EPA";
ConnectivityManager.NetworkCallback networkCallback;
BroadcastReceiver connectivityChange;
ConnectivityManager connectivityManager;
#Override
public boolean onStartJob(JobParameters job) {
Log.i(LOG_TAG, "Job created");
connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
connectivityManager.registerNetworkCallback(new NetworkRequest.Builder().build(), networkCallback = new ConnectivityManager.NetworkCallback(){
// -Snip-
});
}else{
registerReceiver(connectivityChange = new BroadcastReceiver() { //this is not necesary if you declare the receiver in manifest and you using android <=6.0.1
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "recepcion", Toast.LENGTH_SHORT).show();
handleConnectivityChange(!intent.hasExtra("noConnectivity"), intent.getIntExtra("networkType", -1));
}
}, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
Log.i(LOG_TAG, "Done with onStartJob");
return true;
}
#Override
public boolean onStopJob(JobParameters job) {
if(networkCallback != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)connectivityManager.unregisterNetworkCallback(networkCallback);
else if(connectivityChange != null)unregisterReceiver(connectivityChange);
return true;
}
private void handleConnectivityChange(NetworkInfo networkInfo){
// Calls handleConnectivityChange(boolean connected, int type)
}
private void handleConnectivityChange(boolean connected, int type){
// Calls handleConnectivityChange(boolean connected, ConnectionType connectionType)
Toast.makeText(this, "erga", Toast.LENGTH_SHORT).show();
}
private void handleConnectivityChange(boolean connected, ConnectionType connectionType){
// Logic based on the new connection
}
private enum ConnectionType{
MOBILE,WIFI,VPN,OTHER;
}
ConnectivityManager.NetworkCallback x = new ConnectivityManager.NetworkCallback() { //this networkcallback work wonderfull
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#Override
public void onAvailable(Network network) {
Log.d(TAG, "requestNetwork onAvailable()");
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
//do something
}
else {
//This method was deprecated in API level 23
ConnectivityManager.setProcessDefaultNetwork(network);
}
}
#Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
Log.d(TAG, ""+network+"|"+networkCapabilities);
}
#Override
public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
Log.d(TAG, "requestNetwork onLinkPropertiesChanged()");
}
#Override
public void onLosing(Network network, int maxMsToLive) {
Log.d(TAG, "requestNetwork onLosing()");
}
#Override
public void onLost(Network network) {
}
}
}
and you can call the jobservice from another normal service or boot_complete receiver
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
Job job = dispatcher.newJobBuilder()
.setService(Updater.class)
.setTag("connectivity-job")
.setLifetime(Lifetime.FOREVER)
.setRetryStrategy(RetryStrategy.DEFAULT_LINEAR)
.setRecurring(true)
.setReplaceCurrent(true)
.setTrigger(Trigger.executionWindow(0, 0))
.build();
dispatcher.mustSchedule(job);
}
in manifest...
<service
android:name=".Updater"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"/>
i want to create something like a reminder app which notify user at given times
, i want to use job scheduler api to achieve this let's say i want to run the service at 9 am and 12 am what should be added in the following code to achieve this.
public void startJobService(){
GooglePlayDriver driver = new GooglePlayDriver(this);
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(driver);
Bundle myExtrasBundle = new Bundle();
myExtrasBundle.putString("some_key", "some_value");
Job myJob = dispatcher.newJobBuilder()
.setService(jobservice.class)
.setTag("unique-tag")
.setExtras(myExtrasBundle)
.build();
dispatcher.mustSchedule(myJob);
}
//this is the JobService class
public class jobservice extends JobService{
private static final String TAG = "jobservice";
#Override
public boolean onStartJob(JobParameters job) {
Log.d(TAG, "onStartJob: "+job.getExtras());
return true;
}
#Override
public boolean onStopJob(JobParameters job) {
return false;
}
}
Unfortunately it seems that the JobService does not provide this api. Two things you can do:
Use Alarm Manager to trigger the job service when you want or
Run you job service frequent (let's say once per hour). Then check if the current time is on your desired interval. If yes proceed, if no abort the job.
For example:
final Job job = dispatcher.newJobBuilder()
.setService(MyService.class)
.setTag(MyService.class.getName())
.setRecurring(true)
.setLifetime(Lifetime.FOREVER)
.setTrigger(Trigger.executionWindow(HOUR, 2 * HOUR))
.setReplaceCurrent(false)
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.build();
And then inside your Job Service:
#Override
public boolean onStartJob(JobParameters job) {
if (!nowIsInDesiredInterval()) {
Timber.i("Now is not inside the desired interval. Aborting.");
return false;
}
// else execute your job
// .....
return false;
}