I am working with the iBeacon technology, and I am trying to create a RangingService that will search for nearby iBeacons every five seconds and run in the background of my application. The code below is not working. I'm sure I'm making some dumb mistake somewhere, but I can see in my log files that Checkpoint 3 and 4 are being reached every five seconds, while Checkpoints 1 and 2 are never being reached. Thus, the nearby beacons are not being detected. I don't have much experience with Services or Beacons, so I would appreciate any help, especially from #davidgyoung.
Please forgive me if the code below isn't indented perfectly :) Thanks so much to anyone who can help me.
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.widget.EditText;
import android.widget.Toast;
import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconConsumer;
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.RangeNotifier;
import org.altbeacon.beacon.Region;
import java.util.Collection;
public class RangingService extends Service implements BeaconConsumer {
private BeaconManager beaconManager = BeaconManager.getInstanceForApplication(this);
Handler handler;
String b = "";
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onStart(Intent intent, int startId) {
// Let it continue running until it is stopped.
Log.d("Service", "Started");
handler = new Handler(){
#Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
Log.d("Checkpoint", "5 seconds have passed");
}
};
new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
while(true)
{
try {
startJob();
Log.d("Checkpoint", "Job has started");
Thread.sleep(5000);
handler.sendEmptyMessage(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
//return START_STICKY;
}
public void startJob() {
beaconManager.bind(this);
Log.d("The first beacon", "Starting job for realzies");
onBeaconServiceConnect();
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("Service", "Ended");
}
#Override
public void onBeaconServiceConnect() {
Log.d("Checkpoint3", "Checkpoint3");
beaconManager.setRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
Log.d("Checkpoint1", "Checkpoint1");
if (beacons.size() > 0) {
Log.d("Checkpoint2", "Checkpoint2");
//EditText editText = (EditText)RangingActivity.this.findViewById(R.id.rangingText);
Beacon firstBeacon = beacons.iterator().next();
String a = "The first beacon " + firstBeacon.toString() + " is about " + firstBeacon.getDistance() + " meters away. RSSI = " + firstBeacon.getRssi();
Log.d("Service", a);
//logToDisplay(a);
}
}
});
try {
beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
Log.d("Checkpoint4", "Checkpoint4");
} catch (RemoteException e) { }
}
}// Update code formatting
Main Activity:
public void didEnterRegion(Region arg0) {
// In this example, this class sends a notification to the user whenever a Beacon
// matching a Region (defined above) are first seen.
Log.d(TAG, "did enter region.");
startService(new Intent(this, RangingService.class));
}
A few tips:
Don't call onBeaconServiceConnect(); manually. This is a callback method that gets called by the Android Beacon Library when the beacon scanning service is ready to go as a result of the call to beaconManager.bind(this); If you call it yourself, you defeat its purpose, and will cause problems.
You should not call beaconManager.bind(this); every five seconds -- just call it once when the service starts up. Calling it repeatedly will cause problems.
Make sure that the Android Beacon Library is configured for the types of beacons you are using. By default it will only detect open source AltBeacon packets. If you are using a proprietary beacon type, you need to create a BeaconParser and add configure it with the library. You can do a Google search for "BeaconParser" and your proprietary beacon type to get the proper line of code to configure the library.
Related
I want to run my service forever in the background but it stops after sometime I checked every solution on Youtube and Internet but I didn't get the answer,I tried every solution like using START_STICKY in onStartCommand() or using onTaskRemoved() method but it did not work.Any help would be appreciated.
This is my TheService class code. `
`
package apphub.secretapp;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.os.SystemClock;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Random;
/**
* Created by as on 12/24/2017.
*/
public class TheService extends Service implements
MediaRecorder.OnInfoListener {
String AudioSavePathInDevice = null;
MediaRecorder mediaRecorder ;
Random random ;
String RandomAudioFileName = "ABCDEFGHIJKLMNOP";
public static final int RequestPermissionCode = 1;
MediaPlayer mediaPlayer ;
private MediaRecorder mRecorder;
private long mStartTime;
//setting maximum file size to be recorded
private long Audio_MAX_FILE_SIZE = 1000000;//1Mb
private int[] amplitudes = new int[100];
private int i = 0;
private File mOutputFile;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent,flags,startId);
Toast.makeText(this, "Service Started", Toast.LENGTH_SHORT).show();
return START_STICKY;
}
private void startRecording() {
mRecorder = new MediaRecorder();
mRecorder.setOnInfoListener(this);
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setMaxFileSize(Audio_MAX_FILE_SIZE);
mRecorder.setOutputFormat
(MediaRecorder.OutputFormat.MPEG_4);
Toast.makeText(this, "Recording started", Toast.LENGTH_SHORT).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
{
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC);
mRecorder.setAudioEncodingBitRate(48000);
} else {
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setAudioEncodingBitRate(64000);
}
mRecorder.setAudioSamplingRate(16000);
mOutputFile = getOutputFile();
mOutputFile.getParentFile().mkdirs();
mRecorder.setOutputFile(mOutputFile.getAbsolutePath());
try {
mRecorder.prepare();
mRecorder.start();
mStartTime = SystemClock.elapsedRealtime();
} catch (IOException e) {
}
}
protected void stopRecording(boolean saveFile) {
mRecorder.stop();
mRecorder.release();
mRecorder = null;
mStartTime = 0;
if (!saveFile && mOutputFile != null) {
mOutputFile.delete();
}
// to stop the service by itself
}
private File getOutputFile() {
SimpleDateFormat dateFormat = new SimpleDateFormat
("yyyyMMdd_HHmmssSSS", Locale.US);
return new File(Environment.getExternalStorageDirectory().getAbsolutePath().toString()
+ "/Voice Recorder/RECORDING_"
+ dateFormat.format(new Date())
+ ".m4a");
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
Intent i =new Intent(getApplicationContext(),this.getClass());
i.setPackage(getPackageName());
startService(i);
super.onTaskRemoved(rootIntent);
}
#Override
public void onInfo(MediaRecorder mr, int what, int extra) {
if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
getOutputFile();
startRecording();
}
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
startRecording();
}
}
`
The simple answer is: You can't! Android is an OS created for mobile devices. Mobile devices are small battery operated computers with constrained memory. With that in mind the OS will kill your service whenever it needs memory.
In further on latest versions of the OS (specially Nougat and Oreo), those limitations are being imposed more heavily to give extra battery to users.
Any tricks, hacks and work-around you find online are just that, tricks and hacks. They might work in certain conditions or certain devices for a little bit, but you still won't have your service running forever, specially not on latest Androids.
The best scenario to try to have your Service run for as much as possible is to do two things:
return START_STICKY (like you're already doing). This indicates to the OS that you would like your Service to run for as long as possible, but there are zero guarantees that it will.
Use a foreground service. Call the methods startForeground(int, Notification) with a notification to show on the device notification panel. This will bring your process to a foreground state and allow it to stay for a bit longer, but again, no guarantees. PS.: Remember to remove the notification on your service onDestroy.
I've got an app with many many services (delivery application for businesses), which connects to an XMPP server to post reports. It's mainly working brilliantly. However, on the odd occasion when connecting over (1/3G) the connection takes too long and the OS kills the app. What's annoying me is that the connection is spawned by a service started by Alarm manager, and I'm using AndroidConnectionConfiguration (aSmack lib), which apparently spawns a seperate thread for connecting to get away from NetworkOnMainUIException. Yet, every so often, my app is still sent a sigabrt signal 6. Why is this happening? I'm not doing anything on the UI, or near it and thought that regardless of time, Android would leave it alone until it was finished or times out itself? Or am I wrong?
Is there anything else I can do to stop this happening? I don't care if XMPP is connected or not, as it will always retry and send when its able, but I can't have the app crashing.
Edit I should say that I'm using aSmack 0.8.10 - And Openfire 3.9.1. But they aren't the problem and work wonderfully well. It's only on the odd occasion that the connection takes too long and Android kills it.
Edit 2 Some code:
package com.goosesys.dta_pta_test.Singletons;
import org.jivesoftware.smack.AndroidConnectionConfiguration;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.SmackAndroid;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import android.content.Context;
import com.google.gson.Gson;
import com.goosesys.dta_pta_test.MessageListenerService;
import com.goosesys.gooselib.Logging;
import com.goosesys.gooselib.Utilities.AppSettings;
import com.goosesys.gooselib.Utilities.Utility;
public class XmppConnector
{
private static XmppConnector instance;
private static boolean isConnected = false;
private static AndroidConnectionConfiguration acc;
private static XMPPConnection xConnection;
private static Context context;
public static void init(Context contxt)
{
if(instance == null)
{
instance = new XmppConnector();
}
context = contxt;
}
public static XmppConnector getInstance()
{
return instance;
}
public static boolean connect() throws XMPPException
{
if(isConnected)
return true;
SmackAndroid.init(context);
acc = new AndroidConnectionConfiguration(AppSettings.XMPP_SERVER_HOST,
AppSettings.XMPP_SERVER_PORT, "Smack");
acc.setSecurityMode(SecurityMode.disabled);
xConnection = new XMPPConnection(acc);
xConnection.addConnectionListener(new ConnectionListener(){
#Override
public void reconnectionSuccessful()
{
Logging.Debug("XmppConnector", "...reconnected to XMPP Server");
}
#Override
public void reconnectionFailed(Exception e)
{
Logging.Debug("XmppConnector", "...reconnection failed: " + e);
}
#Override
public void reconnectingIn(int seconds)
{
Logging.Debug("XmppConnector", "...reconnecting in: " + seconds);
}
#Override
public void connectionClosedOnError(Exception e)
{
Logging.Debug("XmppConnector", "...connection closed on error: " + e);
}
#Override
public void connectionClosed()
{
Logging.Debug("XmppConnector", "...connection closed");
}
});
xConnection.connect();
if(xConnection.isConnected())
{
isConnected = true;
// LOGIN ONCE CONNECTED TO THE SERVER //
xConnection.login(Utility.getAndroidID(context),
AppSettings.XMPP_KEYSTORE_PASSWORD);
// CREATE CHAT MANAGER //
xConnection.getChatManager().addChatListener(new ChatManagerListener(){
#Override
public void chatCreated(final Chat chat, boolean createdLocally)
{
if(!createdLocally)
chat.addMessageListener(new MessageListenerService(context));
}
});
}
else
{
isConnected = false;
}
return isConnected;
}
public static boolean sendMessage(String jsonObj)
{
Message m = new Gson().fromJson(jsonObj, Message.class);
if(m == null)
{
Logging.Error("XmppConnector", "Message object is null.. Aborting");
return false;
}
if(isConnected)
{
xConnection.sendPacket(m);
return true;
}
else
{
return false;
}
}
}
and the small service that kicks it off:
public class BGCollectorProc extends IntentService
{
private DatabaseHelper dbHelper;
public BGCollectorProc()
{
super("BGCollectorProc");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Logging.Debug("BGCollectorProc", "Spinning...");
//dbHelper = DatabaseHelper.getHelper(getApplicationContext());
try
{
// initialise the connection
XmppConnector.init(this);
// get the static reference
XmppConnector.getInstance();
// connect to the server
if(XmppConnector.connect())
{
Logging.Info("BGCollectorProc", "CONNECTED TO XMPP SERVER");
}
}
catch(XMPPException e)
{
e.printStackTrace();
}
/*
// Worker thread area //
try {
postDeliveries();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
postGeoLogs();
*/
return Service.START_NOT_STICKY;
}
Cheers.
SIGABRT usually means that something fatal has happened within dalvik. It's not Android ActivityManager that kills your process. It's the VM that terminates itself because of a VM fault. Usually there will be more information about the fault. Maybe you didn't recognize the logs as related to the SIGABRT. At least I would expect to find some more information about the cause of the SIGABRT in the logs.
BTW: A service that holds an active XMPPConnection is an ideal candidate for a sticky Android service, that should be running as long as the XMPPConnection should be active.
My app takes a sequence of screenshots, but instead of getting lots of them, I get one screenshot that overwrites itself all the time. I want to get taken screenshots named by the date they were taken (it will make them unique and will solve the problem, i think).
For that I do the following:
$(date +%m.%d.%Y-%H:%M)
Full line:
os.write(("/system/bin/screencap -p /sdcard/fly/$(date +%m.%d.%Y-%H:%M).png").getBytes("ASCII"));
But the files don't appear.
I tried /sdcard/fly/screenshot%d.png but the files get named exactly like in the code "screenshot%d".
How can I properly name my files according to the date they were taken?
Full code:
package ru.startandroid.develop.p0921servicesimple;
import java.io.IOException;
import java.io.OutputStream;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service {
final String LOG_TAG = "myLogs";
public void onCreate() {
super.onCreate();
Log.d(LOG_TAG, "onCreate");
}
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(LOG_TAG, "onStartCommand");
someTask();
return super.onStartCommand(intent, flags, startId);
}
public void onDestroy() {
super.onDestroy();
Log.d(LOG_TAG, "onDestroy");
}
public IBinder onBind(Intent intent) {
Log.d(LOG_TAG, "onBind");
return null;
}
void someTask() {
new Thread(new Runnable() {
public void run() {
for (int i = 1; i <= 25; i++) {
Log.d(LOG_TAG, "i = " + i);
try {
Process sh = Runtime.getRuntime().exec("su", null);
OutputStream os = sh.getOutputStream();
os.write(("/system/bin/screencap -p /sdcard/fly/bob.png").getBytes("ASCII"));
os.flush();
os.close();
sh.waitFor();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
}
}
Shell directives will not work when you do not run them in shell. It's just string for exec.
What about generating filenames in Java? There's for sure easy way of getting the same string as date +%m.%d.%Y-%H:%M writes out.
I'm trying to bind an Activity to a LocalService to interact with it. But in my Activity I am only able to make calls to methods defined in my LocalBinder and not in my LocalService. What am I doing wrong?
Not starting scratch I read another question and I have read a little how to code some sample code and my code resembles that sample code. Also I have been reading some of the Service Documentation for convenience here is a small quote from that section of the documentation:
"A service is "bound" when an application component binds to it by calling bindService(). A bound service offers a client-server interface that allows components to interact with the service, send requests, get results, and even do so across processes with interprocess communication (IPC). A bound service runs only as long as another application component is bound to it. Multiple components can bind to the service at once, but when all of them unbind, the service is destroyed."
But I can't do that. As mentioned above the best I can do is to have my Activity call methods defined in my LocalBinder. I have achieved nothing like the part highlighted in black above.
If it helps here are the relevant portions of my code.
LocalService to be bound to:
/**************************************************************************************************
* Filename: LocalService.java
* Project name: Local Service Sample
* Application name: Local Service
* Description: This file contains the LocalService (extends Service) for our Local Service app
**************************************************************************************************/
package com.marie.localservicesample;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;
public class LocalService extends Service {
private NotificationManager mNM;
// Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it.
private int NOTIFICATION = R.string.local_service_started;
// just some arbitrary numbers for test purposes
public static int statusCode = 99;
public static int emptyMsg = 549;
// I get my Extras from onStartCommand and use in ServiceWorker() thread
public static final String EXTRA_MAC = "com.marie.localservicesample.EXTRA_MAC";
private String macString;
public static final String EXTRA_MESSENGER = "com.marie.localservicesample.EXTRA_MESSENGER";
private Messenger messenger;
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
//private static final String macString = "00:06:66:02:D0:EC";
Boolean stop_receive_data = false;
// This is the object that receives interactions from clients. See
// RemoteService for a more complete example - or not because
// this is a local service
private final IBinder mBinder = new LocalBinder();
#Override
public IBinder onBind(Intent intent) {
Log.i("onBind", "called in LocalService" );
Log.i("onBind", "intent: " + intent.toString());
Log.i("onBind", "mBinder: " + mBinder);
return mBinder;
}
#Override
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// Display a notification about us starting. We put an icon in the status bar.
showNotification();
}
// Call this at the end of onStartCommand() after we got the Extras
public void afterStartCommand() {
Thread thr = new Thread(null, new ServiceWorker(), "LocalService");
thr.start();
}
/*
* This is the ServiceWorker thread that passes messages to the handler defined in
* the Controller activity.
*/
class ServiceWorker implements Runnable
{
public void run() {
// do background processing here... something simple
Looper.prepare();
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice btDevice = btAdapter.getRemoteDevice(macString);
BluetoothSocket btSocket = null;
InputStream btIstream = null;
OutputStream btOstream = null;
try {
btSocket = btDevice.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e1) {
e1.printStackTrace();
}
try {
btSocket.connect();
} catch (IOException e1) {
e1.printStackTrace();
}
try {
btIstream = btSocket.getInputStream();
btOstream = btSocket.getOutputStream();
} catch (IOException e1) {
e1.printStackTrace();
}
try {
int data = btIstream.read();
// reset the bluetooth device
while (data != 63) {
Log.d("LocalService", "resetting bluetooth device");
btOstream.write('r');
data = btIstream.read();
}
StringBuffer strBuffer = new StringBuffer("");
Boolean dataBegin = false;
int ndxPlus = 0;
while (data != -1) {
char printableB = (char) data;
if (data < 32 || data > 126) {
//printableB = ' ';
}
//Log.d("LocalService", Character.toString(printableB) + "(" + data + ")");
if (data == 63) {
btOstream.write('$');
btOstream.write(',');
}
if (data == 45) {
btOstream.write('1');
btOstream.write(',');
dataBegin = true;
}
if (dataBegin == true) {
strBuffer = strBuffer.append(Character.toString(printableB));
}
if (data == 13) {
dataBegin = false;
//Log.d("LocalServiceDataString", strBuffer.toString());
// send data to the handler to plot the data
Message msg = Message.obtain();
msg.what = Controller.MESSAGE_MAC;
msg.obj = strBuffer;
try {
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
strBuffer = new StringBuffer("");
if (ndxPlus < 0) {
btOstream.write('+');
ndxPlus++;
}
}
data = btIstream.read();
if (stop_receive_data) data = -1;
}
} catch (IOException e1) {
e1.printStackTrace();
}
try {
btSocket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
LocalService.this.stopSelf();
Looper.loop();
// stop the service when done...
// Or use the unbindBtn in the MainActivity class?
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("LocalService", "Received start id " + startId + ": " + intent);
Bundle extras = intent.getExtras();
messenger = (Messenger)extras.get(EXTRA_MESSENGER);
macString = extras.getString(EXTRA_MAC);
afterStartCommand();
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
#Override
public void onDestroy() {
// Cancel the persistent notification.
mNM.cancel(NOTIFICATION);
stop_receive_data = true;
// Tell the user we stopped.
Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
}
/**
* Show a notification while this service is running.
*/
private void showNotification() {
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(R.string.local_service_started);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.stat_sample, text, System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Controller.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.local_service_label), text, contentIntent);
// Send the notification.
mNM.notify(NOTIFICATION, notification);
}
}
Activity that binds to LocalService:
/**************************************************************************************************
* Filename: Binding.java
* Project name: Local Service Sample
* Application name: Local Service
* Description: This file contains the Binding class for our Local Service application
**************************************************************************************************/
package com.marie.localservicesample;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
/*
* Example of binding and unbinding to the local service.
* This demonstrates the implementation of a service which the client will
* bind to, receiving an object through which it can communicate with the service.
*/
public class Binding extends Activity {
private ILocalBinder mBoundService;
private boolean mIsBound;
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 = (ILocalBinder)service;
int statusCode = mBoundService.getStatusCode();
Log.d("Binding.java","called onServiceConnected. statusCode: " + statusCode);
Toast.makeText(Binding.this, R.string.local_service_connected,
Toast.LENGTH_SHORT).show();
}
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;
Log.d("Binding", "called onServiceDisconnected");
Toast.makeText(Binding.this, R.string.local_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
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(Binding.this, LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
if (mIsBound) {
int statusCode = mBoundService.getStatusCode();
if (statusCode != 0) Log.d("doUnbindService", "Binding.java statusCode: " + statusCode);
// Tell the user we did an unbind
Toast.makeText(this, R.string.local_service_unbound, Toast.LENGTH_SHORT).show();
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.local_service_binding);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.bind);
button.setOnClickListener(mBindListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(mUnbindListener);
}
private OnClickListener mBindListener = new OnClickListener() {
public void onClick(View v) {
doBindService();
}
};
private OnClickListener mUnbindListener = new OnClickListener() {
public void onClick(View v) {
doUnbindService();
}
};
#Override
protected void onDestroy() {
super.onDestroy();
doUnbindService();
}
}
My ILocalBinder and LocalBinder:
/**************************************************************************************************
* Filename: ILocalBinder.java
* Project name: Local Service Sample
* Application name: Local Service
* Description: This file contains an example interface for my LocalBinder
**************************************************************************************************/
package com.marie.localservicesample;
public interface ILocalBinder {
public int getStatusCode();
}
/**************************************************************************************************
* Filename: LocalBinder.java
* Project name: Local Service Sample
* Application name: Local Service
* Description: This file contains the LocalBinder class for our Local Service application
**************************************************************************************************/
package com.marie.localservicesample;
import android.os.Binder;
import com.marie.localservicesample.LocalService;
/**
* Class for clients to access. 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 implements ILocalBinder {
#Override
public int getStatusCode() {
return LocalService.statusCode;
}
}
Thanks!
See the local service example.
Just copy the binder class code they have into your service instead of making a separate file for it: (inside the LocalService class declaration)
public class LocalService {
// This is the object that receives interactions from clients. See
// RemoteService for a more complete example.
private final IBinder mBinder = new LocalBinder();
/**
* Class for clients to access. 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 {
LocalService getService() {
return LocalService.this;
}
}
...
}
and then:
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();
Now you can access your service directly using mBoundService.
I want to test my bound service with ServiceTestCase.
The testing consists of binding to MyBindServer, and sending a Message.
Watching the logs, you can see the service is started when onBind() is called,
and a message is sent from testAHello(), but, the server's handleMessage() is never called.
From the logs:
I/TestRunner( 2099): started: testAHello(com.inthinc.mybindserver.test.MyBindServerTest)
I/MyBindServerTest( 2099): setUp()
I/MyBindServer( 2099): onBind, action=com.inthinc.mybindserver.START
I/MyBindServerTest( 2099): testAHello
I/MyBindServerTest( 2099): sending SAY_HELLO
[here is where I expect to see the output from handleMessage()]
I/MyBindServerTest( 2099): tearDown()
I/TestRunner( 2099): finished:testAHello(com.inthinc.mybindserver.test.MyBindServerTest)
I/TestRunner( 2099): passed: testAHello(com.inthinc.mybindserver.test.MyBindServerTest)
Here is the code for MyBindServer.java:
package com.inthinc.mybindserver;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;
public class MyBindServer extends Service {
static final String TAG = "MyBindServer";
public static final int MSG_SAY_HELLO = 1;
final Messenger mMessenger = new Messenger(new IncomingHandler());
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
Log.i(TAG, String.format("handleMessage, what=%d", msg.what));
switch (msg.what) {
case MSG_SAY_HELLO:
Log.i(TAG, "hello");
break;
default:
super.handleMessage(msg);
}
}
}
#Override
public IBinder onBind(Intent intent) {
Log.i(TAG, String.format("onBind, action=%s", intent.getAction()));
return mMessenger.getBinder();
}
}
Here is the code for MyBindServerTest.java:
package com.inthinc.mybindserver.test;
import com.inthinc.mybindserver.MyBindServer;
import android.content.Intent;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.test.ServiceTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
public class MyBindServerTest extends ServiceTestCase<MyBindServer> {
private static final String TAG = "MyBindServerTest";
Messenger mServer = null;
public MyBindServerTest() {
super(MyBindServer.class);
}
public MyBindServerTest(Class<MyBindServer> serviceClass) {
super(serviceClass);
}
#Override
public void setUp() {
try {
super.setUp();
Log.i(TAG, "setUp()");
Intent bindIntent = new Intent("com.inthinc.mybindserver.START");
IBinder binder = bindService(bindIntent);
assertNotNull(binder);
mServer = new Messenger(binder);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void tearDown() {
try {
super.tearDown();
Log.i(TAG, "tearDown()");
} catch (Exception e) {
e.printStackTrace();
}
}
#SmallTest
public void testAHello() {
Log.i(TAG, "testAHello");
assertNotNull(mServer);
Message msg = Message.obtain(null, MyBindServer.MSG_SAY_HELLO);
Log.i(TAG, "sending SAY_HELLO");
try {
mServer.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
I was able to get this working using the procedure below..anyone is welcome to chime in if this is incorrect, but the example above works (i.e. MyBindServer's handler receives messages)
It seems as though ServiceTestCase's bindService() method intends to act like a local service. In this case, the goal is to test as a separate process, which means using the following instead of ServiceTestCase's bindService:
Intent bindIntent = new Intent(<registered intent>); //Where registered intent is declared in the manifest file
getContext().bindService(bindIntent,mConn,Context.BIND_AUTO_CREATE);
where mConn is a ServiceConnection object implemented to do whatever your test needs it to do, in the case above, set mServer.
With the above, MyBindServer's handleMessage() is called for the testAHello() test.
UPDATE: I have noticed that depending on how quickly the test processing is done, teardown() can be called before the binding is ready to use. In the case above adding control variables to throttle the program flow based on mConn's onServiceConnected being called provided consistent results.
E.g.
protected boolean bound = false;
protected boolean processed = false;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
Log.i(TAG,"Service conn");
mServer = new Messenger(service);
if(mServer != null
&& mServer != null){
bound = true;
}
processed = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
Log.i(TAG,"Service Disconn");
}
};
Then add:
while(!processed){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
to testAHello()