When i try to bind service from UI thread , my BluetoothLeService becomes null after sometime. mBlutoothLeService is set properly but after executing multiple gattUpdateReceiver it becomes null.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
if (device != null && device.getName() != null) {
Intent gattServiceIntent = new Intent(getActivity(), BluetoothLeService.class);
getActivity().bindService(gattServiceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
});
}
};
private final ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName,
IBinder service) {
Log.e(TAG, "service connected");
mBluetoothLeService = ((BluetoothLeService.LocalBinder) service)
.getService();
mBluetoothLeService.connect(mDeviceAddress);
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
Log.e(TAG, "service disconnected");
mBluetoothLeService = null;
}
};
I tried binding service using handler (new Handler(getActivity().getMainLooper())) but still mBluetoothLeService becomes null.
But when I start bind service in OnCreate() of new fragment it works without any issue. Is it something to do with UI thread?
You may not get your current activity using getActivity() with this code in a Runnable ---
#Override
public void run() {
if (device != null && device.getName() != null) {
Intent gattServiceIntent = new Intent(getActivity(), BluetoothLeService.class);
getActivity().bindService(gattServiceIntent,
mServiceConnection, Context.BIND_AUTO_CREATE);
}
});
So idea is to use the bind service at onCreate().
However, you can get the activity in asyntask with ---
class MyClass extends AsyncTask<String, String, String> {
private Activity currentActivity;
public MyClass(Activity activity) {
currentActivity = activity;
}
#Override
public void run() {
if (device != null && device.getName() != null) {
Intent gattServiceIntent = new Intent(currentActivity, BluetoothLeService.class);
getActivity().bindService(gattServiceIntent,
mServiceConnection, Context.BIND_AUTO_CREATE);
}
});
}
Related
I have a media player service, and I need the fragment UI to be updated or in sync with the service data.
Currently I am using a Broadcast to send and receive the data. But my question is, is there a better way to do this in android?
In Service:
private void startBroadcast() {
if(broadcastThread!= null && !broadcastThread.isInterrupted())
broadcastThread.interrupt();
broadcastThread = new Thread(){
#Override
public void run() {
try {
while(!isInterrupted()) {
Intent intent = new Intent(FILTER);
intent.putextra("foo",1);
sendBroadcast(intent);
sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
broadcastThread.start();
}
In Fragment:
private BroadcastReceiver serviceReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int foo= intent.getIntExtra("foo", 0);
// bla bla
}
};
.
.
#Override
public void onResume() {
super.onResume();
Objects.requireNonNull(getActivity()).registerReceiver(serviceReceiver ,FILTER);
}
you can use Bind Service to connect service to your activity
and after that update all of your fragment with that
bindService(oIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
private ServiceConnection mServiceConnection =
new ServiceConnection(){
public void onServiceConnected(
ComponentName cName, IBinder service){
MyBinder binder = (MyService.MyBinder) service;
mService = binder.getService();
// Get a reference to the Bound Service object.
mServiceBound = true;
}
public void onServiceDisconnected(ComponentName cName){
mServiceBound= false;
}
};
and after you want change your activity you should unbind connection
if (mServiceBound){
unbindService(mServiceConnection);
mServiceBound = false;
}
I had a similar problem with MediaPlayer, I had to update UI component in every seconds, too.
I used Runnable + Handler combo, like this:
//...
private Handler mHandler;
private Runnable mSeekBarUpdater
//...
#Override
protected void onStart() {
super.onStart();
//...
int result;
// ...
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
mMediaPlayer.setOnCompletionListener(this);
// Start playing and set isPlaying to true. This attribute is required on rotating screen
mMediaPlayer.start();
isPlaying = true;
/**
* Preparing Handler object and a Runnable in order to
* update SeekBar status
*/
if (mHandler == null) {
mHandler = new Handler();
}
if (mSeekBarUpdater == null) {
mSeekBarUpdater = new Runnable() {
#Override
public void run() {
if (mMediaPlayer != null && mHandler != null) {
if (mMediaPlayer.isPlaying()) {
// Refreshing SeekBar position
mCurrentPosition = mMediaPlayer.getCurrentPosition();
mSeekBar.setProgress(mCurrentPosition);
// .. put your code here in order to refresh your UI components
}
// Refreshing on every second
mHandler.postDelayed(this, 1000);
}
}
};
}
PlayerActivity.this.runOnUiThread(mSeekBarUpdater);
}
}
i have created broadcast receiver for passing data between service to activity, when first time open app broadcast receiver called oknly once but when app open second time then broadcast receiver called twice
-> code for pass data service to
final Intent broadcastIntent = new Intent();
broadcastIntent.setAction(MainActivity.MyWebRequestReceiver.PROCESS_RESPONSE);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra(BUNDLE, args);
sendBroadcast(broadcastIntent);
and here is code for handle response in activity and register receiver
IntentFilter filter = new IntentFilter(MyWebRequestReceiver.PROCESS_RESPONSE);
filter.addCategory(Intent.CATEGORY_DEFAULT);
receiver = new MyWebRequestReceiver();
registerReceiver(receiver, filter);
registerReceiver(broadcastReceiver, new IntentFilter("broadCastName"));
handle its response
public class MyWebRequestReceiver extends BroadcastReceiver {
public static final String PROCESS_RESPONSE = "com.as400samplecode.intent.action.PROCESS_RESPONSE";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive");
Bundle bundle = intent.getParcelableExtra(AutoOpenAppService.BUNDLE1);
String mainUrl = bundle.getString(MAINURL);
Boolean wvVisible = bundle.getBoolean(WVBOOLEAN);
Bundle args = intent.getParcelableExtra(AutoOpenAppService.BUNDLE);
ArrayList<? extends UrlTimeModel> list = args.getParcelableArrayList(LIST);
Long TimeOut = args.getLong(TIMEOUT);
if (mainUrl != null) {
if (!isFinishing()) {
setWebView(mainUrl);
}
}
Log.d(TAG, "VISIBLE : " + wvVisible);
if (wvVisible) {
webView.setVisibility(View.VISIBLE);
} else {
if (alert != null) {
alert.dismiss();
}
moveTaskToBack(true);
}
assert list != null;
if (list.size() < 0) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
alert.dismiss();
MainActivity.this.finish();
MainActivity.super.onBackPressed();
MainActivity.this.moveTaskToBack(true);
}
}, TimeOut);
}
LoadSubURl((ArrayList<UrlTimeModel>) list, TimeOut);
}
}
unregister in onstop()
#Override
protected void onStop() {
super.onStop();
if (timer != null && timerTask != null) {
timerTask.cancel();
timer.cancel();
timer.purge();
timer = null;
timerTask = null;
}
// TODO: 20/12/17 Call Hide Icon Method
HideIconMethod();
if (bound) {
unbindService(serviceConnection);
bound = false;
}
this.unregisterReceiver(receiver);
this.unregisterReceiver(broadcastReceiver);
}
I used the BluetoothLeGatt example code to write an app that automatically connects to a bonded BLE peripheral upon launching the app. Now i am trying to display the data from one of the peripheral's characteristic in a textView. The BluetoothLeGatt example code only demonstrates this using ExpandableListView.OnChildClickListener, my app should require no user input and simply get he data from the characteristic. This is what i have so far:
private TextView mConnectionState;
private TextView mDataField;
private String mDeviceName;
private String mDeviceAddress;
private ExpandableListView mGattServicesList;
private BluetoothLeService mBluetoothLeService;
private boolean mConnected = false;
private BluetoothGattCharacteristic mNotifyCharacteristic;
private final String LIST_NAME = "NAME";
private final String LIST_UUID = "UUID";
// Code to manage Service lifecycle.
private final ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
if (!mBluetoothLeService.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
// Automatically connects to the device upon successful start-up initialization.
mBluetoothLeService.connect(mDeviceAddress);
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
mBluetoothLeService = null;
}
};
// Handles various events fired by the Service.
// ACTION_GATT_CONNECTED: connected to a GATT server.
// ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
// ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
// ACTION_DATA_AVAILABLE: received data from the device. This can be a result of read
// or notification operations.
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
mConnected = true;
updateConnectionState(R.string.connected);
mConnectionState.setTextColor(Color.parseColor("#FF17AA00"));
invalidateOptionsMenu();
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
mConnected = false;
updateConnectionState(R.string.disconnected);
invalidateOptionsMenu();
clearUI();
} else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
// Show all the supported services and characteristics on the user interface.
//displayGattServices(mBluetoothLeService.getSupportedGattServices());
} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_device_control);
final Intent intent = getIntent();
mDeviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME);
mDeviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS);
mConnectionState = (TextView) findViewById(R.id.connection_state);
mDataField = (TextView) findViewById(R.id.data);
Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
#Override
protected void onResume() {
super.onResume();
registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
if (mBluetoothLeService != null) {
final boolean result = mBluetoothLeService.connect(mDeviceAddress);
Log.d(TAG, "Connect request result=" + result);
}
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(mGattUpdateReceiver);
}
#Override
protected void onDestroy() {
super.onDestroy();
unbindService(mServiceConnection);
mBluetoothLeService = null;
}
private void updateConnectionState(final int resourceId) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mConnectionState.setText(resourceId);
}
});
}
private void displayData(String data) {
if (data != null) {
mDataField.setText(data);
}
}
private void clearUI() {
mGattServicesList.setAdapter((SimpleExpandableListAdapter) null);
mDataField.setText(R.string.no_data);
}
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
return intentFilter;
}
Figured it out.
Iterated through the services, then the characteristics after services were discovered.
UUID chara = UUID.fromString("c97433f0-be8f-4dc8-b6f0-5343e6100eb4");
List<BluetoothGattService> servs = mBluetoothLeService.getSupportedGattServices();
for (int i = 0; servs.size() > i; i++) {
List<BluetoothGattCharacteristic> charac = servs.get(i).getCharacteristics();
for (int j = 0; charac.size() > i; i++) {
BluetoothGattCharacteristic ch = charac.get(i);
if (ch.getUuid() == chara) {
mBluetoothLeService.readCharacteristic(ch);
mBluetoothLeService.setCharacteristicNotification(ch, true);
}
}
}
I've created a ServiceConnection object called connection.
However, the onServiceConnected method is never called.
Any ideas on how to get it to work? Thanks
Heres my code:
public static boolean print(String message){
if(printerInitialized != true){
Log.e(TAG,"Unitialized printer. Can't print");
return false;
}
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra("MAC", printerUuid);
sendIntent.putExtra("DATA", message);
sendIntent.setComponent(new ComponentName("com.blueslib.android.app","com.blueslib.android.app.PrintService"));
context.getApplicationContext().bindService(sendIntent,connection, Context.BIND_AUTO_CREATE);
return true;
}
public static ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
Log.e("System.out", "Service Disconnected");
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("System.out", "Service connected");
}
};
Good Morning All,
I am currently buidling a media player for Android, and I am having trouble binding my player service to an Activity and pulling data from it. Bear with me...a lot of code follows.
My code---
Interface:
interface MyInterface{
void playFile( in String file);
void shuffle ();
String getPlayingData();
}
Service:
public class MyService extends Service {
public Context context;
public static String nowPlayingData = null;
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final MyInterface.Stub mBinder = new MyInterface.Stub() {
public void playFile(String file) throws DeadObjectException {
//Song playing method working great!
}
public void shuffle()throws DeadObjectException {
//This method picks a random song and passes it to nowPlayingData string
}
public String getPlayingData() throws RemoteException {
return nowPlayingData;
}
};
#Override
public void onCreate() {
//Toast.makeText(this, "My Service Created", Toast.LENGTH_LONG).show();
Log.d(TAG, "onCreate");
mp.setLooping(false); // Set looping
}
#Override
public void onDestroy() {
//Toast.makeText(this, "My Service Stopped", Toast.LENGTH_LONG).show();
Log.d(TAG, "onDestroy");
mp.stop();
}
#Override
public void onStart(Intent intent, int startid) {
//Toast.makeText(this, "My Service Started", Toast.LENGTH_LONG).show();
Log.d(TAG, "onStart");
}
}
Main Screen Activity, Shuffle Button:
Service is started in onCreate. When I click on the shuffle button it uses a method in the service to play a random song, thus setting the nowPlayingData string.
public class mainMenu extends Activity {
private MyInterface mInterface;
private ServiceToken mToken;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent startedSvc = new Intent(mainMenu.this, MyService.class);
boolean success = this.bindService(
startedSvc, svcConn, Context.BIND_AUTO_CREATE);
this.startService(startedSvc);
findViewById(R.id.shuffle).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
sInterface.shuffle();
} catch (RemoteException e) {
e.printStackTrace();
}
Intent play_screen = new Intent(MyPlayer.getContext(), NowPlaying.class);
startActivity(play_screen);
}
Player Activity:
I want to bind to the service, and pull nowPlayingData over using the interfaces getPlayingData method.
public class NowPlaying extends Activity implements OnClickListener {
public static String nowPlayingData = null;
MyInterface mInterface;
boolean isConnected = false;
RemoteServiceConnection conn = null;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.now_playing);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
Log.i("Log", "Player Loaded");
bindService();
getData();
fillView();
}
public void fillView(){
//This method needs the nowPlayingData string to update the view
}
class RemoteServiceConnection implements ServiceConnection {
public void onServiceConnected(ComponentName className,
IBinder boundService ) {
mInterface = MyInterface.Stub.asInterface((IBinder)boundService);
isConnected = true;
Log.d("Now Playing", "Service Connected" );
}
public void onServiceDisconnected(ComponentName className) {
sInterface = null;
// updateServiceStatus();
isConnected = false;
Log.d( getClass().getSimpleName(), "onServiceDisconnected" );
getData();
}
};
private void bindService() {
if(conn == null) {
conn = new RemoteServiceConnection();
Intent i = new Intent();
i.setClassName("com.musicplayer.MyPlayer", "com.musicplayer.MyPlayer.MyService");
bindService(i, conn, Context.BIND_AUTO_CREATE);
Log.d( getClass().getSimpleName(), "bindService()" );
} else {
Toast.makeText(NowPlaying.this, "Cannot bind - service already bound", Toast.LENGTH_SHORT).show();
}
}
private void getData() {
if(conn == null) {
Log.i("getData", "No Connection");
} else {
try {
String data = mInterface.getPlayingData();
Log.i("Data Recieved", data);
Log.d( getClass().getSimpleName(), "invokeService()" );
} catch (RemoteException re) {
Log.e( getClass().getSimpleName(), "RemoteException" );
}
}
}
}
I have been studying many examples online, and perhaps my mish-mash of attempts has caused this to not work. My code works all the way through to binding the service, but OnServiceConnected is never called, and conn remains null.
Any help you guys can provide is greatly appreciated.
Thanks,
Josh
Android will not make re-entrant event calls to your activity, so onServiceConnected can't be called until onCreate has returned