Reputation: 63
Let's pretend that I have a flutter application and I have a Foreground Service that has a worker thread and keeps sending me updates about user's location, this is the service's code which returns a random integer numbers for now :
Android Service code
public class LocationUpdatesService extends Service {
static final int NOTIFICATION_ID = 100;
static final String NOTIFICATION = "com.example.fitness_app";
NotificationManagerCompat m_notificationManager;
private Intent m_broadcastInent;
private final String TAG = this.getClass().getSimpleName();
private AtomicBoolean working = new AtomicBoolean(true);
private int steps = 0;
private Runnable runnable = new Runnable() {
@Override
public void run() {
while(working.get()) {
steps++;
m_notificationManager.notify(NOTIFICATION_ID,
createNotification("Steps Counter" + steps ,
R.drawable.common_full_open_on_phone, 1));
m_broadcastInent.putExtra("steps", steps);
sendBroadcast(m_broadcastInent);
}
}
};
@Override
public void onCreate() {
m_broadcastInent = new Intent(NOTIFICATION);
m_notificationManager = NotificationManagerCompat.from(this);
createNotificationChannel();
startForeground(NOTIFICATION_ID, createNotification("Steps Counter" ,
R.drawable.common_full_open_on_phone, 0));
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(runnable).start();
return Service.START_STICKY;
}
@Override
public void onDestroy() {
working.set(false);
m_notificationManager.cancel(NOTIFICATION_ID);
super.onDestroy();
}
private Notification createNotification(String title, int icon, int steps) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this,
getString(R.string.BACKGROUND_SERVICE_NOTIFICATION_CHANNEL_ID));
builder.setNumber(steps);
builder.setSmallIcon(icon);
builder.setContentTitle(title);
builder.setOnlyAlertOnce(true);
return builder.build();
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getString(R.string.BACKGROUND_SERVICE_NOTIFICATION_CHANNEL_ID);
String description =
getString(R.string.BACKGROUND_SERVICE_NOTIFICATION_CHANNEL_DESCRIPTION);
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel =
new NotificationChannel(getString(
R.string.BACKGROUND_SERVICE_NOTIFICATION_CHANNEL_ID), name, importance);
channel.setDescription(description);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
}
In MainActivity.java I am recieving broadcasts from the service and I should send them to the flutter side:
MainActivity
public class MainActivity extends FlutterActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final String ONE_TIME_BACKGROUND_METHOD_CHANNEL = "fitness_app/method_one_time_service";
private static final String EVENTS_STREAM_CHANNEL = "fitness_app/event_one_time_service";
private Intent m_serviceIntent;
private MethodChannel m_methodChannel;
private EventChannel m_eventchannel;
private EventChannel.EventSink m_stepsStreamSink;
private EventChannel.StreamHandler m_eventCallHandler;
private MethodChannel.Result m_result;
private EventChannel.EventSink m_eventSink;
private BroadcastReceiver m_serviceBroadcastReciever = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Log.d(TAG, "milliseconds " + intent.getIntExtra("steps", 0));
Bundle bundle = intent.getExtras();
if (bundle != null) {
int steps = bundle.getInt("steps");
/////////////////////////////////////////
/////////////////////////////////////////
// I need Here To add Data To the stream
/////////////////////////////////////////
/////////////////////////////////////////
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
m_serviceIntent = new Intent(this, LocationUpdatesService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, m_serviceIntent, 0);
m_methodChannel = new MethodChannel(getFlutterView(), ONE_TIME_BACKGROUND_METHOD_CHANNEL);
m_methodChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
if (methodCall.method.equals("START_STEPS_COUNTER")) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(m_serviceIntent);
} else {
startService(m_serviceIntent);
}
} else {
stopService(m_serviceIntent);
}
}
});
m_eventchannel = new EventChannel(getFlutterView(), EVENTS_STREAM_CHANNEL);
m_eventCallHandler = new EventChannel.StreamHandler() {
@Override
public void onListen(Object o, EventChannel.EventSink eventSink) {
m_eventSink = eventSink;
}
@Override
public void onCancel(Object o) {
}
};
m_eventchannel.setStreamHandler(m_eventCallHandler);
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onResume() {
super.onResume();
registerReceiver(m_serviceBroadcastReciever, new IntentFilter(LocationUpdatesService.NOTIFICATION));
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
Flutter dart code
void start() async {
try {
await _methodChannel.invokeMethod(PlatformMethods.STEPS_COUNTER_START);
Stream<int> stream = _eventChannel.receiveBroadcastStream();
} on PlatformException catch (e) {
print(
" Faild to run native service with thrown exception : ${e.toString()}");
}
Everything here works fine. I can Trigger the service using the Methodchannel, I receive the data from the service using the BroadCastREciever. All I need to do is to return a stream from the native code using EventChannel.
Upvotes: 1
Views: 1925
Reputation: 977
Create a class that extends BroadcastReceiver and pass a EventChannel.EventSink.
class SinkBroadcastReceiver(private val sink: EventChannel.EventSink) {
override fun onReceive(context: Context, intent: Intent) {
val bundle = intent.getExtras()
if (bundle != null) {
val steps = bundle.getInt("steps")
sink.success(steps)
}
}
}
Then, instead od create the BroadcastReceiver in the declaration you can create it in the onListen and call registerReceiver there:
m_eventCallHandler = new EventChannel.StreamHandler() {
@Override
public void onListen(Object o, EventChannel.EventSink eventSink) {
SinkBroadcastReceiver receiver = new SinkBroadcastReceiver(eventSink);
registerReceiver(receiver, new IntentFilter(LocationUpdatesService.NOTIFICATION));
// TODO : Save receiver in a list to call unregisterReceiver later
}
@Override
public void onCancel(Object o) {
}
};
You may need to track all the receivers in a list because you may need to unregister when the activity stops. Also when you stop the service you may need to traverse the list of registered BroadcastReceiver to unregister all the instances.
This way you may also have more than one listener on the dart code for the same event.
Upvotes: 1