What would be the behaviour of onHandleIntent on device shutdown?
I know on IntentService, the service keeps running as long as onHandleIntent didn't finish its job.
so coming to think about it its a general question about services behaviour on device shutdown, will they "wake" up once we start up the device again?
if not, theres a way to do so? I want my intentservice to keep running until onHandleIntent finished, no matter what happened.
EDIT:
I will add my code for better understanding.
I'm trying to save request on SQLite and keep running until that table is empty, then the service will shut down.
so incase of device shutdown, I will still continue from the same spot I was before the shudown.
P.S - I try to use executor for better performances (its a test and not proven yet)
and if someone got better suggestion I’d love to hear them cause Im new in this subject
onHandleIntent
#Override
protected void onHandleIntent(Intent intent) {
helper = new DBHelper(getApplicationContext());
executor = Executors.newFixedThreadPool(5);
File file;
Log.e("requestsExists",helper.requestsExists()+"");
while (helper.requestsExists()) {
ArrayList<String> requestArr = helper.getRequestsToExcute(5);
//checks if the DB requests exists
if (!requestArr.isEmpty()) {
//execute them and delete the DB entry
for(int i = 0; i < requestArr.size(); i++) {
file = new File(requestArr.get(i));
Log.e("file",file.toString());
Future<String> future = executor.submit(new MyThread(file,getApplicationContext()));
Log.e("future object", future.toString());
try {
long idToDelete = Long.parseLong(future.get());
Log.e("THREAD ANSWER", future.get() + "");
helper.deleteRequest(idToDelete);
} catch (InterruptedException e) {
Log.e("future try", "");
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
executor.shutdown();
}
MyThread
public class MyThread implements Callable {
private File _file;
private Context context;
private DBHelper helper;
public MyThread(File file, Context context) {
this._file = file;
this.context = context;
}
#Override
public String call() throws Exception {
HttpClient client = Utility.getNewHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpPost post = new HttpPost("http://192.168.9.62/mobile_api/timeline/moment/upload");
try {
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
FileBody fileBody = new FileBody(_file);
builder.addPart("content", fileBody);
builder.addPart("type", new StringBody("file", ContentType.TEXT_PLAIN));
builder.addPart("title", new StringBody("service test", ContentType.TEXT_PLAIN));
builder.addPart("userType", new StringBody("user", ContentType.TEXT_PLAIN));
builder.addPart("uid", new StringBody(MyInfiActivity.friends_uid, ContentType.TEXT_PLAIN));
builder.addPart("momentId", new StringBody("1", ContentType.TEXT_PLAIN));
builder.addPart("storyId", new StringBody("8", ContentType.TEXT_PLAIN));
Utility.addCookiesToPost(post);
post.setEntity(builder.build());
client.execute(post, localContext);
} catch (IOException e) {
Log.e("Callable try", post.toString());
}
return "1";
}
}
I'll try to go point by point to answer your question
What would be the behaviour of onHandleIntent on device shutdown?
Since the device is going to be totaly turned off, all services will be forced and stopped including your IntentService.
You can subscrive to ACTION_SHUTDOWN to know when the device is going to be turned off and do some stuff before it actually goes off.
public class ShutdownHandlerReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Handle here, but don't make actions that takes too long
}
}
Also you will have to add this to the manifest
<receiver android:name=".ShutdownHandlerReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_SHUTDOWN" />
</intent-filter>
</receiver>
Will they "wake" up once we start up the device again?
No they will not, because they have already disposed and stuff like that, but you can subscribe to ACTION_BOOT_COMPLETED to know when the device is ready. From the doc:
Broadcast Action: This is broadcast once, after the system has
finished booting. It can be used to perform application-specific
initialization, such as installing alarms. You must hold the
RECEIVE_BOOT_COMPLETED permission in order to receive this broadcast.
public class BootedReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Handle here, you can start again your stopped intentservices
}
}
And in the manifest:
<receiver android:name=".BootedReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
And then you can restart the quee of your intenter service, and I guess this is the best way to go for it.
AFAIK, your IntentService will stop when the device shutdown if it was running at that time and the system will NOT automatically start it for us on boot, we will have to do this ourselves.
See this for how to do it.
Related
I am writing an android app that listens for incoming text messages and then syncs with the backend. The app so far is running ok but after some time the app does not receive any incoming message unless when I open the app again. Have explored various options suggesting implementing JobService or just service but the more research am doing the more am getting confused. For the JobService I can see it only meant to schedule job after some time and the service might be killed once the job completes. I need help on the best way to work it out. Below is my code for the broadcast receiver class;
public class MessageReceiver extends BroadcastReceiver {
private static MessageListener messageListener;
Handler mHandler;
private static final String TAG = "MessageReceiver";
public MessageReceiver() {
}
#TargetApi(Build.VERSION_CODES.M)
#Override
public void onReceive(final Context context, Intent intent) {
if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.getAction())) {
final Bundle data = intent.getExtras();
HandlerThread bgHandlerThread=new HandlerThread("MyCoolBackgroundThread");
bgHandlerThread.start();
mHandler=new Handler(bgHandlerThread.getLooper());
Runnable backgroundRunnable = new Runnable() {
#Override
public void run() {
passReceivedMsg(data);
}
};
mHandler.post(backgroundRunnable);
}
}
I have implemented an interface where I pass the received TextMessage Object,
private void passReceivedMsg(final Bundle bundleData) {
if (bundleData !=null ){
try {
final Object[] pdusObj = (Object[]) bundleData.get("pdus");
if (pdusObj != null) {
for (int i = 0; i < pdusObj.length; i++) {
SmsMessage currentMessage =
SmsMessage.createFromPdu((byte[]) pdusObj[i]);
Log.d(TAG, "run: currentMessage: "+currentMessage);
messageListener.getReceivedMessage(currentMessage);
Log.d(TAG, "handleMessage: message "+currentMessage);
}
}
}
catch (Exception e){
Log.d(TAG, "onReceive: Error occurred "+e);
}
}
}
public static void bindListener (MessageListener listener){
messageListener = listener;
}
The MessageListener is as below;
public interface MessageListener {
void getReceivedMessage(SmsMessage message);
}
Below is my manifest
<receiver
android:name=".utils.MessageReceiver"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
I do not know where you think your MessageListener object is coming from.
Please understand that your application process does not run forever. As soon as your app's UI moves to the background, Android can terminate your process at any time. This gets rid of any MessageListener object that may exist.
And so what is happening, most likely, is that you set up this MessageListener, and it works for a while, until that original process gets terminated. Future SMS messages will start a fresh process for your app, but you no longer have your MessageListener, and so your work does not get done.
Instead, get rid of MessageListener and whatever static field you are using to hold the reference to it. Have your BroadcastReceiver use WorkManager to do its background work, where your Worker can handle everything itself, without relying upon any activities, etc. of your app necessarily having run in the current process.
I'm trying to test a started service that will return its results via a local broadcast receiver/intent.
I have that working... but I want to unit/integration test it.
I can find plenty of code on how to do that in the old ApplicationTestCase/etc way, but I can't find a good example of how to do it in the new 'Rule' framework.
Here is the Integration Test I have so far...
LocalBroadcastManager instance = LocalBroadcastManager.getInstance(
InstrumentationRegistry.getTargetContext());
final boolean[] called = new boolean[1];
final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
called[0] = true;
}
};
instance.registerReceiver(receiver, new IntentFilter("com.foo"));
mServiceRule.startService(serviceIntent);
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
assertThat(called[0], is(true));
and here is the relevant part of the (this is all a sample project I made to proof of concept how to get this code working) onHandleIntent code:
Intent returnIntent = new Intent();
intent.setAction("com.foo");
LocalBroadcastManager.getInstance(getApplicationContext()).
sendBroadcast(returnIntent);
Note: I'd even accept a robolectric solution as long as it used the newest 3.2 API (Need that for API 25 support)
I have an app that downloads data and put it into an SQLite Database when a notification is issued. This works fine while the app is in use but I need it to work when the app is closed too.
I have set up a BroadcastReceiver within that is called when the app is closed but I'm not sure how to get it to continue with adding to the database.
Here is the code I am using:
AndroidManifest.xml
<manifest....
<application...
<receiver android:name=".broadcast.PacksReceiver" >
<intent-filter>
<action android:name="ADD_PACK" >
</action>
</intent-filter>
</receiver>
PacksReceiver
public class PacksReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("PacksReceiver", "onReceive");
String message = intent.getStringExtra("message");
PacksActivity pa = new PacksActivity();
pa.downloadPack(null, message);
}
}
PacksActivity
public void downloadPack(View v, String thisPackID){
Log.d("download", "pack");
//THIS LOG IS CALLED EVERYTIME
vRef = v;
if(vRef != null){
runOnUiThread(new Runnable() {
#Override
public void run() {
onScreenProgressBar = (ProgressBar) vRef.findViewById(R.id.onScreenProgress);
onScreenProgressCircle = (ProgressBar) vRef.findViewById(R.id.onScreenProgressCircle);
dlPercent = (TextView) vRef.findViewById(R.id.dlPercent);
onScreenProgressCircle.setVisibility(View.VISIBLE);
onScreenProgressBar.setVisibility(View.VISIBLE);
onScreenProgressCircle.setProgress(0);
}
});
}
if(thisPackID == null){
thisPackID = pack_id;
}
String url = MyApp.getAppContext().getString(R.string.serverURL) +
MyApp.getAppContext().getString(R.string.getAppendixA) + "/" + thisPackID;
Intent appA_Intent = new Intent(Intent.ACTION_SYNC, null, this, DownloadService.class);
appA_Intent.putExtra("url", url);
appA_Intent.putExtra("onCreate", "false");
appA_Intent.putExtra("receiver", downloadPackReceiver);
appA_Intent.putExtra("downloadType", "GET_APPENDIX_A");
appA_Intent.putExtra("requestId", 101);
MyApp.getAppContext().startService(appA_Intent);
}
start the Service from
onReceive()
method because you can get mutiple broadcast one after another.
Write the code to add data in your database in your PackReciever inside OnRecieve() Method because that is where you recieve push notifications.
Don't call activity inside the receiver. Instead, use IntentService to download all packs. IntentService automatically finishes once it completes its work.
Override its onHandleIntent() method and download packs and save to database there.
I have problem and after some search I have not found any positive solutions.
After research I have idea that there is not implementation for my problem but this question may be is my last chance.
What do I need to get?
There is application that gets information about mobile network strength signal. I do it by
PhoneStateListener. Of course it works great but when my device goes to sleep mode, listener does not work:
https://code.google.com/p/android/issues/detail?id=10931
https://code.google.com/p/android/issues/detail?id=7592
WakeLock solves problem only in case, if device switch off by timeout. In case when I press hard power button, my device gets sleep mode as well. We can not override power button action.
My goal is get strength signal always when my device is enabled. It does not matter what mode is. All time it should collecting data.
Question:
Are there any ideas? How to achieve that? Are there ways to do this or may be there are some hacks? All solves are welcome. If you had some useful experience, please share this.
Thanks to all for help!!! I hope, this topic will get complete information about this problem.
Alarm manager is the way to go - the tricky part is to keep the phone awake after the alarm manager receiver returns. So
setup an alarm (notice you should also register an "On Boot completed" receiver to set up the alarm after a reboot - your alarms do not survive a reboot) :
Intent monitoringIntent = new Intent(context, YourReceiver.class);
monitoringIntent.setAction("your action");
PendingIntent pi = PendingIntent.getBroadcast(context, NOT_USED,
monitoringIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager)
context.getSystemService(Context.ALARM_SERVICE);
// here is the alarm set up
am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + INITIAL_DELAY,
INTERVAL_BETWEEN_ALARMS, pi);
receive it - the receiver holds a WakeLock in its onReceive() which never fails :
public abstract class YourReceiver extends BroadcastReceiver {
#Override
final public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if ("your action".equals(action)) {
// monitoring - got broadcast from ALARM
try {
d("SS : " + new Signal().getSignalStrength(context));
} catch (InterruptedException e) {
e.printStackTrace();
}
// Actu8ally the lines above will ANR
// I did it with WakefulIntentService :
// WakefulIntentService.sendWakefulWork(
// context, YourWakefulService.class);
// Will be posting it asap
} else {
w("Received bogus intent : " + intent);
return;
}
}
}
If you are lucky (yourRetrieveSignal() is fast enough) this will work, otherwise you will need a (Wakeful)IntentService pattern in your receiver.
The WakefulIntentService will take care of the wake lock (if you want to avoid a dependency have a look here) - EDIT : keep in mind you can't define listeners in an intent service - see here.
If the receiver ANRs on you, you have to try the WakefulIntentService pattern. In either case you might use this :
This proved the most difficult part actually :
class Signal {
static volatile CountDownLatch latch; //volatile is an overkill quite probably
static int asu;
private final static String TAG = Signal.class.getName();
int getSignalStrength(Context ctx) throws InterruptedException {
Intent i = new Intent(TAG + ".SIGNAL_ACTION", Uri.EMPTY, ctx,
SignalListenerService.class);
latch = new CountDownLatch(1);
asu = -1;
ctx.startService(i);
Log.d(TAG, "I wait");
latch.await();
ctx.stopService(i);
return asu;
}
}
where :
public class SignalListenerService extends Service {
private TelephonyManager Tel;
private SignalListener listener;
private final static String TAG = SignalListenerService.class.getName();
private static class SignalListener extends PhoneStateListener {
private volatile CountDownLatch latch;
private SignalListener(CountDownLatch la) {
Log.w(this.getClass().getName(), "CSTOR");
this.latch = la;
}
#Override
public void onSignalStrengthChanged(int asu) {
Signal.asu = asu;
latch.countDown();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.w(TAG, "Received : " + intent.getAction());
Tel = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
listener = new SignalListener(Signal.latch);
#SuppressWarnings("deprecation")
final int listenSs = PhoneStateListener.LISTEN_SIGNAL_STRENGTH;
Tel.listen(listener, listenSs);
return START_STICKY;
}
#Override
public void onDestroy() {
Log.w(TAG, "onDestroy");
Tel.listen(listener, PhoneStateListener.LISTEN_NONE);
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
This is working code (but not the pinnacle of elegance admittedly - comments/corrections welcome). Do not forget to register your services in the manifest and acquire permissions.
EDIT 2013.07.23 : I did not use the onReceive - if you use it it will ANR - this is working code if you use a WakefulIntentService in onReceive and in there you call SignalListenerService.
From my understanding of PhoneStateListener you can't do this while the application CPU is in sleep mode. You can either keep the device awake, which would ruin battery life. Alternatively you can use an alarm (see AlarmManager) to wake the device on intervals, so you can collect the data (impacts battery life still).
Some samples of using AlarmManager can be found here
CommonsWare's location polling example is really good about waking the phone and putting it to sleep again. I think it might help have a look: https://github.com/commonsguy/cwac-locpoll
One of the possible workarounds of android issue 10931 is to send the android.intent.action.SCREEN_ON intent to the 'phone' process after the screen turned off.
Create and register BroadcastReceiver to listen for notifications when the screen turns off
start(Context context) {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
context.registerReceiver(mScreenReceiver, filter);
}
final BroadcastReceiver mScreenReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
Log.v(LOGTAG, "Screen is off. Running workaround");
new Thread(mReportScreenIsOnRunnable).start();
}
}
};
Send the SCREEN_ON intent to the phone process only.
public final Runnable mReportScreenIsOnRunnable = new Runnable() {
#Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Runtime.getRuntime().exec(new String[] { "su", "-c",
"am broadcast -a android.intent.action.SCREEN_ON com.android.phone" });
} catch (IOException e) {
e.printStackTrace();
}
}
};
After receiving this intent the phone process would resume sending cell location
updates.
Root privileges are required.
This solution is a bit hacky, dangerous and works not on all phones. It can lead to higher power consumption, but not so much more than if you keep the screen turned on.
I have an app in which I'm trying to detect WHEN the Internet connection appears and when it disappears.
At the moment, when it appears, I'm starting a new thread (different from the UI) which connects my app to a remote server.
For that I'm hardly trying to implement a broadcast receiver which LISTENS for connectivity, but I'm having problems in understanding the concept.
In my onCreate() I have somethig like:
onCreate()
{
cThread = new Thread(new ClientThread(syncToken));
cThread.start();
}
When there is connection to the Internet I'm sending data through the socket, when there is not I'm storing the data in a database. And when the Internet appears I'm restarting my thread to reconnect and send the old data (which hasn't been sent because of network crashing) and the new one.
Let's say I would implement something like this:
DoRefreshBroadcastReceiver refreshBroadcastReceiver;
...
onResume() {
// register the refreshing complete broadcast receiver
IntentFilter intentFilter = new IntentFilter(DO_REFRESH);
refreshBroadcastReceiver = new doRefreshBroadcastReceiver();
registerReceiver(refreshBroadcastReceiver, intentFilter);
}
public class DoRefreshBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// call method to run fetch code...
}
}
Does this mean that when the Internet connection is detected my onReceive() gets called? And I could start my thread there?
What is the concept of using an intent? Because I really don't get it. How to use it, and what its purpose?
THE IDEA: I don't really know how to use this intent in this case or how to use it in my app!
Would this thing detect the connection to the Internet even when I'm not in this activity?
EDIT:
Here is how my onReceive looks like:
onCreate()
{
cThread = new Thread(new ClientThread(syncToken));
// cThread.start();
connIntentFilter = new IntentFilter(
"android.net.conn.CONNECTIVITY_CHANGE");
connListener = new MyConnectivityListener();
}
public void onReceive(Context context, Intent intent)
{
mNetworkInfo = (NetworkInfo) intent
.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (mNetworkInfo != null && mNetworkInfo.isConnected())
{
/*
* if(mNetworkInfo.getType()==ConnectivityManager.TYPE_WIFI);
*
*
* else
*/
cThread.start();
}
else {
System.out.println("There is no internet connection!");
try {
cThread.stop();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
mNetworkInfo != null && mNetworkInfo.isConnected()
Does this mean it's connected or should I verify for a certain type of connection on the emulator?
*I think that I should start my thread directly in onReceive(). As soon as my app starts it detects the Internet connection and BroadcastReceiver gets fired, doesn't it?
Try something like this...
public class MyActivity extends Activity {
private MyConnectivityListener connListener = null;
private IntentFiler connIntentFilter = null;
private Boolean connIntentFilterIsRegistered = false;
#Override
protected void onCreate(...) {
...
connIntentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
connListener = new MyConnectivityListener();
}
#Override
protected void onResume() {
...
if (!connIntentFilterIsRegistered) {
registerReceiver(connListener, connIntentFilter);
connIntentFilterIsRegistered = true;
}
}
#Override
protected void onPause() {
...
if (connIntentFilterIsRegistered) {
unregisterReceiver(connListener);
connIntentFilterIsRegistered = false;
}
}
protected class MyConnectivityListener extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// The NetworkInfo for the affected network is sent
// as an extra; it should be consulted to see what
// kind of connectivity event occurred.
}
}
}
A BroadcastReceiver is effectively a 'listener' which listens for events either sent by the system or, in some cases, by your own application components.
In this case, the system broadcasts android.net.conn.CONNECTIVITY_CHANGE whenever there is a connection change (connected/disconnected). By registering your BroadcastReceiver to 'listen' for that event, you can get the extra included in the Intent from your BroadcastReceiver's onReceive(...) method and do whatever you need to do accordingly. The extra is a `NetworkInfo object which will contain information about the particular network and whether it is connected or not.