Socket IO event firing multiple times NodeJS - java

I am building a quiz application in NodeJS and Android using socket.IO,
I am facing a problem when I emit an event quizzoStatus from the server, the event fires first time once, second time twice and so on.
Here I attach my code snippet
///server side: NodeJS
socket.on('sendQuizzoAnsPoints', async (data)=>{
try {
const obj = JSON.parse(data);
const game = await QuizzoPlayModel.findOne({_id:obj.gameId});
const player = await UserModel.findOne({_id: game.playerId});
const opponent = await UserModel.findOne({_id: game.opponentId});
if(obj.userId == game.opponentId){
let update = {
opponentPoints: game.opponentPoints + obj.points || 0,
opponentWA: game.opponentWA + obj.wrongAns || 0,
};
await QuizzoPlayModel.findByIdAndUpdate(obj.gameId, update).lean().exec();
userNamespace.to(player.socketId).emit('quizzoStatus', {
fullName: opponent.fullName,
points: game.playerPoints + obj.points,
wrongAns: obj.wrongAns,
gameId: obj.gameId
});
}
if(obj.userId == game.playerId) {
let update = {
playerPoints: game.playerPoints + obj.points || 0,
playerWA: game.playerWA + obj.wrongAns || 0,
};
await QuizzoPlayModel.findByIdAndUpdate(obj.gameId, update).lean().exec();
userNamespace.to(opponent.socketId).emit('quizzoStatus', {
fullName: player.fullName,
points: game.playerPoints+ obj.points,
wrongAns: obj.wrongAns,
gameId: obj.gameId
});
}
} catch (e) {
console.log(e);
}
});
Here I listen a event named sendQuizzoAnsPoints and then I emit an event to the player or opponent in another event named quizzoStatus.
The quizzoStatus event fires multiple times from server to android.
Here I attached android code
/// Android code
socket.emit("sendQuizzoAnsPoints", new Gson().toJson(quizzoStatusRequestDto));
socket.on("quizzoStatus", new Emitter.Listener(){
#Override
public void call(Object... args){
runOnUiThread(new Runnable(){
#Override
public void run(){
Log.e("opponet point", opponentPoints + " " + quizzoStatusResponseDto.getPoints());
}
});
}
});

The problem is in Android. You are assigning new listener every time without removing the previous one. You need to create a variable of that Emmiter listener and remove it on onDestroy or somewhere else when the work is done:
//variable of Emmiter.Listener
Emmiter.Listener quizzoStatus = new Emitter.Listener(){
#Override public void call(Object... args){
runOnUiThread(new Runnable(){
#Override public void run(){
Log.e("opponet point", opponentPoints + " " + quizzoStatusResponseDto.getPoints());
}
});
}
};
//assigning the listener
socket.on("quizzoStatus", quizzoStatus);
. . . .
#Override protected void onDestroy(){
super.onDestroy();
//removing the listener...
socket.off("quizzoStatus", quizzoStatus);
}
Hope this will work

Related

EventChannel not being called every time

I am very new to flutter+dart framework. I am trying to understand how EventChannel works. I have set up EventChannel to capture the number of an incoming call.
On the android side, I have set up an BroadcastReceiver as follows.
public class CallEventHandler extends BroadcastReceiver implements EventChannel.StreamHandler {
private static final String TAG = "[SAMPLE]";
private static final int NUMBER_LEN = 10;
private EventChannel.EventSink eventSink = null;
private Activity activity = null;
public CallEventHandler(Activity activity) {
this.activity = activity;
}
#Override
public void onListen(Object arguments, EventChannel.EventSink events) {
Log.i(TAG, "[onListen] setting up events");
eventSink = events;
}
#Override
public void onCancel(Object arguments) {
Log.i(TAG, "[onCancel] cancel events");
eventSink = null;
activity = null;
}
#Override
public void onReceive(Context context, Intent intent) {
try {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
if(state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
if(incomingNumber != null) {
Log.i(TAG, "[CallEventHandler] Incoming number : " + incomingNumber);
if(incomingNumber.length() > NUMBER_LEN) {
incomingNumber = incomingNumber.substring(incomingNumber.length() - NUMBER_LEN, incomingNumber.length());
Log.i(TAG, "[CallEventHandler] Incoming number after : " + incomingNumber);
if(activity != null) {
String finalIncomingNumber = incomingNumber;
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
if(eventSink != null) {
Log.i(TAG, "[CallEventHandler] HERESSSSS : " + finalIncomingNumber);
eventSink.success(finalIncomingNumber);
}
}
});
}
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
In the onReceive method, I am getting the incoming number and I am sending it to EventSink.
In my MainActivity I am setting up the CallEventHandler as follows:
private final String eventId = "SAMPLE_ID";
private CallEventHandler handler = new CallEventHandler(this);
#Override
public void onStart() {
super.onStart();
...
registerReceiver(handler, filter);
}
#Override
public void onStop() {
super.onStop();
unregisterReceiver(handler);
}
#Override
public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new EventChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), eventId)
.setStreamHandler(handler);
}
On the Flutter side, the code is as follows:
class EventHandler {
static const String TAG = "[SAMPLE]";
final String _eventId = "SAMPLE_ID";
EventChannel? _evtChannel;
Stream<String>? _evtStream;
EventHandler() {
debugPrint(TAG + " Setting up EventHandler");
_evtChannel = EventChannel(_eventId);
_evtStream = _evtChannel?.receiveBroadcastStream().distinct().map((dynamic
event) => getString(event as String));
}
void startListening(void Function(String data)? onData) {
debugPrint(TAG + " starting listening");
_evtStream?.listen((data) {
debugPrint(TAG + " In listening");
onData!(data);
});
}
}
In my UI code, I have a StatefulWidget (MySamplePage) where I am registering my callback when the call is received
void initState() {
widget.handler.startListening((incomingNumber) {
debugPrint(_tag + " data : $incomingNumber");
...
});
}
In my stateful home page build method, I initialize the handler in initState and added a route in build method
class _HomeScreenState extends State<HomeScreen> {
#override
void initState() {
super.initState();
debugPrint(_tag + "initState");
_handler = EventHandler();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
'/caller': (context) => MySamplePage(
handler: _handler
),
},
...
);
}
}
The issue I am facing is that, when the widget is opened I am receiving the first incoming call, as expected. But if I make another call, then that second call is not captured by the stream. If I press the back button, and reopen the Widget everything works as expected, the first incoming call is printed in the console. I know the the Android code is sending the event from the onReceive method (The `HERESSSSS' line is printed every time), but the flutter stream is not getting the values. I am not sure what I am doing wrong here. Can anyone please help?
My log is
I/flutter (11836): [SAMPLE][HomeScreen]initState
I/flutter (11836): [SAMPLE][EventHandler] Setting up EventHandler
V/AutofillManager(11836): requestHideFillUi(null): anchor = null
I/flutter (11836): [SAMPLE][EventHandler] starting listening
I/[SAMPLE] (11836): [onListen] setting up events
I/[SAMPLE] (11836): [CallEventHandler] Receiver start
I/[SAMPLE] (11836): [CallEventHandler] Receiver start
I/[SAMPLE] (11836): [CallEventHandler] Incoming number : +91XXXXXXXXXX
I/[SAMPLE] (11836): [CallEventHandler] Incoming number after : XXXXXXXXXX
I/[SAMPLE] (11836): [CallEventHandler] HERESSSSS : XXXXXXXXXX
I/flutter (11836): [SAMPLE][EventHandler] In listening
I/flutter (11836): [SAMPLE] data : XXXXXXXXXX
In the subsequent incoming calls, the last line is not printed
Thank you
Ok, I have managed to resolve it, but don't know if this is the correct approach. The issue is that MySamplePage is a StatefulWidget, And I am calling setState in its State object. That might be the reason it's unable to listen to the stream anymore. I have called startListening is the setState method and changed the code accordingly (remove the previous subscription and re-listen to the stream)
void startListening(void Function(String data)? onData) {
debugPrint(TAG + " starting listening");
if(_subscription != null) {
_subscription?.cancel();
_subscription = null;
}
_subscription ??= _evtStream?.listen((data) {
debugPrint(TAG + " In listening");
onData!(data);
});
}
Here _subscription is a variable of type StreamSubscription<String>?. Hope this answer is helpful. And I should have posted complete code earlier.

Send string from thread bluetooth service to textView in main activity

I want to send value from string (distance to obstacle) to my TextView in main activity.
I tried to use Handler, but still not working (crash) or receive nothing.
A part code which receive data from HC-05 (screen where you see in debug value assignet to variable)
enter image description here
#Override
public void run() {
byte[] buffer = new byte[1024];
int bytes;
while(true){
try {
bytes = inputStream.read(buffer);
final String comingMsg = new String(buffer,0,bytes);
Log.d(TAG,"InputStream: " + comingMsg);
/*mHandler2.post(new Runnable() {
#Override
public void run() {
Message message = new Message();
message.obj = comingMsg;
mHandler2.sendMessage(message);
}
});*/
}catch (IOException e){
Log.e(TAG,"Write: Error reading input." + e.getMessage());
active=false;
break;
}
}
}
Here It's parts of code from MainActivity where I tried put something to get values from service.
[I add, that for this moment i want to see something values from bluetooth in textView. Later I want to create parse string and send custom text to custom TextView - example: FL: (Front Left)- to one textView, FR: (Front Right) - to second textView]
There is method implementThreads(), because I wanted to do 6 Threads to 6 TextView which every time is refreshing value from string in Services (there I tried get value from Bluetooth Service)
Log.d(TAG,"Check intent - result");
if(getIntent().getIntExtra("result",0)==RESULT_OK){
mDevice = getIntent().getExtras().getParcelable("bonded device");
myBluetoothService = new MyBluetoothService(getApplicationContext());
startConnection(mDevice,MY_UUID);
Log.d(TAG,"Check is active service");
checkIfActive();
}
Log.d(TAG,"Check intent - connect_to_paired");
if(getIntent().getIntExtra("connect_to_paired",0)==RESULT_OK){
mDevice = getIntent().getExtras().getParcelable("bonded_paired_device");
myBluetoothService = new MyBluetoothService(getApplicationContext());
startConnection(mDevice,MY_UUID);
Log.d(TAG,"Check is active service");
checkIfActive();
}
}
#Override
public void onStart(){
super.onStart();
myBluetoothService = new MyBluetoothService(getApplicationContext());
}
public void checkIfActive(){
Log.d(TAG,"CheckIfActive: Started");
if(myBluetoothService.active){
Log.d(TAG,"CheckIfActive: Running method implementThreads()");
implementThreads();
}
}
public void implementThreads(){
Log.d(TAG,"ImplementThreads: Started");
Thread thread = new Thread(){
#Override
public void run() {
try{
sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
}
};
thread.start();
}
public void startConnection(BluetoothDevice device,UUID uuid){
Log.d(TAG,"StartConnection: Initializing connection");
myBluetoothService.startClient(device,uuid);
}
Thanks all for help, because It's very important for me !
Use this to interect with UI Thread for operations like updating textviews etc.
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
//YOUR CODE HERE
Message message = new Message();
message.obj = comingMsg;
mHandler2.sendMessage(message);
}
});

Tunring my android to beacon and detecting

Is this right way of doing? AM using this samples https://altbeacon.github.io/android-beacon-library/samples.html
public class App extends Application
implements BootstrapNotifier, BeaconConsumer, RangeNotifier {
private final String TAG = "Application ";
protected static final Region beaconRegion = new Region("2f234454-cf6d-4a0f-adf2-f4911ba9ffa6", null, null, null);
protected BeaconManager beaconManager = null;
private RegionBootstrap regionBootstrap;
private BackgroundPowerSaver backgroundPowerSaver;
protected static String sLog = "";
#Override
public void onCreate() {
super.onCreate();
logIt(TAG, beaconRegion.getId1()+"onCreate - In"+beaconRegion.getUniqueId());
beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
beaconManager.getBeaconParsers().clear();
beaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));//iBeacon (tutti)
//--- wake up the app when a beacon is seen
regionBootstrap = new RegionBootstrap(this, beaconRegion);
//--- activate power saver
backgroundPowerSaver = new BackgroundPowerSaver(this);
beaconManager.bind(this);
logIt(TAG, "onCreate - Out");
}
private void logIt (String TAG, String msg) {
sLog += TAG + msg + "\n";
Log.w(TAG, msg);
}
//-------------------------//
//--- BootstrapNotifier ---//
//-------------------------//
#Override
public void didDetermineStateForRegion(int state, Region region) {
String msg = "didDetermineStateForRegion ";
switch(state) {
case MonitorNotifier.INSIDE:
msg +="(INSIDE)";
break;
case MonitorNotifier.OUTSIDE:
msg +="(OUTSIDE)";
break;
default:
msg +="(state=" +state +")";
break;
}
logIt(TAG, msg);
}
#Override
public void didEnterRegion(Region arg0) {
logIt(TAG, "didEnterRegion - In");
try {
beaconManager.startRangingBeaconsInRegion(beaconRegion);
logIt(TAG,"dER - startRangingBeaconsInRegion OK");
} catch (RemoteException e) {
logIt(TAG, "dER - startRangingBeaconsInRegion Err " +e);
}
logIt(TAG, "didEnterRegion - Out");
}
#Override
public void didExitRegion(Region region) {
logIt(TAG, "didExitRegion - In");
try {
beaconManager.stopRangingBeaconsInRegion(beaconRegion);
logIt(TAG,"dXR - stopRangingBeaconsInRegion OK");
} catch (RemoteException e) {
logIt(TAG, "dXR - stopRangingBeaconsInRegion Err " +e);
}
logIt(TAG, "didExitRegion - Out");
}
//----------------------//
//--- BeaconConsumer ---//
//----------------------//
#Override
public void onBeaconServiceConnect() {
logIt(TAG, "onBeaconServiceConnect - In");
beaconManager.setRangeNotifier(this);
logIt(TAG, "onBeaconServiceConnect - Out");
}
//---------------------//
//--- RangeNotifier ---//
//---------------------//
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
logIt(TAG, "didRangeBeaconsInRegion - " +beacons.size() +" beacons");
Toast.makeText(getApplicationContext(), beaconRegion.getId1()+" beacon detected "+beacons.size(),
Toast.LENGTH_SHORT).show();
for(Beacon beac: beacons)
{
System.out.println(beac.getId1()+"id 1"+TAG);
if(beac.getId1().equals("2f234454-cf6d-4a0f-adf2-f4911ba9ffa6")/send notification
}
}
}
So Basically that class, which extends application am listening to beacons around. Below, is how am turning my phone into a beacon.I am doing this inside an activity on click of button. So there is two phones with app downloaded on both, once he clicks on one app the button I want the other phone to detect it since i have implemented in extends application class.
the turn ur android to beacon code.
Beacon beacon = new Beacon.Builder()
.setId1("2f234454-cf6d-4a0f-adf2-f4911ba9ffa6") // UUID for beacon
.setId2("1") // Major for beacon
.setId3("5") // Minor for beacon
.setManufacturer(0x004C) // Radius Networks.0x0118 Change this for other beacon layouts//0x004C for iPhone
.setTxPower(-56) // Power in dB
.setDataFields(Arrays.asList(new Long[]{0l})) // Remove this for beacon layouts without d: fields
.build();
BeaconParser beaconParser = new BeaconParser()
.setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24");
beaconTransmitter = new BeaconTransmitter(MenuActivity.this, beaconParser);
beaconTransmitter.startAdvertising(beacon, new AdvertiseCallback() {
#Override
public void onStartFailure(int errorCode) {
Log.e("tag", "Advertisement start failed with code: " + errorCode);
}
#Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
Log.i("tag", "Advertisement start succeeded.");
Toast.makeText(MenuActivity.this, "advertisement start succeeded",
Toast.LENGTH_SHORT).show();
System.out.println("startedddddddddddd");
}
});
// beaconTransmitter.stopAdvertising();
}
catch(Exception o)
{
System.out.println("affda "+o.getMessage());
}
I am also havig an issue, that didenterregion and didRangeBeaconsInRegion are fired many times, so am sending many notification to the user multiple times. its not user friendly.
The didRangeBeaconsInRegion callback is supposed to get called many times -- that's how ranging works. It is called approximately once per second when the beacon is detected to tell you it is there and give you a distance estimate.
If you want to fire a notification only once for the first time you get a call to this method, then you can set a flag for this specific beacon.
Here is an example of some code you might use to do that:
// Add this to the top of your class
private HashMap<String,Boolean> mAlreadySentNotification = new HashMap<String,Boolean>();
...
// Add this to the inside of your didRangeBeaconsInRegion method
if (mAlreadySentNotification.get(beacon.toString())) {
mAlreadySentNotification.put(beacon.toString(), true);
// Send notification here.
}

onBatchScanResults is not called in Android BLE

I am now using the new BLE api in android developing.
Basic idea is using bluetooth scanning result to inflate the recyclerview(list);
I followed the BLE guide on google developer
Now I have two problem:
1. onBatchScanResults listener is never triggered, butonScanResult works well, is that because the scanner only sense 1 sensor nearby?
my BLE scanner is much slower compared with other applications.
The following is the two core functions' code snippet.
private void scanBLE(boolean enable) {
final BluetoothLeScanner mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
if (enable) {
mScanning = true;
mBluetoothLeScanner.startScan(mScanCallback);
} else {
if (mScanning) {
mScanning = false;
mBluetoothLeScanner.stopScan(mScanCallback);
}
}
Log.i(TAG, "now the scanning state is" + mScanning);
}
// Device scan callback.
private ScanCallback mScanCallback =
new ScanCallback() {
public void onScanResult(int callbackType, android.bluetooth.le.ScanResult result) {
addBeaconTolist(result, beaconsList);
mAdapter.notifyDataSetChanged();
};
public void onScanFailed(int errorCode) {
Log.i(TAG, "error code is:" + errorCode);
};
public void onBatchScanResults(java.util.List<android.bluetooth.le.ScanResult> results) {
Log.i(TAG, "event linstener is called!!!!");
Log.i(TAG, "batch result are:" + results);
beaconsList.clear();
for (int i = 0; i < results.size(); i++) {
ScanResult result = results.get(i);
addBeaconTolist(result, beaconsList);
}
mAdapter.notifyDataSetChanged();
};
};
in MainFragment is like following:
beaconsList = new ArrayList<BeaconsInfo>();
mAdapter = new BeaconsAdapter(beaconsList);
mRecyclerView.setAdapter(mAdapter);
scannBLE(true);
Whether or not you get batch results or individual results depends on your scan settings.
In order to get batch results, you need to adjust the ScanSettings. Check out the documentation for the ScanSettings.Builder, and try using SCAN_MODE_LOW_POWER, which batches up results. You can also try adjusting the batch interval with setReportDelay(long reportDelayMillis); You can see a blog post I wrote about the power benefits of these settings here.
It's not totally clear what you mean by "my BLE scanner is much slower compared with other applications", but it may be that the app's UI lags because you are not updating it on the UI thread. Try wrapping your calls to notifyDatasetChanged like this:
runOnUiThread(new Runnable() {
#Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
Try setUseHardwareBatchingIfSupported(true). This solves the problem for me on moto360 2nd gen. I think this is auto implemented for newer API.

Stopping Executor service when user presses Back or Home button

I am trying to ensure that the executor service stops when the Back button or Home button is pressed.
Currently I have this code in my onCreate function in the main activity class:
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
final Wifi wifiObject = new Wifi((WifiManager) getSystemService(Context.WIFI_SERVICE));
Runnable periodicTask = new Runnable() {
public void run() {
// For each AP in the database, we will fill the AP's ArrayList with their corresponding RSS values
for (Map.Entry<String, String> entry : AccessPoints.entrySet()) {
int APNoToBeSent = 0;
try {
wifiObject.scanWifi(entry.getKey(), accessPointMeanRSSArrayList, accessPointRSSFrequencyArrayList);
}
catch (Exception e) {
}
++APNoToBeSent;
}
System.out.println("Mean AP0 = " + accessPointMeanRSSArrayList.get(0));
System.out.println("Frqcy AP0 = " + accessPointRSSFrequencyArrayList.get(0));
}
};
executor.scheduleAtFixedRate(periodicTask, 0, 2, TimeUnit.SECONDS);
I've been doing some reading on this but am unsure whether this should be happening in onStop() or onPause() or something completely different?

Categories

Resources