I need to do a logout after some time, so I'm opening the login window in my app using.
startActivity(intent);
Problem is that, if the user has my app in the background, my activity will pop up.
Is there a way to easily open an activity but keep my app in the background?
It can be done with Android appliaction component Service.
you can read about it in official documentation by links below.
https://developer.android.com/training/run-background-service/create-service#java
https://developer.android.com/guide/components/services?hl=en
Initialize your Service
public class MyBackgroundService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
//onCreate - Service created
}
#Override
public void onDestroy() {
//onDestroy - Service destroyed (Stopped)
}
#Override
public void onStart(Intent intent, int startid) {
//onStart - Service started
}
}
Then call Service in your Main Activity
startService(new Intent(this, MyBackgroundService.class));
And don't forget about declare in Manifest.
<service android:enabled="true"
android:name=".MyBackgroundService" />
You can implement "local logout" after some time and when user returns to activity you can detect it. More: https://developer.android.com/guide/components/activities/activity-lifecycle
Related
I am have main activity (to-do list application) that also call to background Service and this Service call to some GooglePlacesActivity class. the problem is when the Service is on the application go to the GooglePlacesActivity instead of doing this activity in the background and do the main activity as it should to do.
I will be happy if you can explain to me why this is happened and how I can fix this problem.
( I have two activities and one service. the main activity need to work in the front and the service need to call to the second activity and do the second activity in the background )
Background Service -
public class BackgroundProcess extends Service {
public GooglePlacesActivity PlacesActivity;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Let it continue running until it is stopped.
Intent in=new Intent().setClass(BackgroundProcess.this,GooglePlacesActivity.class);
in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(in);
return START_STICKY;
}
}
My program purpose: trigger the BACK button in a service
I tried many ways, no one can achieve this purpose, finally I discovered AccessibilityService, it may be the most possible ways to implement this function.
I created this AccessibilityService, and tested it is work
package com.accessibilityservice;
public class MyAccessibilityService extends AccessibilityService {
public MyAccessibilityService() {
}
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
}
#Override
public void onInterrupt() {
}
}
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackSpoken"
android:accessibilityFlags="flagDefault"
android:canRetrieveWindowContent="true"
android:description="desc"
android:notificationTimeout="100"
android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity" />
And then I tried to move performGlobalAction to service, but it does not perform the action.
public class MyService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
MyAccessibilityService mas=new MyAccessibilityService();
mas.performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
}
}
I also tried to send a custom event in different way, but no one can send to MyAccessibilityService
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//method1
AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_CLICKED);
event.setContentDescription("this is description");
View view = new View(this);
ViewParent parent = view.getParent();
if (parent != null) {
parent.requestSendAccessibilityEvent(view, event);
}
//method2
AccessibilityManager manager = (AccessibilityManager) getApplicationContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
AccessibilityEvent event = AccessibilityEvent.obtain();
event.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT);
event.setPackageName("p");
event.setClassName("c");
manager.sendAccessibilityEvent(event);
}
How can I send a custom event or message to MyAccessibilityService, so that I can recognize the event and message to perform the action?
1 Create a static field of your service
public static MyAccessibilityService instance;
2 then initialize it in onServiceConnected()
#Override
protected void onServiceConnected() {
super.onServiceConnected();
instance = this;
}
3 don't foget to clear it in onUnbind()
#Override
public boolean onUnbind(Intent intent) {
instance = null;
return super.onUnbind(intent);
}
onServiceConnected() is called by the System when user gives permission to your app in device Settings
onUnbind() is when users revokes that permission
4 Use your instance everywhere you want
if(MyAccessibilityService.instance != null)
MyAccessibilityService.instance.performGlobalAction(....)
You can't run an Accessibility service the way in which you are attempting. Accessibility services can only be run as accessibility services. The reason here is obvious, if you understand the capabilities of accessibility services. Things they can do:
Track keyboard events
Investigate contents of text fields
arbitrarily send action events
These can be easily used for malicious purposes. Allowing an application, or even a standard service, to launch a process that can do such things would be terrible security. As such only the system can launch an accessibility service, with all of the relevant permissions. The only place it does this is from the accessibility settings menu. This way users know, at least, when an accessibility service is running, and can be more cautious about the types of these services that they run.
This also leads to the overall answer: You can't. At least not without running an actual accessibility service, and then sending intents directly to that. But the user would have to separately launch your service themselves.
I am not sure if this is the correct way to go but it will get your work done. Have a broadcast receiver registered within your Accessibility service method onServiceConnected() like below.
#Override
protected void onServiceConnected() {
super.onServiceConnected();
registerReceiver(listener, new IntentFilter("my_custom_event_listener));
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(listener);
}
And your broadcast receiver within your accessibility service
private class MyCustomListener extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
if(extras != null){
String action = extras.getString("action");
if(action.equals("back")
performGlobalAction(GLOBAL_ACTION_BACK);
}
}
Instead of creating an event, send a broadcast like below to perform back action:
Intent back = new Intent("my_custom_event_listener");
Bundle data = new Bundle();
data.putString("action", "back");
back.putExtras(data);
sendBroadcast(back);
I am implementing in my Android app a splash screen which:
dowloads a sqlite database from a server
loads urls to get JSONs
creates a sqlite database in the device and execute several queries
I am using AsyncTask to do everything, my problem will occur if the user close the app in the middle of the process or turn off the device because the app:
could be creating a database or executing crucial queries in the device
could be downloading the sqlite db from a server
could be running several important process
etc
Definitely, the entire process (3-5 seconds) is important.
So... How could I avoid this? should I use handlers, loaders, on-(pause, stop, destroy) methods in order to get my objective? Can you give me an example?
As mentioned in the comment above, you should use a service as their lifecycle is separate to that of the activity.
Create the service like so:
public class MyService extends Service {
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Do everything you need to here, then call stop:
Log.d("DEBUG", "Started...");
stopSelf();
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
Intent intent = new Intent("com.example.androidexample.SERVICE_STOPPING");
sendBroadcast(intent);
super.onDestroy();
}
}
Then in the activity:
public class MainActivity extends Activity {
private ServiceCompleteReceiver receiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter filter;
receiver = new ServiceCompleteReceiver();
filter = new IntentFilter("com.example.androidexample.SERVICE_STOPPING");
startService(new Intent(this, MyService.class));
registerReceiver(receiver, filter);
}
public class ServiceCompleteReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Do whatever needs to be done here
unregisterReceiver(receiver);
}
}
}
EDIT :
Don't forget to add it to your manifest as well
<service
android:name="com.example.androidexample.MyService"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
</service>
If my app is running and I press home button, the app goes in background. Now if I long press the home button and kill the app by swiping it from the recent app list, none of the events like onPause(), onStop() or onDestroy() gets called rather the process is terminated.
So if i want my services to stop, kill notifications and unregister listeners, how can I do that?
I just resolved a similar kind of issue.
Here is what you can do if its just about stopping service when application is killed by swiping from Recent app list.
Inside your Manifest file, keep flag stopWithTask as true for Service. Like:
<service
android:name="com.myapp.MyService"
android:stopWithTask="true" />
But as you say you want to unregister listeners and stop notification etc, I would suggest this approach:
Inside your Manifest file, keep flag stopWithTask as false for Service. Like:
<service
android:name="com.myapp.MyService"
android:stopWithTask="false" />
Now in your MyService service, override method onTaskRemoved. (This will be fired only if stopWithTask is set to false).
public void onTaskRemoved(Intent rootIntent) {
//unregister listeners
//do any other cleanup if required
//stop service
stopSelf();
}
Refer my question for more details, which contains other part of code, too.
We need to create a service that would clear the application from recent service
public class ClearService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("ClearService", "Service Started");
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("ClearService", "Service Destroyed");
}
#Override
public void onTaskRemoved(Intent rootIntent) {
Log.e("ClearService", "END");
//Code here
stopSelf();
}
}
register this service in manifest.xml
<service android:name="com.package.ClearService" android:stopWithTask="false" />
Then start this service on your splash activity
startService(new Intent(getBaseContext(), ClearService.class));
And now whenever you will clear your app from android recent Then this method onTaskRemoved() will execute.
I resolved similar issue. If you want after swiping from recent task and on next launch it to behave properly then follow below steps:-
1) Save process ID in shared preference:
SharedPreferencesUtils.getInstance().putInt(SharedPreferencesUtils.APP_PROCESS_ID, android.os.Process.myPid());
2) When application is launched from launcher after clear from recent task then do:
int previousProcessID = mSharedPreferencesUtils.getInt(SharedPreferencesUtils.APP_PROCESS_ID);
int currentProcessID = android.os.Process.myPid();
if ((previousProcessID == currentProcessID)) {
// This ensures application not killed yet either by clearing recent or anyway
} else {
// This ensures application killed either by clearing recent or by anyother means
}
When you press home - onPause and onStop of your Activity is being called, so at this time you have to do all savings and cleanup, because Android platform doesn't further guarantee that onDestroy or any other lifecycle method would be invoked, so the process could be killed without any notification.
ViewModel.onCleared() can be useful, if the goal is to release some resource (perhaps a system running somewhere else on the network) when the user executes a surprise exit by swiping, rather than by pressing the "stop" or button. [This is how I originally arrived at this question].
Application doesn't get a notification, and Activity.onDestroy() gets called for configuration changes such as changes in orientation, so the answer isn't there. But ViewModel.onCleared gets called when the Application is swiped away (as well as when the user backs out of the activity). If the resource you want to use is associated with more than one activity in the stack, you can add reference counts or some other mechanism to decide if ViewModel.onClear should release the resource.
This is yet another of many good reasons to use ViewModel pattern
I don't really know why the above approaches are not working on my case even I set android:stopWithTask="false" that onTaskRemoved() not called.
Another good approach would be using AndroidViewModel. This one even works on the case when user exits the applcation on pressing back button.
Just bound ViewModel class to your MainActivity then do your task onCleared() callback.
Example:
public class MainViewModel extends AndroidViewModel {
public MainViewModel(#NonNull Application application) {
super(application);
}
#Override
protected void onCleared() {
// Do your task here
Log.e("MainViewModel", "OnCleared mainViewModel");
super.onCleared();
}
}
then bound it to your MainActivity:
MainViewModel viewModel = new ViewModelProvider(this).get(MainViewModel.class);
~ Voila!
As Bob Cram mentioned in his answer, View Model's onCleared() method is the answer.
It works in both cases :
When the user removes the app by swiping the app from background.
When the user clear all the app using the clear list button.
Service's onTaskRemoved() will work when the user swipes the app from the background, but will not work when the apps are cleared using the kill all button.
But the viewModel's onCleared() method works in both cases. You can use if to stop any ongoing process or clearing any task in the remote server.
override fun onCleared() {
super.onCleared()
Log.d(TAG , "App Killed")
}
You need to save your data when on onPause() is called.
Look at this life cycle diagram:
Android Developer
You can see that an app can be killed after onPause() or onStop().
Handle your data there and recover it in onRestart() \ onCreate().
good luck!
This worked for me on android 6,7,8,9.
Make one service like this:
public class OnClearFromRecentService extends Service {
#Override public IBinder onBind(Intent intent) {
return null; }
#Override public int onStartCommand(Intent intent, int flags, int
startId) {
Log.d("ClearFromRecentService", "Service Started");
return START_NOT_STICKY; }
#Override public void onDestroy() {
super.onDestroy();
Log.d("ClearFromRecentService", "Service Destroyed"); }
#Override public void onTaskRemoved(Intent rootIntent) {
Log.e("ClearFromRecentService", "END");
//Code here
stopSelf(); } }
2) Register this service in manifest.xml:
<service android:name="com.example.OnClearFromRecentService"
android:stopWithTask="false" />
3) Then start this service on your splash activity
startService(new Intent(getBaseContext(),
OnClearFromRecentService.class));
In my app, I am using the IntentService class to start another activity in the background. But the issue I got is that suppose from the IntentService class I start my activity, which opens my activity, after that I don't close my activity. Then I notice that when IntentService class again wants to start my same activity it is not called as the same activity is not close.
So, my question is: How can I start the same activity again and again whether it is open or close from the IntentService class?
Code in IntentService class
public class AlarmService extends IntentService
{
public void onCreate() {
super.onCreate();
}
public AlarmService() {
super("MyAlarmService");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, startId, startId);
return START_STICKY;
}
#Override
protected void onHandleIntent(Intent intent) {
startActivity(new Intent(this,
AlarmDialogActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
}
use launchMode tag in manifest file
<activity
android:name=".ActivityName"
android:launchMode="singleTask" />
it will not create a different instance of activity if already available..
see this link launchMode for better understanding