I have a BroadcastReceiver here:
NotificationServiceReceiver:
public class NotificationServiceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(RestService.ACTION_PENDING_REMINDERS_UPDATED)) {
//Reminders updated
NotificationServer.startNotificationWorkRequest(context);
}
}
A Notification Server:
public class NotificationServer extends IntentService {
private static final String LOG_TAG = "NotificationService";
public static final String ACTION_SHOW_NOTIFICATION = "com.android.actions.SHOW_NOTIFICATION";
// this is a bypass used for unit testing - we don't want to trigger this service when the calendar updates during
// the intergration tests
public static boolean sIgnoreIntents = false;
private WorkManager mWorkManager;
private LiveData<List<WorkStatus>> mSavedWorkStatus;
public NotificationServer() {
super(NotificationServer.class.getName());
mWorkManager = WorkManager.getInstance();
}
/**
* Handles all intents for the update services. Intents are available to display a particular notification, clear all
* notifications, refresh the data backing the notification service and initializing our timer. The latter is safe to
* call always, it will check the current state of on-device notifications and update its timers appropriately.
*
* #param intent - the intent to handle. One of ACTION_SHOW_NOTIFICATION,
* ACTION_REFRESH_DATA or ACTION_INIT_TIMER.
*/
#Override
protected void onHandleIntent(Intent intent) {
startNotificationWorkRequest(this);
}
public void startNotificationWorkRequest(Context context) {
WorkContinuation continuation = mWorkManager
.beginUniqueWork(IMAGE_MANIPULATION_WORK_NAME,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequest.from(CleanupWorker.class));
}
}
I want to start a WorkManager task onReceive of the Broadcast Receiver. The problem is I can't do this statically as I need access to the current WorkManager object. The example code that Google provides here: https://github.com/googlecodelabs/android-workmanager/blob/master/app/src/main/java/com/example/background/BlurActivity.java
Grabs the ViewModel like this: ViewModelProviders.of(this).get(BlurViewModel.class);
I can't do this obviously because my notification server class is not a view model. How should I approach this problem?
For anyone that sees this, you can use WorkManager.getInstance() to get the WorkManager object statically. There is only one instance of WorkManager, just make sure you initialize it like this on the start of your application: WorkManager.initialize(this, new Configuration.Builder().build());
Android Custom Work Manager Config official documentation
Related
Pass additional parameters to a dynamically registered BroadcastReceiver.
The problem is basic: I want to pass parameters to a BroadcastReceiver. Can this be done? Even when the receiver is created dynamically?
Additionally, say I create an anonymous BroadcastReceiver i.e. as a variable implementation. Can I reference the encapsulating class variables? Check the code below for how I stop / start the file observer.
// Create the external media broadcast receiver.
mExternalMediaBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
// if action = media removed, stop the file observer.
EncapsulatingFragment.this.mFileObserver.stopWatching()
}
};
Is this valid?
Is this valid?
So long as the receiver has the same lifetime as EncapsulatingFragment.this, probably.
I want to pass parameters to a BroadcastReceiver. Can this be done? Even when the receiver is created dynamically?
Create an actual class and pass in the values to the constructor:
class WhateverReceiver extends BroadcastReceiver {
FileObserver mFileObserver;
WhateverReceiver(FileObserver observer) {
mFileObserver = observer;
}
#Override
public void onReceive(final Context context, final Intent intent) {
// if action = media removed, stop the file observer.
mFileObserver.stopWatching();
}
}
Then, in your fragment:
mExternalMediaBroadcastReceiver = new WhateverReceiver(mFileObserver);
All that being said... you might want to consider whether this logic should be implemented in your fragment. I/O-related stuff ideally lies outside of a fragment, such as in a repository object.
I'm trying to start a IntentService to register to a firebase cloud messaging on Android O.
On Android O it's not allowed to start a Intent Service "in a situation when it isn't permitted" and every one tells me to use a JobService but not how to use it.
What constraints should the JobInfo.Builder have in order to have a "situation where it's permitted", i keep getting the same IllegalStateException
Here's my JobService
#Override
public boolean onStartJob(JobParameters params) {
Intent intent = new Intent(this, RegistrationIntentService.class);
getApplicationContext().startService(intent);
return false;
}
#Override
public boolean onStopJob(JobParameters params) {
return false;
}
public static void scheduleJob(Context context) {
ComponentName serviceComponent = new ComponentName(context, MyJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(MyJobService.JOB_ID, serviceComponent);
builder.setMinimumLatency(1 * 1000); // wait at least
JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
if(jobScheduler != null) jobScheduler.schedule(builder.build());
}
If you are using support library version 26.1.0 or higher you have access to the JobIntentService which is similar to an Intent Service with the added benefits of the job scheduler, you do not need to manage anything other than starting it.
According to the docs
Helper for processing work that has been enqueued for a job/service. When running on Android O or later, the work will be dispatched as a job via JobScheduler.enqueue. When running on older versions of the platform, it will use Context.startService.
You can find out more details here JobIntentService.
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v4.app.JobIntentService;
public class JobIntentNotificationService extends JobIntentService {
public static void start(Context context) {
Intent starter = new Intent(context, JobIntentNotificationService.class);
JobIntentNotificationService.enqueueWork(context, starter);
}
/**
* Unique job ID for this service.
*/
static final int JOB_ID = 1000;
/**
* Convenience method for enqueuing work in to this service.
*/
private static void enqueueWork(Context context, Intent intent) {
enqueueWork(context, JobIntentNotificationService.class, JOB_ID, intent);
}
#Override
protected void onHandleWork(#NonNull Intent intent) {
// do your work here
}
}
And the way you call it is
JobIntentNotificationService.start(getApplicationContext());
You will need to add this permission for pre Oreo devices
<!-- used for job scheduler pre Oreo -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
Firebase actually has a dedicated service for receiving messages called FirebaseMessagingService. This Firebase page should contain all the info to get you started in that regard.
Aside from that, you're trying to access the application context from the Service, while you should be using the base context of the parent service:
getApplicationContext().startService(intent);
to
startService(intent);
If you want to launch certain jobs from the FirebaseMessagingService, look into their JobDispatcher library which is pretty great.
To know the difference between IntentService and Service in Android, I created the below posted small test of an IntentService class. The IntentService class can be started using
startService(intent); which will result in a call to nStartCommand(Intent intent, int flags, int startId). Also to send values from the IntentService class to the MainActivity
for an example, we should send it via sendBroadcast(intent); and the MainActivity should register a broadcastReceiver for that action so it can receive the values sent via
sendBroadcast(intent);
so far I cant see any difference between Service and IntentService!! Since they are similar in the way of starting them and the way they broadcast data,can you please tell me in
which context they differ?
please tell me why i am receiving those errors and how to solve it
MainActivity
public class MainActivity extends AppCompatActivity {
private final String TAG = this.getClass().getSimpleName();
private Button mbtnSend = null;
private int i = 0;
private BroadcastReceiver mBCR_VALUE_SENT = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(MyIntentService.INTENT_ACTION)) {
int intnetValue = intent.getIntExtra(MyIntentService.INTENT_KEY, -1);
Log.i(TAG, SubTag.bullet("mBCR_VALUE_SENT", "intnetValue: " + intnetValue));
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
registerReceiver(this.mBCR_VALUE_SENT, new IntentFilter(MyIntentService.INTENT_ACTION));
this.mbtnSend = (Button) findViewById(R.id.btn_send);
this.mbtnSend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), MyIntentService.class);
intent.putExtra("intent_key", ++i);
startService(intent);
}
});
}
}
MyIntentService:
public class MyIntentService extends IntentService {
private final String TAG = this.getClass().getSimpleName();
public final static String INTENT_ACTION = "ACTION_VALUE_SENT";
public final static String INTENT_KEY = "INTENT_KEY";
public MyIntentService() {
super(null);
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* #param name Used to name the worker thread, important only for debugging.
*/
public MyIntentService(String name) {
super(name);
setIntentRedelivery(true);
}
#Override
public void onCreate() {
super.onCreate();
Log.w(TAG, SubTag.msg("onCreate"));
}
#Override
protected void onHandleIntent(Intent intent) {
Log.w(TAG, SubTag.msg("onHandleIntent"));
int intent_value = intent.getIntExtra("intent_key", -1);
Log.i(TAG, SubTag.bullet("", "intent_value: " + intent_value));
Intent intent2 = new Intent();
intent2.setAction(MyIntentService.INTENT_ACTION);
intent2.putExtra(MyIntentService.INTENT_KEY, intent_value);
sendBroadcast(intent2);
SystemClock.sleep(3000);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.w(TAG, SubTag.msg("onStartCommand"));
return super.onStartCommand(intent, flags, startId);
}
In short, a Service is a broader implementation for the developer to set up background operations, while an IntentService is useful for "fire and forget" operations, taking care of background Thread creation and cleanup.
From the docs:
Service A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use.
IntentService Service is a base class for IntentService Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
Refer this doc - http://developer.android.com/reference/android/app/IntentService.html
Service
This is the base class for all services. When you extend this class, it’s important that you create a new thread in which to do all the service’s work, because the service uses your application’s main thread, by default, which could slow the performance of any activity your application is running.
IntentService
This is a subclass of Service that uses a worker thread to handle all start requests, one at a time. This is the best option if you don’t require that your service handle multiple requests simultaneously. All you need to do is implement onHandleIntent(), which receives the intent for each start request so you can do the background work.
Below are some key differences between Service and IntentService in Android.
1) When to use?
The Service can be used in tasks with no UI, but shouldn’t be too long. If you need to perform long tasks, you must use threads within Service.
The IntentService can be used in long tasks usually with no communication to Main Thread. If communication is required, can use Main Thread handler or broadcast intents. Another case of use is when callbacks are needed (Intent triggered tasks).
2) How to trigger?
The Service is triggered calling to method onStartService().
The IntentService is triggered using an Intent, it spawns a new worker thread and the method onHandleIntent() is called on this thread.
for more clarity refer this
http://www.onsandroid.com/2011/12/difference-between-android.html
I have a ContentProvider which fetches data from sqlite database and loads it via Loader. It's updated by a Service which runs an AsyncTask to download data from server and update it in the ContentProvider. I hope it's clear but to be sure:
ListFragment takes data from ContentProvider via Loader,
ContentProvider gets updated with a Service.
Now, when my local sqlite database is empty the first time I launch the app, it shows that it has no events, even though they're being currently downloaded via Service. I would rather have a ProgressBar shown at this moment (the infinite spinning wheel, not a bar). But if I show a ProgressBar when there are no results from database, it would be there even after fetching data from sever in this specific case when there are no records in the external database (and it occurs quite often in my case). So:
When the data is downloaded for the first time by the Service I
would like to show a ProgressBar until ContentProvider gives
non-empty result OR the Service finished it's job.
When ContentProvider returned nothing AND Service finished
it's job (and fetched empty result) I would like the app to show
"no results found".
My problem is probably: how to notify the ListFragment that the Service is still running or that it finished ts job. I mean - I shouldn't store any reference to the calling Fragment inside the Service. It goes against the idea of ContentProviders, doesn't it? So how?
Note: I don't really know which fragment of code would be helpful here, so if you feel that you need to see some specific frag, just tell me in comments. Thanks!
Since you're not so much interested in posting actual progress back to the UI, the simplest way to implement this would be using a pair of custom broadcasts, and maybe a static boolean to show run state as well.
Basically, your service can notify any component of your application that's interested when it is beginning a download and when it has finished it. So you can define two custom action strings:
public static final String ACTION_DOWNLOADSTART = "com.mypackage.intent.ACTION_DOWNLOADSTART";
public static final String ACTION_DOWNLOADCOMPLETE = "com.mypackage.intent.ACTION_DOWNLOADCOMPLETE";
Then have your service broadcast them at the proper points in the code:
Intent start = new Intent(ACTION_DOWNLOADSTART);
sendBroadcast(start);
//...Service does its work
Intent finish = new Intent(ACTION_DOWNLOADCOMPLETE);
sendBroadcast(finish);
You can register for these callbacks anywhere in your application with a BroadcastReceiver and act accordingly (i.e. check the status of the ContentProvider and show/hide progress if necessary).
Another common practice, if you want to be able to check if a Service is running at any given point, is simply to include a private static boolean member that you can toggle when the Service is active (perhaps between onCreate()/onDestroy() but perhaps elsewhere) and an accessor method like isRunning(). Then your application can also check at any time if the Service is running by just calling that method.
There are various techniques how to communicate between Fragment / Activity and a Service.
One of them is using ResultReceiver and sending it to IntentService in Intent extra.
You create custom receiver ServiceResultReceiver extending ResultReceiver.
public class ServiceResultReceiver extends ResultReceiver {
private Receiver mReceiver;
public ServiceResultReceiver(Handler handler) {
super(handler);
}
public void setReceiver(Receiver receiver) {
mReceiver = receiver;
}
public interface Receiver {
public void onReceiveResult(int resultCode, Bundle resultData);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (mReceiver != null) {
mReceiver.onReceiveResult(resultCode, resultData);
}
}
}
Make your Fragment implement ServiceResultReceiver.Receiver interface. Create receiver
and initialize it to your Fragment. You than pass the receiver to service and in service just get the receiver from intent and call receiver.send() to send anything back to the receiver.
public class MyFragment extends ListFragment implements ServiceResultReceiver.Receiver {
private ServiceResultReceiver mReceiver;
....
#Override
public void onCreate(Bundle savedInstanceState) {
...
mReceiver = new ServiceResultReceiver(new Handler());
mReceiver.setReceiver(this);
}
public void startMyService() {
final Intent intent = new Intent(getActivity(), MyService.class);
intent.putExtra("receiver", mReceiver);
getActivity().startService(intent);
}
#Override
public void onReceiveResult(int resultCode, Bundle resultData) {
// service finished
}
}
public class MyService extends IntentService {
...
#Override
protected void onHandleIntent(Intent intent) {
// download data and update database
....
final ResultReceiver receiver = intent.getParcelableExtra("receiver");
if (receiver != null) {
receiver.send(0, null);
}
}
}
So I have a small little app which downloads a very small amount of data from the net. Everything else works just fine and downloads properly, but when connection changes (I lose wifi range) the download won't complete and the user doesn't get their data.
I have an idea how to handle this. I set up a BroadcastReceiver on my main Activity which communicates with my IntentService. When the IntentService completes the download, I then unregister the receiver. To top all this, I set up a Broadcastreceiver to listen connectivity changes and if connection is available, and if there is a connection, the main activity sends an Intent to start the download. See here:
Main Activity:
public class Sample extends Activity {
private BroadcastReceiver connectivityReceiver;
private ResponseReceiver receiver;
protected void onCreate(Bundle sis){
super.onCreate(sis);
IntentFilter intentFilter = new IntentFilter(
"android.net.conn.CONNECTIVITY_CHANGE");
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if (Network.isOnline()) {
fireUpDownloadingIntent();
}
}
}, intentFilter);
}
public class ResponseReceiver extends BroadcastReceiver {
public static final String ACTION_RESP = "com.irough.intent.action.URL_LOADED";
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getBooleanExtra(DLService.DOWNLOAD_COMPLETE, false) {
unRegisterReceiver(connectivityReceiver);
}
}
}
}
DLService.java:
public class DLService extends IntentService {
public static final String DOWNLOAD_COMPLETE = "dlc";
public DLService() {
super("DLService");
}
#Override
protected void onHandleIntent(Intent intent) {
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(ResponseReceiver.ACTION_RESP);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra(DOWNLOAD_COMPLETE, true);
sendBroadcast(broadcastIntent);
}
}
The code about should work just fine, but is there an easier or better way to do it? Doesn't have to be done on Service, Asynctask force closes on me when connection drops and that's why put the download action to a service.
If you lose the connection in your download, I imagine your download will throw some sort of exception. If I were, I'd simply notify the user (using the android notification api), and give them the option to try to redownload the data.
Preferably though, (and contrary to my previous post in a similar question), you could use my new favorite class in the android, the AsyncTaskLoader. It sounds like it exactly fits the bill for what you want to do here. Bascially, if there's an error downloading, just have your loader return null. Then in your onLoaderFinished hook in your activity, do what ever you need to do in regards to informing the user. Note that this class is only available to API levels 3 and above, but can still be accessed by lower API levels through the android compatibility package.