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?
Related
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.
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?!
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?
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" />