I implemented FlutterFire Messaging with the subscribeToTopic method, as the snippet below:
final _topics = ['topicName'];
Future<void> subscribeTopics() async {
for (final topic in _topics) {
await FirebaseMessaging.instance.subscribeToTopic(topic);
}
}
Future<void> unsubscribeTopics() async {
for (final topic in _topics) {
await FirebaseMessaging.instance.unsubscribeFromTopic(topic);
}
}
And everything is fine in debug mode, but in my Crashlytics I'm receiving some reports about it (iOS only):
[firebase_messaging/unknown] The request timed out
[firebase_messaging/unknown] An unknown error has occurred
[firebase_messaging/unknown] A data connection is not currently allowed
[firebase_messaging/unknown] The Internet connection appears to be offline
All the errors appears to be about internet connection, so my question is: "Should I validate the user connection before use FCM or the lib is ready to deal with it but only in Android?"
Crashlytics Stacktrace
Non-fatal Exception: FlutterError
0 ??? 0x0 MethodChannelFirebaseMessaging.subscribeToTopic + 368 (method_channel_messaging.dart:368)
1 ??? 0x0 Messaging.subscribeTopics + 40 (messaging.dart:40)
2 ??? 0x0 Messaging.observeMessaging + 22 (messaging.dart:22)
It's a general error so you shouldn't worry about it. When the internet connection restores the method will hopefully restart itself. You should also consider the case that It might generate an error while the app is in the background.
You can use try catch block to avoid these messages in your crashlytics dashboard.
final _topics = ['topicName'];
Future<void> subscribeTopics() async {
try {
for (final topic in _topics) {
await FirebaseMessaging.instance.subscribeToTopic(topic);
}
} on FirebaseException {
// do nothing
}
}
Future<void> unsubscribeTopics() async {
try {
for (final topic in _topics) {
await FirebaseMessaging.instance.unsubscribeFromTopic(topic);
}
} on FirebaseException {
// do nothing
}
}
Note It will also ignore all other error messages too. In that case
You can handle it otherwise
final _topics = ['topicName'];
Future<void> subscribeTopics() async {
try {
for (final topic in _topics) {
await FirebaseMessaging.instance.subscribeToTopic(topic);
}
} on FirebaseException catch (e) {
if (e.code == 'unknown') {
// do nothing
} else {
// do something
}
}
}
Future<void> unsubscribeTopics() async {
try {
for (final topic in _topics) {
await FirebaseMessaging.instance.unsubscribeFromTopic(topic);
}
} on FirebaseException catch (e) {
if (e.code == 'unknown') {
// do nothing
} else {
// do something
}
}
}
Related
I am failing to understand, why the error thrown from addItem method in below code is not caught in the try-catch block
void main() async {
var executor = Executor();
var stream = Stream.fromIterable([0, 1, 2, 3, 4, 5, 6, 7]);
try {
await for (var _ in stream) {
executor.submit(() => demoMethod());
}
await executor.execute();
} catch (e) {
print(e);
}
}
Future<void> demoMethod() async {
var list = [1, 2, 3, 1, 4, 5];
var executor = Executor();
var test = Test();
for (var element in list) {
executor.submit(() => test.addItem(element));
}
await executor.execute();
test.list.forEach(print);
}
class Test {
var list = <int>[];
Future<void> addItem(int i) async {
if (list.contains(i)) {
throw Exception('Item exists');
}
list.add(i);
}
}
class Executor {
final List<Future<void>> _futures = [];
bool _disposed = false;
void submit(Future<void> Function() computation) {
if (!_disposed) {
_futures.add(computation());
} else {
throw Exception('Executor is already disposed');
}
}
Future<void> execute() async {
await Future.wait(_futures, eagerError: true);
_disposed = true;
}
}
but below code is able to catch the error properly
void main() async {
var executor = Executor();
try {
for (var i = 0; i < 10; i++) {
executor.submit(() => demoMethod());
}
await executor.execute();
} catch (e) {
print(e);
}
}
I am guessing it has something to do with the stream processing.
It's the stream.
In your other examples, you synchronously run through a loop a and call Executor.submit with all the computations, then immediately call executor.execute().
There is no asychronous gap between calling the function which returns a future, and Future.wait starting to wait for that future.
In the stream code, each stream events starts an asynchronous computation by calling Executor.submit. That creates a future, stores it in a list, and goes back to waiting for the stream.
If that future completes, with an error, before the stream ends and Future.wait gets called, then there is no error handler attached to the future yet. The error is then considered unhandled, and is reported to the current Zone's uncaught error handler. Here that's the root zone, which means it's a global uncaught error, which may crash your entire program.
You need to make sure the future doesn't consider its error unhandled.
The easiest way to do that is to change submit to:
void submit(Future<void> Function() computation) {
if (!_disposed) {
_futures.add(computation()..ignore());
} else {
throw StateError('Executor is already disposed');
}
}
The ..ignore() tells the future that it's OK to not have an error handler.
You know, because the code will later come back and call executor.execute, that any errors will still be reported, so it should be safe to just postpone them a little. That's what Future.ignore is for.
(Also changed Exception to StateError, because that's what you should use to report people using objects that have been disposed or otherwise decommissioned.)
My method processData() is executing before pullAllData() is finished but I need processData() to wait until pullAllData() is completely finished before running. This is causing my isDownloadSuccessful bool to be Null when processData() is ran.
Future getCoinData() async {
calculateNumberOfDataPoints();
pullTimesAndPrices();
return timesAndPrices;
}
Future pullTimesAndPrices() async {
for (String cryptoCurrency in cryptoAbbreviation) {
pullAllData(cryptoCurrency);
processData(cryptoCurrency);
}
}
Future pullAllData(cryptoCurrency) async {
String historicalRequestURL =
'$cryptoAPIURL$cryptoCurrency$currency/ohlc?periods=$periodValue&apikey=$apiKey';
http.Response historicalResponse = await http.get(historicalRequestURL);
isPullSuccessful = (historicalResponse.statusCode == 200);
}
void processData(cryptoCurrency) {
if (isPullSuccessful) {
...
} else {
throw 'Problem pulling data';
}
}
You are marking your function pullTimesAndPrices as async but not using await. Use the await keyword before calling the pullAllData function.
I'm building my first Flutter application and I've run into a bit of an async issue.
When my application executes I'd like it to ask for permissions and wait until they are granted. My main() function looks like this:
import 'permission_manager.dart' as Perm_Manager;
void main() async
{
//Ensure valid permissions
Perm_Manager.Permission_Manager pm = Perm_Manager.Permission_Manager();
var res = await pm.get_permissions();
print(res);
return runApp(MyApp());
}
The Permission Manager class' get_permissions() function uses the Flutter Simple Permissions package to check and ask for permissions.
import 'package:simple_permissions/simple_permissions.dart';
import 'dart:io' as IO;
import 'dart:async';
class Permission_Manager {
/* Get user permissions */
Future<bool> get_permissions() async
{
//Android handler
if (IO.Platform.isAndroid)
{
//Check for read permissions
SimplePermissions.checkPermission(Permission.ReadExternalStorage).then((result)
{
//If granted
if (result)
return true;
//Otherwise request them
else
{
SimplePermissions.requestPermission(Permission.ReadExternalStorage)
.then((result)
{
// Determine if they were granted
if (result == PermissionStatus.authorized)
return true;
else
IO.exit(0); //TODO - display a message
});
}
});
}
else
return true;
}
}
When I run the application it does not wait for the function to complete as intended and prints the value of "res" before the Future is updated.
Launching lib\main.dart on Android SDK built for x86 in debug mode...
Built build\app\outputs\apk\debug\app-debug.apk.
I/SimplePermission(15066): Checking permission : android.permission.READ_EXTERNAL_STORAGE
I/flutter (15066): null
I/SimplePermission(15066): Requesting permission : android.permission.READ_EXTERNAL_STORAGE
The Future returns a value midway through the function! Does anyone know what I'm doing wrong?
To await something you have to call the await keyword on a future instead of .then
final result = await future;
// do something
instead of
future.then((result) {
// do something
});
If you really want to use .then then you can await the generated future:
await future.then((result) {
// do something
});
Just ensure that when using nested asynchronous calls that the async keyword is used on each:
await future.then((result) async{
// do something
await future.then((result_2) {
// do something else
});
});
Got it working. The issue seems to be resolved using a Completer:
import 'package:simple_permissions/simple_permissions.dart';
import 'dart:io' as IO;
import 'dart:async';
class Permission_Manager {
/* Get user permissions */
final Completer c = new Completer();
Future get_permissions() async
{
//Android handler
if (IO.Platform.isAndroid)
{
//Check for read permissions
SimplePermissions.checkPermission(Permission.ReadExternalStorage).then((result)
{
//If granted
if (result)
{
c.complete(true);
}
//Otherwise request them
else
{
SimplePermissions.requestPermission(Permission.ReadExternalStorage).then((result)
{
// Determine if they were granted
if (result == PermissionStatus.authorized)
{
c.complete(true);
}
else
{
IO.exit(0); //TODO - display a message
}
});
}
});
}
else
{
c.complete(true);
}
return c.future;
}
}
We decided to use mqtt protocol for chat module in our mobile application. I want to save messages of topic in server side also. But i saw,mqtt client is global here,so one way is i have to subscribe single instance of mqtt client to all topics and save messages in database. but is it right approach to do it. i am just worring about it.
private void buildClient(){
log.debug("Connecting... "+CLIENT_ID);
try {
mqttClient = new MqttClient(envConfiguration.getBrokerUrl(), CLIENT_ID);
} catch (MqttException e) {
log.debug("build client stopped due to "+e.getCause());
}
chatCallback = new ChatCallback();
mqttClient.setCallback(chatCallback);
mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setCleanSession(false);
}
#Override
public void connect() {
if(mqttClient == null || !mqttClient.getClientId().equals(CLIENT_ID)){
buildClient();
}
boolean tryConnecting = true;
while(tryConnecting){
try {
mqttClient.connect(mqttConnectOptions);
} catch (Exception e) {
log.debug("connection attempt failed "+ e.getCause() + " trying...");
}
if(mqttClient.isConnected()){
tryConnecting = false;
}else{
pause();
}
}
}
#Override
public void publish() {
boolean publishCallCompletedErrorFree = false;
while (!publishCallCompletedErrorFree) {
try {
mqttClient.publish(TOPIC, "hello".getBytes(), 1, true);
publishCallCompletedErrorFree = true;
} catch (Exception e) {
log.debug("error occured while publishing "+e.getCause());
}finally{
pause();
}
}
}
#Override
public void subscribe() {
if(mqttClient != null && mqttClient.isConnected()){
try {
mqttClient.subscribe(TOPIC, 2);
} catch (MqttException e) {
log.debug("subscribing error.."+e.getCause());
}
}
}
#Override
public void disconnect() {
System.out.println(this.mqttClient.isConnected());
try {
mqttClient.disconnect();
log.debug("disconnected..");
} catch (MqttException e) {
log.debug("erro occured while disconneting.."+e.getCause());
}
}
There are two possibilities how to solve this issue:
Write a MQTT client that subscribes to all topics using a wildcard (# in MQTT)
Write a broker plugin that does the job for you, depending on the broker implementation you're using
There is a good description of how to implement both options at the HiveMQ website, also describing limitations of the first option.
Here is a very simple code that I run using command line dart to demonstrate my point:
import 'dart:isolate';
void isolateMain() {
throw new Exception("ouch");
}
bool handleException(IsolateUnhandledException e) {
print("EXCEPTION in isolate: " + e.toString());
return true;
}
void main() {
SendPort sendPort = spawnFunction(isolateMain, handleException);
sendPort.call("Hello").then((e) {
print("Main received " + e);
});
}
and the output:
Exception: ouch
#0 isolateMain (file:///Users/salomon/Workspaces/eclipse/Deployer_Server/bin/deployer_server.dart:7:3)
So, turns out the unhandledExceptionCallback is never called whereas the isolate does throw an exception.
For the record :
> dart --version
Dart VM version: 0.5.20.4_r24275 (Fri Jun 21 05:02:50 2013) on "macos_x64"
So, can someone explain me what did I do wrong ?
Thanks ;)
I don't know if you did wrong, it could be a bug. But it seems exceptions thrown in the isolate's main function aren't caught by the handler. If you change it like this:
import 'dart:isolate';
void isolateMain() {
port.receive((whatever, mahPort) {
throw new Exception("$whatever");
});
}
bool handleException(IsolateUnhandledException e) {
print("EXCEPTION in isolate: ${e.toString()}");
return true;
}
void main() {
SendPort sendPort = spawnFunction(isolateMain, handleException);
sendPort.call("Hello").then((e) {
print("Main received $e");
});
}
... then handleException() will be called.