I used IntentService in my code instead of Service because IntentService creates a thread for me in onHandleIntent(Intent intent), so I don't have to create a Thead myself in the code of my service.
I expected that two intents to the same IntentSerivce will execute in parallel because a thread is generated in IntentService for each invent. But my code turned out that the two intents executed in sequential way.
This is my IntentService code:
public class UpdateService extends IntentService {
public static final String TAG = "HelloTestIntentService";
public UpdateService() {
super("News UpdateService");
}
protected void onHandleIntent(Intent intent) {
String userAction = intent
.getStringExtra("userAction");
Log.v(TAG, "" + new Date() + ", In onHandleIntent for userAction = " + userAction + ", thread id = " + Thread.currentThread().getId());
if ("1".equals(userAction)) {
try {
Thread.sleep(20 * 1000);
} catch (InterruptedException e) {
Log.e(TAG, "error", e);
}
Log.v(TAG, "" + new Date() + ", This thread is waked up.");
}
}
}
And the code call the service is below:
public class HelloTest extends Activity {
//#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent selectIntent = new Intent(this, UpdateService.class);
selectIntent.putExtra("userAction",
"1");
this.startService(selectIntent);
selectIntent = new Intent(this, UpdateService.class);
selectIntent.putExtra("userAction",
"2");
this.startService(selectIntent);
}
}
I saw this log message in the log:
V/HelloTestIntentService( 848): Wed May 05 14:59:37 PDT 2010, In onHandleIntent for userAction = 1, thread id = 8
D/dalvikvm( 609): GC freed 941 objects / 55672 bytes in 99ms
V/HelloTestIntentService( 848): Wed May 05 15:00:00 PDT 2010, This thread is waked up.
V/HelloTestIntentService( 848): Wed May 05 15:00:00 PDT 2010, In onHandleIntent for userAction = 2, thread id = 8
I/ActivityManager( 568): Stopping service: com.example.android/.UpdateService
The log shows that the second intent waited the first intent to finish and they are in the same thread.
It there anything I misunderstood of IntentService. To make two service intents execute in parallel, do I have to replace IntentService with service and start a thread myself in the service code?
Thanks.
The intent queuing is the whole point of using IntentService.
All requests to IntentService are handled on a single worker thread, and only one request will be processed at a time. If you want to do two tasks in parallel, I think you need to use Service and create threads for each task after Service starts.
As for AsyncTask, there's a thread pool for handling all of the tasks. If your task number exceeds thread pool size, some of these AsyncTasks will need to wait until a thread from the pool becomes available. However, the thread pool size changes in different platform versions.
Here's my test result:
Android 2.2: thread pool size = 5
Android 1.5: thread pool size = 1
As far as I know, The IntentService has one handler thread, and each intent queues in that thread. When all queued intents are done, the service exits. It does not create independent threads per intent. I don't know of any Service subclasses that work the way you are describing, you'd probably have to write your own.
I think what you want is an AsyncTask rather than either a Service or an IntentService. Or you could always just shoot from the hip by defining a runnable like this:
Runnable runnable = new Runnable() {
public void run() {
....
}
};
new Thread(runnable).start();
... for each of your tasks. Honestly, that may be easier than dealing this these Android helper classes.
Here is an example of using a Service instead of an IntentService, it might serve your purpose. http://developer.android.com/guide/topics/fundamentals/services.html#ExtendingIntentService
Related
I was reading about services in Android and especially i came down to this
While an app is in the foreground, it can create and run both
foreground and background services freely. When an app goes into the
background, it has a window of several minutes in which it is still
allowed to create and use services. At the end of that window, the app
is considered to be idle. At this time, the system stops the app's
background services, just as if the app had called the services'
Service.stopSelf() methods.
In the code below, when the app goes to background after one minute or so the services gets destroy but the thread still executes.
So what is the point of killing a service? The process/thread is still being execute.
Why killing a service in the first place? What if i want to execute a download process and NOT wanted to be a foreground process?
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
Log.d("sssssssss",msg.toString()+"sssssssssss");
while(true){
Log.d("sssssssss","sssssssssss");
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
//stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work doesn't disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
#Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
System is letting You perform a clean finish of the Service by triggering its destroy method. Your thread won't run forever, I think 30 min is hard limit before app process is killed.
This is new policy since android O to preserve battery life and improve performance. A lot of developers performed heavy operations in background (sockets open non-stop, periodic sensor readings etc.) and without foreground notification users were unaware of why their devices were sluggish and had poor battery uptime.
Read more on Background Execution Limits doc.
I am trying to download some file in background. Earlier i was doing with intent service, and my app wont get freezed in using intent service. But as with oreo and above versions onward Intent service is getting destroyed as soon as app get closed from background. Same processing i did it in a Job service but it seem its running on main thread. What should i do for background processing that shouldn't be running on main thread ?
Below is code for JOB scheduling i did:
public class Util {
// schedule the start of the service every 10 - 30 seconds
#TargetApi(Build.VERSION_CODES.M)
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static void scheduleJob(Context context) {
ComponentName serviceComponent = new ComponentName(context, MyService.class);
JobInfo.Builder builder = new JobInfo.Builder(0, serviceComponent);
builder.setMinimumLatency(1 * 1000); // wait at least
builder.setOverrideDeadline(3 * 1000); // maximum delay
//builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED); // require unmetered network
//builder.setRequiresDeviceIdle(true); // device should be idle
//builder.setRequiresCharging(false); // we don't care if the device is charging or not
JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
jobScheduler.schedule(builder.build());
}
}
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class MyService extends JobService {
private static final String TAG = "SyncService";
public Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
public SharedPreferences sharedPreferences;
public ComplexObject complexObject;
private Context context;
public static final String NOTIFICATION_CHANNEL_ID = "10001";
#Override
public boolean onStartJob(JobParameters params) {
System.out.println("RUnning this Job.......");
context = this;
this.sharedPreferences = context.getSharedPreferences(context.getResources().getString(R.string.shared_preference_key), Context.MODE_PRIVATE);
//30-40 HTTP call to process the data
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
return true;
}
Not sure if this will help you since it's kinda old but:
Your title reads JobIntentService and you use JobService, the answer by
TheWanderer refers to your used class (JobService), but if you will use JobIntentService, its method onHandleWork runs on a background thread:
This method is called on a background thread, so you can do long blocking operations here.
According to the docs:
This service executes each incoming job on a Handler running on your application's main thread. This means that you must offload your execution logic to another thread/handler/AsyncTask of your choosing. Not doing so will result in blocking any future callbacks from the JobManager - specifically onStopJob(android.app.job.JobParameters), which is meant to inform you that the scheduling requirements are no longer being met.
So, you'll need to use an AsyncTask or some other form of asynchronous method to execute your logic. You should use something cancellable, though, since, when onStopJob() is called, you're supposed to stop whatever you're doing.
Also remember to call jobFinished() when your logic has been completed.
You can use JobIntentService as a solution to download/upload some file or even communicate with server. One another good even better approach is WorkManager. Note that IntentService runs on UI thread that is the onHandleIntent(Intent intent) runs on UI thread. So we should use a separate Thread or use of for example AsyncTask. But JobIntentService has the function onHandleWork(#NonNull Intent intent) which is run on a background thread and so we just do the work on it. In WorkManager approach, the work is done in doWork() method of Worker and this function also runs on a background thread and so we should write the main code inside it. I think JobIntentService and WorkerManager are both good for such purpose. JobIntentService just runs the work once, but by WorkManager we can do a work once or repeat it periodically (the interval time between periods has a minimum of 15 minutes).
I have two Bounded Services, and some Activities that bind themselves to the services when they need it.
The problem is the performance of the app is slow when the activities write the information got from the services in the display.
Android's documentation says about the Stated Services:
Caution: A services runs in the same process as the application in
which it is declared and in the main thread of that application, by
default. So, if your service performs intensive or blocking operations
while the user interacts with an activity from the same application,
the service will slow down activity performance. To avoid impacting
application performance, you should start a new thread inside the
service.
But it says nothing about the Bound Services running in other threads. So i decided to start the services in this way:
private void startRunService()
{
final Intent intent = new Intent(this, RunService.class);
Thread thread = new Thread(new Runnable() {
public void run() {
startService(intent);
}
});
thread.start();
}
private void startLinkerRunService()
{
final Intent intent = new Intent(this, LinkerRunService.class);
Thread thread = new Thread(new Runnable() {
public void run() {
startService(intent);
}
});
thread.start();
}
So starting each service in a different service I got an unexpected improvement of the performance. But I'm not sure that it is a good idea.
Do you know if it has side effects?
Thanks in advance.
i have an IntentService that should act like a manager and create Tasks in a queue (Runnable) that are submitted to a ThreadPool.
Im a little bit confused of the lifecycle of an IntentService:
The method protected abstract void onHandleIntent (Intent intent) runs already on a separated Thread. In the onHandleIntent I would create a new Runnable instance and submit it to the ThreadPool. My Service looks like this:
public class SyncService extends IntentService {
private final ThreadPoolExecutor threadPool;
public SyncService() {
super("SyncService");
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
threadPool = new ThreadPoolExecutor(1, 1, 20, TimeUnit.SECONDS, queue);
}
#Override
public void onCreate() {
super.onCreate();
EventBus.getInstance().register(this);
}
#Override
public void onDestroy() {
super.onDestroy();
EventBus.getInstance().unregister(this);
}
#Override
protected void onHandleIntent(Intent intent) {
if (intent.getAction().equals("sync")){
threadPool.submit(new SyncRunnable());
}else
if(intent.getAction().equals("delete")){
threadPool.submit(new DeleteRunnable());
} else
if(intent.getAction().equals("register")){
threadPool.submit(new RegisterRunnable())
}
}
}
My questions:
Is it a good idea to use a ThreadPool in a IntentService?
If I use a ThreadPool, than the IntentService will be destroyed if the Threadpool has no more Runnables to execute or queued, right?
Is IntentService already something that I want to achieve and should I simply execute my (long running) Runnable code in the
onHandleIntent() because this method alread runs on the
IntentService worker Thread? If yes, is there a queue limit for
intent, since onHandleIntent() could run up to 30 seconds before
finishing and handling the next Intent.
Is it a good idea to use a ThreadPool in a IntentService?
Not really. IntentService is already a single threaded (serial) variant of what you try to achieve. I would derive directly from Service.
If I use a ThreadPool, than the IntentService will be destroyed if the Threadpool has no more Runnables to execute or queued, right?
No. IntentService can go into the destroyed state once you return from onHandleIntent - i.e. immediately because threadPool.submit is non-blocking. Within the source it calls stopSelf(int) with the startId it got when the service was started.
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
A Service will go into destroyed state if you call stopSelf with the latest (highest) startId. It will keep running if a newer start is in the queue.
If the service goes into destroyed state it will not kill your thread pool because it has no knowledge about it. The problem is that Android now thinks that your service is dead and it will no longer count as a reason to keep your app process. The service running vs destroyed state is essentially just a way to tell Android that there is something going on and you don't want to get destroyed.
If you want to do it the right way you have to keep the service state in sync with what is actually going on.
Is IntentService already something that I want to achieve and should I simply execute my (long running) Runnable code in the onHandleIntent() because this method alread runs on the IntentService worker Thread?
If you are happy with single threaded serial execution yes. That's what onHandleIntent does for you.
If yes, is there a queue limit for intent, since onHandleIntent() could run up to 30 seconds before finishing and handling the next Intent.
There is no limit (it's a linked list as far as I can tell). But there is also nothing that stops you from producing more tasks than it can handle which will ultimately lead to some kind of overflow.
I have an infinite loop in my IntentService to update my view once every 30 seconds based on the input from the main activity.
public class IntentServiceTest extends IntentService {
String Tag = "IntentServiceTest";
String ACTION_RCV_MESSAGE = "com.jsouptest8.intent.action.MESSAGE";
public IntentServiceTest(){
super("IntentServiceTest");
Log.d(Tag, "IntentServiceTest constructor");
}
#Override
protected void onHandleIntent(Intent intent) {
// TODO Auto-generated method stub
Log.d(Tag, "in onHandleIntent");
String url = intent.getStringExtra("URL");
Document doc;
int i=0;
try{
while(true){
Log.d(Tag, "entered try block...");
Log.d(Tag, "url = "+url);
doc = Jsoup.connect(url)
.get();
Log.d(Tag, "past Jsoup.connect");
Element data = doc.select("table").get(1).attr("bgcolor", "#f4f36f");
Log.d(Tag, data.toString());
Log.d(Tag, data.text());
Log.d(Tag, "creating intent...");
Intent broadcastIntent = new Intent();
Log.d(Tag, "setting action...");
broadcastIntent.setAction(ACTION_RCV_MESSAGE);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra("OUTPUT", data.toString());
Log.d(Tag, "sending broadcast: "+(i++));
sendBroadcast(broadcastIntent);
Thread.sleep(30*1000);
}
}
catch(StackOverflowError e){
Log.d(Tag, "in StackOverflowError block...");
Log.d(Tag, "creating intent...");
Intent broadcastIntent = new Intent();
Log.d(Tag, "setting action...");
broadcastIntent.setAction(ACTION_RCV_MESSAGE);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra("OUTPUT", "系統忙線中, 請稍後再試");
Log.d(Tag, "sending broadcast...");
sendBroadcast(broadcastIntent);
}
catch(Exception e){
Log.d(Tag, "in catch Exception block...");
onHandleIntent(intent);
}
}
}
The problem is, I am stuck in this loop. Even if I kill the main activity and then return to it to enter a new input and the IntentService still returns based on the old input.
I need to know how I can update myself from the URL every 30 second without getting stuck. Thanks!
An IntentService is meant to finish of a task and return. It does this task in a new thread. Do not use while loop in IntentService. Your IntentService will get killed after sometime. I am telling this from personal experience. I tried using a while loop in it. And at the end of the while loop I used sleep(60000) i.e 1 minute. But I found that my IntentService was killed after sometime.
I would recommend you not to use an AlarmManager for 30 seconds, as some have siggested. Because 30 seconds is too short. it will drain the battery. For AlarmManager use a minimum 1 minute with RTC.
If you still want it to be 30 seconds, use a service. In the service use your logic. But do that in a separate thread i.e spawn a new thread in your Service and used while loop there and sleep(). And do not forget to use startForeGround. This reduces the probabilty of android killing your service greatly.
Using a while statement inside an IntentService, or any kind of Service for that matter is a bad idea. It is especially a bad idea inside an IntentService because the IntentService is supposed to finish a task and then get terminated automatically, you are in essence defeating the whole purpose of using an IntentService.
I would recommend to remove the loop in the IntentService and to use an alarm to wake up the IntentService every 30 seconds. That way, your service gets called every 30 seconds for sure and for the time that it is not processing, it can actually go back to sleep. Moreover, to handle cases where a new call to the IntentService is received while the IntentService is servicing an older request, you can add code to the onStartCommand method of your IntentService to see if the call should be enqueued for processing or ignored altogether.
Set an alarm using this method:
public void setRepeating (int type, long triggerAtMillis, long
intervalMillis, PendingIntent operation)
Link: http://goo.gl/E9e6
For a more efficient approach, use setInexactRepeating (but that does not guarantee a 30 second wakeup)
PS. We don't normally override the onStartCommand of an IntentService but it can be done if your app really that functionality.
in this link you'll find a service that updates itself using a timer
Keep Service running
If your comfortable with the while loop just write an if statement that exists the loop
if(thisIsTrue)
{
break; // this will exit the loop!
}
It would be better that you keep the loop or timer or any such running task in the MainActivity itself and execute IntentService everytime. Because IntentService will perform task and finish itself everytime or queue the task to be delivered further.
From the Docs -
IntentService will receive the Intents, launch a worker thread, and
stop the service as appropriate.
It uses work queue processor pattern to maintain the task.