I have an app that runs a webview as a service so the audio can contiune to play while the screen is locked. The app works great with audio streams such as podcasts. But I also wanted it to work with flash video. I am able to load the flash video stream in the webview and have it play smooth and steady but once the screen goes off or is locked the audio becomes choppy. The behavior is the same on 3g and WiFi. I tried to use this posts suggestion of using a wake lock with:
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Tag");
wl.acquire();
//do what you need to do
wl.release();
However this has no effect. I'm not sure where exactly in my code to put it but I've placed it in the oncreate for the service, which had no effect, and I placed it in the oncreate for my main activity with the same results.
However later in the post the person asking the question said that WIFI_MODE_FULL_HIGH_PERF was able to fix the problem. But as I said I tested it on 3g and the audio stuttered when the screen was turned off.
Any ideas how I can stop this behavior?
Also I know this is a CPU intensive and a battery hog app but I'm just developing it for personal use.
This is my full code for the service:
public class MyService extends Service {
private NotificationManager nm;
private static boolean isRunning = false;
ArrayList<Messenger> mClients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
int mValue = 0; // Holds last value set by a client.
static final int MSG_REGISTER_CLIENT = 1;
static final int MSG_UNREGISTER_CLIENT = 2;
static final int MSG_SET_INT_VALUE = 3;
static final int MSG_SET_STRING_VALUE = 4;
PowerManager.WakeLock wl;
#Override
public IBinder onBind(Intent intent) {
wl.acquire();
return null;
}
#Override
public void onCreate() {
super.onCreate();
Log.i("MyService", "Service Started.");
showNotification();
isRunning = true;
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Tag");
}
private void showNotification() {
nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(R.string.service_started);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.ic_launcher, text, System.currentTimeMillis());
notification.flags = Notification.FLAG_ONGOING_EVENT;
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.service_label), text, contentIntent);
// Send the notification.
// We use a layout id because it is a unique number. We use it later to cancel.
nm.notify(R.string.service_started, notification);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("MyService", "Received start id " + startId + ": " + intent);
return START_STICKY; // run until explicitly stopped.
}
public static boolean isRunning()
{
return isRunning;
}
#Override
public void onDestroy() {
super.onDestroy();
nm.cancelAll();
wl.release();
nm.cancel(R.string.service_started); // Cancel the persistent notification.
Log.i("MyService", "Service Stopped.");
isRunning = false;
}
}
My main code:
public class MainActivity extends Activity {
Button btnStart, btnStop, btnBind, btnUnbind, btnUpby1, btnUpby10;
Messenger mService = null;
boolean mIsBound;
WebView mWebView;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
try {
Message msg = Message.obtain(null, MyService.MSG_REGISTER_CLIENT);
mService.send(msg);
} catch (RemoteException e) {
// In this case the service has crashed before we could even do anything with it
}
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been unexpectedly disconnected - process crashed.
mService = null;
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnStart = (Button)findViewById(R.id.btnStart);
btnStop = (Button)findViewById(R.id.btnStop);
btnBind = (Button)findViewById(R.id.btnBind);
btnUnbind = (Button)findViewById(R.id.btnUnbind);
btnStart.setOnClickListener(btnStartListener);
btnStop.setOnClickListener(btnStopListener);
btnBind.setOnClickListener(btnBindListener);
CheckIfServiceIsRunning();
//webview
mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setPluginsEnabled(true);
mWebView.loadUrl(url);
mWebView.setWebViewClient(new HelloWebViewClient());
}
private void CheckIfServiceIsRunning() {
//If the service is running when the activity starts, we want to automatically bind to it.
if (MyService.isRunning()) {
doBindService();
}
}
private OnClickListener btnStartListener = new OnClickListener() {
public void onClick(View v){
startService(new Intent(MainActivity.this, MyService.class));
}
};
private OnClickListener btnStopListener = new OnClickListener() {
public void onClick(View v){
doUnbindService();
stopService(new Intent(MainActivity.this, MyService.class));
}
};
private OnClickListener btnBindListener = new OnClickListener() {
public void onClick(View v){
doBindService();
}
};
private OnClickListener btnUnbindListener = new OnClickListener() {
public void onClick(View v){
doUnbindService();
}
};
void doBindService() {
bindService(new Intent(this, MyService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
if (mIsBound) {
// If we have received the service, and hence registered with it, then now is the time to unregister.
if (mService != null) {
try {
Message msg = Message.obtain(null, MyService.MSG_UNREGISTER_CLIENT);
mService.send(msg);
} catch (RemoteException e) {
// There is nothing special we need to do if the service has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
private class HelloWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
#Override
protected void onDestroy() {
super.onDestroy();
try {
doUnbindService();
} catch (Throwable t) {
Log.e("MainActivity", "Failed to unbind from the service", t);
}
}
#Override
protected void onResume() {
super.onResume();
}
}
From your description it sounds like you are releasing the WakeLock too soon.
If you are just putting that block of code in the onCreate for the Service then you really aren't getting a WakeLock while your streaming code is running. You must have the lock acquired for the duration of the background work.
The pattern I've used for a service like this is:
Create the WakeLock in onCreate (but do not acquire it)
In onDestroy if the WakeLock is held release it. (mostly for safety)
When starting the actual background work, either in onBind or onStartCommand, acquire the lock.
When done with the background work, release the wake lock.
The problem may be specifically with the way a WebView works. That said since this isn't production code, you could try simply 'leaking' the wake lock by removing the release just to see if that helps. Just create and acquire the lock at the start of your activity and not bother with the Service.
The Service isn't really giving you much. The way your code is currently structured you are still going to have problems. Your Activity can be still deleted when in the background even if you have a Service, simply having a service won't stop this. Further because the WebView is a View it really requires an Activity and view hierarchy.
Related
I am developing an Android Wear OS 2.0 application. Every time a user gets an SMS from a given number, the watch should start vibrating, and a UI with a given text should appear, with a button, which stops the vibration. It works in the following way:
In the SmsReciever.java I'm checking if the phone number is matching, or the UI screen is already active.
public class SmsReceiver extends BroadcastReceiver {
//interface
private static SmsListener mListener;
#Override
public void onReceive(Context context, Intent intent) {
Bundle data = intent.getExtras();
Object[] pdus = (Object[]) data.get("pdus");
for(int i=0;i<pdus.length;i++){
SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdus[i], "3gpp");
String sender = smsMessage.getDisplayOriginatingAddress();
String alertPhoneNumber = "301112233";
if (sender.toLowerCase().contains(alertPhoneNumber.toLowerCase()))
{
String messageBody = smsMessage.getMessageBody();
//Pass the message text to interface
mListener.messageReceived(messageBody);
} else if (AlarmActivity.active) {
Intent intent1 = new Intent();
intent1.setClassName("hu.asd.watchtest", "hu.asd.watchtest.AlarmActivity");
intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent1);
}
}
}
public static void bindListener(SmsListener listener) {
mListener = listener;
}
}
I have needed the else if part, because sometimes when the UI was active, and I've received a new message, the vibration stopped. So that part starts the AlarmActivity (which handles the vibrating).
In the MainActivity.java I'm binding a new listener, so now every time I get the right message in the SmsReciever, the AlarmActivity should run:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
requestPermissions(Manifest.permission.RECEIVE_SMS, PERMISSIONS_REQUEST_RECEIVE_SMS);
SmsReceiver.bindListener(new SmsListener() {
#Override
public void messageReceived(String messageText) {
Intent myIntent = new Intent(MainActivity.this, AlarmActivity.class);
MainActivity.this.startActivity(myIntent);
}
});
}
In the AlarmActivity.java the application wakes the screen up, then gets the Vibrator, sets the onClickListeners to the stop vibrating button, and then starts the actual vibration. I also change the active state here:
public class AlarmActivity extends WearableActivity {
static boolean active = false;
#Override
public void onResume() {
super.onResume();
active = true;
}
#Override
public void onStart() {
super.onStart();
active = true;
}
#Override
public void onStop() {
super.onStop();
active = false;
}
#Override
public void onPause() {
super.onPause();
active = false;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_alarm);
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP|PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
"MyWakelockTag");
wakeLock.acquire();
findViewById(R.id.stop_button).getBackground().setLevel(5000);
final Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
final long[] mVibratePattern = new long[]{0, 400, 800, 600, 800, 800, 800, 1000};
final int[] mAmplitudes = new int[]{0, 255, 0, 255, 0, 255, 0, 255};
final Button stopButton = findViewById(R.id.stop_button);
stopButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
vibrator.cancel();
wakeLock.release();
new SendEmail().execute();
Intent myIntent = new Intent(AlarmActivity.this, MainActivity.class);
AlarmActivity.this.startActivity(myIntent);
}
});
setAmbientEnabled();
vibrator.vibrate(VibrationEffect.createWaveform(mVibratePattern, mAmplitudes, 0)); // 0 to repeat endlessly.
}
}
If the user presses the Button, then the MainActivity starts again. The problem is, that out of 50 cases, I can send multiple text messages, the watch will vibrate until I press the stop button, but in 1 case, it'll only vibrate once, then it will stop vibrating. It usually happens, if the AlarmActivity is active already and vibrating, or vibration was cancelled before. I'm guessing that I do way to much work with intents, or something with the Vibrator instances? Or when I get a new text, the watch requires the Vibrator, and my Application can't get it?
This is my first Android application, and I tried a lot of different implementations, but still not perfect.
I am new in android. I am creating a Lock screen application. In my application, I want to disable all the outside keys like Home key, Back key.. I already disabled the Back key using:
#Override
public void onBackPressed() {
return;
// Do nothing!
}
But i referred a lot of sites and questions in Stack Overflow to disable the Home key in my app. But nothing worked.
My App working on API 16 .. Please help me. Any help would be appreciated.
Thanks a lot in advence
I recommend reading:
How-To Create a Working Kiosk Mode in Android
Disable the home button and detect when new applications are opened
Since Android 4 there is no effective method to deactivate the home
button. That is the reason why we need another little hack. In general
the idea is to detect when a new application is in foreground and
restart your activity immediately.
At first create a class called KioskService that extends Service and
add the following snippet:
public class KioskService extends Service {
private static final long INTERVAL = TimeUnit.SECONDS.toMillis(2); // periodic interval to check in seconds -> 2 seconds
private static final String TAG = KioskService.class.getSimpleName();
private static final String PREF_KIOSK_MODE = "pref_kiosk_mode";
private Thread t = null;
private Context ctx = null;
private boolean running = false;
#Override
public void onDestroy() {
Log.i(TAG, "Stopping service 'KioskService'");
running =false;
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Starting service 'KioskService'");
running = true;
ctx = this;
// start a thread that periodically checks if your app is in the foreground
t = new Thread(new Runnable() {
#Override
public void run() {
do {
handleKioskMode();
try {
Thread.sleep(INTERVAL);
} catch (InterruptedException e) {
Log.i(TAG, "Thread interrupted: 'KioskService'");
}
}while(running);
stopSelf();
}
});
t.start();
return Service.START_NOT_STICKY;
}
private void handleKioskMode() {
// is Kiosk Mode active?
if(isKioskModeActive()) {
// is App in background?
if(isInBackground()) {
restoreApp(); // restore!
}
}
}
private boolean isInBackground() {
ActivityManager am = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
ComponentName componentInfo = taskInfo.get(0).topActivity;
return (!ctx.getApplicationContext().getPackageName().equals(componentInfo.getPackageName()));
}
private void restoreApp() {
// Restart activity
Intent i = new Intent(ctx, MyActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ctx.startActivity(i);
}
public boolean isKioskModeActive(final Context context) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
return sp.getBoolean(PREF_KIOSK_MODE, false);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Add the following method in your AppContext class to start the service
via application context creation.
#Override
public void onCreate() {
super.onCreate();
instance = this;
registerKioskModeScreenOffReceiver();
startKioskService(); // add this
}
private void startKioskService() { // ... and this method
startService(new Intent(this, KioskService.class));
}
Last, add the service declaration and the permission for retrieving
the foreground process to the manifest:
<service android:name=".KioskService" android:exported="false"/>
<uses-permission android:name="android.permission.GET_TASKS"/>
Basically, the thread checks every two seconds if your application is
running in foreground. If not, the thread will immediately recreate
your activity.
This question already has answers here:
Press home button to stop service
(3 answers)
Closed 6 years ago.
I am implementing following service to play sound. This sound stop when someone press on any menu button. But If I press home key of device sound still playing. I am adding this code
#Override
protected void onPause() {
super.onPause();
objIntent = new Intent(this, PlayAudio.class);
stopService(objIntent);
}
in my activity class. But still sound not stop. why onPause method is not working?
Service code-
public class PlayAudio extends Service{
private static final String LOGCAT = null;
MediaPlayer objPlayer;
public void onCreate(){
super.onCreate();
Log.d(LOGCAT, "Service Started!");
objPlayer = MediaPlayer.create(this, R.raw.test);
}
public int onStartCommand(Intent intent, int flags, int startId){
objPlayer.start();
Log.d(LOGCAT, "Media Player started!");
if(objPlayer.isLooping() != true){
Log.d(LOGCAT, "Problem in Playing Audio");
}
return 1;
}
public void onStop(){
objPlayer.stop();
objPlayer.release();
}
public void onPause(){
objPlayer.stop();
objPlayer.release();
}
public void onDestroy(){
objPlayer.stop();
objPlayer.release();
}
#Override
public IBinder onBind(Intent objIndent) {
return null;
}
}
you can stop service on home key press..try this
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_HOME)
{
objIntent = new Intent(this, PlayAudio.class);
stopService(objIntent);
}
});
write one Thread in Service which continuosly checks for running tasks and stop the Service if top task's top activity package name is equal to the package name of launcher app...
new Thread() {
public void run() {
final List<ResolveInfo> activities = getPackageManager().queryIntentActivities(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME), 0);
final ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
while(true) {
String packageName = activityManager.getRunningTasks(5).get(0).topActivity.getPackageName();
if(activities.contains(packageName)) {
break;
}
try {
Thread.sleep(500); // wait for half second
} catch (InterruptedException e) {
}
}
stopSelf();
};
}.start();
I need to have background music in all my activities. It should stop when the application is not foreground. As I'm developing for 2.3 I can't use the ActivityLifeCycleCallBacks class. I implemented the solution at Checking if an Android application is running in the background and then decided to make the mediaplayer a singleton and use it in a service.
Everything works fine and if I press home, select quit from the menu or I make the application go background any way the sound stops but... after some random time when I'm doing something else or even when the screen is turned off the music will start again out of the blue. Even if I kill the application from task manager the will start again later again.
This is my first singleton and my first time playing with service so I guess I'm missing something really basic. I think I'm closing the service but apparently I'm not.
Here is the code:
PlayAudio.java
import ...
public class PlayAudio extends Service{
private static final Intent Intent = null;
MediaPlayer objPlayer;
private int length = 0;
boolean mIsPlayerRelease = true;
private static PlayAudio uniqueIstance; //the singleton
static PlayAudio mService;
static boolean mBound = false; // boolean to check if the service containing this singleton is binded to some activity
public static boolean activityVisible; // boolean to check if the activity using the player is foreground or not
//My attempt to make a singleton
public static PlayAudio getUniqueIstance(){
if (uniqueIstance == null) {
uniqueIstance = new PlayAudio();
}
return uniqueIstance;
}
public static boolean isActivityVisible() {
return activityVisible;
}
public static void activityResumed() {
activityVisible = true;
}
public static void activityPaused() {
activityVisible = false;
}
static public ServiceConnection mConnection = new ServiceConnection() {// helper for the activity
public void onServiceConnected(ComponentName className,
IBinder service) {
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
public static Intent createIntent (Context context) { //helper for the activity using the player
Intent intent = new Intent(context, PlayAudio.class);
return intent;
}
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
PlayAudio getService() {
// Return this instance so clients can call public methods
return PlayAudio.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public void onCreate(){
super.onCreate();
Log.d(LOGCAT, "Service Started!");
objPlayer = MediaPlayer.create(this,R.raw.kickstarterreduced);
objPlayer.setLooping(true);
mIsPlayerRelease = false;
}
public int onStartCommand(Intent intent, int flags, int startId){
objPlayer.start();
Log.d(LOGCAT, "Media Player started!");
if(objPlayer.isLooping() != true){
Log.d(LOGCAT, "Problem in Playing Audio");
}
return 1;
}
public void onStop(){
objPlayer.setLooping(false);
objPlayer.stop();
objPlayer.release();
mIsPlayerRelease = true;
}
public void onPause(){
if(objPlayer.isPlaying())
{
objPlayer.pause();
length=objPlayer.getCurrentPosition(); // save the position in order to be able to resume from here
}
}
public void resumeMusic() // if length is 0 the player just start from zero
{ if (mIsPlayerRelease == true) {
objPlayer = MediaPlayer.create(this,R.raw.kickstarterreduced);
mIsPlayerRelease = false;
}
if(objPlayer.isPlaying()==false )
{
if (length != 0) objPlayer.seekTo(length);
objPlayer.start();
}
}
}
And this are the methods I have implemented in every activity's class
SharedPreferences sharedPrefs;
PlayAudio playerIstanced;
public static boolean activityVisible;
#Override
public void onStart() {
super.onStart();
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
}
#Override
public void onResume() {
super.onResume();
playerIstanced= PlayAudio.getUniqueIstance(); //call singleton
bindService(PlayAudio.createIntent(this), playerIstanced.mConnection, Context.BIND_AUTO_CREATE); // create the service
if (sharedPrefs.getBoolean("sound", true) == true) {// if sound is enabled in option it will start the service
startService(PlayAudio.createIntent(this));
playerIstanced.mService.activityResumed();
if (playerIstanced.mBound == true) {
playerIstanced.mService.resumeMusic();
}
}
}
#Override
public void onPause() {
super.onPause();
playerIstanced.mService.activityPaused();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
//If the phone lags when changing activity (between onPause() and the other activity onResume() the music won't stop. If after 500ms onResume() is not called it means the activity went background...Am I messing with service here?
if (playerIstanced.mService.isActivityVisible() != true) {
playerIstanced.mService.onPause();
}
}
}, 500);
}
#Override
public void onStop(){
super.onStop();
// Unbind from the service
if (playerIstanced.mService.mBound) {
playerIstanced.mService.mBound = false;
unbindService(playerIstanced.mService.mConnection);
}
}
}
Stop music automatically when user exit from app
This part has to be in EVERY activity's onPause:
public void onPause(){
super.onPause();
Context context = getApplicationContext();
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> taskInfo = am.getRunningTasks(1);
if (!taskInfo.isEmpty()) {
ComponentName topActivity = taskInfo.get(0).topActivity;
if (!topActivity.getPackageName().equals(context.getPackageName())) {
StopPlayer();
Toast.makeText(xYourClassNamex.this, "YOU LEFT YOUR APP. MUSIC STOP", Toast.LENGTH_SHORT).show();
}
}
}
This part has to be in EVERY activity's onResume:
Play music automatically when user resume the app
Public void onResume()
{
super.onResume();
StartPlayer();
}
Hope it helps!!
You can check my answer according to this topic may it will sove your issue.
You need to manually stop the service using Context.stopService() or stopSelf(). See the Service Lifecycle section of http://developer.android.com/reference/android/app/Service.html.
Service Lifecycle
There are two reasons that a service can be run by the system. If someone calls Context.startService() then the system will retrieve the service (creating it and calling its onCreate() method if needed) and then call its onStartCommand(Intent, int, int) method with the arguments supplied by the client. The service will at this point continue running until Context.stopService() or stopSelf() is called. Note that multiple calls to Context.startService() do not nest (though they do result in multiple corresponding calls to onStartCommand()), so no matter how many times it is started a service will be stopped once Context.stopService() or stopSelf() is called; however, services can use their stopSelf(int) method to ensure the service is not stopped until started intents have been processed.
I believe you can simply put playerIstanced.stopSelf() in the onStop() call of each activity.
My understanding is that the service continues to run quietly after your application stops. After a while the system kills the service to free up resources, and then after a while more when resources are available it restarts the service. When the service restarts its onResume() is called and the music begins playing.
it helped me stop the mediaplayer.
Use Handler(getMainLooper()) to start and stop MediaPlayer.
final Handler handler = new Handler(getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
mediaPlayer.start();
}
});
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
}
, 30 * 1000);
I am continuing to study from the book "Pro Android 2," working through the Service example that consists of two classes: BackgroundService.java and MainActivity.java. The MainActivity class is shown below and has a couple buttons. The unbind button, unbindBtn, stops the Service but doesn't appear to do much else like kill the thread the Service started.
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.d(TAG, "starting service");
Button bindBtn = (Button)findViewById(R.id.bindBtn);
bindBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Intent backgroundService = new Intent(MainActivity.this, com.marie.mainactivity.BackgroundService.class);
startService(backgroundService);
}
});
Button unbindBtn = (Button)findViewById(R.id.unbindBtn);
unbindBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
stopService(new Intent(MainActivity.this, BackgroundService.class));
}
});
}
}
The documentation says "if your service is going to do any CPU intensive work or blocking operations..., you should create a new thread within the service to do that work." And that's exactly what the BackgroundService class does below. As you can see below I've added a while(true) loop in the thread's run() method to see what happens to the thread when I stop the Service.
public class BackgroundService extends Service {
private NotificationManager notificationMgr;
#Override
public void onCreate() {
super.onCreate();
notificationMgr = NotificationManager)getSystemService(NOTIFICATION_SERVICE);
displayNotificationMessage("starting Background Service");
Thread thr = new Thread(null, new ServiceWorker(), "BackgroundService");
thr.start();
}
class ServiceWorker implements Runnable
{
public void run() {
// do background processing here...
long count = 0;
while (true) {
if (count++ > 1000000)
{
count = 0;
Log.d("ServiceWorker", "count reached");
}
}
//stop the service when done...
//BackgroundService.this.stopSelf();
}
}
#Override
public void onDestroy()
{
displayNotificationMessage("stopping Background Service");
super.onDestroy();
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void displayNotificationMessage(String message)
{
Notification notification = new Notification(R.drawable.note, message, System.currentTimeMillis());
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
notification.setLatestEventInfo(this, "Background Service", message, contentIntent);
notificationMgr.notify(R.id.app_notification_id, notification);
}
}
When I press the unbind button, unbindBtn, in the MainActivity class I trust the Service in this example will be stopped. But from what I can see in logcat the thread that was started by the Service continues to run. It's like the thread is now some kind of orphan with no apparent way to stop it. I've seen other source code use a while(true) loop in a thread's run() method. This seems bad unless a way to break out of the loop is provided. Is that typically how it's done? Or are there other ways to kill a thread after the Service that started it has stopped?
You should provide a 'running' boolean.
while(running) {
//do your stuff
}
You want to make it something that you can update. Perhaps your Service's onDestroy() method should call a stopProcessing() method on your Runnable, which will set the 'running' boolean to false.