I'm using a JobService to keep my notifications running even when app is closed by the user, but when cellphone goes to battery save mode or 15%, my service stop to work.
I would like to understand if exists a way to avoid it, thanks
TelaPrincipal.java
public class TelaPrincipal extends AppCompatActivity {
cancelarJob();
startarJob();
[...]
}
public void cancelarJob(){
JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
scheduler.cancel(123);
Log.d(TAG, "Job cancelled");
}
public void startarJob(){
ComponentName componentName = new ComponentName(this, ExampleJobService.class);
JobInfo info = new JobInfo.Builder(123, componentName)
.setRequiresCharging(false)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setPersisted(true)
.setPeriodic(15 * 60 * 1000)
.build();
JobScheduler scheduler = (JobScheduler) 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");
}
}
You don't need any type of service of job to keep a notification around. Notifications stay until the user swipes them away. But no, there is no way to force this- when the battery is low, the OS kills everything it can to minimize power usage. Which is the right choice- the user will almost certainly prefer to miss your notification than have his phone die on him (although again, you don't need anything like this to keep a notification around)
Related
My problem is that in my app not show notification.
My application does that each time a button is pressed I create new thread and show notification with info that thread is running or waiting (this works fine). Then, if the thread is running, it will randomly sleep for 5-10 seconds and get data from the rest api and a notification should be displayed that the thread is finished (this notification is not displayed).
Fineshed notifications show after i press again button. As you can see in the image.
Image:
constructor view:
public MainView() {
Button ipButton = getIpButton();
setMargin(true);
setHorizontalComponentAlignment(Alignment.START, ipButton);
add(ipButton);
}
button:
private Button getIpButton() {
final UI ui = UI.getCurrent();
final VaadinSession session = VaadinSession.getCurrent();
Button ipButton = new Button("My IP");
AtomicInteger orderIndex = new AtomicInteger();
ipButton.addClickListener(_e -> {
int orderThread = orderIndex.getAndIncrement();
openBeginNotification(orderThread);
executor.submit(() -> {
try {
UI.setCurrent(ui);
VaadinSession.setCurrent(session);
long sleepTime = (long) (Math.random() * (10 - 5) + 5);
System.out.printf("%d: %ds\n", orderThread, sleepTime);
Thread.sleep(sleepTime * 1000);
IpDTO ip = restTemplate.getForObject("http://ip.jsontest.com/", IpDTO.class);
System.out.printf("%d: %s\n", orderThread, ip);
try {
VaadinSession.getCurrent().lock();
getFinishNotification(orderThread).open(); // here not show notification
VaadinSession.getCurrent().unlock();
} catch (Exception e) {
e.printStackTrace();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
});
return ipButton;
}
notification methods:
private void openBeginNotification(int orderThread) {
Notification notification;
if (executor.getActiveCount() == MAX_THREADS) {
// thread is in front
notification = getWaitNotification(orderThread);
} else {
// thread run
notification = getRunNotification(orderThread);
}
notification.open();
}
private Notification getRunNotification(int orderThread) {
return getNotification("Task " + orderThread + ": run", NotificationVariant.LUMO_PRIMARY);
}
private Notification getWaitNotification(int orderThread) {
return getNotification("Task " + orderThread + ": wait", NotificationVariant.LUMO_CONTRAST);
}
private Notification getFinishNotification(int orderThread) {
return getNotification("Task " + orderThread + ": finish", NotificationVariant.LUMO_SUCCESS);
}
private Notification getNotification(String notificationText, NotificationVariant variant) {
Notification notification = new Notification(notificationText, 1000);
notification.addThemeVariants(variant);
return notification;
}
First, you need to enable #Push to make Vaadin open a websocket connection that makes it possible for the server to directly send messages to the browser without waiting for the browser to send a message asking for changes (which happens when you click a button). The #Push annotation should be in different location depending on the Vaadin version you're using, so please refer to documentation to find the right place.
Second, please use UI::access instead of manually doing setCurrent and locking. While I didn't spot anything in your example that would break the happy case, there are still also a whole bunch of edge cases that you'd need to take into account. As an example, you're not cleaning up after setCurrent which might cause memory leaks and you're not unlocking in case something related to the notification throws an exception.
I am working on an developing an application for Mobile and Wearable to get sensor data at the same time from both mobile and handheld. When I press 'start collecting data' it sends a message to the Wearable to start the Sensor Service and starts collecting data, it also start collecting sensor data simultaneously from mobile sensors. Similarly, when I press the 'Stop' it stops collecting data. I am sending every value of Wearable sensor data back to mobile using DataItem to be saved later on in the mobile storage.
public void onSensorChanged(SensorEvent event) {
int sensorType = event.sensor.getType();
if (sensorType == Sensor.TYPE_ACCELEROMETER) {
float[] values = event.values;
Log.d(TAG, "onSensorChanged: Changed" );
sendSensorData (values);
}
}
private void sendSensorData (float[] values) {
PutDataMapRequest putDataMapRequest= PutDataMapRequest.create(NEW_VALUE);
putDataMapRequest.getDataMap().putFloatArray(KEY, values);
putDataMapRequest.getDataMap().putLong("Time", System.currentTimeMillis());
PutDataRequest putDataRequest= putDataMapRequest.asPutDataRequest().setUrgent();
Task<DataItem> dataItemTask = Wearable.getDataClient(this).putDataItem(putDataRequest);
dataItemTask.addOnSuccessListener(new OnSuccessListener<DataItem>() {
#Override
public void onSuccess(DataItem dataItem) {
Log.d(TAG, "onSuccess: "+ dataItem);
}
});
}
I am using the onDataChanged in Mobile package to listen to data changes from Wearable. I am using System.currentTimeMillis() in Wearable package to ensure a continuous stream of sensor data back to mobile.
This is the code on the receiving side i.e. the handheld.
public void onDataChanged(DataEventBuffer dataEventBuffer) {
for(DataEvent event: dataEventBuffer) {
if (event.getType() == DataEvent.TYPE_CHANGED) {
DataItem dataItem= event.getDataItem();
Uri uri = dataItem.getUri();
String path = uri.getPath();
if(path.equals(NEW_VALUE)) {
DataMap dataMap = DataMapItem.fromDataItem(dataItem).getDataMap();
getSensorData (dataMap);
//Log.d(TAG, "onDataChanged: "+ dataMap);
}
}
}
super.onDataChanged(dataEventBuffer);
}
The problem is that when I stop collecting data and compare both data records, wearable data records received by the mobile are very less in number which I understand is due to communication time between mobile and sensor and which causes the delay. I do understand the data records will not be exactly similar in number in any ideal scenario. Is there any way that I can minimize the delay of the data between the two devices sensor data?
The problem you are facing is because the sample rate of the sensors inside your phone is different than the sample rate of the wearable device. For instance the accelerometer on your phone may run at 400 Hz, and the one on your wearable at only 100 Hz or less.
A while ago I compiled a small list showing the maximum sensor sample rate of smartphones. As you can see almost every model has a different sample rate:
https://docs.google.com/spreadsheets/d/1vZEryeslHOq-pl_C-scoS4us21goxg8oyqviUgDGq2k/edit?usp=sharing
I am developing an Android app (the app runs on Android 6): I want the app to send a notification to the user when it is near a BLE device (a device that I have at home). So I continuously scan, I scan through a service (which is running in the background). It works well when the phone screen is on; but, when the screen turns off, a few seconds later the application can no longer find the BLE (the scan is still running, but there is no callback.
if (enable) {
if (mScanning) return;
// Stops scanning after a pre-defined scan period.
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (!mScanning) return;
try {
mScanning = false;
mBluetoothLeScanner.stopScan(mScanCallback);
Log.i(TAG_LOG, "Stop scanning after pre-defined scan periode");
} catch (Exception e){Log.e(TAG_LOG,"mBluetoothLeScanner.stopScan Exception:=>"+e.getMessage());}
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothLeScanner.startScan(filters, settings, mScanCallback);
Log.i(TAG_LOG, "Start scanning ....");
}
private ScanCallback mScanCallback = new ScanCallback() {
//When a BLE advertisement has been found
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
Log.i(TAG_LOG, "Name: "+result.getDevice().getName()+". Adresse: "+result.getDevice().getAddress()+". Rssi: "+result.getRssi());
//scanDevices(false);
if(result.getDevice().getName() != null && result.getDevice().getName().toString().equals(deviceName)){
mDeviceAdress = result.getDevice().getAddress();
mDevice = mBluetoothAdapter.getRemoteDevice(mDeviceAdress);
Log.i(TAG_LOG, "Device found");
scanDevices(false);
}
}
You can't make this work. Scanning is a very expensive operation that Android won't allow in the background. Instead, make an attempt to connect to the device. I had success doing this in a WorkManager job, running every 15 minutes. Battery drain was negligible and it was pretty reliable. Note that a connection state 0x85 usually represents the device being out of range, and 0x80 means a different device is already connected to it (or the phone is already connected to too many different devices). Full error list is at https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/master/stack/include/gatt_api.h#27
The Job Schedular set as follows
ComponentName mServiceComponent = new ComponentName(context, TestJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(jobId, mServiceComponent);
builder.setPeriodic(3 * 60 * 1000);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE);
builder.setRequiresDeviceIdle(false);
builder.setRequiresCharging(false);
builder.setPersisted(true);
JobScheduler jobScheduler = (JobScheduler) ChaseForceApplication.getAppContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(builder.build());
The TestJobService class is like this:
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class TestJobService extends JobService {
#Override
public boolean onStartJob(JobParameters params) {
Utility.writeToTheFile(ChaseForceApplication.getAppContext(), "\n\n Job Scheduler StartJob with jobid="+params.getJobId()+" set at " + new Date().toString());
sendBroadcast(new Intent(this, OnSingleAlarmReceiver.class));
return false;
}
#Override
public boolean onStopJob(JobParameters params) {
Log.i(ChaseForceApplication.TAG, "on stop job: " + params.getJobId());
Utility.writeToTheFile(this, "on stop job: " + new Date().toString());
return false;
}
}
It's working on most devices, even in other xiaomi phones but in Xiaomi Redmi 3S it is not working.
Is any setting required for Job Schedular to make it work on that device?
It seems that Xiaomi MIUI operative system don't allow JobScheduler to run https://web.archive.org/web/20171001070316/http://c.mi.com/thread-8779-1-1.html
From your app settings/info page, try to enable auto start and then retry with job scheduler. It will work. You need to enable autostart for your app.
I'm trying to better understand the behavior of threads in my android app. For some reason, when I use while(true) in one of my worker threads, code within that thread's run method that exists sequentially BEFORE the while(true) loop never executes. To be clear, I'm not sure if the code(toast messages) actually isn't executing or if the way the thread synchronization is handled by the Android OS is causing my Toast messages not to display. This behavior appears to be some sort of blocking but I can't figure out why this happens.
My app uses 3 threads: the UI thread(default/main thread in an Android app), a thread to infinitely read data from the device's USB port during runtime, and a thread to process this data via messages from the USB-read thread. The problem seems to occur in my USBController class. When I comment out my infinite while loop, all of the Toast messages before the start of the loop display just fine. When I don't comment out my while(true), NO TOAST MESSAGES EVER DISPLAY! I'm pretty confused by this, I think i'm misunderstanding something fundamental about thread handling by the Android OS. Even if a while loop were to cause blocking, which i don't think it since it resides in a worker thread, why wouldn't the toast messages that occur before the while loop be triggered? Is this a synchronization issue? Am I misusing Android's Handler-Looper system?
Code below. Note: I've included the relevant portion of the main activity and the entirety of the USBController class. My implementation of this class relies heavily on the USB to Serial library found here mik3y/usb-serial-for-android. I don't think it's necessary, but i've included the class that contains my third thread, SensorDataBuffer, that receives messages from the thread UsbController.
UsbController.java
public class UsbController extends Thread{
...
#Override
public void run() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_DEFAULT); //sets thread to default queing priority
Looper.prepare();
Toast.makeText(mContext.getApplicationContext(), "Hello from UsbController's run method!", Toast.LENGTH_SHORT).show();
// **********************USB otg*******************************
//Obtain permission to use Android device's USB intent
PendingIntent mPermissionIntent;
mPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
// Find all available drivers from attached devices.
ProbeTable customTable = new ProbeTable();
customTable.addProduct(0x03EB, 0x2044, CdcAcmSerialDriver.class);
UsbManager manager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
UsbSerialProber prober = new UsbSerialProber(customTable);
List<UsbSerialDriver> availableDrivers = prober.findAllDrivers(manager);
if (availableDrivers.isEmpty()) {
Toast.makeText(mContext.getApplicationContext(), "No available USB drivers found",Toast.LENGTH_SHORT).show(); // Toast message for debugging
}
else { // open connection to first avail. driver
UsbSerialDriver driver = availableDrivers.get(0);
Toast.makeText(mContext.getApplicationContext(), "Driver found",Toast.LENGTH_SHORT).show(); // Toast message for debugging
UsbDeviceConnection connection = manager.openDevice(driver.getDevice());
Toast.makeText(mContext.getApplicationContext(), "Device Driver Opened",Toast.LENGTH_SHORT).show(); // Toast message for debugging
if (connection == null) { // You probably need to call UsbManager.requestPermission(driver.getDevice(), ..)
Toast.makeText(mContext.getApplicationContext(),"Connection to device not allowed, need permissions",Toast.LENGTH_LONG).show();
manager.requestPermission(driver.getDevice(),mPermissionIntent); //conn test
if (manager.hasPermission(driver.getDevice())==true){
Toast.makeText(mContext.getApplicationContext(),"Permissions granted",Toast.LENGTH_SHORT).show();
}
}
else { // Read some data! Most have just one port (port 0).
List<UsbSerialPort> myPortList = driver.getPorts();
UsbSerialPort port = myPortList.get(0);
Toast.makeText(mContext.getApplicationContext(),"USB OTG Connection Established",Toast.LENGTH_SHORT).show();
try {
port.open(connection);
port.setParameters(9600, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE); // sets baud rate,databits, stopbits, & parity
port.setDTR(true); //necessary to make Arduino Micro begin running it's program
Toast.makeText(mContext.getApplicationContext(),"port opened, parameters set, DTR set",Toast.LENGTH_SHORT).show();
byte buffer[] = new byte[16];
String incompPacket = "";
Toast.makeText(mContext.getApplicationContext(), "hi again!"), Toast.LENGTH_LONG).show();
while (true){ //continuous loop to read data
numBytesRead = port.read(buffer, 100);
arduinoData = new String(buffer, "US-ASCII");
String raw = arduinoData.substring(0, numBytesRead);
if (numBytesRead > 0) {
...
}
}
} catch (IOException e) {
Toast.makeText(mContext, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
}
Looper.loop();
}
}
MainActivity.java
...
#Override
protected void onCreate(Bundle savedInstanceState) {
//Multi-threading
//Create thread to handle incoming data from USB Controller thread
SensorDataBuffer pressureDataBuffer = new SensorDataBuffer(MainActivity.this);
Thread bufferThread = new Thread(pressureDataBuffer);
bufferThread.start();
//Create USB Serial Worker thread which will continuously receive data
UsbController serialDataLink = new UsbController(PlayFrets.this);
Thread sensorMonitorThread = new Thread(serialDataLink);
sensorMonitorThread.start();
//Toast.makeText(this, "USB Controller thread started", Toast.LENGTH_SHORT).show();
//Build GUI
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE); //Removes action bar from display
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); //Removes status bar from display
//Create AsyncTask to load the note files. A splash screen will be displayed while task is executing
new AsyncTask_NoteFileLoader(this).execute();
}
...
SensorDataBuffer.java
public class SensorDataBuffer extends Thread{
//Handler subclass which accepts messages one by one in
//the main activitiy's FIFO message que called a "Looper"
//The worker thread, sensorMonitor, runs UsbController in parallel
//with the UI thread and continuously formats and sends pressure sensor
//values read from the microcontroller to the Handler which updates the
//corresponding pressure state logic variables in the UI thread.
public void run(){
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO); //TODO:priority was previously more favorable, test this to ensure UI doesn't lag
Looper.prepare(); //create MessageQue to receive messages from USB Controller thread
UsbController.setHandler(bufferHandler);
bufferHandler = new Handler(Looper.myLooper()) {
//do stuff
};
Looper.loop();
}
}
How about using HandlerThreads, Handlers and Runnables instead? Makes your code a lot cleaner and easier to maintain.
In your onCreate() just create a couple of them:
HandlerThread usbThread = new HandlerThread("USBController");
usbThread.start();
usbHandler = new Handler(usbThread.getLooper());
HandlerThread sensorThread = new HandlerThread("SensorDataBuffer");
sensorThread.start();
sensorHandler = new Handler(sensorThread.getLooper());
Then you create your Runnables and post them to the Handlers
usbHandler.post(new Runnable(){
run(){
//....
numBytesRead = port.read(buffer, 100);
if (numBytesRead > 0) {
sensorHandler.post(new Runnable(){run(){//doSomething}});
}
//....
if(isStillRunning)
usbHandler.post(this);
}
});
You can let the runnable post itself and it will run forever. From within you can post runnables to other handlers (like the Main Thread Handler) to show your Toasts.