Media Player Bound Service Locks Up UI When Pausing Thread - java

I know that pausing a thread can easily lockup the UI, and it is generally a bad idea. However it was my understanding that if something is running as a service that will not cause any issues, because the service will pause and the main app will continue running.
With that in mind, I am either doing something wrong, or just misunderstood the use of a service for a MediaPlayer.
I create the object
public AudioService AudioService;
public boolean AudioServiceBound = false;
and then in my SurfaceView's onStart event I bind it:
public void onStart() {
Intent intent = new Intent(gameContext, AudioService.class);
gameContext.bindService(intent, myConnection, Context.BIND_AUTO_CREATE);
}
throughout the rest of the class I run methods that pause and resume the AudioService based on the onResume and onPause events.
I have tried to introduce a new ability to my service. Within my main update loop I run the function HalfOverSwitch() seen below:
public void HalfOverSwitch()
{
if (( ((float)player.getCurrentPosition()) / ((float)player.getDuration()) > 0.5) && !transitioning)
{
transitioning = true;
MediaPlayer temp = MediaPlayer.create(this, R.raw.dumped);
temp.setVolume(0, 0);
temp.setLooping(true);
temp.start();
for (int i = 0; i < 100; i++)
{
player.setVolume(100-i, 100-i);
temp.setVolume(i, i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
player = temp;
player.setVolume(100,100);
transitioning = false;
}
}
Because that function doesn't return anything and is running in a different thread, it was my understanding that the main activity would not pause. It does however. That brings up the question, what is the best way to do something like that, and what is the point of making my AudioService a service at all (and not just a class)?

Service runs in the same thread in which service is created.
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. "

Related

Background service in Application class

I'd like to run a service in background but after I read the docs I'm a bit confused.
(I'm targeting SdkVersion 27, which means I can't start a BroadcastReceiver from the AndroidManifest.xml and need to do these tasks within the Application class, afaik.)
Before I was starting the IntentService within a BroadcastReceiver, which I started by using a PendingIntent, which was triggered by an AlarmManager. It felt a bit too much, so I started the service directly on the onCreate() of the Application.
It's working, but I'm not sure if that's a good practice.
The service is supposed to run forever and fire it's own threads for operations that can take up to one minute and run again as soon as they are finished (+ 5 seconds).
Pseudocode of the services purpose
MyService // starts on Application creation an runs "forever"
threads = []
itemIds = []
async loop manageThreads // start / kill
itemIds = getItemIdsFromDatabase()
loop itemIds vs threads
if noThreadRunningForCurrentItemId
threads.push(new ItemThread(itemId).start())
loop threads vs itemIds
if threadRunsForNoneExistingItemId
threads[currentItemId].kill()
sleep(20000) // manage threads every 20 seconds
ItemThread(int itemId)
doSomething()
sleep(5000)
ItemThread(itemId) // restart thread every 5 seconds
I'd like to avoid that the service get's killed by Android, blocks other Threads or leads to memory leaks.
What's the best practise for this use-case, any idea?
I don't think my question is opinion-based, because I guess that a pattern exists, which I'm not aware of yet.
App.java
public class App extends Application {
#Override
public void onCreate() {
super.onCreate();
// Start MyService to run in the background
Intent service = new Intent(this, MyService.class);
this.startService(service);
}
}
MyService.java
public class MyService extends IntentService {
public MyService() {
super("MyService")
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
try {
int i = 0;
while(true) {
Log.d("MyService", "i = " + String.valueOf(i));
i++;
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

Service not running after closing app

I am trying to make background service that will run 15 sec after user closes tha app, I have done service that runs 15 sec (loop with Logs), bud when I close tha app, then it stopes
and another problem is, when I try to stop it from main activity by stopService(intent); then the onDestroy method is called, but thread with loop continues
.. please can someone help me?
*sorry for my english - no native :D
public class NotificationService extends Service {
final private class MyThread implements Runnable {
int service_id;
MyThread(int service_id) {
this.service_id = service_id;
}
#Override
public void run() {
synchronized (this) {
for (int i = 0; i < 15; i++) {
try {
wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("onStartCommand", "loop:" + i);
}
stopSelf(service_id);
}
}
}
Thread thread;
#Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show();
}
#Override
public int onStartCommand(#Nullable Intent intent, int flags, int startId) {
Log.e("onStartCommand", "started");
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
thread = new Thread(new MyThread(startId));
thread.start();
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
Log.e("onDestroy", "onDestroy");
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
super.onDestroy();
}
}
I am trying to make background service that will run 15 sec after user closes tha app, I have done service that runs 15 sec (loop with Logs), bud when I close tha app, then it stopes
Your code only starts the loop thread when startService(yourNotificationService)is called on the Activity or Broadcast Receiverthat is responsible for calling it does so. It then kills itself with stopSelf(service_id).
If, after you have returned from onStartCommand(), you immediately kill the app without calling stopSelf(service_id) (i.e. your 15 seconds is not up), then your Service will MOST LIKELY restart itself given the START_STICKY return value. However, after you call stopSelf(service_id) you are telling the Service to kill itself; after you close your app, there is nothing to tell your Service to restart through the onStartCommand() call.
and another proble is, when I try to stop it from main activity by stopService(intent); then the onDestroy method is called, but thred with loop continues
A Service is an Android component; it is not another process or thread, it runs in the same process and thread as the main UI thread unless you specify otherwise, as seen here.
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 your case, calling stopService(intent) tells the Service to stop itself, which it does. It does not stop the Thread you started (the MyThread instance). To do that, you must first make your Thread interruptible; see here to do that. Once you do that, you need to change your onDestroy() code to actually interrupt the MyThread instance, as here
#Override
public void onDestroy() {
Log.e("onDestroy", "onDestroy");
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
thread.interrupt();
super.onDestroy();
}

Thread within service dies for some reason*

I've posted about this project before, I'm trying to create a service, from a Home Activity, that constantly checks if the screen is locked or not (ScreenLockService is the IntentService's name). If so, it creates another service to "listen" (Listener is the IntentService's name) for sound. If not, it stops the existing Listener service if one is running.
So to do so, I've created a Thread within the onHandleIntent method of SLS that should always be checking if the screen is locked or not. Here is how that's implemented so far:
(Sorry for the wall of code, I'll try to make it look pretty)
Thread checkScreen = new Thread(
new Runnable() {
#Override
public void run() {
Boolean lockInput = false;
while(!Thread.interrupted()) {
//The first time the screen becomes locked is special, it should start the whole process from there
KeyguardManager firstKeyManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (firstKeyManager.inKeyguardRestrictedInputMode()) {
lockInput = true;
}
//If the screen has been locked for the first time:
while(lockInput) {
//Put a timer here do slow the app down and figure out what's going wrong
new Timer().schedule(
new TimerTask() {
#Override
public void run() {
//if the screen is locked start listener service else stop listener service (METHODS WITHIN THIS SERVICE HANDLE THIS)
KeyguardManager secondKeyManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (secondKeyManager.inKeyguardRestrictedInputMode()) {
startListener();
}
else if (!secondKeyManager.inKeyguardRestrictedInputMode()) {
stopListener();
}
}
}, 10000 //10 seconds
);
}
}
}
}
);
//Might be relevant to the problem, if the thread dies I need it to start back up again.
//This is part of the onHandleIntent method, at the very bottom, and not inside the thread.
checkScreen.setName("checkScreen");
Log.d(TAG, "Starting SLS thread");
checkScreen.start();
checkScreenAlive = true;
while(checkScreenAlive){
if(!checkScreen.isAlive()){
Log.d(TAG, "Restarting check screen!");
checkScreen.start();
}
}
Now, I have Log messages all over the place so I can see what state the app is in (starting services, stopping services, checking the screen, listening, etc). When I debug it and lock the screen for the first time, nothing will be logged until 10 seconds later it spams Listener Service already running about 20 times then the service dies.
Maybe I don't fully understand how the timer works in java, but I have no clue why the service is dying. I probably don't even need to do this in a thread, or maybe not even use an IntentService and use a regular Service instead. I've read about the differences and I think what I have is right.
If I should post more code I can, don't hesitate to ask. I'm trying to make this as straightforward as possible, this is my first app and I'm still easily confused by some of this stuff.
You have while(lockInput) { which never gets set to false and will generate a lot of Timer().schedule events.
This schedule will be kicked after 10seconds which is where you are seeing the delay.
I would start by changing
while(lockInput) {
...
}
to
if(lockInput) {
lockInput = false; //Only want this called once per check
//Put a timer here do slow the app down and figure out what's going wrong
new Timer().schedule(
new TimerTask() {
#Override
public void run() {
//if the screen is locked start listener service else stop listener service (METHODS WITHIN THIS SERVICE HANDLE THIS)
KeyguardManager secondKeyManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (secondKeyManager.inKeyguardRestrictedInputMode()) {
startListener();
}
else if (!secondKeyManager.inKeyguardRestrictedInputMode()) {
stopListener();
}
}
}, 100 //Fire of the check almost instantly
);
}
Thread.sleep(10000); //So we are yeilding to the system, don't want an essential while(true) loop

how to control the threads running within a process on android platform

Is there any way/tool to control the threads running within a process on android platform, such as let some threads delay for a random time.
Background: I am a QA engineer. I'd like to see if some threads are forced to run slowly, what will happen to the whole app? I'd like to see other kind of errors rather than ANR. For multiple threading, if programmer doesn't use or use bad strategies to sync threads, some bugs might happen. So I want to do this kind of testing.
You only have to take care of the UIThread, well actually Android will take care of it for you. Taking that into account, try to don't do any intensive operation in this thread as you won't have full control of it (see activity lifecycle)
As long as the UIthread is fine, you won't notice a slow responding app, for the rest of threads I suggest you taking a look in different classes that will ease the task of communicating back with this UIThread;
Asynctask & Handlers, there are more options, but these two are the most important (imo)
The rest of threads, you can control them as you would in Java, even sleeping them if needed.
Let's see an example:
public class MapView extends SurfaceView implements Runnable{
Thread t = null;
SurfaceHolder holder;
boolean draw = false;
#Override
public void run() {
while (draw) {
if (!holder.getSurface().isValid())
continue;
Canvas c = holder.lockCanvas();
//Draw something
holder.unlockCanvasAndPost(c);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
void pause() {
draw = false;
try {
t.join(); // this will cause the main thread to wait for this one to
//finish and then it can safely access its data.
} catch (InterruptedException e) {
e.printStackTrace();
}
t = null;
}
void resume() {
draw = true;
t = new Thread(this);
t.start(); // This will look for our run method (this)
}
}
In this example, a normal Thread is used to control how/when and with what delay we draw. The resume and pause methods let us control that thread so we can stop drawing if the activity using it is in background and restart it when it comes back (Overriding onPause and onResume)

Waiting for a Runnable to complete before running another Runnable

I have an Android app with a main tab activity, and several activities within the individual tabs. In my main activity's onCreate(), I have a runnable that creates a list, and in the individual activities, I make use of this list.
In the individual activities's onCreate(), I also have Runnables that operate on the list. However, I need these Runnables to only run when the main tab activity's Runnable completes creating the list, otherwise I'd get a null list. I'm trying to find an elegant way of doing this. Right now, in my main activity's Runnable, I'm setting a global boolean variable isDone, and in my individual activity's Runnable, I'm waiting for isDone to be set via a while loop. This works, but probably isn't the best way of doing so.
Any thoughts?
Thanks.
Edit:
I'm trying the following code out, but I'm getting runtime errors:
In my MainActivity's Runnable:
mainRunnable = new Runnable() {
public void run() {
try {
generateList();
synchronized(this) {
listDone = true;
notifyAll();
}
} catch (Exception e) {
Log.e("BACKGROUND_PROC", e.getMessage());
}
}
};
Thread thread = new Thread(null, mainRunnable, "Background");
thread.start();
In my OtherActivity's Runnable:
otherRunnable = new Runnable() {
public void run() {
synchronized(MainActivity.mainRunnable) {
if (!MainActivity.getListDone()) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
}
};
Thread thread = new Thread(null, otherRunnable, "Background");
thread.start();
The mainRunnable seems to run completely, but the otherRunnable seems to cause the app to crash. I get the following error message:
01-10 15:41:25.543: E/WindowManager(7074): Activity com.myapp.MainActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView#40539850 that was originally added here
01-10 15:41:25.543: E/WindowManager(7074): android.view.WindowLeaked: Activity com.myapp.MainActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView#40539850 that was originally added here
You can use the wait and notify methods.
To do this, there needs to be some globally accessible object whose lock isn't used by anything else in the program at this point in time. I'm assuming that the list-creating Runnable itself can play this role.
So you could add something like this to the list-creating Runnable class:
private boolean listsDone = false;
boolean getListsDone() {
return listsDone;
}
And something like this to its run() method, immediately after it's done creating the lists:
synchronized (this) {
listsDone = true;
notifyAll();
}
And something like this to the other Runnables' run() methods, at the point where they need to wait:
synchronized (listCreatingRunnableObject) {
if (!listCreatingRunnableObject.getListsDone()) {
try {
listCreatingRunnableObject.wait();
} catch (InterruptedException e) {
// handle it somehow
}
}
}
Update: To clarify, both synchronized blocks need to be synchronized over the same object, and you have to call wait() and notifyAll() on that object. If the object is the Runnable, then it can be implicit for the first one (as in the above code), but if it's the activity, you need to explicitly use the activity object in both cases.
You can use a Queue like this:
public class RunQueue implemements Runnable
{
private List<Runnable> list = new ArrayList<Runnable>();
public void queue(Runnable task)
{
list.add(task);
}
public void run()
{
while(list.size() > 0)
{
Runnable task = list.get(0);
list.remove(0);
task.run();
}
}
}
This allows you to use one thread rather than multiple threads. And you can maintain all your existing "Runnable" objects while simultaneously cleaning up any code they have for waits and joins.
Set up a CountDownLatch with a value of 1 in the main thread, then have the dependent threads wait on it. When the main thread is done, you Count Down the latch to 0 and the waiters will start right up.
An active wait using a while loop is not a good idea at all. The simplest thing would be for the first Runnable to just fire up the rest of them as its last step. If that can't be made to work for some reason, take a look at posting a message to a Handler.
Is there a reason you are using Runnables and not Threads? If you use Threads, you can use the various thread communication primitives which exist for this exact reason (wait() and join() in particular).
I have created a helper method that contains all the boilerplate code for posting a runnable and waiting until it finishes running.
The logic is similar to what #Taymon describes, but the implementation is more general.
Check it out:
https://gist.github.com/Petrakeas/ce745536d8cbae0f0761
Maybe you can refer to Looper in Android. Simply, a thead keep running task from queue in a while loop.

Categories

Resources