I've got the following scenario for Android: I have an app that when launched starts a service. That service checks a url every 30 minutes. If it gets a specific response, it sends a Broadcast which the app receives and processes. That scenario is working great for me.
I'd also like my service to continue running after the user has stopped running the application (app out of foreground.) Which I've got working as well.
The problem that I'm facing is that when the Activity receives the Broadcast message, I can't get the Activity to move back to the foreground. I've tried various combinations of intents, but haven't figured it out. What am I doing wrong?
My BroadcastReceiver code looks like this:
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
launchApp();
}
};
private void launchApp() {
Intent vukaniActivity = new Intent(this, Vukani.class);
// I've tried multiple different flags to no avail.
vukaniActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(vukaniActivity);
}
The flags you want are:
vukaniActivity.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
Also, make sure you're running on the UI Thread.
runOnUiThread(new Runnable(){
#Override
public void run(){
launchApp();
}
});
Related
I'm trying to create an autorun service: so that the application launches every time after unlocking the screen, after entering the graphic key or password if it exists (on Android 7,8,9,10). I wrote the code dynamically through the borocast receiver (ACTION_SCREEN_OFF) but it works while the application is on the stack (running) and I want it to always start. The method through registering in the manifest in android 9 already does not work the listeners. How to implement this?
public class WordsBase extends AppCompatActivity {
ScreenReceiver resiverStart;
#Override
protected void onPause() {
super.onPause();
resiverStart= new ScreenReceiver();
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
registerReceiver(resiverStart,filter);
}
}
public class ScreenReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Intent intent1 = new Intent(context, WordsBase.class);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent1);
}
throw new UnsupportedOperationException("Not yet implemented");
}
}
I understand that you want to do the following:
If the user unlocks the device, you want to start your app.
Why don't you do the following:
Use the USER_PRESENT receiver (android.intent.action.USER_PRESENT). Please not that you have to register explicitly to this receiver, just registering it in the manifest is not enough
If the respective broadcast is fired, start your app and make sure you are still registered to the broadcast (to have your app started again the next time the user unlocks the device).
I need to have a two way communication between my activity and a running IntentService.
The scenario is like this: the app can schedule alarms which on run, start an IntentService which fetches some data from web and process it. There are three possible situations when IntentService finishes:
The app is in focus, which means that when the IntentService will finish, the app needs to refresh its views with the new data.
The app is closed and when opened after IntentService has finished the work, so the app will have access to processed data
The app is opened while the IntentService is running, in which case I need to have a way from the activity to ask the IntentService if its doing something in the background.
For 1. I have already implemented a BroadcastReceiver in my activity which gets registered with the LocalBroadcastManager. When IntentService finishes the work, sends a broadcast and the activity reacts. This works fine
For 2. There is nothing needed to be done
For 3. I don't know what to do. So far I've tried this:
In Activity:
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(BROADCAST_SEND_TO_SERVICE));
In IntentService
private LocalBroadcastManager localBroadcastManager;
private BroadcastReceiver broadcastReceiverService = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(BROADCAST_SEND_TO_SERVICE)) {
//does not reach this place
//Send back a broadcast to activity telling that it is working
}
}
};
#Override
protected void onHandleIntent(Intent intent) {
localBroadcastManager = LocalBroadcastManager.getInstance(context);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BROADCAST_SEND_TO_SERVICE);
localBroadcastManager.registerReceiver(broadcastReceiverService, intentFilter);
.... //do things
}
The problem with my implementation is that n the IntentService the BroadcastReceiver does not fire onReceive. Any suggestions or maybe a simpler way for the Activity to ask the IntentService what it is doing?
LE:
Trying to get atomicboolean.
In Service:
public static AtomicBoolean isRunning = new AtomicBoolean(false);
#Override
protected void onHandleIntent(Intent intent) {
isRunning.set(true);
// do work
// Thread.sleep(30000)
isRunning.set(false);
}
In Activity, restarting the app while service is running:
Log(MyIntentService.isRunning.get());
//this returns always false, even if the intent service is running
On AndroidManifest
<service
android:name=".services.MyIntentService"
android:exported="false" />
I have a very strange bug happening in my app. I am building an Alarm APP and I am using SQLite to store alarm data and broadcast receivers to manage the alarm manager calls.
The code of onReceive strangely does not behave in the same way in some cases. I try to start an Activity when the receiver receives a broadcast, nearly 90% of cases everything goes well and I manage to start the Activity, but in some cases strangely enough the receiver executes the instruction "startActivity(i)" but nothing happens.
It is really hard to reproduce the BUG and during my debugging I have learned what I have mentioned, but more than this is really difficult for me to understand how a call to startActivity() in most cases works and in some cases does not work. I have searched through the Stack community but no one seamed to have this kind of problem, everybody just had problems in starting the activity because they had not set the flag or because they had not registered the receiver in the manifest. Below I am posting the code.
public class AlarmReceiver extends WakefulBroadcastReceiver {
// The app's AlarmManager, which provides access to the system alarm services.
private AlarmManager alarmMgr;
// The pending intent that is triggered when the alarm fires.
private PendingIntent alarmIntent;
#Override
public void onReceive(Context context, Intent intent) {
Utils.logToFile("Received Alarm ,I am in onReceive(), ALARM ID: "+intent.getExtras().getInt(Constants.ALARM_ID));
Intent intent = new Intent(context, StopAlarm.class);
Bundle b = new Bundle();
b.putInt(Constants.ALARM_ID, intent.getExtras().getInt(Constants.ALARM_ID));
if(intent.getExtras().containsKey(Constants.SNOOZE_ALARM)){
b.putString(Constants.SNOOZE_ALARM, intent.getExtras().getString(Constants.SNOOZE_ALARM));
}
i.putExtras(b);
//this flag is needed to start an Activity from a BroadcastReceiver
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
//this method reads from the DB and sets the next alarm
//I tried commenting this method so that no DB action is
//performed and still the bug happened
setAlarm(context.getApplicationContext());
//this method just logs data into a file that I have created to keep track of events
//since not always the device is connected with LogCat
Utils.logToFile("Received Alarm, Intent(context, StopAlarm.class);");
}
Do I need to set any other flag and how is it possible that startActivity(intent) behaves incorrectly in some cases?
EDIT
<activity
android:label="#string/app_name"
android:name="package.activity.StopAlarm"
android:windowSoftInputMode="stateAlwaysHidden"
android:screenOrientation="sensorPortrait">
</activity>
<receiver android:name="package.receivers.AlarmReceiver" />
I have finally solved the issue by creating an IntentService and by starting the activity from the IntentService and setting two flags to the Intent. After doing this I placed the code which reads from DB in the activity that is started from the IntentService. I have tested nearly 60 times the behaviour and in all the tests the app behaved correctly. I am posting the code below.
public class MyAlarmReceiver extends WakefulBroadcastReceiver {
// The app's AlarmManager, which provides access to the system alarm services.
private static AlarmManager alarmMgr;
// The pending intent that is triggered when the alarm fires.
private static PendingIntent alarmIntent;
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, AlarmIntentService.class);
Bundle b = new Bundle();
b.putInt(Constants.ALARM_ID, intent.getExtras().getInt(Constants.ALARM_ID));
if(intent.getExtras().containsKey(Constants.SNOOZE_ALARM)){
b.putString(Constants.SNOOZE_ALARM, intent.getExtras().getString(Constants.SNOOZE_ALARM));
}
i.putExtras(b);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startWakefulService(context, i);
}
This is the IntentService I needed to implement
public class AlarmIntentService extends IntentService {
public AlarmIntentService() {
super("AlarmIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
Intent i = new Intent(this, StopAlarm.class);
Bundle b = new Bundle();
b.putInt(Constants.ALARM_ID, intent.getExtras().getInt(Constants.ALARM_ID));
if(intent.getExtras().containsKey(Constants.SNOOZE_ALARM)){
b.putString(Constants.SNOOZE_ALARM, intent.getExtras().getString(Constants.SNOOZE_ALARM));
}
i.putExtras(b);
//THESE ARE THE FLAGS NEEDED TO START THE ACTIVITY AND TO PREVENT THE BUG
//(CLEAR_TASK is crucial for the bug and new task is needed to start activity from outside of an activity)
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);
MyAlarmReceiver.completeWakefulIntent(intent);
}
}
This is the activity started by the IntentService. Here I set the next alarm.
public class StopAlarm extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.stop_alarm);
//this method reads from the DB and sets the next alarm
MyAlarmReceiver.setAlarm(getApplicationContext());
...
I had a similar issue. From my experience the problem is,that startActivity(i) is asynchronous. So in this case the program will simultaneously call activities onCreate(),onStart(), etc. but also call QlokAlarmReceiver.completeWakefulIntent(intent) (without waiting for the activity to be closed), which will release the wakeLock. Because of that the device can go to sleep during executing the activities onCreate() or onStart().
Rubin, I know, that my answer is a contradiction of your solution, but my logs clearly indicated such order of events:
- startActivity called
- onCreate of the activity called
- completeWakefulIntent(intent); called in between logs from onStart of the activity
My workaround this is to start a wakelock with a timeout of eg 20 seconds just before calling startActivity and then start another wakeLock in the activities onCreate, which will be released in the onDestroy method.
I'm not sure if my solution goes along with best practises, but I haven't found a better solution so far.
public class BootUpReceiver extends BroadcastReceiver{
#Override
public void onReceive(final Context context, Intent intent) {
//Delay 10 sec so that device could establish network
Intent i = new Intent(context, SplashActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
I m starting an activity on android startup but android takes some time to establish network connection therefore I want to delay 10 sec the app launch so that my app can use internet.
Best practice for doing this kind of background processing stuff is to create a splash screen and load it using various animation and =attractive stuff at the start up of heavy activity for a particular amount of time. So that user can wait until background process done.
Your approach seems like lots of coding required.
Hope this would help.
Instead of making some random delay (which is not dependable), you should use another BroadcastReceiver that can detect network state change. For example: NetworkStateReceiver. This will detect when network state is changed. See here: Check INTENT internet connection
So your application should have 2 BroadcastReceivers: BootupReceiver and NetworkStateReceiver.
In BootUpReceiver's onReceive(), set some flag in preferences as true.
Then in NetworkStateReceiver's onReceive(), check the flag, if the flag is true, open your Activity and set the flag as false.
(To set values in SharedPreferences in a BroadcastReceiver, see: Shared preferences inside broadcastreceiver)
Although this will work, but this is not a good practice to automatically starting an Activity. User may not like it.
Make it with a runnable
public class BootUpReceiver extends BroadcastReceiver{
#Override
public void onReceive(final Context context, Intent intent) {
Handler handler = new Handler();
int delay = 100;
handler.postDelayed(startApp, delay);
Runnable startApp = new Runnable() {
#Override
public void run() {
Intent i = new Intent(context, SplashActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
};
}
}
App A has this BroadcastReceiver in its manifest (within <application>):
And this receiver:
public class RemoteControl extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.w(TAG, "Look what I did!");
}
}
I'm trying to trigger this from App B:
public void onClick(View v) {
Log.w(TAG, "Sending stuff");
Intent i = new Intent("app.a.remotecontrol");
i.setData("http://test/url");
sendBroadcast(i);
}
For whatever reason, the onReceive() in App A is never triggered even though it's broadcasted from App B. What can be the cause of this?
EDIT & SOLUTION: I forgot to write that I used setData() on the Intent before broadcasting it. That was indeed the problem: as soon as I removed setData(), the broadcast worked as intended.
Originally I forgot to write that I used setData() on the Intent before broadcasting it. That was indeed the problem: as soon as I removed setData(), the broadcast worked as intended.
I've switched to use putExtra() instead for the Intent metadata:
Intent i = new Intent("app.a.remotecontrol");
i.putExtra("url", "http://test/url");
sendBroadcast(i);