Scheduling JobService on Android - android

I have a job service that i want to excecute for every three hours, I have made the jobservice class but I don't know how to excecute it every three hours.
here is my Jobservice class
public class CleanupJobService extends JobService {
private static final String TAG = CleanupJobService.class.getSimpleName();
#Override
public boolean onStartJob(JobParameters params) {
Log.d(TAG, "Cleanup job started");
new CleanupTask().execute(params);
//Work is not yet complete
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
//No need to reschedule any jobs
return false;
}
/* Handle access to the database on a background thread */
private class CleanupTask extends AsyncTask<JobParameters, Void, JobParameters> {
#Override
protected JobParameters doInBackground(JobParameters... params) {
String where = String.format("%s = ?", DatabaseContract.TaskColumns.IS_COMPLETE);
String[] args = {"1"};
int count = getContentResolver().delete(DatabaseContract.CONTENT_URI, where, args);
Log.d(TAG, "Cleaned up " + count + " completed tasks");
return params[0];
}
#Override
protected void onPostExecute(JobParameters jobParameters) {
//Notify that the work is now done
jobFinished(jobParameters, false);
}
}
}
and registered it on Manifest
<service
android:name=".data.CleanupJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"/>
Any idea to resolve this ? Thanks!

solved, I am using jobInfo class to solve this, the code should look like this
ComponentName jobService = new ComponentName(getContext(), CleanupJobService.class);
JobInfo task = new JobInfo.Builder(CLEANUP_JOB_ID, jobService)
.setPeriodic(jobInterval)
.setPersisted(true)
.build();
jobInterval is the predefined time that we want the service to excecute, the type is Long and have a millis format.

Related

JobScheduler Cannot Accept Parameters. Must Be Empty Constructor

I am playing around with JobScheduler and I am finding out limitations. I would like to perform a request following a Job, however, I am unable to add parameters to JobScheduler without receiving an error.
has no zero argument constructor
Here is example code (this works)
public class MyJob extends JobService {
private JobParameters params;
private MyLargeTask largeTask;
#Override
public boolean onStartJob(JobParameters params) {
// get param to use if if needed ...
this.params = params;
largeTask = new MyLargeTask();
largeTask.execute();
return false;
}
#Override
public boolean onStopJob(JobParameters params) {
if (largeTask != null)
largeTask.cancel(true);
return false;
}
// large task used ...
private class MyLargeTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPostExecute(Void aVoid) {
jobFinished(params, false);
super.onPostExecute(aVoid);
}
#Override
protected Void doInBackground(Void... params) {
// Instead of making a request here, I have to call method outside of the asynctask
return null;
}
}
}
If I try to add a constructor, I have no idea how to access it since the only reference to MyJob is during the process of retrieving JobInfo.
ComponentName componentName = new ComponentName(getApplicationContext(), MyJob.class);
JobInfo jobInfo = new JobInfo.Builder(1, componentName).setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED).setRequiresCharging(true).build();
JobScheduler jobScheduler = (JobScheduler) getApplicationContext().getSystemService(JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(jobInfo);
So I am unable to add a constructor such as
public MyJob(RequestModel request) {
...
}
Is there a pattern that can address this issue? My only thought is to create a static method where I execute requests and then call that method in doInBackground. But all of that can be avoided if I can create a constructor.
You can add parameters to a job via JobInfo.Builder.setExtras(PersistableBundle extras).
You can't pass normal Java objects, because your app's process may be killed by the time that the job is run.

onHandleIntent is not getting called

In the App i am developing i am trying to use IntentService as shown below in the code. the IntentService is declared as shown below in the manifest file.
the issue i am facing now is, when I run App the onHandleIntent is never get called.
I checked some examples in the internt but non of them was helpful, because the recommended hints to solve the issue did not work.
I started the service as follows:
this.mButtonFetchURL.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mServiceIntent = new Intent(getApplicationContext(), TwitterTrendsAPIService.class);
mServiceIntent.putExtra(CONST_KEY_REQUEST_URL, BASE_REQUEST_URL);
startService(mServiceIntent);
clearEditText(mEditTextURLContents);
}
});
please let me know how to solve it.
code:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.pc_.twittertrendsnearlocation">
<uses-permission android:name="android.permission.INTERNET" />
<application
....
....
....
<service
android:name=".services.TwitterTrendsAPIService"
android:exported="false"
android:enabled="true"/>
</application>
code
public class TwitterTrendsAPIService extends IntentService {
private static final String TAG = TwitterTrendsAPIService.class.getSimpleName();
private boolean mIsFetching = false;
private String mBaseRequestURL = null;
private RequestQueue mRequestQueue = null;
private JsonArrayRequest mJsonArrayRequest = null;
private final static String CONST_KEY_JSON_ARRAY_TRENDS = "trends";
private JSONObject mEntireJSONObject = null;
private JSONArray mEntireTrendsArray = null;
public TwitterTrendsAPIService() {
super(null);
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* #param name Used to name the worker thread, important only for debugging.
*/
public TwitterTrendsAPIService(String name) {
super(name);
}
#Override
public void onCreate() {
super.onCreate();
Log.w(TAG, "[onCreate]");
this.setupVolley();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.w(TAG, "[onStartCommand]");
return Service.START_NOT_STICKY;
}
#Override
public void onHandleIntent(Intent intent) {
Log.w(TAG, "[onHandleIntent]");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.w(TAG, "[onDestroy]");
}
private void setupVolley() {
this.mRequestQueue = Volley.newRequestQueue(this);
}
private class ServiceRunnable implements Runnable {
#Override
public void run() {
fetchJSONData();
stopSelf();
}
}
private void fetchJSONData() {
Log.w(TAG, "#fetchJSONData");
this.mJsonArrayRequest = new JsonArrayRequest(Request.Method.GET, this.mBaseRequestURL, null, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
try {
mEntireJSONObject = response.getJSONObject(0);
mEntireTrendsArray = mEntireJSONObject.getJSONArray(TwitterTrendsAPIService.CONST_KEY_JSON_ARRAY_TRENDS);
Log.i(TAG, "mEntireTrendsArray.length(): " + mEntireTrendsArray.length());
Log.i(TAG, "mEntireTrendsArray.get(0): " + mEntireTrendsArray.get(0));
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
}
}
Delete onStartCommand(), or chain to the superclass' implementation of onStartCommand(). Right now, you are overriding the built-in implementation, and IntentService uses that to set up the background thread and call onHandleIntent().

Activity to Android Library communication options (IPC)

Lets say I have a SDK in form of Android Library (aar) that offers some basic media processing (it has its own UI as a single activity). Currently, any client Android app, when invoking my SDK sends required data via Bundle.
Now, for various reasons some extra info for the data being sent may be required after my SDK is invoked so I would need a two-way communication with the caller app.
In short, from within the SDK I need to be able to check if the client app has implemented some interface so that SDK can use it to communicate with the client app (the client may choose not to provide the implementation in which case the SDK will fallback to internal, the default implementation..).
Anyway, the way I've done it initialy, is as following:
Within SDK I have exposed the data provider interface:
public interface ISDKDataProvider {
void getMeSomething(Params param, Callback callback);
SomeData getMeSomethingBlocking(Params param);
}
a Local binder interface that should return an instance of the implemented interface:
public interface LocalBinder {
ISDKDataProvider getService();
}
Then, on the client side, an application using the SDK, must provide a service that does the job and implements those interfaces:
public class SDKDataProviderService extends Service implements ISDKDataProvider {
private final IBinder mBinder = new MyBinder();
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public void getMeSomething(Params param, Callback callback) {
// ... do something on another thread
// once done, invoke callback and return result to the SDK
}
#Override
public SomeData getMeSomethingBlocking(Params param);{
// do something..
// return SomeData
}
public class MyBinder extends Binder implements LocalBinder {
#Override
public ISDKDataProvider getService() {
return ISDKDataProvider.this;
}
}
}
Additionally, when invoking SDK, the clinet app passes the ComponentName via bundle options:
sdkInvokationOptions.put("DATA_PROVIDER_EXTRAS", new ComponentName(getPackageName(), SDKDataProviderService.class.getName()));
..from the SDK, I then check whether the service exists and whether we can bind to it:
final ComponentName componentName = // get passed componentname "DATA_PROVIDER_EXTRAS"
if (componentName != null) {
final Intent serviceIntent = new Intent(componentName.getClassName());
serviceIntent.setComponent(componentName);
bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
}
where mConnection is:
private boolean mBound;
private ISDKDataProvider mService;
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
final LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
This seem to work ok and it looks clean but my question is there a better way\practice to accomplish the same type of a communication?
Your API should be simple, for example a static class/singleton:
MyAPI.start()
MyAPI.stop()
MyAPI.sendMessgage(mgs,callback)
MyAPI.setCallback(callback)
About the service, I think you should decide who is in charge of it.
If its the user - leave him the implementation, just give the API.
If you always want your API to run as a service, implement it yourself and inside the singleton handle the messaging (you can do so with intents, for example).
I used this architecture for image processing service too :)
My API wrapping class looked like:
public class MyAPI {
public static final String TAG = "MyAPI";
public MyAPI() {
}
public static MyAPI.Result startMyAPI(ScanParams scanParams) {
try {
Log.d("MyAPI", "in startMyAPI");
if (scanParams.ctx == null || scanParams.appID == null || scanParams.api_key == null) {
Log.d("MyAPI", "missing parameters");
return MyAPI.Result.FAILED;
}
if (scanParams.userID == null) {
scanParams.userID = "no_user";
}
if (scanParams.minBatteryThreshold == null) {
scanParams.minBatteryThreshold = Consts.DEFAULT_BATTERY_THRESHOLD;
}
if (scanParams.minCpuThreshold == null) {
scanParams.minCpuThreshold = Consts.DEFAULT_CPU_THRESHOLD;
}
if (!DeviceUtils.checkBatteryLevel(scanParams.ctx, (float)scanParams.minBatteryThreshold)) {
ReportUtils.error("low battery");
return MyAPI.Result.FAILED;
}
if (MyAPIUtils.isRunning(scanParams.ctx)) {
return MyAPI.Result.FAILED;
}
Intent intent = new Intent(scanParams.ctx, MyAPIService.class);
ServiceParams serviceParams = new ServiceParams(scanParams.appID, scanParams.api_key, scanParams.userID, scanParams.minBatteryThreshold, scanParams.minCpuThreshold);
intent.putExtra("SERVICE_PARAMS", serviceParams);
scanParams.ctx.startService(intent);
} catch (Exception var3) {
var3.printStackTrace();
}
return MyAPI.Result.SUCCESS;
}
public static void getBestCampaignPrediction(Context ctx, String apiKey, String appID, String creativeID, AppInterface appInterface) {
try {
String deviceID = DeviceUtils.getDeviceID(ctx);
GetBestCampaignTask getBestCampaignTask = new GetBestCampaignTask(ctx, apiKey, deviceID, appID, creativeID, appInterface);
getBestCampaignTask.execute(new Void[0]);
} catch (Exception var7) {
var7.printStackTrace();
}
}
public static boolean sendAdEvent(Context ctx, String apiKey, Event event) {
boolean res = false;
try {
boolean isValid = Utils.getIsValid(ctx);
if (isValid) {
Long timeStamp = System.currentTimeMillis();
event.setTimeStamp(BigDecimal.valueOf(timeStamp));
event.setDeviceID(DeviceUtils.getDeviceID(ctx));
(new SendEventTask(ctx, apiKey, event)).execute(new Void[0]);
}
} catch (Exception var6) {
var6.printStackTrace();
}
return res;
}
public static enum PredictionLevel {
MAIN_CATEGORY,
SUB_CATEGORY,
ATTRIBUTE;
private PredictionLevel() {
}
}
public static enum Result {
SUCCESS,
FAILED,
LOW_BATTERY,
LOW_CPU,
NOT_AUTHENTICATED;
private Result() {
}
}
}
You can see that startMyAPI actually starts a service and getBestCampaignPrediction runs an async task that communicates with the service behind the scenes and returns its result to appInterface callback. This way the user get a very simple API

How to automatically change wallpaper after every 30 seconds using JobScheduler?

I am creating application where there is an option of checkbox allowing user to change wallpaper automatically after 30 seconds. I am using JobScheduler and have passed arraylist of images by serializing them to JsonArray and then to String and passed it using PersistanceBundle:
JsonArray result = (JsonArray) new Gson().toJsonTree(wallpaperModelArrayList,
new TypeToken<List<WallpaperModel>>() {
}.getType());
PersistableBundle persistableBundle=new PersistableBundle();
persistableBundle.putString("wallpaper",result.toString());
mJobScheduler = (JobScheduler)
getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(1,
new ComponentName(getPackageName(),
JobSchedulerService.class.getName()));
builder.setExtras(persistableBundle);
builder.setPeriodic(30000);
And my JobService class:
public class JobSchedulerService extends JobService {
private static final String TAG = "JobSchedulerService";
private String images;
#Override
public boolean onStartJob(JobParameters params) {
Log.i(TAG, "onStartJob:");
images = params.getExtras().getString("wallpaper");
changeWallpaper(params);
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
//Log.i(TAG, "onStopJob:");
return true;
}
private void changeWallpaper(JobParameters params) {
Gson gson = new Gson();
Type listType = new TypeToken<List<WallpaperModel>>(){}.getType();
List<WallpaperModel> list = gson.fromJson(images, listType);
Glide.with(this)
.load(list.get(0).getUrlImage())
.asBitmap()
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super
Bitmap> glideAnimation) {
try {
WallpaperManager.getInstance(getApplicationContext()).setBitmap(resource);
} catch (IOException e) {
e.printStackTrace();
}
}
});
jobFinished(params, false);
}
}
I don't know how to set wallpaper(images one by one) after every 30 secs out of the list I have in JobService?Can Anyone tell me how setperiodic function works(what happens after 30secs)? This approach can be wrong. Can anyone guide me on how to do this? Thanks in advance.

how to pass data between service and it's application in the right way?

i'm a newbie in android. In my app i create a many-to-many chat, and need to update from server a list of Messages. In order to do so, i created a service that updates every second from the server.
My problem is that i don't know how to pass data back to the application. I know that I should do it using intent and broadcast receiver, but in that I stuck with Bundle object that i have to serialize in order to pass it to the app, and it does not make sense to me, since this operation is not that efficient.
For now i'm using the ref to my application (i think it's not that good but don't know why), and after every update from server in the service i activate the application function, and updates it's fields directly. Moreover i think maybe my code will do some good for beginners as well :)
public class UpdateChatService extends Service {
private static final long DELAY_FOR_CHAT_TASK = 0;
private static final long PERIOD_FOR_CHAT_TASK = 1;
private static final TimeUnit TIME_UNIT_CHAT_TASK = TimeUnit.SECONDS;
//private Task retryTask; TODO: check this out
private ScheduledExecutorService scheduler;
private boolean timerRunning = false;
private long RETRY_TIME = 200000;
private long START_TIME = 5000;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
scheduleChatUpdate();
}
private void scheduleChatUpdate() {
BiggerGameApp app = (BiggerGameApp) getApplication();
this.scheduler = Executors.newScheduledThreadPool(3);
this.scheduler.scheduleAtFixedRate(new UpdateChatTask(app),
DELAY_FOR_CHAT_TASK, PERIOD_FOR_CHAT_TASK,
TIME_UNIT_CHAT_TASK);
timerRunning = true;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!timerRunning) {
scheduleChatUpdate();
}
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
if (scheduler != null) {
scheduler.shutdown();
}
timerRunning = false;
}
}
Here is the code of the asynchronous task the runs in the service.
Please tell me what i'm doing wrong, and how should pass data from the service to the application.
public void run() {
try {
if (this.app.getLastMsgFromServer() == null) {
this.app.setLastMsgFromServer(new Message(new Player(DEFAULT_EMAIL), "", -1));
this.app.getLastMsgFromServer().setMessageId(-1);
}
Gson gson = new GsonBuilder()
.registerTypeAdapter(DateTime.class, new DateTimeTypeConverter())
.create();
ServerHandler serverHandler = new ServerHandler();
String jsonString = gson.toJson(this.app.getLastMsgFromServer());
// Sending player to servlet in server
String resultString = serverHandler.getResultFromServlet(jsonString, "GetListOfMessages");
if (resultString.contains("Error")) {
return;
}
// Parsing answer
JSONObject json = new JSONObject(resultString);
Status status = null;
String statusString = json.getString("status");
if (statusString == null || statusString.length() == 0)
return;
status = Status.valueOf(statusString);
if (Status.SUCCESS.equals(status)) {
ArrayList<Message> tempChat = null;
JSONArray jsonList = json.getJSONArray("data");
MyJsonParser jsonParser = new MyJsonParser();
tempChat = jsonParser.getListOfMessagesFromJson(jsonList.toString());
if (tempChat != null && tempChat.size() != 0) {
// After getting the chat from the server, it saves the last msg
// For next syncing with the server
this.app.setLastMsgFromServer(tempChat.get(LAST_MSG_INDEX));
tempChat.addAll(this.app.getChat());
if (tempChat.size() > SIZE_OF_USER_CHAT) {
tempChat = (ArrayList<Message>) tempChat.subList(0, SIZE_OF_USER_CHAT - 1);
}
this.app.setChat(tempChat);
this.app.updateViews(null);
}
}
return;
Is the Service local only (I'm going to assume "yes")?
Communication with a local-only service can be done by passing an instance of android.os.Binder back, as shown below:
public class UpdateChatService extends Service {
public static final class UpdateChat extends Binder {
UpdateChatService mInstance;
UpdateChat(UpdateChatService instance) {
mInstance = instance;
}
public static UpdateChat asUpdateChat(IBinder binder) {
if (binder instanceof UpdateChat) {
return (UpdateChat) binder;
}
return null;
}
public String pollMessage() {
// Takes a message from the list or returns null
// if the list is empty.
return mInstance.mMessages.poll();
}
public void registerDataSetObserver(DataSetObserver observer) {
mInstance.mObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mInstance.mObservable.unregisterObserver(observer);
}
}
private ScheduledExecutorService mScheduler;
private LinkedList<String> mMessages;
private DataSetObservable mObservable;
#Override
public IBinder onBind(Intent intent) {
return new UpdateChat(this);
}
#Override
public void onCreate() {
super.onCreate();
mObservable = new DataSetObservable();
mMessages = new LinkedList<String>();
mScheduler = Executors.newScheduledThreadPool(3);
mScheduler.scheduleAtFixedRate(new UpdateChatTask(), 0, 1, TimeUnit.SECONDS);
}
#Override
public void onDestroy() {
super.onDestroy();
mScheduler.shutdownNow();
mObservable.notifyInvalidated();
}
class UpdateChatTask implements Runnable {
int mN = 0;
public void run() {
// This example uses a list to keep all received messages, your requirements may vary.
mMessages.add("Message #" + (++mN));
mObservable.notifyChanged();
}
}
}
This example could be used to feed an Activity (in this case a ListActivity) like this:
public class ChattrActivity extends ListActivity implements ServiceConnection {
LinkedList<String> mMessages;
ArrayAdapter<String> mAdapter;
UpdateChat mUpdateChat;
DataSetObserver mObserver;
Runnable mNotify;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMessages = new LinkedList<String>();
mNotify = new Runnable() {
public void run() {
mAdapter.notifyDataSetChanged();
}
};
mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mMessages);
getListView().setAdapter(mAdapter);
// Bind to the Service if you do not need it to persist when this Activity
// dies - otherwise you must call #startService(..) before!
bindService(new Intent(this, UpdateChatService.class), this, BIND_AUTO_CREATE);
}
/**
* #see android.app.ListActivity#onDestroy()
*/
#Override
protected void onDestroy() {
super.onDestroy();
if (mUpdateChat != null) {
mUpdateChat.unregisterDataSetObserver(mObserver);
unbindService(this);
}
}
public void onServiceConnected(ComponentName name, IBinder service) {
mUpdateChat = UpdateChat.asUpdateChat(service);
mObserver = new DataSetObserver() {
#Override
public void onChanged() {
String message;
while ((message = mUpdateChat.pollMessage()) != null) {
mMessages.add(message);
}
runOnUiThread(mNotify);
}
#Override
public void onInvalidated() {
// Service was killed - restart or handle this error somehow.
}
};
// We use a DataSetObserver to notify us when a message has been "received".
mUpdateChat.registerDataSetObserver(mObserver);
}
public void onServiceDisconnected(ComponentName name) {
mUpdateChat = null;
}
}
If you need to communicate across processes you should look into implementing an AIDL interface - but for "local" versions this pattern works just fine & doesn't involve abusing the global Application instance.
You can use a static memory shared between your service and rest of application (activities). If you do not plan to expose this service to external apps, then sharing static memory is better than serializing/deserializing data via bundles.
Bundles based approach is encouraged for components that are to be exposed to outside world. A typical app usually has just the primary activity exposed in app manifest file.
If your don't pulibc your service , the static memory and the callback function can do.
If not , you can send broadcast.

Categories

Resources