In my app, I am using JobScheduler for running a task in a period of time with setPeriodic function.
How can I call a method in Fragment from the Scheduler Service?
WeeklyJobService.java
#Override
public boolean onStartJob(JobParameters params) {
doBackgroundWork(params);
return true;
}
private void doBackgroundWork(JobParameters params) {
// Here I want to call a method in Fragment
jobFinished(params, false);
}).start();
}
TopWeekFragment.java
For example let the function be adding two numbers and setting the value in a TextView
private void sum()
{
int i=22, j=10;
textview.setText(String.valueOf(i+j));
}
JobSchedule Function
private void scheduleJob(View v) {
#SuppressLint("JobSchedulerService")
ComponentName componentName = new ComponentName(Objects.requireNonNull(getActivity()), WeeklyJobService.class);
JobInfo info = new JobInfo.Builder(123, componentName)
.setRequiresCharging(true)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setPersisted(true)
.setPeriodic(15 * 60 * 1000)
.build();
JobScheduler scheduler = (JobScheduler) getActivity().getSystemService(JOB_SCHEDULER_SERVICE);
int resultCode = scheduler.schedule(info);
if (resultCode == JobScheduler.RESULT_SUCCESS) {
Log.d(TAG, "Job scheduled");
} else {
Log.d(TAG, "Job scheduling failed");
}
}
The concept is, for every periodic time, function sum must be repeated.
How to achieve this?
Related
I am trying to test the reliability of using JobScheduler to schedule background tasks on Android API 23, because I noticed that sometimes it misbehaves and stops working.
This is how I schedule a job in SystemManager.java class:
public static void scheduleUploadJob(Context context) {
ComponentName componentName = new ComponentName(context, BackgroundUploadJobScheduler.class);
JobInfo info = new JobInfo.Builder(123, componentName)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.setPeriodic(120*60*1000)
.build();
JobScheduler scheduler = (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE);
int resultCode = scheduler.schedule(info);
if (resultCode == JobScheduler.RESULT_SUCCESS) {
Log.d("ScheduleJob()", "Job scheduled successfully!");
} else {
Log.d("ScheduleJob()", "Job scheduled Failed!");
}
}
public class BackgroundUploadJobScheduler extends JobService {
private static final String TAG = "JobSevice";
public static boolean jobCancelled = false;
#Override
public boolean onStartJob(JobParameters params) {
Log.d(TAG, "Events upload Job started");
doBackgroundUploading(params, this);
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
Log.d(TAG, "upload Job cancelled before completion");
jobCancelled = true;
return true;
}
public synchronized void doBackgroundUploading(final JobParameters params, final Context context) {
new Thread(new Runnable() {
#Override
public void run() {
if (jobCancelled) {
return;
}
UploadObject uploadObject= //get it from RoomDB
NetworkManager.upload(context,uploadObject);
}
jobFinished(params, false);
}
}).start();
}
I do the network call in the NetworkManager.java class using Retrofit:
call.enqueue(new Callback() {
#Override
public void onResponse(Call call, Response response) {
if (response.isSuccessful()) {
SystemManager.scheduleEventRecordUploadJob(context);
}else{
SystemManager.scheduleEventRecordUploadJob(context);
}
}
#Override
public void onFailure(Call call, Throwable t) {
SystemManager.scheduleEventRecordUploadJob(context);
}
});
The problem
After I initialized the app and made the first Network call, I exited the app, disconnected the USB connection and turned off the screen. I left it overnight and came to check the records on my server DB for results.
The device kept sending the server network data with almost the same rate at least twice per minute for a continuous 10 hours! (This wasn't expected, cus the device should enter doze right?)
After that there was no more requests sent! From 3 AM till 9:30 AM I got nothing sent to the server. I turned on my device screen, connected it to power, opened the app... waited for an hour, still nothing more is being sent. Why? Did the service just get killed? How can I solve such an issue?
N.B
Of course, I do not intend to do such heavy repeating work like this on a real application. But, I am just doing a test app to understand the behavior of JobScheduler and JobService. And why does it just die out of nothing all of a sudden and doesn't start again?!
My App must be aware of taking picture by any camera apps on user device and do some progress on that picture.
On API < 24 i used a broadcast receiver which catches com.android.camera.NEW_PICTURE events and it works fine.
But according to this document to achieve that goal in API >= 24 we must use Job-Scheduler instead.
I used this code and it works fine for one picture in one job execution time. but when i take consecutive pictures job scheduler only handles first one.
#TargetApi(Build.VERSION_CODES.N)
public class CameraJobService extends JobService
{
private static final String TAG = "CameraJobService";
#Override
public boolean onStartJob(final JobParameters params)
{
Log.i(TAG, "onStartJob: " + this);
// do in background thread
new Thread(() -> {
doJob(params);
// mark the job as 'finished'
jobFinished(params, false);
// create a new job
prepareJob(getApplicationContext());
}).start();
// mark the job as 'on processing'
return true;
}
#Override
public boolean onStopJob(JobParameters params)
{
Log.i(TAG, "onStopJob: " + this);
// reschedule job if it was terminated by os
return true;
}
private void doJob(JobParameters params)
{
if (params.getTriggeredContentAuthorities() != null && params.getTriggeredContentUris() != null)
{
//some job here
Log.d(TAG, "doJob: ");
}
}
#TargetApi(Build.VERSION_CODES.N)
public static void prepareJob(Context context)
{
Log.i(TAG, "prepareJob");
JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(1001,
new ComponentName(context, CameraJobService.class));
builder.addTriggerContentUri(
new JobInfo.TriggerContentUri(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
if (scheduler != null)
{
scheduler.schedule(builder.build());
}
}
}
I initialized my JobService and BroadcastReceiver Here in MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// use job scheduler
CameraJobService.prepareJob(this);
} else {
// use broadcast receiver
CameraBroadcastReceiver.register(this);
}
}
I think this problem happens because one job is in progress now and we can't start new one before current job be complete. but is there any way to handle this problem?
In my application I want to get GPS location of through a foregrond service and receiver. every thing is running fine but after two or three successful run. service is not able to detect internet and returns false
here is the code
Code for receiver of calling service in activity
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent ll24 = new Intent(getBaseContext(), AlarmReceiver.class);
PendingIntent recurringLl24 = PendingIntent.getBroadcast(getBaseContext(), 0, ll24, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarms.setRepeating(AlarmManager.RTC_WAKEUP,0, 30*10*100, recurringLl24);
}
//.....
}
& in receiver class
public class AlarmReceiver extends WakefulBroadcastReceiver
{
static Context context;
Date curTime,sdf,edf;
private static final String LOG_TAG = "ForegroundService";
#Override
public void onReceive(Context context, Intent intent)
{
try
{
Calendar c = Calendar.getInstance();
curTime = new SimpleDateFormat( "HH:mm" ).parse(c.get( Calendar.HOUR_OF_DAY)+":"+c.get( Calendar.MINUTE));
sdf = new SimpleDateFormat( "HH:mm" ).parse("08:00");
edf = new SimpleDateFormat( "HH:mm" ).parse("20:00");
if(curTime.after(sdf) && curTime.before(edf))
{
Intent ll24Service = new Intent(context, ForegroundService.class);
ll24Service.setAction(Constants.ACTION.STARTFOREGROUND_ACTION);
ForegroundService.IS_SERVICE_RUNNING = true;
startWakefulService(context,ll24Service);
}
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
in service class
public class ForegroundService extends Service
{
...
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
AlarmReceiver.completeWakefulIntent(intent);
if (intent.getAction().equals(Constants.ACTION.STARTFOREGROUND_ACTION))
{
Log.i(LOG_TAG, "Received Start Foreground Intent ");
showNotification();
Calendar c = Calendar.getInstance();
curDateTime = c.get( Calendar.YEAR)+"-"+ String.valueOf(c.get( Calendar.MONTH )+1)+"-"+ c.get( Calendar.DAY_OF_MONTH)+" "+c.get( Calendar.HOUR_OF_DAY)+":"+c.get( Calendar.MINUTE)+":"+c.get( Calendar.SECOND);
gps = new GPS(ForegroundService.this);
if(gps.canGetLocation())
{
double latitude = gps.getLatitude();
double longitude = gps.getLongitude();
latt=Double.toString(latitude);
longi =String.valueOf(longitude);
insertData();
}
else
{
gps.showSettingsAlert();
}
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i(LOG_TAG, "In onDestroy");
}
private void insertData()
{
try
{
String QUERY = "insert into GpsLoc values('"+usr+"','"+latt+"','"+longi+"','"+curDateTime+"')";
myDatabase.execSQL(QUERY);
Log.i(LOG_TAG, "insert");
ConnectionDetector cd = new ConnectionDetector(getApplicationContext());
Boolean isInternetPresent = cd.isConnectingToInternet();
if(isInternetPresent)
{
Log.i(LOG_TAG, "chk int");
addToJason();
}
else
{
Log.i(LOG_TAG, "No int");
stopForeground(true);
stopSelf();
}
}
catch(Exception ex)
{
Toast.makeText(getApplicationContext(), "Data not inserted : "+ex.toString(), Toast.LENGTH_LONG).show();
}
}
Every thing is running fine as shown in the log:
03-19 12:11:48.160: I/ForegroundService(17476): Received Start
Foreground Intent
03-19 12:11:48.270: D/Network(17476): Network
03-19 12:11:48.340: I/ForegroundService(17476): insert
03-19 12:11:48.390: I/ForegroundService(17476): chk int
03-19 12:11:49.340: I/ForegroundService(17476): uploaded
03-19 12:11:49.350: I/ForegroundService(17476): In onDestroy
But after two or three run same log became
03-19 12:15:34.670: I/ForegroundService(17476): Received Start Foreground Intent
03-19 12:15:34.740: D/Network(17476): Network
03-19 12:15:34.780: I/ForegroundService(17476): insert
03-19 12:15:34.780: I/ForegroundService(17476): No int
03-19 12:15:34.850: I/ForegroundService(17476): In onDestroy
the problem is at checking internet connection while app is not running and it is called through service. Please suggest any solution
Some times it throuws :
03-27 12:30:07.240: I/ForegroundService(1543):
android.view.WindowManager$BadTokenException: Unable to add window --
token null is not for an application
and some times : Error converting result jsonobject
Please help Problem Strat Here when we called to detect internet in service
ConnectionDetector cd = new ConnectionDetector(getApplicationContext());
Boolean isInternetPresent = cd.isConnectingToInternet();
Add this dependency in your gradle file.
compile 'com.firebase:firebase-jobdispatcher:0.8.5'
Create a Firebase JobService File and declare in manifest.
<service
android:exported="false"
android:name="._User_Classes.User_Call_Record.ScheduledJobService">
<intent-filter>
<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>
</intent-filter>
</service>
Declare and start the JobService from your activity or fragment
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(this));
Job myJob = dispatcher.newJobBuilder()
.setService(ScheduledJobService.class) // the JobService that will be called
.setRecurring(false) //repeat the job or not
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.setTrigger(Trigger.executionWindow(0, 60))
.setLifetime(Lifetime.FOREVER)
.setTag("demo") // uniquely identifies the job
.build();
dispatcher.mustSchedule(myJob);
Below is the JobService class which will be called each time the job is executed.
public class ScheduledJobService extends JobService {
#Override
public boolean onStartJob(JobParameters job) {
if(isConnected(this))
{
//internet available
}
else{
//internet not available
}
//perform your operations
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new
GooglePlayDriver(this));
Job myJob = dispatcher.newJobBuilder()
.setService(ScheduledJobService.class) // the JobService that will be called
.setRecurring(false) //repeat the job or not
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.setTrigger(Trigger.executionWindow(0, 60))
.setLifetime(Lifetime.FOREVER)
.setTag("demo") // uniquely identifies the job
.build();
dispatcher.mustSchedule(myJob);
return true;
}
#Override
public boolean onStopJob(JobParameters job) {
return false;
}
public static NetworkInfo getNetworkInfo(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo();
}
/**
* Check if there is any connectivity
*
* #param context
* #return
*/
public static boolean isConnected(Context context) {
NetworkInfo info = getNetworkInfo(context);
return (info != null && info.isConnected());
}
}
I hope it helps
I have a problem on Android 7 that not support the broadcast event "android.hardware.action.NEW_PICTURE" longer. I write now for Android 7 a JobService but it will not fire when a Picture is shot by the internal camera.
I don't know what is the problem, can everybody help me.
If any example source in the www that's for Android 7 and JobService for the replacement the broadcast "android.hardware.action.NEW_PICTURE" .
Thanks for help !
Here is my example Code:
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class ZNJobService extends JobService {
private static Zlog log = new Zlog(ZNJobService.class.getName());
static final Uri MEDIA_URI = Uri.parse("content://" + MediaStore.AUTHORITY + "/");
static final int ZNJOBSERVICE_JOB_ID = 777;
static JobInfo JOB_INFO;
#RequiresApi(api = Build.VERSION_CODES.N)
public static boolean isRegistered(Context pContext){
JobScheduler js = pContext.getSystemService(JobScheduler.class);
List<JobInfo> jobs = js.getAllPendingJobs();
if (jobs == null) {
log.INFO("ZNJobService not registered ");
return false;
}
for (int i = 0; i < jobs.size(); i++) {
if (jobs.get(i).getId() == ZNJOBSERVICE_JOB_ID) {
log.INFO("ZNJobService is registered :-)");
return true;
}
}
log.INFO("ZNJobService is not registered");
return false;
}
public static void registerJob(Context pContext){
Log.i("ZNJobService","ZNJobService init");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ) {
if (! isRegistered(pContext)) {
Log.i("ZNJobService", "JobBuilder executes");
log.INFO("JobBuilder executes");
JobInfo.Builder builder = new JobInfo.Builder(ZNJOBSERVICE_JOB_ID, new ComponentName(pContext, ZNJobService.class.getName()));
// Look for specific changes to images in the provider.
builder.addTriggerContentUri(new JobInfo.TriggerContentUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
// Also look for general reports of changes in the overall provider.
//builder.addTriggerContentUri(new JobInfo.TriggerContentUri(MEDIA_URI, 0));
JOB_INFO = builder.build();
log.INFO("JOB_INFO created");
JobScheduler scheduler = (JobScheduler) pContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
int result = scheduler.schedule(JOB_INFO);
if (result == JobScheduler.RESULT_SUCCESS) {
log.INFO(" JobScheduler OK");
} else {
log.ERROR(" JobScheduler fails");
}
}
} else {
JOB_INFO = null;
}
}
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
public boolean onStartJob(JobParameters params) {
log.INFO("onStartJob");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (params.getJobId() == ZNJOBSERVICE_JOB_ID) {
if (params.getTriggeredContentAuthorities() != null) {
for (Uri uri : params.getTriggeredContentUris()) {
log.INFO("JobService Uri=%s",uri.toString());
}
}
}
}
this.jobFinished(params,false);
return false;
}
#Override
public boolean onStopJob(JobParameters params) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
log.INFO("onStopJob");
}
return true;
}
}
In the below code you can pass the flag immediate as false for normal operation (i.e. schedule within system guidelines for an app's good behaviour). When your app's main activity starts you can pass immediate as true to force a quick retrieval of media content changes.
You should run code in the onStartJob() method in a background job. (As shown below.)
If you only want to receive media from the camera and not other sources you should just filter out URI's based on their path. So only include "*/DCIM/*". (I haven't put this in the below code though.)
Also the Android job scheduler has a policy where it denies your service if it detects over abuse. Maybe your tests have caused this in your app, so just uninstall and reinstall to reset it.
public class ZNJobService extends JobService {
//...
final Handler workHandler = new Handler();
Runnable workRunnable;
//...
public static void registerJob(Context context, boolean immediate) {
final JobInfo jobInfo = createJobInfo(context, immediate);
final JobScheduler js = context.getSystemService(JobScheduler.class);
final int result = js.schedule(jobInfo);
if (result == JobScheduler.RESULT_SUCCESS) {
log.INFO(" JobScheduler OK");
} else {
log.ERROR(" JobScheduler fails");
}
}
private static JobInfo createJobInfo(Context context, boolean immediate) {
final JobInfo.Builder b =
new JobInfo.Builder(
ZNJOBSERVICE_JOB_ID, new ComponentName(context, ZNJobService.class));
// Look for specific changes to images in the provider.
b.addTriggerContentUri(
new JobInfo.TriggerContentUri(
MediaStore.Images.Media.INTERNAL_CONTENT_URI,
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
b.addTriggerContentUri(
new JobInfo.TriggerContentUri(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
if (immediate) {
// Get all media changes within a tenth of a second.
b.setTriggerContentUpdateDelay(1);
b.setTriggerContentMaxDelay(100);
} else {
// Wait at least 15 minutes before checking content changes.
// (Change this as necessary.)
b.setTriggerContentUpdateDelay(15 * 60 * 1000);
// No longer than 2 hours for content changes.
// (Change this as necessary.)
b.setTriggerContentMaxDelay(2 * 60 * 60 * 1000);
}
return b.build();
}
#Override
public boolean onStartJob(final JobParameters params) {
log.INFO("onStartJob");
if (params.getTriggeredContentAuthorities() != null && params.getTriggeredContentUris() != null) {
// Process changes to media content in a background thread.
workRunnable = new Runnable() {
#Override
public void run() {
yourMethod(params.getTriggeredContentUris());
// Reschedule manually. (The 'immediate' flag might have changed.)
jobFinished(params, /*reschedule*/false);
scheduleJob(ZNJobService.this, /*immediate*/false);
}};
Postal.ensurePost(workHandler, workRunnable);
return true;
}
// Only reschedule the job.
scheduleJob(this, /*immediate*/false);
return false;
}
#Override
public boolean onStopJob(final JobParameters params) {
if (workRunnable != null) {
workHandler.removeCallbacks(workRunnable);
workRunnable = null;
}
return false;
}
private static void yourMethod(Uri[] uris) {
for (Uri uri : uris) {
log.INFO("JobService Uri=%s", uri.toString());
}
}
}
I'm using jobscheduler since it's no longer possible to declare a network connectivity intent in manifest. I've tried using .setPeriodic(10 * 1000) to make the job periodic but in doing so, the job runs after 10 seconds even if there is no available network as if it is ignoring this line .setRequiredNetworkType(NETWORK_TYPE_ANY)
And if I remove .setPeriodic(10 * 1000) then the job runs only once. What I want to achieve is the same that was possible before the background optimisation and disabling apps from waking based on a connectivity change: If there is an available network then run the jobservice, if there is none do nothing.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main5);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
JobScheduler mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = null;
builder = new JobInfo.Builder(1, new ComponentName(getPackageName(),
MyJobService.class.getName()))
.setRequiredNetworkType(NETWORK_TYPE_ANY)
.setPeriodic(10 * 1000);
if (mJobScheduler.schedule(builder.build()) <= 0) {
Log.e("gch", "can't Schedule job for MyJobService");
} else {
Log.d("gch", "Schedule job for MyJobService");
}
}
}
and this is the jobService
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class MyJobService extends android.app.job.JobService {
public MyJobService() {
}
#Override
public boolean onStartJob(JobParameters jobParameters) {
Log.e("gch", "onStartJob");
Toast.makeText(this, "onStartJob", Toast.LENGTH_SHORT).show();
Intent inent = new Intent(this, AnotherActivity.class);
inent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(inent);
return false;
}
#Override
public boolean onStopJob(JobParameters jobParameters) {
Log.e("gch", "onStartonStopJob");
Toast.makeText(this, "onStartonStopJob", Toast.LENGTH_SHORT).show();
return true;
}
}
I've added this to the manifest
<service
android:name=".MyJobService"
android:permission="android.permission.BIND_JOB_SERVICE" />