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?!
Related
I have created a service that sends StringRequest to a server and gets response. Now i want this service to keep on sending and getting response even when my app is closed. but i can only get it done once. Please anyone guide my how to keep looping in a service?
Here is the code i want the service to repeat
public class MyService extends Service {
public static final String URL = "Some URL";
public MyService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
RequestQueue myQueue = Volley.newRequestQueue(getApplicationContext());
StringRequest myRequest = new StringRequest(Request.Method.POST, URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.i("My Log", response.toString());
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.i("My Log", error.toString());
}
});
myQueue.add(myRequest);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
your service is probably still alive after execution of StringRequest. your problem is that this happens once, at start. create inside onCreate some new Handler instance and use postDelayed for interval requesting (reset Handler and start again in onResponse and onErrorResponse
but above will be working as long as your app is on foreground (Activity visible). on newer system versions (9+, most on market currently) you have to use ForegroundService with sticky Notification informing that something happens in the background in context of your app
I have used a timer to call a method at 15 minutes interval inside a job scheduler. But the job scheduler only running on the app running stage, if I kill the app the job scheduler was not worked. How to run the job scheduler to work after I killing my app?
here my code snippet
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startJob();
}
private void startJob(){
JobScheduler jobScheduler = (JobScheduler)getApplicationContext()
.getSystemService(JOB_SCHEDULER_SERVICE);
ComponentName componentName = new ComponentName(this,
RecallMyServicesis.class);
JobInfo jobInfo = new JobInfo.Builder(1, componentName)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setRequiresCharging(true)
.setOverrideDeadline(15*60*1000)
.setPersisted(true)
.build();
int result = jobScheduler.schedule(jobInfo);
if(result == JobScheduler.RESULT_SUCCESS)
{
Log.d("MainActivity","Success");
}else
{
Log.d("MainActivity","Failed");
}
}
}
RecallMyServicesis.java
public class RecallMyServicesis extends JobService {
private static final String TAG = "RecallMyServicesis";
#Override
public boolean onStartJob(JobParameters jobParameters) {
System.out.println("Job Fired");
for(int i = 0; i < 10; i++){
System.out.println("Job Fired i : "+i);
Toast.makeText(this, "Job Fired i : "+i, Toast.LENGTH_SHORT).show();
SystemClock.sleep(1000);
}
jobFinished(jobParameters, false );
return true;
}
#Override
public boolean onStopJob(JobParameters jobParameters) {
return false;
}
}
AndroidManifest.xml
<service android:name=".RecallMyServicesis"
android:permission="android.permission.BIND_JOB_SERVICE" />
I am trying to work on JobScheduler and eventually, It is not working on Android 7.0. I have Samsung s7 device. But when I am deploying apk in Moto e4 which have android 7.1 version, the jobscheduler is working fine on background and app as well, even I kill the app, it is running.
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 use service for get request to server.
I should run this service for always and not stop it!
I write below code in service, but just show for 5 time and when receive to 5 step. then not show Toast!
But I want always getData() and show Toast.
Service class :
public class NotifyService extends Service {
private static final String TAG = "HelloService";
private boolean isRunning = false;
#Override
public void onCreate() {
Log.i(TAG, "Service onCreate");
isRunning = true;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service onStartCommand");
//Creating new thread for my service
//Always write your long running tasks in a separate thread, to avoid ANR
new Thread(new Runnable() {
#Override
public void run() {
//Your logic that service will perform will be placed here
//In this example we are just looping and waits for 5000 milliseconds in each loop.
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(5000);
} catch (Exception e) {
}
if (isRunning) {
ExploreSendData sendData = new ExploreSendData();
sendData.setPageIndex(1);
sendData.setPageSize(10);
sendData.setShowFollows(false);
sendData.setShowMovies(true);
sendData.setShowNews(true);
sendData.setShowReplies(false);
sendData.setShowSeries(true);
sendData.setShowSuggestions(false);
InterfaceApi api = ApiClient.getClient().create(InterfaceApi.class);
Call<ExploreResponse> call = api.getExplore(new SharedPrefrencesHandler(NotifyService.this)
.getFromShared(SharedPrefrencesKeys.TOKEN.name()), sendData);
call.enqueue(new Callback<ExploreResponse>() {
#Override
public void onResponse(Call<ExploreResponse> call, Response<ExploreResponse> response) {
if (response.body().getData() != null && response.body().getStatusCode() != 401
&& response.body().getStatusCode() != 402) {
Toast.makeText(NotifyService.this, "Test Show message ever 5second", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ExploreResponse> call, Throwable t) {
}
});
}
}
//Stop service once it finishes its task
stopSelf();
}
}).start();
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
Log.i(TAG, "Service onBind");
return null;
}
#Override
public void onDestroy() {
isRunning = false;
Log.i(TAG, "Service onDestroy");
}
}
I copy this service code from internet, but just show 5times. I want show always.
How can I edit my codes and fix it? Please help me. Thanks
The problem is not in the service, services start and continue living as long as the app is alive and android doesn't kill it. For an infinite loop replace the "for loop" with "While loop". The below loop doesn't end.
while (true) {
......
......
......
}
Is networking using HttpUrlConnection etc (for uploading data to server) possible with JobScheduler? Or I've to go with GCMNetworkManager? How can I perform network operation scheduling?
MainActivity.class
jobScheduler = (JobScheduler)getSystemService(JOB_SCHEDULER_SERVICE);
btnStartJob.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
ComponentName jobService =
new ComponentName(getPackageName(), MyJobService.class.getName());
JobInfo jobInfo =
new JobInfo.Builder(MYJOBID, jobService).setPeriodic(10000).build();
int jobId = jobScheduler.schedule(jobInfo);
if(jobScheduler.schedule(jobInfo)>0){
Toast.makeText(MainActivity.this,
"Successfully scheduled job: " + jobId,
Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(MainActivity.this,
"RESULT_FAILURE: " + jobId,
Toast.LENGTH_SHORT).show();
}
}});
MyJobService.class
public class MyJobService extends JobService {
public MyJobService() {
}
#Override
public boolean onStartJob(JobParameters jobParameters) {
Toast.makeText(this,"MyJobService.onStartJob()",Toast.LENGTH_SHORT).show();
//networking is not working here
return false;
}
#Override
public boolean onStopJob(JobParameters jobParameters) {
Toast.makeText(this,
"MyJobService.onStopJob()",
Toast.LENGTH_SHORT).show();
return false;
}
}
Is networking using HttpUrlConnection etc (for uploading data to server) possible with JobScheduler?
Of course. You should use the .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) on the JobInfo builder though, to ensure your job does not run if there is no network available.
You have to do your network operation in JobService on a separate thread though, because the onStartJob is executed on the main thread.