I have an Activity that starts a service which isn't local. Sometimes I check if is alive to perform actions.
My attempt at the moment was to use a static boolean variable. Reading some posts on SO I found out this not works because each process has it's own classloader.
Iterating over all running services is expensive to do a simple task like this.
Other solutions points out to use AIDL. In a very near future in my service, I'll store a WeakReference for the current running activity to execute it again in case of crash. Assuming for now I just want to check the service' state, is this an expensive solution too?
P.S.: I know it's an ugly solution to not handle exception properly. It's just a try.
EDIT: To clarify what I'm doing I post some code. This is the Service classs:
public class CrashRecover extends Service {
private volatile boolean stop = false;
private Thread backgroundThread;
private Messenger serviceMessenger = null;
private static boolean running = false;
...
#Override
public int onStartCommand(Intent intent, int flags, int startID){
serviceMessenger = new Messenger(new ServiceHandler(serviceLooper));
return START_STICKY;
}
#Override
public void onCreate(){
super.onCreate();
HandlerThread handlerThread = new HandlerThread("CrashRecoverThread", Process.THREAD_PRIORITY_BACKGROUND);
handlerThread.start();
serviceLooper = handlerThread.getLooper();
backgroundThread = new Thread(){
#Override
public void run(){
synchronized(this){
try {
while(!stop){
sleep(500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
running = true;
}
#Override
public void onDestroy(){
super.onDestroy();
try {
Message destroyMessage = Message.obtain();
destroyMessage.arg1 = CrashRecover.DESTROY_SERVICE;
serviceMessenger.send(destroyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
running = false;
}
#Override
public IBinder onBind(Intent arg0) {
return serviceMessenger.getBinder();
}
public static boolean isRunning(){
return CrashRecover.running;
}
...
private class ServiceHandler extends Handler{
public ServiceHandler(Looper looper){
super(looper);
}
#Override
public void handleMessage(Message message){
switch(message.what){
case REGISTER_CLIENT:
//addActivityToRespawn(null);
//respawnActivity();
Log.i("INFO", "Service is registered");
break;
case UNREGISTER_CLIENT:
activityParams = message.getData();
//respawnActivity();
if(backgroundThread.isAlive()){
stop = true;
}
Log.i("INFO", "Service is unregistered");
break;
case DESTROY_SERVICE:
Log.i("INFO", "Service is destroyed");
break;
default:
super.handleMessage(message);
}
}
}
}
And this is my class when I verify if service is running:
public class Main extends Activity {
private Button serviceButton, crashButton;
private Intent serviceIntent;
private ClientMessageHandler clientHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
...
clientHandler = new ClientMessageHandler();
serviceIntent = new Intent(Main.this, CrashRecover.class);
startService(serviceIntent);
}
...
#Override
public void onBackPressed(){
if(CrashRecover.isRunning()){
Log.i("INFO", "Service is running");
//Execute some actions
}
}
...
}
If you aren't doing this very often then I'd suggest using the "iterate over running services" method. There shouldn't be that many services running on your phone and iterating over them just accesses some internal data structures that Android keeps. Should work just fine.
Related
Hi in project I'm using service for chat communication using SignalR. Chat communication is working fine but when the app goes to background the service got stopped I need to run the services fully till my app get deleted
Here is me service code
public class SignalRService extends Service {
private HubConnection mHubConnection;
private HubProxy mHubProxy;
private Handler mHandler; // to display Toast message
private final IBinder mBinder = new LocalBinder(); // Binder given to clients
public SignalRService() {
}
#Override
public void onCreate() {
super.onCreate();
mHandler = new Handler(Looper.getMainLooper());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
int result = super.onStartCommand(intent, flags, startId);
startSignalR();
return result;
}
#Override
public void onDestroy() {
Log.i("onDestroy","onDestroy");
mHubConnection.stop();
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
// Return the communication channel to the service.
startSignalR();
return mBinder;
}
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
public SignalRService getService() {
// Return this instance of SignalRService so clients can call public methods
return SignalRService.this;
}
}
/**
* method for clients (activities)
*/
public void sendMessage(String message) {
String SERVER_METHOD_SEND = "Send";
mHubProxy.invoke(SERVER_METHOD_SEND, message);
}
/**
* method for clients (activities)
*/
public void sendMessage_To(String receiverName, String message) {
String SERVER_METHOD_SEND_TO = "SendChatMessage";
mHubProxy.invoke(SERVER_METHOD_SEND_TO, receiverName, message);
}
private void startSignalR() {
Platform.loadPlatformComponent(new AndroidPlatformComponent());
Credentials credentials = new Credentials() {
#Override
public void prepareRequest(Request request) {
request.addHeader("User-Name", "BNK");
}
};
String serverUrl = "http://10.10.10.180/signalr/hubs";
mHubConnection = new HubConnection(serverUrl);
mHubConnection.setCredentials(credentials);
String SERVER_HUB_CHAT = "ChatHub";
mHubProxy = mHubConnection.createHubProxy(SERVER_HUB_CHAT);
ClientTransport clientTransport = new ServerSentEventsTransport(mHubConnection.getLogger());
SignalRFuture<Void> signalRFuture = mHubConnection.start(clientTransport);
try {
signalRFuture.get();
} catch (InterruptedException | ExecutionException e) {
Log.e("SimpleSignalR", e.toString());
return;
}
sendMessage("Hello from BNK!");
String CLIENT_METHOD_BROADAST_MESSAGE = "broadcastMessage";
mHubProxy.on(CLIENT_METHOD_BROADAST_MESSAGE,
new SubscriptionHandler1<CustomMessage>() {
#Override
public void run(final CustomMessage msg) {
final String finalMsg = msg.UserName + " says " + msg.Message;
// display Toast message
mHandler.post(new Runnable() {
#Override
public void run() {
Log.i("message","message: "+finalMsg);
Toast.makeText(getApplicationContext(), finalMsg, Toast.LENGTH_SHORT).show();
}
});
}
}
, CustomMessage.class);
}}
And here is the activity code
public class MainActivity extends AppCompatActivity {
private final Context mContext = this;
private SignalRService mService;
private boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
intent.setClass(mContext, SignalRService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
// Unbind from the service
Log.i("onStop","onStop");
if (mBound) {
unbindService(mConnection);
mBound = false;
}
super.onStop();
}
public void sendMessage(View view) {
if (mBound) {
// Call a method from the SignalRService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
EditText editText = (EditText) findViewById(R.id.edit_message);
EditText editText_Receiver = (EditText) findViewById(R.id.edit_receiver);
if (editText != null && editText.getText().length() > 0) {
String receiver = editText_Receiver.getText().toString();
String message = editText.getText().toString();
mService.sendMessage_To(receiver, message);
mService.sendMessage(message);
}
}
}
/**
* Defines callbacks for service binding, passed to bindService()
*/
private final ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to SignalRService, cast the IBinder and get SignalRService instance
SignalRService.LocalBinder binder = (SignalRService.LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
Log.i("onServiceDisconnected","onServiceDisconnected");
mBound = false;
}
};}
My manifest code for service
<service
android:name=".SignalRService"
android:enabled="true"
android:exported="true" >
</service>
Plese help me on this
If you bind the service with any component the system will automatically destroy the service if no other client is bound with it.
If you want to run a service independently then you have to start a service rather than bind. But you can't communicate with a service if you start it with startService()
For more details you can see the documentation here
You can BOTH start AND bind the service.
In this way, even if multiple components bind to the service at once, then ALL of them unbind, the service will NOT be destroyed. Refer to A service can essentially take two forms: Bound
your service can work both ways: it can be started (to run indefinitely) and also allow binding. It's simply a matter of whether you implement a couple callback methods: onStartCommand() to allow components to start it and onBind() to allow binding.
// onBind method just return the IBinder, to allow clients to get service.
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// onStartCommand just return START_STICKY to let system to
// try to re-create the service if the servcie's process is killed.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
// and make startSignalR public to allow client to call this method.
public void startSignalR() {
}
In your clients, no need to keep a boolean mBound.
Just bind service when onCreate, unbind service when onDestroy. DO NOT unbind when onStop. Since onStop may called many times, for example dialog popup will invoke onStop, but your activity is still on foreground, this will cause your service destroyed.
Refer to my answer for question: Pass Service From one Activity to Another for sample code.
I need to execute multipe tasks in parallel inside a custom service to get these working :
- Location service and activity recognition API.
- Geofence API and REST API calls.
I'm new to threads in java and android, and i found that the best way to implement this is to use a ThreadPoolExecutor instead of making my own thread classes and dealing with all the Handler Looper stuff.
When i execute my app, the service starts, Location updates and activity updates works fine inside a thread. but, when i close the app, the service restarts (when return START_STICKY;) and the thread is not working anymore.When (return START_NOT_STICKY;), the service disappears.
(In my case, i can't use startforeground())
I'm using this library(smart-location-lib) for location and activity updates.
- Here's my custom service code :
public class LocationService extends Service {
private ThreadPoolExecutor mDecodeThreadPool;
private BlockingQueue<Runnable> mDecodeWorkQueue;
private int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
private final int KEEP_ALIVE_TIME = 1;
private final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
public LocationService () {
}
#Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "Location services created", Toast.LENGTH_SHORT).show();
mDecodeWorkQueue = new LinkedBlockingQueue<Runnable>();
mDecodeThreadPool = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2, // Initial pool size
NUMBER_OF_CORES * 2, // Max pool size
KEEP_ALIVE_TIME,
KEEP_ALIVE_TIME_UNIT,
mDecodeWorkQueue);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "Location services started", Toast.LENGTH_SHORT).show();
mDecodeThreadPool.execute(new LocationRunnable(getApplicationContext()));
return START_STICKY;
}
#Override
public void onLowMemory() {
super.onLowMemory();
Log.v("LOW MEMORY", "|||||||||||||||||||||||||||||||||||||");
}
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onDestroy() {
Toast.makeText(this, "Location services stopped", Toast.LENGTH_LONG).show();
mDecodeThreadPool.shutdown();
mDecodeThreadPool.shutdownNow();
super.onDestroy();
}
}
- Here's my Runnable class code :
public class LocationRunnable implements Runnable, OnLocationUpdatedListener, OnActivityUpdatedListener {
SmartLocation smartLocation;
public LocationRunnable(Context ctx) {
smartLocation = new SmartLocation.Builder(ctx).logging(true).build();
}
#Override
public void run() {
Log.v("THREAD", "THREAD STARTED");
startLocation();
}
private void startLocation() {
smartLocation.location().start(this);
smartLocation.activity().start(this);
}
#Override
public void onActivityUpdated(DetectedActivity detectedActivity) {
if (detectedActivity != null) {
Log.v("ACTIVITY", "ACTIVITY UPDATED");
} else {
Log.v("ACTIVITY", "NULL");
}
}
int i = 0;
#Override
public void onLocationUpdated(Location location) {
Log.v("LOCATION", "LOCATION UPDATED" + i++);
}
private String getNameFromType(DetectedActivity activityType) {
switch (activityType.getType()) {
case DetectedActivity.IN_VEHICLE:
return "in_vehicle";
case DetectedActivity.ON_BICYCLE:
return "on_bicycle";
case DetectedActivity.ON_FOOT:
return "on_foot";
case DetectedActivity.STILL:
return "still";
case DetectedActivity.TILTING:
return "tilting";
default:
return "unknown";
}
}
}
I'm not really sure if this is the right or the best way to get what i need.
Any help is greatly appreciated !
I realize the question is old, but it might be of help to others.
I think it is due to the fact that the code from Runnable.run() exits immediately, thereby ending the parent thread, so that the changes in location no longer have an object to be posted to.
smartLocation.location().start(this); // this <-- is the Runnable
And the reason you get update until restart might be due to garbage collection not clearing up the no longer used Runnable object or some existing reference to it within your code.
I have a service that is going to act as an internal clock as part of another app, and it contains a Timer which runs every one second to update the time.
Problem is, when I close and re-open the app, the Service binds itself and all the code runs again in the service and then there are two Timers per second, and so on.
I've tried following the Local Service example, but the same thing happens http://developer.android.com/reference/android/app/Service.html#LocalServiceSample
Is it something to do with the bind/unbind of the service?
I'd like for just one process to run, so I'm looking for a way really to see if the process is running already, and if not then start it.
LocalService
`public class LocalService extends Service {
// This is the object that receives interactions from clients.
private final IBinder mBinder = new LocalBinder();
private long dateOfLastSync;
private long timeNow;
private final Handler handler = new Handler();
private static Timer timer = new Timer();
private Context ctx;
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
// The LocalBinder provides the getService() method for clients to retrieve the current instance of LocalService.
LocalService getService() {
Log.d("INTERNALCLOCK", "getService");
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
#Override
public void onCreate() {
Log.d("INTERNALCLOCK", "onCreate");
ctx = this;
startService();
}
#Override
public void onDestroy() {
Log.d("INTERNALCLOCK", "onDestroy");
Toast.makeText(this, "Service Stopped ...", Toast.LENGTH_SHORT).show();
}
private void startService()
{
timer.scheduleAtFixedRate(new mainTask(), 0, 1000);
}
private class mainTask extends TimerTask
{
public void run()
{
toastHandler.sendEmptyMessage(0);
}
}
public void getTimeFromServer() {
FetchServerTime fetchTime = new FetchServerTime();
fetchTime.execute();
}
#Override
public IBinder onBind(Intent intent) {
Log.d("INTERNALCLOCK", "onBind");
getTimeFromServer();
return mBinder;
}
/** method for clients */
public long getTimeNow() {
Log.d("INTERNALCLOCK", "getTimeNow");
return timeNow;
}
public void startTimeCounter() {
Log.d("INTERNALCLOCK", "startTimeCounter");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("INTERNALCLOCK", "onStartCommand");
return START_STICKY;
}
private final Handler toastHandler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
timeNow += 1000;
Log.d("INTERNALCLOCK", "time is " + timeNow);
Toast.makeText(getApplicationContext(), "test", Toast.LENGTH_SHORT).show();
}
};
class FetchServerTime extends AsyncTask<Integer, String, Long> {
private String mUrl;
private Date date;
private AlarmManager mAlarmManager;
public FetchServerTime() {
mUrl = "XXX";
}
#Override
protected Long doInBackground(Integer... params) {
(get server time code)
}
#Override
protected void onPostExecute(Long result) {
super.onPostExecute(result);
Log.d("INTERNALCLOCK", "Server time on post execute: " + result);
timeNow = result;
dateOfLastSync = result;
}
}
`
InternalClockActivity
public class InternalClockActivity extends Activity implements CanUpdateTime {
private TextView timeLabel;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.d("INTERNALCLOCK", "onCreate - main");
timeLabel = (TextView) findViewById(R.id.time);
doBindService();
}
private LocalService mBoundService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
mBoundService = ((LocalService.LocalBinder)service).getService();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// Because it is running in our same process, we should never
// see this happen.
mBoundService = null;
}
};
private boolean mIsBound;
void doBindService() {
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
bindService(new Intent(InternalClockActivity.this,
LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
if (mIsBound) {
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
#Override
protected void onDestroy() {
super.onDestroy();
doUnbindService();
}
public void updateTime(String time){
timeLabel.setText(time);
}
}
I just created some static variables to see if the timer was running, i.e.
private static boolean isDownloadTaskRunning = false;
private void startService()
{
if (!isDownloadTaskRunning) {
// Timers are not running already
// start timer to download date at intervals
timerToDownloadNewData.scheduleAtFixedRate(new DownloadDateTask(), 0, TIMES.FIFTEEN_MINS.getSeconds());
// set running to true
isDownloadTaskRunning = true;
}
}
In a game application I have the following scenario:
From the main game Activity, the player starts several game tasks that run in the background with varying duration.
The player should be able to view the progress of the running game tasks in a separate View.
To do this, I created two Activitys and a Service, defined as follows:
Service ProgressService handles several ProgressBars running simultaneously on parallel threads.
Activity WorkScreen2 creates a game task, starts the Service with startService() with task parameters passed in a Bundle.
Activity ProgressScreen binds to the Service to get and display the ProgressBars of the running tasks.
Both activities run under separate TabHosts of one TabActivity.
The problem I'm having is that the ServiceConnection.onServiceConnected() method is never called. I get a Java.lang.NullPointerException because I try to call a method of the Service object that should be assigned in this method. See code below.
I use getApplicationContext().bindService() to bind the Activity to the Service because TabSpec cannot bind to Services. This method returns true. Therefore, binding is successful.
Here is the Service:
public class ProgressService extends Service implements GameConstants {
public static final String BROADCAST_PROGRESS = "com.mycompany.android.mygame.progressbroadcast";
private static final long UPDATE_INTERVAL = 500;
private IBinder mBinder;
private List<ProgressBar> mProgressBarList;
private List<String> mStaffNameList;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
ProgressBar progressBar = new ProgressBar(ProgressService.this);
mProgressBarList.add(progressBar);
Bundle bundle = msg.getData();
String staffName = bundle.getString(WorkScreen2.STAFF_NAME);
mStaffNameList.add(staffName);
int taskDurationMillis = bundle.getInt(WorkScreen2.TASK_DURATION) * 1000;
progressBar.setMax(taskDurationMillis / 1000);
long startTimeMillis = SystemClock.uptimeMillis();
long elapsedTimeMillis = SystemClock.uptimeMillis()
- startTimeMillis;
Intent intent = new Intent();
intent.setAction(BROADCAST_PROGRESS);
while (elapsedTimeMillis < taskDurationMillis) {
try {
Thread.sleep(UPDATE_INTERVAL);
} catch (InterruptedException e) {
e.printStackTrace();
}
elapsedTimeMillis = SystemClock.uptimeMillis()
- startTimeMillis;
int elapsedTimeSeconds = (int) elapsedTimeMillis / 1000;
progressBar.setProgress(elapsedTimeSeconds);
sendBroadcast(intent);
}
progressBar.setVisibility(View.GONE);
mProgressBarList.remove(progressBar);
mStaffNameList.remove(staffName);
sendBroadcast(intent);
if (mProgressBarList.isEmpty()) {
stopSelf(msg.arg1);
}
}
}
#Override
public void onCreate() {
super.onCreate();
mBinder = new ProgressServiceBinder();
mProgressBarList = Collections
.synchronizedList(new ArrayList<ProgressBar>());
mStaffNameList = Collections.synchronizedList(new ArrayList<String>());
}
/*
* Creates a thread for each game task with parameters passed in
* <code>intent</code>
*/
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "starting service", Toast.LENGTH_LONG).show();
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
Handler serviceHandler = new ServiceHandler(thread.getLooper());
Message msg = serviceHandler.obtainMessage();
msg.arg1 = startId;
msg.setData(intent.getExtras());
serviceHandler.sendMessage(msg);
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class ProgressServiceBinder extends Binder {
ProgressService getService() {
return ProgressService.this;
}
}
public List<ProgressBar> getProgressBarList() {
return mProgressBarList;
}
public List<String> getStaffNameList() {
return mStaffNameList;
}
#Override
public void onDestroy() {
Toast.makeText(this, "Service done", Toast.LENGTH_SHORT).show();
}
}
And this is the Activity that binds to it:
public class ProgressScreen extends ListActivity {
private final String TAG = "ProgressScreen";
private ProgressScreenAdapter mAdapter;
private ProgressService mProgressService;
private List<ProgressBar> mProgressBarList;
private List<String> mStaffNameList;
#Override
public void onCreate(Bundle bundle) {
Log.i(TAG, "ProgressScreen oncreate");
super.onCreate(bundle);
setContentView(R.layout.progress_screen_layout);
IntentFilter filter = new IntentFilter();
filter.addAction(ProgressService.BROADCAST_PROGRESS);
registerReceiver(receiver, filter);
doBindService();
mAdapter = new ProgressScreenAdapter(this, mStaffNameList, mProgressBarList);
setListAdapter(mAdapter); // Returns true
/*
* This is where I get the NullPointerException
* mProgressService is null here
*/
mProgressBarList = mProgressService.getProgressBarList();
mStaffNameList = mProgressService.getStaffNameList();
}
#Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction(ProgressService.BROADCAST_PROGRESS);
registerReceiver(receiver, filter);
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(receiver);
}
boolean doBindService() {
return getApplicationContext().bindService(new Intent(this, ProgressService.class), mConnection, Context.BIND_AUTO_CREATE);
}
void doUnbindService() {
getApplicationContext().unbindService(mConnection);
}
ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
mProgressService = ((ProgressService.ProgressServiceBinder) binder).getService();
Toast.makeText(ProgressScreen.this, "Connected to ProgressService", Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName name) {
mProgressService = null;
}
};
private BroadcastReceiver receiver = new BroadcastReceiver () {
#Override
public void onReceive(Context context, Intent intent) {
mAdapter.notifyDataSetChanged();
}
};
}
And the Service is started from the main Activity as follows:
Intent intent = new Intent(WorkScreen2.this, ProgressService.class);
intent.putExtra(TASK_DURATION, task.getDuration());
intent.putExtra(STAFF_NAME, staff.getName());
startService(intent);
The AndroidManifest.xml contains
<service
android:name=".ProgressService"
android:label="#string/progress_service">
</service>
ServiceConnection's onServiceConnected() is called, but nobody guarantees that it will be called before onCreate continues execution. So, what happens here - you successfuly bind to the service (that's why onBind returns true), but you're not fully connected - onServiceConnected() has not yet been called, so your local mProgressService object is not yet initalized, and therefore you get the NullPointerException.
Solution:
Move these two lines:
mProgressBarList = mProgressService.getProgressBarList();
mStaffNameList = mProgressService.getStaffNameList();
from onCreate() to onServiceConnected() function (use the service object after it is initialized in onServiceConnected()).
Check AndroidManifest.xml of yours and add service that you tried to bind.
You have to return your Binder inner class from
private final IBinder mBinder = new ServiceBinder();
public class ServiceBinder extends Binder {
public PlayerActivity getService() {
return PlayerActivity.this;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
New to android, trying to figure out Services. I'm trying to bind a service to an activity, I'm following the examples in the documentation, but I keep getting a NullPointerException on the line marked below(appService.playSong(title)). Checking it in the debugger reveals that appService is indeed null.
public class Song extends Activity implements OnClickListener,Runnable {
protected static int currentPosition;
private ProgressBar progress;
private TextView songTitle;
private MPService appService;
private ServiceConnection onService = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder rawBinder) {
appService = ((MPService.LocalBinder)rawBinder).getService();
}
public void onServiceDisconnected(ComponentName classname) {
appService = null;
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.song);
Intent bindIntent = new Intent(Song.this,MPService.class);
bindService(bindIntent,onService,
Context.BIND_AUTO_CREATE);
Bundle b = getIntent().getBundleExtra("songdata");
String title = b.getString("song title");
// ...
appService.playSong(title); // nullpointerexception
// ...
}
Here's the relevant part of the service:
package org.example.music;
// imports
public class MPService extends Service {
private MediaPlayer mp;
public static int currentPosition = 0;
public List<String> songs = new ArrayList<String>();
public static String songTitle;
private static final String MEDIA_PATH = new String("/mnt/sdcard/");
#Override
public void onCreate() {
super.onCreate();
mp = new MediaPlayer();
songs = Music.songs;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
public class LocalBinder extends Binder {
MPService getService() {
return MPService.this;
}
}
private final IBinder binder = new LocalBinder();
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public void playSong(String songPath) {
try {
mp.reset();
mp.setDataSource(songPath);
mp.prepare();
mp.start();
mp.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer arg0) {
nextSong();
}
});
songTitle = songPath.substring(12,songPath.length()-4);
} catch (IOException e) {
Log.v(getString(R.string.app_name),e.getMessage());
}
}
public void nextSong() {
if (++currentPosition >= songs.size()) {
currentPosition = 0;
}
String song = MEDIA_PATH+songs.get(currentPosition);
playSong(song);
}
public void prevSong() {
if (--currentPosition<0) {
currentPosition=songs.size()-1;
}
String song = Music.MEDIA_PATH+songs.get(currentPosition);
playSong(song);
}
public int getSongPosition() {
return mp.getCurrentPosition();
}
public MediaPlayer getMP() {
return mp;
}
}
I have registered the service in AndroidManifest.xml and set android:enabled="true". Do you see any obvious mistakes here?
There are two kinds of binds you can make local and remote. Local is only for use by your application and remote if for use by any application that implements certain interface.
You should start with local binding.
Local binding tutorial.
Remote binding tutorial.
My solution without bind:
public class MyActivity extends Activity{
#Override
public void onCreate(Bundle savedInstanceState){
...
Intent it = new Intent(MyService.ACTIVITY_START_APP);
it.setClass(getApplicationContext(), MyService.class);
startService(it);
}
...
#Override
protected void onResume() {
super.onResume();
registerBroadcastReceiver();
}
#Override
protected void onPause() {
super.onPause();
this.unregisterReceiver(this.receiver);
}
...
private BroadcastReceiver receiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(MyService.BROADCAST_INIT)) {
//do your stuff here after init
}
}
};
private void registerBroadcastReceiver(){
IntentFilter filter = new IntentFilter();
filter.addAction(HMyService.BROADCAST_INIT);
this.registerReceiver(receiver, filter);
}
}
Your service:
public class MyService extends Service{
public static final String BROADCAST_INITIAL_DATA = "org.myapp.BROADCAST_INIT";
public static final String ACTIVITY_START_APP = "org.myapp.ACTIVITY_START_APP";
#Override
public int onStartCommand (Intent intent, int flags, int startId){
super.onStartCommand(intent, flags, startId);
if(intent.getAction().equals(ACTIVITY_START_APP)){
//do your initialization
//inform the client/GUI
Intent i = new Intent();
i.setAction(BROADCAST_INIT);
sendBroadcast(i);
}else{
//some other stuff like handle buttons
}
}
}
good luck.
You are assuming that the bindService() will connect to the service synchronously, but the connection will be available only after onCreate() finshed.
The framework runs onCreate() on the UI thread and bindService() just makes a note to connect to the service later. Connecting to a service will always be done on the UI thread, so this can only happen after onCreate was executed. You can't even count on the connection being set up right after onCreate(). It will happen sometime after that :). Also, the framework might disconnect the service on it's will, though it should only happen in low memory conditions.
So, move the code which works with appService from onCreate() to onServiceConnected() and it's gonna work.
From a quick glance it looks like you are trying to access your service before the binding has completed. You have to make sure onServiceConnected has fired before trying to call any methods on your service.
Example:
Intent bindIntent = new Intent(Song.this,MPService.class);
bindService(bindIntent,onService, Context.BIND_AUTO_CREATE);
//Wait until service has bound
while(appService == null){
Thread.sleep(100);
}
appService.playSong(title);
This example isn't the best but it demonstrates that you have to wait until the binding has completed before trying to access the service.