For continuous scanning of advertising data, i tried in starting and stopping the mBluetoothAdapter.stopLeScan(mLeScanCallback) function ,but still it is happening only once .when i tried to call the scanning twice, in logcat i can see that LE scan has already started ,as the second call is not executing . how to call repeated scanning and stopping here .
private void scanLeDevice(final boolean enable) {
if (enable){
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
System.out.println("hello");
invalidateOptionsMenu();
}
}, SCAN_PERIOD);
System.out.println(SCAN_PERIOD);
mScanning = true;
System.out.println("starttt 1!");
mBluetoothAdapter.startLeScan(mLeScanCallback);
//////
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
System.out.println("hello");
invalidateOptionsMenu();
}
}, SCAN_PERIOD);
System.out.println(SCAN_PERIOD);
mScanning = true;
System.out.println("starttt 1!");
mBluetoothAdapter.startLeScan(mLeScanCallback);
//////////
}
else
{
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
invalidateOptionsMenu();
}
This works pretty well for me,
I have a method called discoverBLEdevices, which is called in onCreate.
#SuppressLint("NewApi")
private void discoverBLEDevices() {
// TODO Auto-generated method stub
startScan.run();
Log.e("BLE_Scanner", "DiscoverBLE");
}
I have a startScan Runnable, in which I start the scan and invoke another runnable to stop the scan as shown below :
private Runnable startScan = new Runnable() {
#Override
public void run() {
scanHandler.postDelayed(stopScan, 500);
mBLEAdapter.startLeScan(mLeScanCallback);
Log.e("BLE_Scanner", "Start Scan");
}
};
After 500 msec I invoke a runnable called stopScan which stops the scanning and calls startScan runnable again.
private Runnable stopScan = new Runnable() {
#Override
public void run() {
mBLEAdapter.stopLeScan(mLeScanCallback);
scanHandler.postDelayed(startScan, 10);
Log.e("BLE_Scanner", "Stop Scan");
}
};
The callback which I have is as follows :
// Device scan callback.
#SuppressLint("NewApi")
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
#SuppressLint("NewApi")
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
String Address = device.getAddress();
String Name = device.getName();
Log.e("mLeScanCallback",""+Address +" : "+Name);
}
};
The entire code :
import android.annotation.SuppressLint;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
#SuppressLint("NewApi")
public class BluetoothLE extends Service {
private BluetoothAdapter mBLEAdapter;
private BluetoothManager manager;
private Handler scanHandler = new Handler();
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
turnonBLE();
discoverBLEDevices();
}
#SuppressLint("NewApi")
private void turnonBLE() {
// TODO Auto-generated method stub
manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
mBLEAdapter = manager.getAdapter();
mBLEAdapter.enable();
Toast.makeText(getApplicationContext(), "BTLE ON Service",
Toast.LENGTH_LONG).show();
Log.e("BLE_Scanner", "TurnOnBLE");}
#SuppressLint("NewApi")
private void discoverBLEDevices() {
// TODO Auto-generated method stub
startScan.run();
Log.e("BLE_Scanner", "DiscoverBLE");
}
private Runnable startScan = new Runnable() {
#Override
public void run() {
scanHandler.postDelayed(stopScan, 500);
mBLEAdapter.startLeScan(mLeScanCallback);
}
};
// Device scan callback.
#SuppressLint("NewApi")
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
#SuppressLint("NewApi")
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
String Address = device.getAddress();
String Name = device.getName();
Log.e("mLeScanCallback",""+Address +" : "+Name);
}
};
private Runnable stopScan = new Runnable() {
#Override
public void run() {
mBLEAdapter.stopLeScan(mLeScanCallback);
scanHandler.postDelayed(startScan, 10);
}
};
}
In case it doesn't update, I added a few lines by turning on/turning off the Bluetooth adapter. Works fine in my case.
fun discoverBLEDevices(){
startScan.run()
Log.d("Scan", " Discover BLE")
}
private val startScan:Runnable = Runnable {
scanHandler.postDelayed(stopScan,10000)
bluetoothLeScanner.startScan(bleScanner)
// bluetoothAdapter.enable()
Log.e("BLE_Scanner", "Start Scan")
}
private val stopScan:Runnable = Runnable {
bluetoothLeScanner.stopScan(bleScanner)
bluetoothAdapter.disable()
bluetoothAdapter.enable()
scanHandler.postDelayed(startScan, 1000)
Log.e("BLE_Scanner", "Stop Scan")
}
Related
I followed a tutorial on Bluetooth Low Energy, and I have started to add my own menus, etc.
The question I have is, why would MainActivity be static and put inside another class.
The full tutorial is available on Github: https://github.com/kaviles/BLE_Tutorials
Below is the Scanner_BTLE class that contains static MainActivity.
public class Scanner_BTLE {
private MainActivity ma;
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler;
private long scanPeriod;
private int signalStrength;
private UUID[] uuids;
// ToDO add Runtime Permissions to activate it momentarily once user asks for scanning
// ToDo this is currently done in App permitions while in dedug mode, add the software in the code so //permisiions does not have to be done on phone
// ToDo see "https://andela.com/insights/how-to-scan-for-android-bluetooth-low-energy-devices-successfully/"
// ToDO Look at "B- or add Runtime Permissions..... " on how to add it in code
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public Scanner_BTLE(Scanner_BTLE.MainActivity mainActivity, long scanPeriod, int signalStrength) {
ma = mainActivity;
mHandler = new Handler();
this.scanPeriod = scanPeriod;
this.signalStrength = signalStrength;
final BluetoothManager bluetoothManager =
(BluetoothManager) ma.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
}
public boolean isScanning() {
return mScanning;
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void start() {
if (!Utils.checkBluetooth(mBluetoothAdapter)) {
Utils.requestUserBluetooth(ma);
ma.stopScan();
}
else {
scanLeDevice(true);
}
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void stop() {
scanLeDevice(false);
}
//ToDo Use the Callback to find only the sps UUID of Relay
// If you want to scan for only specific types of peripherals,
// you can instead call startLeScan(UUID[], BluetoothAdapter.LeScanCallback),
// providing an array of UUID objects that specify the GATT services your app supports.
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void scanLeDevice(final boolean enable) {
if (enable && !mScanning) {
Utils.toast(ma.getApplicationContext(), "Starting BLE scan...");
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
Utils.toast(ma.getApplicationContext(), "Stopping BLE scan...");
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
ma.stopScan();
}
}, scanPeriod);
mScanning = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
mBluetoothAdapter.startLeScan(mLeScanCallback);
}
}
else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
final int new_rssi = rssi;
if (rssi > signalStrength) {
mHandler.post(new Runnable() {
#Override
public void run() {
ma.addDevice(device, new_rssi);
}
});
}
}
};
public static class MainActivity extends AppCompatActivity implements View.OnClickListener, AdapterView.OnItemClickListener {
private final static String TAG = MainActivity.class.getSimpleName();
public static final int REQUEST_ENABLE_BT = 1;
public static final int BTLE_SERVICES = 2;
private HashMap<String, BTLE_Device> mBTDevicesHashMap;
private ArrayList<BTLE_Device> mBTDevicesArrayList;
private ListAdapter_BTLE_Devices adapter;
private ListView listView;
private Button btn_Scan;
private BroadcastReceiver_BTState mBTStateUpdateReceiver;
private Scanner_BTLE mBTLeScanner;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Utils.toast(getApplicationContext(), "BLE not supported");
finish();
}
mBTStateUpdateReceiver = new BroadcastReceiver_BTState(getApplicationContext());
mBTLeScanner = new Scanner_BTLE(this, 5000, -75);
mBTDevicesHashMap = new HashMap<>();
mBTDevicesArrayList = new ArrayList<>();
adapter = new ListAdapter_BTLE_Devices(this, R.layout.btle_device_list_item, mBTDevicesArrayList);
listView = new ListView(this);
listView.setAdapter(adapter);
listView.setOnItemClickListener(this);
btn_Scan = (Button) findViewById(R.id.btn_scan);
((ScrollView) findViewById(R.id.scrollView)).addView(listView);
findViewById(R.id.btn_scan).setOnClickListener(this);
}
#Override
protected void onStart() {
super.onStart();
registerReceiver(mBTStateUpdateReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
}
#Override
protected void onResume() {
super.onResume();
// registerReceiver(mBTStateUpdateReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
}
#Override
protected void onPause() {
super.onPause();
// unregisterReceiver(mBTStateUpdateReceiver);
stopScan();
}
#Override
protected void onStop() {
super.onStop();
unregisterReceiver(mBTStateUpdateReceiver);
stopScan();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check which request we're responding to
if (requestCode == REQUEST_ENABLE_BT) {
// Make sure the request was successful
if (resultCode == RESULT_OK) {
// Utils.toast(getApplicationContext(), "Thank you for turning on Bluetooth");
}
else if (resultCode == RESULT_CANCELED) {
Utils.toast(getApplicationContext(), "Please turn on Bluetooth");
}
}
else if (requestCode == BTLE_SERVICES) {
// Do something
}
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Context context = view.getContext();
// Utils.toast(context, "List Item clicked");
// do something with the text views and start the next activity.
stopScan();
String name = mBTDevicesArrayList.get(position).getName();
String address = mBTDevicesArrayList.get(position).getAddress();
Intent intent = new Intent(this, Activity_BTLE_Services.class);
intent.putExtra(Activity_BTLE_Services.EXTRA_NAME, name);
intent.putExtra(Activity_BTLE_Services.EXTRA_ADDRESS, address);
startActivityForResult(intent, BTLE_SERVICES);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_scan:
Utils.toast(getApplicationContext(), "Scan Button Pressed");
if (!mBTLeScanner.isScanning()) {
startScan();
}
else {
stopScan();
}
break;
default:
break;
}
}
public void addDevice(BluetoothDevice device, int rssi) {
String address = device.getAddress();
if (!mBTDevicesHashMap.containsKey(address)) {
BTLE_Device btleDevice = new BTLE_Device(device);
btleDevice.setRSSI(rssi);
mBTDevicesHashMap.put(address, btleDevice);
mBTDevicesArrayList.add(btleDevice);
}
else {
mBTDevicesHashMap.get(address).setRSSI(rssi);
}
adapter.notifyDataSetChanged();
}
public void startScan(){
btn_Scan.setText("Scanning...");
mBTDevicesArrayList.clear();
mBTDevicesHashMap.clear();
mBTLeScanner.start();
}
public void stopScan() {
btn_Scan.setText("Scan Again");
mBTLeScanner.stop();
}
}
}
I am trying to set up a basic Android App that will have Bluetooth LE functionality as per the documentation on the Android Developers site. I successfully can get to the point where I am starting a scan, but it is not finding any of the peripherals I have been using to test. Here is the code as it is:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
#Bind(R.id.button) Button mButton;
#Bind(R.id.listView) ListView mListView;
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning = true;
private Handler mHandler;
private static final long SCAN_PERIOD = 10000;
BluetoothLeScanner mBluetoothAdapterBluetoothLeScanner;
ArrayList<String> bluetoothArray = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mButton.setOnClickListener(this);
Context mContext = getBaseContext();
final BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
mBluetoothAdapterBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1900);
}
}
#Override
public void onClick(View view) {
scanLeDevice(mScanning);
}
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
// mHandler.postDelayed(new Runnable() {
// #Override
// public void run() {
// mScanning = false;
// mBluetoothAdapter.stopLeScan(mLeScanCallback);
// }
// }, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.d("test" ,device.getName());
}
});
}
};
}
Do you know exactly what the advertising data content is? Maybe it's blocked by the bluetooth stack(bluetooth.default.so) .For example, if the advertising device has not set the "discoverable bit" in AD flag, it will be blocked and you will not receive the report in your callback function.
I am developing an App which connects to heart rate sensor and retrives the values. I am using BLE 4.0.
With the help of google developer portal, I was able to build some code, but the app is crashing.
Please help me out. And also I need to sync the data which I have not able to able properly.
Code:
public class PollingTest extends Activity{
public Button btn2;
public static String UUID = "00001101-0000-1000-8000-00805F9B34FB";
public boolean mScanning;
public Handler mHandler;
public LeDeviceListAdapter mLeDeviceListAdapter;
public BluetoothAdapter mBluetoothAdapter;
static final int REQUEST_ENABLE_BT = 1;
public static final long SCAN_PERIOD = 10000; // Stops scanning after 10 seconds.
#Override
protected void onCreate(Bundle savedInstanceStat){
super.onCreate(savedInstanceStat);
setContentView(R.layout.pollingactivity);
btn2 = (Button)findViewById(R.id.pollB);
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
//mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
btn2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mHandler = new Handler();
// Use this check to determine whether BLE is supported on the device. Then you can
// selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(getApplicationContext(), "Bluetooth Low Energy is not supported on this Device ", Toast.LENGTH_SHORT).show();
finish();
}
// Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
// BluetoothAdapter through BluetoothManager.
mBluetoothAdapter = bluetoothManager.getAdapter();
// Checks if Bluetooth is supported on the device.
if (mBluetoothAdapter == null) {
Toast.makeText(getApplicationContext(), "Bluetooth not Supported", Toast.LENGTH_SHORT).show();
finish();
return;
}
else {
if (!mBluetoothAdapter.isEnabled()) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, 1);
} else {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, 1);
scanLeDevice(true);
}
}
}
});
}
#Override
protected void onResume() {
super.onResume();
// Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled,
// fire an intent to display a dialog asking the user to grant permission to enable it.
if (!mBluetoothAdapter.isEnabled()) {
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// User chose not to enable Bluetooth.
if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
finish();
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
#Override
protected void onPause() {
super.onPause();
scanLeDevice(false);
//mLeDeviceListAdapter.clear();
}
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
invalidateOptionsMenu();
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
invalidateOptionsMenu();
}
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
//String macAddress;
//macAddress = device.getAddress();
Toast.makeText(getApplicationContext(),device.getAddress(),Toast.LENGTH_LONG).show();
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
/*switch (device.getAddress().toUpperCase()){
case "8C:DE:52:64:8E:D1":
Toast.makeText(getApplicationContext(), "First",Toast.LENGTH_LONG).show();
}*/
}
});
}
};
static class ViewHolder {
TextView deviceName;
TextView deviceAddress;
}
private class LeDeviceListAdapter extends BaseAdapter {
private ArrayList<BluetoothDevice> mLeDevices;
private LayoutInflater mInflator;
public LeDeviceListAdapter() {
super();
mLeDevices = new ArrayList<>();
mInflator = PollingTest.this.getLayoutInflater();
}
public void addDevice(BluetoothDevice device) {
if(!mLeDevices.contains(device)) {
mLeDevices.add(device);
}
}
public BluetoothDevice getDevice(int position) {
return mLeDevices.get(position);
}
public void clear() {
mLeDevices.clear();
}
#Override
public int getCount() {
return mLeDevices.size();
}
#Override
public Object getItem(int i) {
return mLeDevices.get(i);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
}
}
Here I have commented onResume & onPause methods because,if dont,the app is getting crashed.
Log
I think device in LeScan if not able to access/receive bluetooth object for some weird reason or something. Please help me out
Thank You
It's because DeviceScanActivity.this returns 'null' (that pattern is likely not correctly set up), so when you call DeviceScanActivity.this.getLayoutInflater(), you get a NullPointerException, because you're trying to call getLayoutInflater() on something that does not exist.
Background:
I've created a custom service as:
public class FloatingViewService extends Service {
public static FloatingViewService self;
onCreate() {
self = this;
addView(....)
}
...
...
public void updateText ( String newText) { this.textView.setText(newText) };
}
OnCreate event of this service, it sets a view using WindowManager.addView(...) and also set an instance pointer in self variable for future use.
Now this view is just a textview, that stays on the top of activities, regardless.
What I want to achieve:
I want to send some data from a static method that runs using ExecutorService instance, which should update textview text.
How I use this service:
Inside of an activity, I make a call to a static method that logs some values:
public class MyActivity: Activity
{
public void log() {
LogUtil.log(new Runnable() {
#Override
public void run() {
//log api call
FloatingViewService.self.updateText("New Text");
}
}) ;
}
}
Now you can see that I am making a call to an updateText method present in service, from different thread.
Here is how the LogUtil is:
public class LogUtil {
private static ExecutorService taskExecutorService = ThreadUtils.createTimedExecutorService(TASK_POOL_SIZE, TASK_POOL_IDLE_ALIVE_SECONDS,
TimeUnit.SECONDS, new LowPriorityThreadFactory());
public static log(Runnable runnable) {
taskExecutorService.submit(new Runnable() {
#Override
public void run() {
try {
runnable.run();
} catch (Exception ex) {
../
}
}
});
Now the problem is, it cannot update textview text. I can understand it is due to thread. But I have no clue on how to achieve it - is there any UIthread for service ?
Here is my code for example .. you shuold be able to pick the necesary parts from it. as Selvin said you have to create an Incoming handler on both sides to send information from one thread to the other...
Here is my service code
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import com.pekam.myandroidtheme.*;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
public class MyService extends Service {
private NotificationManager nm;
private Timer timer = new Timer();
private int counter = 0, incrementby = 1;
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;
final Messenger mMessenger = new Messenger(new IncomingHandler()); // Target we publish for clients to send messages to IncomingHandler.
#Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
class IncomingHandler extends Handler { // Handler of incoming messages from clients.
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
break;
case MSG_SET_INT_VALUE:
incrementby = msg.arg1;
break;
default:
super.handleMessage(msg);
}
}
}
private void sendMessageToUI(int intvaluetosend) {
for (int i=mClients.size()-1; i>=0; i--) {
try {
// Send data as an Integer
mClients.get(i).send(Message.obtain(null, MSG_SET_INT_VALUE, intvaluetosend, 0));
//Send data as a String
Bundle b = new Bundle();
b.putString("str1", "ab" + intvaluetosend + "cd");
Message msg = Message.obtain(null, MSG_SET_STRING_VALUE);
msg.setData(b);
mClients.get(i).send(msg);
} catch (RemoteException e) {
// The client is dead. Remove it from the list; we are going through the list from back to front so this is safe to do inside the loop.
mClients.remove(i);
}
}
}
#Override
public void onCreate() {
super.onCreate();
Log.i("MyService", "Service Started.");
showNotification();
timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 0, 100L);
isRunning = true;
}
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());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, TabBarActivity.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;
}
private void onTimerTick() {
Log.i("TimerTick", "Timer doing work." + counter);
try {
counter += incrementby;
sendMessageToUI(counter);
} catch (Throwable t) { //you should always ultimately catch all exceptions in timer tasks.
Log.e("TimerTick", "Timer Tick Failed.", t);
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (timer != null) {timer.cancel();}
counter=0;
nm.cancel(R.string.service_started); // Cancel the persistent notification.
Log.i("MyService", "Service Stopped.");
isRunning = false;
}
}
here is my android form app code
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.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import com.pekam.myandroidtheme.*;
public class MyServiceControllerActivity extends Activity {
Button btnStart, btnStop, btnBind, btnUnbind, btnUpby1, btnUpby10;
TextView textStatus, textIntValue, textStrValue;
Messenger mService = null;
boolean mIsBound;
final Messenger mMessenger = new Messenger(new IncomingHandler());
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MyService.MSG_SET_INT_VALUE:
textIntValue.setText("Int Message: " + msg.arg1);
break;
case MyService.MSG_SET_STRING_VALUE:
String str1 = msg.getData().getString("str1");
textStrValue.setText("Str Message: " + str1);
break;
default:
super.handleMessage(msg);
}
}
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
textStatus.setText("Attached.");
try {
Message msg = Message.obtain(null, MyService.MSG_REGISTER_CLIENT);
msg.replyTo = mMessenger;
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;
textStatus.setText("Disconnected.");
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.exampleservice);
btnStart = (Button)findViewById(R.id.btnStart);
btnStop = (Button)findViewById(R.id.btnStop);
btnBind = (Button)findViewById(R.id.btnBind);
btnUnbind = (Button)findViewById(R.id.btnUnbind);
textStatus = (TextView)findViewById(R.id.textStatus);
textIntValue = (TextView)findViewById(R.id.textIntValue);
textStrValue = (TextView)findViewById(R.id.textStrValue);
btnUpby1 = (Button)findViewById(R.id.btnUpby1);
btnUpby10 = (Button)findViewById(R.id.btnUpby10);
btnStart.setOnClickListener(btnStartListener);
btnStop.setOnClickListener(btnStopListener);
btnBind.setOnClickListener(btnBindListener);
btnUnbind.setOnClickListener(btnUnbindListener);
btnUpby1.setOnClickListener(btnUpby1Listener);
btnUpby10.setOnClickListener(btnUpby10Listener);
restoreMe(savedInstanceState);
CheckIfServiceIsRunning();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("textStatus", textStatus.getText().toString());
outState.putString("textIntValue", textIntValue.getText().toString());
outState.putString("textStrValue", textStrValue.getText().toString());
}
private void restoreMe(Bundle state) {
if (state!=null) {
textStatus.setText(state.getString("textStatus"));
textIntValue.setText(state.getString("textIntValue"));
textStrValue.setText(state.getString("textStrValue"));
}
}
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(MyServiceControllerActivity.this, MyService.class));
}
};
private OnClickListener btnStopListener = new OnClickListener() {
public void onClick(View v){
doUnbindService();
stopService(new Intent(MyServiceControllerActivity.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();
}
};
private OnClickListener btnUpby1Listener = new OnClickListener() {
public void onClick(View v){
sendMessageToService(1);
}
};
private OnClickListener btnUpby10Listener = new OnClickListener() {
public void onClick(View v){
sendMessageToService(10);
}
};
private void sendMessageToService(int intvaluetosend) {
if (mIsBound) {
if (mService != null) {
try {
Message msg = Message.obtain(null, MyService.MSG_SET_INT_VALUE, intvaluetosend, 0);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
}
}
}
}
void doBindService() {
bindService(new Intent(this, MyService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
textStatus.setText("Binding.");
}
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);
msg.replyTo = mMessenger;
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;
textStatus.setText("Unbinding.");
}
}
#Override
protected void onDestroy() {
super.onDestroy();
try {
doUnbindService();
} catch (Throwable t) {
Log.e("TabBarActivity", "Failed to unbind from the service", t);
}
}
}
You need to stay on UIThread to update UI. A solution may be:
Create a static reference of activity. Remember to set it on resume method of activity and to unset it on pause method.
On the service side you can invoke a method of activiy to update UI.
Translating those operations in pseudocode. The activity will become :
public class MainActivity extends Activity {
public static MainActivity reference;
...
public onResume() {
reference=this;
}
public onPause() {
reference=null;
}
public void needToUpdateText(final String text)
{
runOnUiThread(new Runnable() {
public void run() {
Log.d("UI thread", "I am the UI thread with text "+text);
});
}
}
}
And the service class:
public class FloatingViewService extends Service {
...
public void updateText ( String newText)
{
if (MainActivity.reference!=null)
{
MainActivity.reference.needUpdateText(newText);
}
};
}
I'm starting the Bluetooth Low Energy Scan in the MainActivity:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BLEScanner.start(bluetoothManager.getAdapter());
}
}
The (static) BLEScanner class is as follows:
public class BLEScanner {
public static void start(final BluetoothAdapter bluetoothAdapter) {
bluetoothAdapter.startLeScan(mLeScanCallback);
}
private static BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord) {
String name = device.getName();
String address = device.getAddress();
Log.d("BLESCANNER", name+" "+address);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
The Thread.sleep() in BLEScanner causes the UIThread to be unresponsive. How can i run the BLEScanner class in a seperate class?
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord) {
new Thread(new Runnable() {
#Override
public void run() {
//The code here is executed on on new thread everytime
Log.e("LeScanCallback", Thread.currentThread().getName());//Prints Thread-xxxx
}
}).start();
}
};