I have a Service run independently with activity using startService(). This service handle many requests from activity and create Callable then add into ThreadPoolExecutor. It looks like this:
private ExecutorService requestExecutor;
private CompletionService<Result> requestHandleService;
#Override
public int onStartCommand(final Intent intent, int flags, int startId) {
// Create new Request Task and submit
Callable<Result> request = new Callable<Result>(){
public Result call() throws Exception {
}
};
requestHandleService.submit(task);
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy(){
super.onDestroy();
Log.d(TAg,"onDestroy service");
try{
if(requestExecutor!= null){
requestExecutor.shutdown();
}
}catch(Exception ex){
Ln.e(ex);
}finally{
requestExecutor= null;
requestHandleService= null;
}
}
The problem is that I want this Service run independently and parallel with activity. So activity can't control when to stop it. It should only stop when all tasks finished.
I know there is a way to wait for ThreadPool complete, but this can't work for me, because I don't need to keep the list requests. When this service receive request from activity, it should create new a task and submit immediately in onStartCommand.
Is there any way to solve this?
Have you checked out IntentService, this is sort of what this type of service does. If you need the multiple threads then you could wrap the tasks you submit with a check for the Queue used by the ThreadPool to see if it is empty, and if so, shutdown the service and the threadpool.
Callable is just an interface, so just make a callback out of it.
#Override
public void onCreate() {
mHandler = new Handler();
}
#Override
public int onStartCommand(final Intent intent, int flags, int startId) {
// Create new Request Task and submit
Callable<Result> request = new Callable<Result>(){
public Result call() throws Exception {
Result result = // ... mRealTask.call();
mHandler.postDelayed(queueCheck, 300);
return result;
}
};
requestHandleService.submit(task);
return super.onStartCommand(intent, flags, startId);
}
Runnable queueCheck = new Runnable() {
public void run() {
if (requestExecutor.getQueue().isEmpty()) {
stopService(new Intent(this, getClass()));
mHandler.removeCallbacks(this);
}
}
};
Related
public class CopyService extends Service {
private List<CustomFile> taskList;
private AsyncTask fileTask;
#Override
public void onCreate() {
super.onCreate();
taskList = new ArrayList<>();
fileTask = new fileTaskAsync();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String filePath = intent.getStringExtra("filePath");
String fileType = intent.getStringExtra("fileType");
String taskType = intent.getStringExtra("taskType");
String fileName = intent.getStringExtra("fileName");
CustomFile customFile = new CustomFile();
customFile.filePath = filePath;
customFile.fileType = fileType;
customFile.taskType = taskType;
customFile.fileName = fileName;
taskList.add(customFile);
Notification notification = getNotification();
startForeground(787, notification);
if (fileTask.getStatus() != AsyncTask.Status.RUNNING) {
CustomFile current = taskList.get(0);
taskList.remove(current);
fileTask = new fileTaskAsync().execute(current);
}
stopSelf();
return START_NOT_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private class fileTaskAsync extends AsyncTask<CustomFile, Void, String> {
#Override
protected String doInBackground(CustomFile... customFiles) {
CustomFile customFile = customFiles[0];
FileUtils.doFileTask(customFile.filePath, customFile.fileType,
customFile.taskType);
return customFile.fileName;
}
#Override
protected void onPostExecute(String name) {
sendResult(name);
if (!taskList.isEmpty()) {
CustomFile newCurrent = taskList.get(0);
taskList.remove(newCurrent);
fileTask = new fileTaskAsync().execute(newCurrent);
}
}
}
private void sendResult(String name) {
Intent intent = new Intent("taskStatus");
intent.putExtra("taskName", name);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}
I need to execute multiple tasks in a service one by one. Task is either copying or moving local files. Suppose, user is copying a big file and he wants to copy or move other files. I need the subsequent tasks to be queued and exected one by one.
Currently, I'm creating a list inside the service and running an async task. In onPostExecute, I check for remaining tasks in the list and start the async task again from there. As shown in the code.
But, I'm concerned about memory leaks. And I'm very new to programming so, I don't know what's the best practice in such situations.
I can't use IntentService, because I want the task to continue even if the user hits home button to open some other app.
AS I said in the comments, I think your solution is reasonable. A Foreground Service is a good candidate for long running work that needs to be executed immediately, and from your description your file copying task matches that criteria.
That said, I don't believe AsyncTask is a good candidate for your problem. AsyncTasks are best deployed when you need to do some quick work off the main thread, in the order of a few hundred milliseconds at most, whereas your copy task could presumably take several seconds.
As you have multiple tasks to complete which aren't directly dependent on one another, I would recommend you make use of a thread pool to conduct this work. For that you can use an ExecutorService:
public class CopyService extends Service {
private final Deque<CustomFile> tasks = new ArrayDeque<>();
private final Deque<Future<?>> futures = new LinkedBlockingDequeue<>();
private final ExecutorService executor = Executors.newCachedThreadPool();
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//May as well add a factory method to your CustomFile that creates one from an Intent
CustomFile customFile = CustomFile.fromIntent(intent);
tasks.offer(customFile);
//...Add any other tasks to this queue...
Notification notification = getNotification();
startForeground(787, notification);
for(CustomFile file : tasks) {
final Future<?> future = executor.submit(new Runnable() {
#Override
public void run() {
final CustomFile file = tasks.poll();
//Ddo work with the file...
LocalBroadcastManager.getInstance(CopyService.this).sendBroadcast(...);
//Check to see whether we've now executed all tasks. If we have, kill the Service.
if(tasks.isEmpty()) stopSelf();
}
});
futures.offer(future);
}
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
//Clear pending and active work if the Service is being shutdown
//You may want to think about whether you want to reschedule any work here too
for(Future<?> future : futures) {
if(!future.isDone() && !future.isCancelled()) {
future.cancel(true); //May pass "false" here. Terminating work immediately may produce side effects.
}
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
This shouldn't cause any memory leaks, as any pending work is destroyed along with the Service.
I have an Android app with a service which connects to a peripheral device and listens to its output. The connection to the peripheral is initiated in the service’s onStartCommand() method.
Establishing the connection involves several “hopefullies” and may sometimes fail with an exception.
How can I prevent the service from becoming a zombie that’s running but has no connection to monitor? Calling stopSelf() from within onStartCommand() doesn’t seem to work.
Rough code:
public int onStartCommand(Intent intent, int flags, int startId) {
try {
// connect to device
} catch (ConnectToDeviceException e) {
stopSelf();
}
return START_REDELIVER_INTENT;
}
I solved it by moving the connection code into an AsyncTask and stopping the service from there if the connection fails.
Since onStartCommand() runs on the UI thread and connecting to a device can take some time, taking this off the UI thread by putting it in an AsyncTask is cleaner anyway.
Simplified code:
public int onStartCommand(Intent intent, int flags, int startId) {
StartupTask startupTask = new StartupTask();
startupTask.execute();
return START_REDELIVER_INTENT;
}
private class StartupTask extends AsyncTask<Void, Void, Void> {
boolean success = true;
#Override
protected Void doInBackground(Void... params) {
try {
// connect to device
} catch (ConnectToDeviceException e) {
success = false;
}
}
#Override
protected void onPostExecute(Void result) {
if (!success)
stopSelf();
}
}
I want to check some web API and do something per x minutes. I think I should write a service on Android (is there any other solution?).
But how can do that?
I am thinking about writing a service class and in the manifest file I should add this line:
<service
android:name="com.xx.yy.noti_check"
android:enabled="true"
>
</service>
And in my noti_check class I check my web API like this on onStartCommand:
public class noti_check extends Service {
Context mcont;
private Handler myhandler ;
private long RETRY_TIME = 15000;
private long START_TIME = 2000;
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onStart(Intent intent, int startId) {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mcont=this;
myhandler= new Handler();
myhandler.postDelayed(myRunnable, START_TIME);
return Service.START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
try {
myhandler.removeCallbacks(myRunnable);
}
catch (Exception e) {
// TODO: handle exception
}
}
private Runnable myRunnable = new Runnable() {
public void run() {
try {
new get_notifyalert(mcont).execute("") ;
}
catch (Exception e) {
// TODO: handle exception
}
myhandler.postDelayed(myRunnable, RETRY_TIME);
}
};
}
Is this is the right way?
Is this the right way?
No. Only have a service running when it is actively delivering value to the user. Watching the clock tick is not actively delivering value to the user. Use AlarmManager for periodic work like this.
In my app i am using a Service that periodically checks if there is a new personal message for the logged in user.
The service is started if the user enables the notification feature. Now if the user disables the notification feature i would like to stop the service.
I try to stop the service with the following lines of code.
Intent service = new Intent(getApplicationContext(), MessageService.class);
stopService(service);
The problem is that the service doesn't stop. It goes on working.
Here you can see my message service.
public class MessageService extends Service {
private int intervall;
public MessageService(){
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent,flags,startId);
Bundle intentData = intent.getExtras();
if(intentData != null) {
this.intervall = intentData.getInt("intervall");
}
final Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// async task for calling api otherwise we get an exeception here
new ServiceMessagesTask().execute(MessageService.this);
}
};
new Thread(new Runnable(){
public void run() {
while(true)
{
try {
Thread.sleep(intervall); // repeat after given intervall
handler.sendEmptyMessage(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
I have an activity where the user can edit his preferences. There it is also possible to activate the notification feature.
The notification service is started or stoped in the savePreferences() method:
public void savePreferences(View button) {
EditText login = (EditText)findViewById(R.id.txtbLogin);
EditText password = (EditText)findViewById(R.id.txtbPassword);
CheckBox enableNotification = (CheckBox) findViewById(R.id.cbNotifications);
Spinner spinner = (Spinner) findViewById(R.id.notificationInterval);
if(!login.getText().equals("") && !password.getText().equals("")){
Map<String, Object> preferences = new HashMap<String, Object>();
preferences.put("document_type", CouchbaseHelper.CB_VIEW_USER_PREFERENCES);
preferences.put("login", login.getText().toString());
preferences.put("password", password.getText().toString());
if(enableNotification.isChecked()){
preferences.put("enableNotification", true);
} else {
preferences.put("enableNotification", false);
}
preferences.put("notificationInterval", this.notificationInterval);
CouchbaseHelper couchbaseHelper = new CouchbaseHelper(getApplicationContext());
String documentId = couchbaseHelper.createDocUserPreferences(preferences);
couchbaseHelper.closeDb();
// start notification service if enabled
if(enableNotification.isChecked()){
Intent service = new Intent(getApplicationContext(), MessageService.class);
service.putExtra("intervall", Integer.valueOf(this.notificationInterval)*60*1000);
startService(service);
} else {
// TODO: this is not working!!! service doesnt stop
// try to stop running service
if(isMyServiceRunning()){
Intent service = new Intent(getApplicationContext(), MessageService.class);
stopService(service);
}
}
}
finish();
Intent main = new Intent(Preferences.this, Main.class);
startActivity(main);
}
I'm afraid you really don't get what a service is, service is just a component that do not require UI and is not linked to an activity life cycle, hence it runs in background, BUT background doesn't necessarily means in a separate thread, actually the service runs in the main thread, now that's one thing, killing a service doesn't mean you are killing all the working threads you create within, and in your code you are creating a Thread that is looping forever, that thread although created in the service is not linked in any way to the service life cycle.
So, if you want to stop the thread, get a reference to the thread you are creating in the startCommand method and in the onDestroy method just stop it, instead of having a while(true) validation, go for a flag and just change it to false in the onDestroy so it will stop the thread you created when started the service.
Regards!
How do I start my service in a new thread. I looked on other questions but it dint work for me. What changes do I need to make in my service when normally running and when running in a separate thread?
Rename your public void onStart(final Intent intent, final int startId) method to _onStart and use this new onStart implementation:
#Override
public void onStart(final Intent intent, final int startId) {
Thread t = new Thread("MyService(" + startId + ")") {
#Override
public void run() {
_onStart(intent, startId);
stopSelf();
}
};
t.start();
}
private void _onStart(final Intent intent, final int startId) {
//Your Start-Code for the service
}
For API Levels 5 and Above
public void onStart(Intent, int) was deprecated at API level 5. This should be replaced with public int onStartCommand(Intent, int)
#Override
public int onStartCommand(final Intent intent, final int startId){
//All code from 'onStart()' in above placed here as normal.
}
private void _onStart(final Intent intent, final int startId) {
//Your Start-Code for the service
}
I do not think you can start your service in a new thread, but what you can do is start a new thread in your service.
This is because like the activity, the service has life cycle methods that run on the main thread.
So your service will run on the main thread but it will do the heavy lifting on a new thread that it creates when ever it needs to.
I hope it helps..
Citing 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. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work. More information on this can be found in Processes and Threads. The IntentService class is available as a standard implementation of Service that has its own thread where it schedules its work to be done."
In my project, i have someone like this and it's work:
Thread welcomeThread = new Thread() {
#Override
public void run() {
try {
super.run();
while (isMyServiceRunning() != true) {
sleep(100);
}
} catch (Exception e) {
System.out.println("EXc=" + e);
} finally {
Intent i = new Intent(getApplicationContext(), MainPage.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
}
}
};
welcomeThread.start();