I am creating an Android application that needs to constantly send UDP packets to a server.
The problem is that after 2-5 minutes, after the screen is off, the service stops sending data, until I turn the screen back on (can also confirm on the server side).
Here is the service start command:
startService(new Intent(getBaseContext(), Service.class));
The onStartCommand:
#Override
public int onStartCommand(Intent intent, int flags, int startId){
Toast.makeText(this, "Armed!", Toast.LENGTH_LONG).show();
(new Thread(new App())).start();
return START_REDELIVER_INTENT;
}
And the run method, from the App thread:
public void run(){
Vibrator alarm = (Vibrator) MainActivity.getAppContext().getSystemService(Context.VIBRATOR_SERVICE);
String IP = getWiFiIP(MainActivity.getAppContext());
while(true){
while (IP.equals(wifiAddr)){ //Confirm presence on local network, with host
//Unlocked door
System.out.println("Started locally!");
sendData(KEY, hostAddrLocal);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
IP = getWiFiIP(MainActivity.getAppContext());
}
}
}
I am positive that I am not running out of memory, I have no other applications up, running on Galaxy S4, with Lollipop 5.0
Is it because I am interacting with the main activity, to get the context
MainActivity.getAppContext() ?
I tried many things, including STICKY_SERVICE.
The problem is that after 2-5 minutes, after the screen is off, the service stops sending data, until I turn the screen back on (can also confirm on the server side).
The CPU has powered down, as has the WiFi radio, in all likelihood. You would need to acquire a WakeLock and a WifiLock to keep both powered on. For ordinary users with ordinary hardware, keeping the CPU and WiFi radio on all the time will be very unpopular due to battery drain.
Android is programmed to fall asleep after inactivity. When that happens, it powers down the processor and doesn't allow any app to process. To be an exception to this rule, you need to hold a partial wakelock. Doing so will cause you to use more battery though so try not to hold it longer than needed.
Related
I'm working on an basic reminder application which has a user interface capable to log user set dates to an sqlite database. To complete my application I'd like to have a service which starts and keep running in the background when the phone is restarted, so I can periodically check the database and display a notification message to the user if any of the dates are close.
I can start my service on startup (I can send LOG messages to Android Studio) but I can't display notifications. It seems the examples I have found always rely on an activity, but how would I have an activity without having the main app running? (and ofcourse thats the point of my background service, the user doesnt have to open the main app).
Q1: How can I display a Toast message from this service?
Q2: How can I display a notification from this service?
public class autostartservice extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
for (int i = 0; i < 40; i++) {
synchronized (this) {
try {
wait(1000);
} catch (Exception e) {}
Log.i("myDebug", "Just wait here couple second until the phone boots all the way");
}
}
Toast.makeText(context, "Look at this Toast! Cool uh?", Toast.LENGTH_SHORT).show();
}
}
}
All these kind of things uses a Context to show a Toast or a Notification. A Service "acts as" a Context, so you can do quite the same things as you do in an Activity. You only have to pay attention that a normal Service runs in the same Thread of the UserInterface, so if the Service is blocking (waiting some event in a Blocking way) even the UserInterface is blocked and Notifications or Toasts cannot be displayed while blocked/frozen.
I have an activity that demonstrates information ping results about servers. This job is done every second with runnable like below code:
pingRunnable = new Runnable() {
#Override
public void run() {
pingMethod();
resultsNotification();
handler.postDelayed(this, 1000);
}
};
on the other hand, I have to show some results in the notification realtime and I did it, But there is a problem, that in the first when the device was locked after 10 min app and activity was destroyed by OS and just notification stopped in a stagnant status. then I tried to fix it with below code:
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
wl.acquire();
wl.release();
Actually, I used wl.release(); like this:
#Override
protected void onDestroy() {
wl.release();
super.onDestroy();
}
It got better when I did it, but when the device was locked this again happened after some hours.
Right now I don't know exactly how can I control these services (background(Runnable) and foreground(Notification) ) together for keeping alive activity for a long time such as two or three days or even more.
First up you shouldn't be using an Activity to do this kind of work a Service is the recommended approach. To do this kind of work intelligently you have a number of options with the Android framework, most recently the new WorkManager component. These services should offer you the ability to control when your background work is scheduled - e.g. only running when a valid network connection is present and by allowing you to select a period when the work should run.
https://developer.android.com/topic/libraries/architecture/workmanager/advanced
Secondly, polling every second will have a drastic impact on battery life - if you do this most likely your app will be flagged as a bad citizen and be a likely candidate for uninstalling.
You should consider whether polling in general is the best option for your users. Using cloud messaging would improve the amount of work your app needs to do to, i.e. you could just push out a notification when the server data changes, which avoids you having to do the manual polling. https://firebase.google.com/docs/cloud-messaging/
In Juice, the enum DetailedState adds a new state named
/** Link has poor connectivity. */
VERIFYING_POOR_LINK
but what is this state stand for?
Having searched for the entire project, I found this:
The subclass VerifyingLinkState in WifiStateMachine.java
class VerifyingLinkState extends State {
#Override
public void enter() {
if (DBG) log(getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
sendNetworkStateChangeBroadcast(mLastBssid);
}
#Override
public boolean processMessage(Message message) {
switch (message.what) {
case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
//stay here
break;
case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
try {
mNwService.enableIpv6(mInterfaceName);
} catch (RemoteException re) {
loge("Failed to enable IPv6: " + re);
} catch (IllegalStateException e) {
loge("Failed to enable IPv6: " + e);
}
setNetworkDetailedState(DetailedState.CONNECTED);
mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
sendNetworkStateChangeBroadcast(mLastBssid);
transitionTo(mConnectedState);
break;
default:
return NOT_HANDLED;
}
return HANDLED;
}
}
When it is verifying link state, in the enter() function, it sets the DetailedState to
DetailedState.VERIFYING_POOR_LINK
which will cause the user being confused for getting a state message like the following picture while the connection is really good.
Although this message only stays for a while and then replaced by "Connected" swiftly. But what is this state aim for? what are the risk if I don't set the DetailedState to VERIFYING_POOR_LINK in the enter() function.
VERIFYING_POOR_LINK for certain Samsung Devices are a part of the Smart Network Switch in Wi-Fi which can allow Mobile Data to remain connected over Wi-Fi if poor conditions occur. For Samsung Devices, that's when you would see a detailed state go from CONNECTED to VERIFYING_POOR_LINK.
Most of the logic can be found in: http://androidxref.com/4.3_r2.1/xref/frameworks/base/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
WifiWatchdogStateMachine monitors the connection to a WiFi network. When WiFi
connects at L2 layer, the beacons from access point reach the device and it
can maintain a connection, but the application connectivity can be flaky (due
to bigger packet size exchange).
We now monitor the quality of the last hop on WiFi using packet loss ratio as
an indicator to decide if the link is good enough to switch to Wi-Fi as the uplink.
When WiFi is connected, the WiFi watchdog keeps sampling the RSSI and the
instant packet loss, and record it as per-AP loss-to-rssi statistics. When
the instant packet loss is higher than a threshold, the WiFi watchdog sends a
poor link notification to avoid WiFi connection temporarily.
While WiFi is being avoided, the WiFi watchdog keep watching the RSSI to
bring the WiFi connection back. Once the RSSI is high enough to achieve a
lower packet loss, a good link detection is sent such that the WiFi
connection become available again.
BSSID roaming has been taken into account. When user is moving across
multiple APs, the WiFi watchdog will detect that and keep watching the
currently connected AP.
Power impact should be minimal since much of the measurement relies on passive statistics already being tracked at the driver and the polling is done when screen is turned on and the RSSI is in a certain range.
Although I can't give a for sure (or guaranteed) answer..
I would say that it means that the connection is weak (Less than 30%(?) signal)
Edit:
Android offers the ability to avoid/ignore low-connectivity access points - I'm assuming that this state is suggesting that the connection shouldn't be used.
I don't see any risks in ignoring/neglecting this state.
Edit 2:
In light of recent comments, I have taken this excerpt from the android documentation:
public static final NetworkInfo.DetailedState VERIFYING_POOR_LINK
Added in API level 16 Link has poor connectivity.
It can be found at NetworkInfo.DetailedState
I need some suggestions for approaches to take...
Here's some background info:
Right now I have an Android app and a separate java program running on my server.
The java program continuously go out and gets information from different sites and stores them in 14 different entries in an SQL database on the server.
The Android app then queries the databases to retrieve the info to be displayed.
My goal:
I need suggestions on how to have the app handle checking for updates from the database, and then letting the user know that there is new information.
My first thought is that maybe I need to start a separate thread that queries the database for a time modified. Then if it finds updates, it would pop up on the screen that there is new information.
I'm not too well educated with the way threads or services work, so I guess I'm looking for how to implement this, or whether there is a completely different way to go about update checking that would be better.
Thanks in advance, I appreciate any feedback, input, or suggestions.
Hi Ryan I have also implemented a similar thing in my android app and surprisingly I also had 14 tables in my PostgreSQL Server. First of all, you would want to poll the server periodically even when the app is not in the foreground. For that you need to run a background Service - here you will have to manually create a thread in the service, because Service by default runs on the UI thread OR use an IntentService - you don't have to create a separate thread. Whatever code you write in the intent service will be handled in a different thread automatically
Now you have to make this service execute periodically. For that use an AlarmManager and use the setRepeating()function. In the arguments you have to give a PendingIntent to your Service or IntentService. But don't use an alarm manager if you are going to poll the server for every less than 1 minute. Because the battery will be wasted a lot.
Here is some code that might give you an idea :
function setalarm()
{
Intent intent = new Intent(getBaseContext(), Intent_Service.class);
PendingIntent sender = PendingIntent.getBroadcast(getBaseContext(), 192837, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Random randomGenerator = new Random();
long interval=60000; //1 minute in milliseconds
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC, cal.getTimeInMillis(),interval,sender);
}
This is Intent_Service of type IntentService :
public class BackService extends IntentService
{
Context context=this;
//public Timer t=null;
public BackService()
{
super("myintentservice");
}
#Override
protected void onHandleIntent(Intent intent)
{
try
{
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
wl.acquire();
//..CPU will remain on during this section..
//make our network connections, poll the server and retrive updates
//Provide a notification if you want
wl.release();//Release the powerlock
}
}
}
But if you want instantaneous updates, then use Google Cloud Messaging Services. To know more about how it works see this
Hope this helps you.
After a long search I'm still confused about it although I found some related posts but they were not answering what I was looking for. in my scenario I want to send user's lat long at fixed intervals like every 5 minutes to a server. to do this I used a service with its own process so that it doesn't get killed when Activity destroyed and in service method onStartCommand I used a while loop having condition of always true and in that loop I update location to server and give delay by thread.sleep like shown below
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
While(true)
{
Location currentLocation = getLocation();
updateLocationToServer(currentLocation);
try
{
Thread.sleep(300000)
}
catch(Exception e)
{
e.printStackTrace()
}
}
return Service.START_STICKY;
here I cannot understand the return statement is unreachable then how can the service will be restarted automatically when it is destroyed and secondly using thread.sleep causing ANR (Application Not Responding) while service is a background process and I found that its directly running in UI thread these information are confusing me and in this case what is the best way to get desired functionality.
You should use and AlertManager instead!
AlarmManager mgr=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent i=new Intent(context, OnAlarmReceiver.class);
PendingIntent pi=PendingIntent.getBroadcast(context, 0, i, 0);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), PERIOD, pi);
http://justcallmebrian.com/?p=129
secondly using thread.sleep causing ANR (Application Not Responding) while service is a background process
from
http://developer.android.com/reference/android/app/Service.html
Note that services, like other application objects, run in the main thread of their hosting process.
Try using Alarm Manager instead, see this example of how to set it to repeat every 5 minutes