Here is the situation:
I start a download
I restart the device (to test the robustness of the app)
I use a boot receiver to be notified of the restart
In the onReceive method I call the clearDownloadsAfterReboot() method:
#Override
public void onReceive(final Context context, Intent intent) {
clearDownloadsAfterReboot(context);
}
The clearDownloadsAfterReboot() method get called and tries to remove the downloads like so:
private void clearDownloadsAfterReboot(final Context context){
//Added delay to make sure download has started
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
DownloadManager manager = (DownloadManager) context.getSystemService(context.DOWNLOAD_SERVICE);
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById (DownloadManager.STATUS_FAILED|DownloadManager.STATUS_PENDING|DownloadManager.STATUS_RUNNING|DownloadManager.STATUS_PAUSED);
Cursor c = manager.query(query);
// -------> c.getCount() returns 0 despite the download being visible
while(c.moveToNext()) {
//This never executes because c has a count of 0
int removedInt = manager.remove(c.getLong(c.getColumnIndex(DownloadManager.COLUMN_ID)));
}
}
}, 45000);
}
The cursor returns 0 so the remove() method is never called, but, I can see the download running in the status bar so there is at least 1 download running.
My questions in a nutshell: How do I stop running downloads after a reboot? And, why would the manager.query() method return a cursor which has 0 results when there is a download running?
I know there are other question regarding stopping downloads but these have not been able to solve my issue.
Thanks in advance.
Credit for the first part of this answer goes to #Lukesprog
I needed to use setFilterByStatus() instead of setFilterById(). After exchanging these two methods I was able to successfully stop the download and the manager.query() method returned a cursor containing the correct amount of downloads.
However, for some reason the notification which showed the progress of the download didn't clear after the download had been stopped. In order to clear the notification you need to call manager.remove(c.getLong(c.getColumnIndex(DownloadManager.COLUMN_ID))); twice.
Related
I edited the question so it's not a duplicate
of this
In MainActivity I am doing some file operations. These operations are processed in a single file. So, after that I pass the file through intent at ForceShut. This is because I want to detect when user swipes the app out from recent apps which is to say onTaskRemoved() is called, into which this file is deleted. Now no problem so far. The file is successfully transmitted through the intents, and onTaskRemoved() is called as seen from the Logs. Also the file which I try to delete in onTaskRemoved() is successfuly deleted when I swipe the app and the Logs in there all run fine until the "Application Terminated" shows. But few seconds after, I get a crash alert saying App has stopped while app was even removed from the recent. The crash though appears twice in a row then no crashs appear further on. What could be the problem ? :(
My MainActivity class looks like this
public MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//several file operations go here <--
//removed for simplification
Intent mIntent = new Intent(this, ForceShut.class);
mIntent.putExtra("file", file);
startService(mIntent);
}
}
and ForceShut class looks like this :
public class ForceShut extends Service {
File file;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId){
file =(File) intent.getExtras().get("file");
return START_STICKY;
}
#Override
public void onTaskRemoved(Intent rootIntent){
if(file.exists())
file.delete();
}
}
EDIT
So as #CommonsWare was suggesting I had forgotten to look at the LogCat, instead I was looking only at the "Run" tab logs. So I looked it over and it seems like there is a null pointer exception :
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.os.Bundle android.content.Intent.getExtras()' on a null object reference so it seems like even after swiped onStartCommand is called again. Why could the service start over even after app was swiped ?
EDIT 2
Question is not a duplicate, as #Andreas has pointed out. I edited the question. However I found the workaround myself. I shut down the Service with stopSelf() as it seems like swiping the app out of Recents sort of doesn't get rid of the service which ocasionally restarts. Anyway hope this helps anyone
Why could the service start over even after app was swiped ?
You have started a "sticky" service. The system will automatically restart any sticky service until it is explicitly stopped.
#Override
public int onStartCommand(Intent intent, int flags, int startId){
file =(File) intent.getExtras().get("file");
return START_STICKY;
}
I don't see where you actually stop it with stopSelf().
As far as your NullPointerExceptions, simply check if the objects exist before trying to read from them.
if (intent != null && intent.hasExtra("file"))
file =(File) intent.getExtras().get("file");
The service restarts because of the START_STICKY inside onStartCommand.
You need to use START_NOT_STICKY instead to prevent the service from restarting.
I have been scouting around for a while, but cannot locate any information for calling final() using started services... or rather, when not using bound services. There is tons of info for bound services, but I already have two pretty large "started services" without binding, so I didn't want to modify the existing services more than absolutely necessary.
My app works by reading bluetooth data every 10 seconds, and depending on the data read, the Service will change to a new activity. However, I cannot call final() from my services, so I fear that I might be endlessly stacking activities while the application/services are running.
To change activities, I had to add Intent.FLAG_ACTIVITY_NEW_TASK. Considering the below image/definition from the developer's page, this flag looks like it might already handle my stacking issue? I do NOT allow for users to use the back button on their phones as everything is handled via confirm/cancel buttons and the services. My app MUST be this way for a few reasons. Thus, keeping the stack order isn't important to my application.
Key Points -
I want to ensure i'm not stacking up activities endlessly when starting new activities
Flagging "new task" when starting activities via my services
Stack order is not important to my app
Below is a very small cut of my code with comments to explain what i'm trying to do. Please make sure to look to the onDestroy() method of this service.
public class AlertService extends Service {
final class Threader implements Runnable{
// Scans bluetooth advertisement packets every 10 seconds
// Thread Runs until interrupted
// Stops service via service ID
stopSelf(this.serviceID);
}
#Override
public void onCreate(){
super.onCreate();
}
// Runs a thread until alert is found.
// Alert calls thread.interrupt()
#Override
public int onStartCommand(Intent intent, int flags, int startID){
enableBluetooth();
// Start Thread
thread = new Thread(new Threader(startID));
thread.start();
return START_STICKY;
}
#Override
public void onDestroy(){
thread.interrupt();
Intent alertActivity = new Intent(this, AlertActivity.class)
alertActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(alertActivity);
}
// Unused Method - We will not be binding
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
EDIT via recommendation to use android:taskAffinity -
Using android:taskAffinity won't help me in this situation. By default, all activities in an application have the same affinity. When I create a new task by setting Intent.FLAG_ACTIVITY_NEW_TASK in the intent flags, the new activity will STILL be started in the same task if the activity has the same taskAffinity of the root activity in the task. Since I am just using the default affinity, everything should have the normal stack flow. I just cannot call finish(), which means that I am stacking up tons of activities.
To answer my own question, each new activity called via Intent.FLAG_ACTIVITY_NEW_TASK, was creating a new instance of each activity and putting it on the stack. This is true. However, each activity is NOT making a new Task, which was one of my fears as well.
When I create a new task by setting Intent.FLAG_ACTIVITY_NEW_TASK in the intent flags, the new activity will STILL be started in the same task (not in a new task) if the new activity has the same taskAffinity of the root activity in the task. Since I am just using the default affinity, every activity I create is being put into the same task. This means that nothing is acting any differently than the normal flow of creating activities and such.
Though, since I have disable the back button for my application, these activities created by flagging a new task are not finished, destroyed, or removed from the stack. To solve this, I will use FLAG_ACTIVITY_CLEAR_TOP, which finds a running instance of an activity in the stack (if there is one) and closes all of the activities above it.
Since my application always starts with the home screen, then ends with the home screen, flagging "clear top" will always close all activities above my home screen. So, upon return to the home screen, the only item on the stack will be the home screen.
I will have to test this, but it seems that I will not call finish() from my home activity to achieve this result - Otherwise, upon returning to the home activity, not all of the stack will be cleared.
I'm looking for a way to close an android app after some time while the app has not been in focus. For example if the user open up an other app instead, the app should exit after 5 mins. I have tried using runnable and creating a thread. But those method don't seems to work while the app is in the background (maybe they are pause I'm not sure). So how do I close the app when it is not in focus?
For those who are wonder the reason I want to do this is that the app contains some sensitives data about the user so I want to be sure it is all cleared when they aren't using it.
Something like this might work:
A field inside activity class:
private Thread t = null;
Inside onResume():
if(t!=null) {
if(t.isAlive()) {
t.interrupt();
t.join();
}
t=null;
}
Inside onPause():
t = new Thread() {
public void run() {
try {
sleep(5*60*1000);
// Wipe your valuable data here
System.exit(0);
} catch (InterruptedException e) {
return;
}
}.start();
}
I recommend calling finish() in the onPause() or onStop() callbacks. A TimerTask will not survive onPause() and a Service does not appear, on face value, to give you options. Maybe you can start a service, sleep the thread the service runs on, then kill the processes your app has after the sleep timer expires.
Alternatively, you can just implement some security libraries to help secure the data from other apps.
Here is the Google Services link.
Get the process ID of your application, and kill that process onDestroy() method
#Override
public void onDestroy()
{
super.onDestroy();
int id= android.os.Process.myPid();
android.os.Process.killProcess(id);
}
Refer- how to close/stop running application on background android
Edit- Use this with AlarmManager
The fundamental problem with what you're trying to do is that your Activity may not exist in memory at all when it's "running" in the background. The Android framework may have destroyed the activity instance and even the process it was running in. All that exists may be the persistent state you saved in onSaveInstanceState(...) and a screenshot for the recent apps list. There may be nothing for you to get a reference to and kill.
Frank Brenyah's suggestion to call finish() in onPause() will prevent your activity from running in the background at all, but this is the closest you can get to what you want. You probably only want to do this when isChangingConfigurations() is false. But even when all your app's activities are finished, Android may keep the process and Application instance around to avoid recreating them later. So you may also want to use Bhush_techidiot's suggestion of killing the process. Do this in onPause() because the activity may be destroyed without a call to onDestroy().
I want to know mechanism of Download Managers (not the DownloadManager class introduced in API 9), as in my current project, I need to implement a few things same as download manager.
My basic understanding is that it uses a Service to download files. But what I cannot understand is how will it handle the multiple file download request, e.g. A file is currently being downloaded. Now user adds another file to be downloaded. I don't think a single Service can handle it. Can it? or is there some other method to do it? Any suggestions?
PS forgive me if the question is not clear enough because I myself don't understand my doubt very clearly. :(
They are using a MultiThreaded Socket Wheel.
Means, there is a ForegroundService which handle different Threads within the Service.
The ForegroundService make sure that it will be kept alive. The Threads itself are single Processes which run in a Background Thread.
Threre are several ThreadExecutors available which make sure that you have only a few Threads running paralell or process them thread by thread.
Here is a good tutorial making a SocketWheel http://www.javaspecialists.eu/archive/Issue023.html
Sources:
ThreadExecutor http://developer.android.com/reference/java/util/concurrent/ThreadPoolExecutor.html
ThreadPoolExecutor ExectuorService vs ThreadPoolExecutor (which is using LinkedBlockingQueue)
ForegroundService Android - implementing startForeground for a service?
Edited: for some code
public class DLService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Notification note=new Notification(R.drawable.somegraphics, "some title", randomnr);
Intent i=new Intent(this, ActivityClassToOpenWhenClicked.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pi=PendingIntent.getActivity(this, 0, i, 0);
note.setLatestEventInfo(this, "downloadmanager","downloading.. nothing", pi);
note.flags|=Notification.FLAG_NO_CLEAR;
startForeground(1337, note);
// if (intent.hasExtra("dlurl")) {
new Thread(new Runnable() {
public void run() {
new Client("http://yourfile.com/file.jpg");
}
}).start();
return START_NOT_STICKY;
}
class Client {
public Client(String filetodownload) throws Exception {
//do your http connection. for example one download #
// HttpUrlConnection httpConnection = new .....
}
}
}
Its just a quick coded example, not tested. But it shows the concept.
The service itself accept a Intent which can be a url where the file is located. Then it creates a new Thread which do the job. The Thread will run aslong as it takes to download the file. The foregroundService makes sure that the Service will be kept alive. You can also create a ThreadPoolExecutor which handle multiple Thread at onec. Read http://developer.android.com/training/multiple-threads/create-threadpool.html for instructions.
I've got a couple of activities and an intent service which handles GCM incoming messages.
Right now for every push, I'm sending a Notification, and after the user clicks it, he is redirected to appropriate screen.
I would like to alter this behavior that if the app is visible (any activity is in the foreground), instead of the notification a dialog message is shown (with appropriate action).
Any idea how to implement it?
I have 2 ideas but none of them is perfect:
Keep track of every activity in the application, if the activity is visible, don't show notification, but sent an intent to the activity (not nice solution)
register/unregister the second broadcast receiver in each activity's onResume/onPause, "catch" the incoming GCM broadcast (I'm not sure if it is possible).
Any other solutions?
A possible solution (idea 1):
To detect whether your app is running back- or foreground, you can simply set a boolean in onPause/onResume:
#Override
protected void onResume() {
super.onResume();
runningOnBackground = false;
}
#Override
protected void onPause() {
super.onPause();
runningOnBackground = true;
}
When you start a new intent from an notification this method gets called: (if you are using singleTop), with the boolean you can determine what to do in the onNewIntent method.
#Override
protected void onNewIntent (Intent intent){
if(runningOnBackground){
//do this
}
else{
//do that
}
}
Hope it helps!
I didn't test it, but the docs say you can get the number of running activities per each task.
Try to find your application's task among currently running tasks:
ActivityManager acitivityManager = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
// Get the top of running tasks, limit by 100
List<RunningTaskInfo> tasks = acitivityManager.getRunningTasks(100);
for (RunningTaskInfo taskInfo : tasks) {
if (YOUR_PACKAGE_NAME.equals(taskInfo.baseActivity.getPackageName())) {
if (taskInfo.numRunning > 0) {
// Show dialog
} else {
// Show notification
}
break;
}
}
Google added a note on getRunningTasks():
Note: this method is only intended for debugging and presenting task management user interfaces. This should never be used for core logic in an application, such as deciding between different behaviors based on the information found here. Such uses are not supported, and will likely break in the future. For example, if multiple applications can be actively running at the same time, assumptions made about the meaning of the data here for purposes of control flow will be incorrect.
So use it at your own risk.
Also check if GCM broadcasts are ordered. If so, you can "override" your default BroadcastReceiver with the other ones in each Activity. Just play with the priority of IntentFilters. When the BroadcastReceiver with higher priority receives the message, it can abort it's further propagation. For your application this means that when some Activity is running, it registers the receiver which shows the dialog and aborts broadcast. If no activity is active, then your default receiver shows the notification.