Android Firebase Jobdispatcher not starting - android

Hi Android geeks over there,
I am developing an application which uses 'com.firebase:firebase-jobdispatcher:0.8.5' to schedule jobs. But the jobs are not getting executed in the tested device.
While I checked the dumpsys activity service GcmService I got the following log, in which my job is marked as Not yet run.
What would be the possible reason for this behavior?
I also found a status like READY_BATTERY_LOW, is it because my devices is running low on battery? But while the testing is taking place, the device is having 58% battery.
Also, the device is connected to Jio 4g network.
The dumpsys log is included below, could anyone have some comments to give a light on the issue. :)
(scheduled) com.mypackage.testapp/com.firebase.jobdispatcher.GooglePlayReceiver{u=0 tag="test-sync-job" trigger=window{start=300s,end=360s,earliest=-459s,latest=-399s} requirements=[NET_ANY] attributes=[RECURRING] scheduled=-759s last_run=N/A jid=N/A status=READY_BATTERY_LOW retries=0 client_lib=FIREBASE_JOB_DISPATCHER-1}
Not yet run.
Thanks in advance :)

Here is the bit of making a vocation.
Driver = new GooglePlayDriver(context);
firebaseJobDispatcher = new FirebaseJobDispatcher(driver);
Occupation constraintReminderJob = firebaseJobDispatcher.newJobBuilder()
.setService(ReminderService.class)
.setTag(REMINDER_JOB_TAG)
.setConstraints(Constraint.DEVICE_CHARGING)
.setLifetime(Lifetime.FOREVER)
.setRecurring(true)
.setTrigger(Trigger.executionWindow(
REMINDER_INTERVAL_SECONDS,
REMINDER_INTERVAL_SECONDS + SYNC_FLEXTIME_SECONDS
))
.setReplaceCurrent(true)
.assemble();
firebaseJobDispatcher.schedule(constraintReminderJob);
How about we investigate the above scrap.
Occupation
There are a few ascribes to make an occupation.
A string label that (inside your application) particularly recognizes the Job.
A JobService subclass that will contain all the business rationale identified with the Job.
A JobTrigger will establish that the made Job is presently prepared to execute.
An arrangement of Constraints is required keeping in mind the end goal to execute the made activity. As a matter of course, it is unfilled which implies that Job will be kept running when the JobTrigger is initiated.
A RetryStrategy is in charge of taking care of the disappointment conditions. The default is taken care of utilizing exponential backoff procedure.
A lifetime that indicates the season of the activity in which it ought to stay planned. The
default is to keep the Job planned until the following boot.
Package is for client provided additional items. This is a discretionary parameter.
A boolean demonstrates whether the Job should rehash or not. The default is false which implies that the planned Job will execute just once.
A boolean shows whether the booked Job ought to supplant any prior Job
with a similar tag or not. The default an incentive for this boolean banner is false.
Once your activity is prepared, you can utilize plan() strategy to plan the activity.
open static void scheduleJob(Context setting) {
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
Occupation work = createJob(dispatcher);
dispatcher.schedule(job);
}
ReminderService.class
open class ReminderService broadens JobService {
/**
* This asynctask will run a vocation once conditions are met with the imperatives
* As soon as client gadget gets associated with the power supply. it will produce
* a warning demonstrating that condition is met.
*/
private AsyncTask mBackgroundTask;
#Override
open boolean onStartJob(final jobParameters) {
mBackgroundTask = new AsyncTask() {
#Override
ensured Object doInBackground(Object[] objects) {
Setting = ReminderService.this;
ReminderTasks.executeTasks(context, ReminderTasks.ACTION_CHARGING_REMINDER);
Log.i("TAG", "onStartJob");
return invalid;
}
#Override
secured void onPostExecute(Object o) {
/* false means, that activity is finished. we would prefer not to reschedule it*/
jobFinished(jobParameters, false);
Log.i("TAG", "onStartJob-OnPost");
}
};
mBackgroundTask.execute();
return genuine;
}
#Override
open boolean onStopJob(JobParameters jobParameters) {
on the off chance that (mBackgroundTask != invalid) {
mBackgroundTask.cancel(true);
}
Log.i("TAG", "onStopJob");
/* genuine means, we're not done, if you don't mind reschedule */
return genuine;
}
}
There are three strategies that will be utilized while utilizing FireBase Job Dispatcher.
onStartJob(JobParameters params)- This is the underlying strategy that will be conjured when a vocation is called. It keeps running on the fundamental string. It will restore a Boolean which tells whether an occupation is remaining or not. Returning genuine demonstrates that more work is remaining. We can call jobFinished() when the activity is finished.
onStopJob(JobParameters params)- This technique is considered when your activity is halted. The activity can be halted because of different reasons if the running limitations related to the activity are never again fulfilled. It will restore a Boolean which tells whether work ought to be attempted again or not. On the off chance that returned genuinely, at that point, the system will set up this activity again for execution.
jobFinished(JobParameters params, boolean needsReschedule)- When work has been offloaded to another string, it ought to be called expressly.

Related

Questions on creating a service that connects automatically to a BLE device on android

I am implementing a service that uses the autoconnect feature of bluetoothGatt to connect to the device and monitor it while it is being connected.
I work on the assumption that the device is already bonded (a coworker is responsible for that part) so autoconnect should not have any problems
my code is as follows:
//the callback is for the class I have created that actually does the connection
class BTService: Service(), CoroutineScope, BTConnection.Callback {
private val btReceiver by lazy { BluetoothStateReceiver(this::btStateChange) } //receiver for bt adapter changes
private var connection:BTConnection? = null
private var readJob:Job? = null
override fun onCreate() {
buildNotificationChannels()
registerReceiver(btReceiver, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)) //since I can't register this receiver in AndroidManifest any more I did it here
}
private fun btStateChange(enabled: Boolean) {
if (enabled)
startConnecting()
else
stopConnection()
}
private fun startConnecting() {
val address = prefs.address //get the current saved address
val current = connection //get the current connection
//try to stop the current connection if it is different than the one we want to set up
if (current != null && !current.address.equals(address, true))
current.stop()
if (address.isNullOrBlank())
return
//then we create a new connection if needed
val new = if (current == null || !current.address.equals(address, true)) {
Injections.buildConnection(application, address, this)
} else {
current
}
connection = new
new.connect()
}
//this is one of the callbacks from BTConnection.Callback
override fun connected(address: String) {
if (address != connection?.address) return
val cn = connection ?: return
showConnectionNotification()
val notification = buildForegroundNotification()
startForeground(FOREGROUND_ID, notification)
readJob?.cancel()
readJob = launch {
cn.dataFlow //this is a flow that will be emmitting read data
.cancellable()
.flowOn(Dispatchers.IO)
.buffer()
.onEach(this#BTService::parseData)
.flowOn(Dispatchers.Default)
}
}
private suspend fun parseData(bytes:ByteArray) { //this is where the parsing and storage etc happens
}
private fun stopConnection() {
val cn = connection
connection = null
cn?.stop()
}
override fun disconnected(address: String) { //another callback from the connection class
showDisconnectNotification()
stopForeground(true)
}
my code that stops the connection is
fun stop() {
canceled = true
if (connected)
gatt?.disconnect()
launch(Dispatchers.IO) {
delay(1000)
gatt?.close()
gatt = null
}
}
my code is based (and affected) by this really good article I read:
https://medium.com/#martijn.van.welie/making-android-ble-work-part-2-47a3cdaade07
I have also created a receiver for boot events that will call
context.startService(Intent(context, BTService::class.java))
just to make sure that the service is created at least once and the bt receiver is registered
my questions are:
a) is there a chance that my service will be destroyed while it is not in foreground mode? i.e. when the device is not near by and bluetoothGat.connect is suspending while autoconnecting? is it enough for me to return START_STICKY from onStartCommand() to make sure that even when my service is destroyed it will start again?
b) if there is such a case, is there a way to at least recreate the service so the btReceiver is at least registered?
c) when should close() be called on bluetoothGatt in case of autoconnect = true? only when creating a new connection (in my example where I call Injections.buildConnection)? do I also call it when the bluetoothadapter is disabled? or can I reuse the same connection and bluetoothGatt if the user turns the bluetooth adapter off and on again?
d) is there a way to find out if autoconnect has failed and will not try again? and is there a way to actually test and reproduce such an effect? the article mentioned above says it can happen when the batteries of the peripheral are almost empty, or when you are on the edge of the Bluetooth range
thanks in advance for any help you can provide
a-b) If your app does not have an activity or a service that is in the foreground, the system may kill it at anytime. Pending or active BLE connections doesn't affect the system's point of view when to kill the app whatsoever. (When it comes to scanning for advertisements, the story is completely different though.)
The general approach to make sure autoConnects stay alive is to have a foreground service running at all the time. So don't stop it while the device is currently not connected, if you want to have a pending connection. There is no point in using Job Scheduler, WorkManagers etc. since having a foreground service should be enough to keep the app process alive, and pending/active connections are kept alive as long as the app is. The app does not use any cpu% at all when waiting for pending BLE connections. However some Chinese phone makers are known to not follow the Android documentation, by sometimes killing apps even though they have running foreground services.
c) Each BluetoothGatt object represents and refers to an object inside the Bluetooth process running on the same phone. By default the system allows a total of 32 such objects (last time I checked). In order to release these precious resources, you call close(). If you forget, you will have a leak, meaning your app or some other app might not be able to create a BluetoothGatt object. (When app processes exit, their BluetoothGatt objects are however closed automatically). The API is a bit strangely designed, that there is both a disconnect method and a close method. But anyway, the disconnect method gracefully initiates a disconnection of the connection and you will then get an onConnectionStateChange callback telling when the disconnection is complete. You must however call close in order to free the resource, or call connect if you'd like to re-connect, or you can take an action a bit later. Calling close on a connected BluetoothGatt object will also disconnect, but you won't get any callback due to the object is being destroyed at the same time.
Since all BluetoothGatt objects represents objects in the Bluetooth process, these will "die" or stop working when you turn off Bluetooth, since that involves shutting down the Bluetooth process. This means you need to recreate all BluetoothGatt objects when Bluetooth is restarted. You can call close on the old objects, but it won't do anything since they're dead. Since the documentation doesn't say anything about this, I suggest you call close anyway to be on the safe side if the behaviour is changed in the future.
d) To detect if a connectGatt call fails and will not try again, you can listen to the onConnectionStateChange callback. If this gives an error code, such as 257, it usually means that the system has reached maximum number of connections, or maximum number of some resource. You can test this out by simply initiating pending connections to a bunch of different Bluetooth device addresses.
I would not trust the statement that new connection attempts would be aborted if the peripheral is low on battery or being on the "edge of Bluetooth range". I'd be glad to see a pin point to Android's Bluetooth source code where this happens, since I really believe this is not true at all.
First of all, if you are intending to distribute your app to Google Play Store, you need to be targeting minimum api level 29 if I'm not mistaken, hence you should be using either JobService along with JobScheduler or WorkManager, instead of Service. This is to support the background limitations from Oreo(26) onwards.
a) if you properly implement any of the two options I mentioned above, you can write a proper service that will not terminate unless you stop it. Here are some resources on JobService : (resource1, resource2, resource3)
b) You can re-register as you please upon the onStartJob() method of your JobService, which will recreate your app.
c) Each time you are done with the peripheral ble device, you need to close the gatt connection with it. Here is a snippet from the BluetoothGatt class
/**
* Close this Bluetooth GATT client.
*
* Application should call this method as early as possible after it is done with
* this GATT client.
*/
public void close() {
Also, from the BluetoothAdapter class javadoc, you can see that all the connections are terminated gracefully when ble is disabled.
/**
* Turn off the local Bluetooth adapter—do not use without explicit
* user action to turn off Bluetooth.
* <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth
* system services, and powers down the underlying Bluetooth hardware.
* <p class="caution"><strong>Bluetooth should never be disabled without
* direct user consent</strong>. The {#link #disable()} method is
* provided only for applications that include a user interface for changing
* system settings, such as a "power manager" app.</p>
* <p>This is an asynchronous call: it will return immediately, and
* clients should listen for {#link #ACTION_STATE_CHANGED}
* to be notified of subsequent adapter state changes. If this call returns
* true, then the adapter state will immediately transition from {#link
* #STATE_ON} to {#link #STATE_TURNING_OFF}, and some time
* later transition to either {#link #STATE_OFF} or {#link
* #STATE_ON}. If this call returns false then there was an
* immediate problem that will prevent the adapter from being turned off -
* such as the adapter already being turned off.
*
* #return true to indicate adapter shutdown has begun, or false on immediate error
*/
#RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public boolean disable() {
d) I am not sure on what callback will be triggered. To reproduce, the two items you mentioned seem like valid cases to try.
I hope this helps you perfect your project!

WorkManager stops scheduling periodic Worker after the app closes

maybe this is yet another question about WorkManager, but I really can't find a solution...
I'm trying, as the title suggests, to run a periodic work every 15 minutes. Actually in the worker I'm polling some data every minute. After every poll, every almost 1 second I check if the worker is being stopped and if so return, otherwise keep waiting until 1 minute is reached and poll data again.
According to the documentation this should work and indeed it is, until I kill the app from the recent app screen.
Here is the code:
package com.qsea.app.cordova;
import android.content.Context;
import android.os.SystemClock;
import android.util.Log;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
public class ServerListenerWorker extends Worker {
public static final String TAG = "ServerListenerWorker";
public ServerListenerWorker(
Context appContext,
WorkerParameters workerParams
) {
super(appContext, workerParams);
}
#Override
public Result doWork() {
Log.d(TAG, "Doing work");
final long startTime = SystemClock.elapsedRealtime();
final int maxDelta = 840000; // 14 minutes
while (true) {
// I did this to stop this worker until 15 minutes
// and then let the next worker run
if (SystemClock.elapsedRealtime() - startTime >= maxDelta) {
break;
}
// Here I'm polling data, if the polling results in a failure
// I return Result.retry()
// It avoid waiting if it remains only 1 minute until the max time
if (SystemClock.elapsedRealtime() - startTime >= (maxDelta - 60000)) {
break;
}
for (int i = 0; i < 60; i++) {
SystemClock.sleep(950);
// Here it checks if it is stopped
if (isStopped()) {
Log.d(TAG, "Detected stop"); // this is actually never reached
break;
}
}
}
return Result.success();
}
}
And I do as following to start the work
Constraints constraints = new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build();
PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(ServerListenerWorker.class, 15, TimeUnit.MINUTES)
.addTag(serverListenerWorkerUID)
.setConstraints(constraints)
.build();
WorkManager.getInstance(cordova.getActivity()).enqueueUniquePeriodicWork(serverListenerWorkerUID, ExistingPeriodicWorkPolicy.KEEP, periodicWorkRequest);
I check the correct working of the worker by viewing the log of my server (If I receive the request it is working fine) and it seems to work ok until I close the app. Then it stops running the actual worker and never run a worker again until I reopen the app, where the work seems to be enqueued and resumes right after the app opening.
Am I doing something wrong in the initialization?
I have also <service android:name=".ServerListenerWorker" android:permission="android.permission.BIND_JOB_SERVICE" /> in my AndroidManifest.xml
Is this expected behavior?
I read that chinese ROM have additional restriction to background services and I have a HUAWEI, which seems to be the worst in this. So could it be the device? And if so, how do Facebook, WhatsApp, Instagram and others manage this to work even in these devices?
Let's split this in two different problems.
What are you doing in your workers is battery heavy and would be better if you use a foreground service that notifies the user that your application is running continuously in the background. You can also use WorkManager with the newly introduced support for long-running workers (that under the hood uses a foreground service). More on this in the documentation.
If have a problem with a specific OEMs, please open an issue on the Android issuetracker as this maybe a CDD violation. Google can contact the OEM and request that they fix the ROM. This is going to take time, in the meanwhile, you can take a look at sites like don't kill my app to understand what are the constraints on a specific device and use a library like autostarter to help the user to navigate to the right setting.
BTW, you don't need to be list your workers in the AndroidManifest.xml file

How to check if accessibility service crashed (Android Pie now displays this info in settings)?

I've found out that Android 9 now shows info if accessibility service stopped working.
That was always a pain for developers who try to leverage accessibility API.
Accessibility looks like enabled, but service is stopped. And to get it back to work it is required to turn accessibility off and back on.
I would be glad if Google fixes that completely, but now they just show a hint that it's good to disable-enable it manually.
Not the best stuff, but at least something.
So, I've tried to find out how the system gets to know if the service is crashed. There happened to be a class called AccessibilityUtil and it contains hasServiceCrashed method.
Unfortunately, it checks a hidden field crashed from AccessibilityNodeInfo, which is not available for third-party developers (because of reflection denial) as well as on previous android versions.
So I'm wondering if there is an alternative way to get the info from the system which clarifies that my accessibility service is crashed/stopped working and user's action is required. Starting from Lollipop. Hints appreciated.
I came up with an idea to use a static boolean indicating the status of Accessibility Service and compare it with Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES. I've tested on multiple devices, haven't found any issue with this method.
1.Declare a static boolean in Accessibility Service.
private static boolean bServiceRunning = false;
2.In Accessibility Service, set the boolean value in onServiceConnected and onUnbind
#Override
protected void onServiceConnected() {
super.onServiceConnected();
bServiceRunning = true; //put this at the very beginning to reduce time gap
}
#Override
public boolean onUnbind(Intent intent) {
bServiceRunning = false;
return super.onUnbind(intent);
}
3.Create a static function in Accessibility Service
public static boolean bGetServiceStatus(){
return bServiceRunning;
}
With the boolean flag, I can know if the accessibility service is running in the desired state. When the service is being forced to stop, onUnbind will be called so the boolean value turns into false.
4.We use Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES to get accessibility service switch status
public static boolean bIsAccessibilityServiceEnabled(Context context, Class<?> accessibilityService) {
ComponentName expectedComponentName = new ComponentName(context, accessibilityService);
String strServicesSettingResult = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (strServicesSettingResult == null){
return false;
}
TextUtils.SimpleStringSplitter colonSplitter = new TextUtils.SimpleStringSplitter(':');
colonSplitter.setString(strServicesSettingResult);
while (colonSplitter.hasNext()) {
String strComponentName = colonSplitter.next();
ComponentName enabledService = ComponentName.unflattenFromString(strComponentName);
if (enabledService != null && enabledService.equals(expectedComponentName))
return true;
}
return false;
}
5.And this is what we want, we check with the above two methods the determine the real state of accessibility service.
public static int intIsAccessibilityServiceEnabled_WithCrashCheck(Context context, Class<?> accessibilityService){
//return 0 if Accessibility Service enabled and running
//return -1 if Accessibility Service disabled and not running
//return -2 if Accessibility Service enabled but stopped working or crashed
//first check Accessibility Service boolean
if(bGetServiceStatus()){
//service is running
return 0;
}else{
//service not running, now double check with Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
boolean bResult = bIsAccessibilityServiceEnabled(context, accessibilityService);
if(!bResult){
//Accessibility Service is disabled
return -1;
}else{
//Accessibility Service is enabled, but service is not actually running, Accessibility Service is crashed
return -2;
}
}
}
Using "AccessibilityManager" also works the same, but I prefer a more "lightweight" version with static boolean for better performance.
Note: Using Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES without another doublecheck will cause a bug. The value is not synced. The result doesn't always represent the real service status. The following steps can create such a case :
Start an accessibility service then go to App settings force stop the App.
Now you can see the accessibility service switch is turned off. (Seems enough currently, but the next step will create a problem)
Start the accessibility service again,on Android 9.0+ devices you'll find out the switch is switched on, but the service is actually not running, and now it displays "Not
working. Tap for info."
2022/11/29 Edit: This bug is fixed according to this issue tracker. I can no longer reproduce this bug on my new Android 12 & 13 devices. However, devices with old Android firmware still has this bug. (The patch is also applied to the newest AVD images. To test this bug with AVD, now you must download old revisions of the AVD images.)
Android generally prevents apps from running if they crash repeatedly. This behavior for an accessibility service can obviously affect users who depend on the service, but since these services can effectively control the UI, having one that crashes repeatedly could also make the device unusable.
It hadn't occurred to me that anyone else would be interested in the crashed field in AccessibilityServiceInfo. I populated that field using data only available to the system unfortunately. I compare the list of services that are enabled with the list of those that are bound.
If you're interested if your service is prevented from running, you could probably do something similar by keeping track of when onBind and onUnbind is called and looking at the list of enabled services from AccessibilityManager.
I don't know if this is a solution. But I did find when it doesn't work: if I use "dumpsys accessibility", the services part is empty, looks like:
User state[attributes:{id=0,
currentUser=true,
touchExplorationEnabled=false,
displayMagnificationEnabled=false,
navBarMagnificationEnabled=false,
autoclickEnabled=false}
services:{}]
Maybe you can check if the services is empty.

Android background threading

I'm making image processor app. I need to scan the phone for pictures and list them with their number of pixels. So that's gonna be a a large impact on performance and as I understood, I need to make it work on background thread.
So my question is, what is the best approach for this? I understand that IntentService may be the best solution, but I'm not sure how I will implement progress bar with it, and I need to return Picture objects and later update the UI on shuffle button. I'm doing update with Glide library so that's gonna go smooth.
Reading about Asynctasks, I stumbled about comments how it's bad and leads to leaks in memory and should avoid using it. rXJava is too complicated at the moment.
This is my code:
Main activity:
#OnClick(R.id.shuffle)
public void shuffleList() {
Collections.shuffle(listOfImageFiles);
recyclerViewAdapter = new PictureRecycleViewAdapter(listOfImageFiles, this);
recyclerView.swapAdapter(recyclerViewAdapter, false);
recyclerViewAdapter.notifyDataSetChanged();
}
#OnClick(R.id.scan)
public void processImages() {
//progress bar
listOfPictures = new ArrayList<>();
//Gets data from default camera roll directory. Note that some of the phone companies have different file paths. So instead of hardcoding string paths, I used this instead.
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
File filePath = new File(path);
listOfImageFiles = scanPhotos(filePath);
// async?
for (File file : listOfImageFiles
) {
Bitmap bitmap = BitmapFactory.decodeFile(file.getPath());
//int is sufficient for most today's pixels. long would be overkill - 4 vs 8 bytes
int pixels = bitmap.getHeight() * bitmap.getWidth();
listOfPictures.add(new Picture(file.getPath(), pixels));
}
}
public List<File> scanPhotos(File directory) {
List<File> listOfPictures = new ArrayList<>();
try {
File[] files = directory.listFiles();
for (File file : files
) {
if (file.isDirectory() && !file.isHidden()) {
listOfPictures.addAll(scanPhotos(file));
} else {
if (file.getName().endsWith(".jpg") || file.getName().endsWith(".jpeg") || file.getName().endsWith(".png")) {
listOfPictures.add(file);
}
}
}
} catch (Exception e) {
Log.e(e.getMessage(), e.getMessage());
}
return listOfPictures;
}
IntentService
IntentService is definitely a valid approach. You can use Broadcasts to return your result to another component of the app, be it Activity or another Service, for example:
Start the IntentService - if you need some parameters, place them in the Extras of the service intent.
Your IntentService runs on the background thread until the computation is finished.
Upon finishing, send a broadcast with computation result placed in intent extras.
In your activity, register a BroadcastReceiver that will listen for your computation result broadcast.
Upon getting the broadcast in your Activity, retrieve the computation result from intent extras.
You might also implement broadcasts received by your Service for things like cancellation of the computation or updating the parameters.
One of the advantages of IntentService is that you can easily integrate it with the JobScheduler API to defer execution until certain system conditions are met.
Alternatives
You can use a bus library, such as https://github.com/greenrobot/EventBus to communicate between Activity and Service - the only problem is, EventBus won't work with remote services (running in a separate process).
Like you've mentioned, using RxJava with IO and computation schedulers is also a good idea.
AsyncTask is fine as long as you not tie it with a hard reference to an activity - don't implement it as an inner class of Activity and if you want to communicate the result back, do it through a WeakReference<T>
AsyncTask is fine, you just need to be careful with its implementation.
However, for longer running tasks there are better options. IntentService is a good option.
When it comes to a responsive UI when using an IntentService you could add two things.
Notifications
Create an ongoing notification that indicates that your App is working on something. This lets users know that their CPU cycles are being eaten by something in the background and they are less likely(?) to be confused and cranky about their device running slower.
Additionally, it gives your App more of an allowance for staying alive when Android is looking for background Apps to kill to release memory.
EventBus
You can make UI reporting extremely simple by using an EventBus library. I am personally a fan of greenbot/EventBus, but there are others.
Example
In your Activity:
#Subscribe(threadMode = ThreadMode.MAIN)
public void onProgressEvent(ProgressEvent event) {
mProgressBar.setProgress(event.value);
}
In your IntentService:
EventBus.getDefault().post(new ProgressEvent(5000));

No Longer want ActivityManager - Not a service issue

I am working on a open source application (droidwall fork) and i am stuck with one of the issue were the iptables rules were not applied properly when the system reboots. it works perfectly on most of the android versions. But on some specific ROMS (CM 10.1) it gives the following logcat
12-26 08:39:27.116 I/ActivityManager(582):
No longer want dev.ukanth.ufirewall (pid 2297): empty #17
My code works somethings like below,
private Handler mHandler = new Handler(Looper.getMainLooper());
#Override
public void onReceive(final Context context, final Intent intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
if (Api.isEnabled(context.getApplicationContext())) {
final Handler toaster = new Handler() {
public void handleMessage(Message msg) {
if (msg.arg1 != 0) Toast.makeText(context, msg.arg1, Toast.LENGTH_SHORT).show();
}
};
mHandler.post(
// Start a new thread to enable the firewall - this prevents ANR
new Runnable() {
#Override
public void run() {
if (!Api.applySavedIptablesRules(context.getApplicationContext(), false)) {
// Error enabling firewall on boot
final Message msg = new Message();
msg.arg1 = R.string.toast_error_enabling;
toaster.sendMessage(msg);
Api.setEnabled(context.getApplicationContext(), false, false);
}
}
});
// Start a new thread to enable the firewall - this prevents ANR
}
/*Intent i = new Intent(context, StartupService.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startService(i);*/
}
You can find my Api.java class here.
12-26 08:39:27.116 I/ActivityManager(582): No longer want dev.ukanth.ufirewall (pid 2297): empty #17
This log means that you have reach the maximum allowed empty processes. (16 is the max in your case)
More about empty processes from android doc:
A process that doesn't hold any active application components. The only reason to keep this kind of process alive is for caching purposes, to improve startup time the next time a component needs to run in it. The system often kills these processes in order to balance overall system resources between process caches and the underlying kernel caches.
So, not sure the log you have is directly related to your issue with iptables rules.
After the method onReceived() finished, the system assumes that you're done with the BroadcastReceiver and set the hosted process as lowest priority. And we know that the toaster Handler's handleMessage() method is called asynchronously which is called after the onReceived() method finished, and there is no guarantee that the process is still there to perform the callback method
On most Android versions, the number of processes that running at system start-up is not so many and your process (with lowest priority) has a chance to stay alive until the callback method handleMessaged() is called, but with ROMS (CM 10.1), there might be so many processes running that that moment, system has to kill lower priority processes to free up resources so that higher priority processes can run properly and your process is one good candidate to be killed.
I suggest you start a service to do those asynchronous tasks, that makes your process has higher priority (service-process priority by default or you can use startForeground() method to get the foreground-process priority)
Maybe you can use mHandler.post with some delay to test your case like 20 seconds?

Categories

Resources