I am trying to use the Firebase package with the below line of code.
I really want to know what this line of code actually does?
The official documentation didn't help me much. Can someone explain me, please?
You have to use it, in this way:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
https://flutter.dev/docs/resources/architectural-overview#architectural-layers
The above image is the architecture layers of Flutter, the WidgetFlutterBinding is used to interact with the Flutter engine. Firebase.initializeApp() needs to call native code to initialize Firebase, and since the plugin needs to use platform channels to call the native code, which is done asynchronously therefore you have to call ensureInitialized() to make sure that you have an instance of the WidgetsBinding.
From the docs:
Returns an instance of the WidgetsBinding, creating and initializing it if necessary. If one is created, it will be a WidgetsFlutterBinding. If one was previously initialized, then it will at least implement WidgetsBinding.
You only need to call this method if you need the binding to be initialized before calling runApp.
From the source code:
#override
Future<FirebaseAppPlatform> initializeApp(
{String name, FirebaseOptions options}) async {
if (name == defaultFirebaseAppName) {
throw noDefaultAppInitialization();
}
// Ensure that core has been initialized on the first usage of
// initializeApp
if (!isCoreInitialized) {
await _initializeCore();
}
// If no name is provided, attempt to get the default Firebase app instance.
// If no instance is available, the user has not set up Firebase correctly for
// their platform.
if (name == null) {
MethodChannelFirebaseApp defaultApp =
appInstances[defaultFirebaseAppName];
if (defaultApp == null) {
throw coreNotInitialized();
}
return appInstances[defaultFirebaseAppName];
}
assert(options != null,
"FirebaseOptions cannot be null when creating a secondary Firebase app.");
// Check whether the app has already been initialized
if (appInstances.containsKey(name)) {
throw duplicateApp(name);
}
_initializeFirebaseAppFromMap(await channel.invokeMapMethod(
'Firebase#initializeApp',
<String, dynamic>{'appName': name, 'options': options.asMap},
));
return appInstances[name];
}
The invokeMapMethod will invoke a method on the above channel with the specified arguments, which will then call the initializeApp() method in the native code,
https://github.com/FirebaseExtended/flutterfire/blob/master/packages/firebase_core/firebase_core/android/src/main/java/io/flutter/plugins/firebase/core/FlutterFirebaseCorePlugin.java#L227
There are also different ways to initialize Firebase, which you can check here:
No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp() in Flutter and Firebase
In the other ways we do not call WidgetsFlutterBinding.ensureInitialized() since the runApp() function calls it internally:
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
https://github.com/flutter/flutter/blob/bbfbf1770c/packages/flutter/lib/src/widgets/binding.dart#L1012
A simple answer is that if Flutter needs to call native code before calling runApp
WidgetsFlutterBinding.ensureInitialized();
makes sure that you have an instance of the WidgetsBinding, which is required to use platform channels to call the native code.
You only need to call this method if you need the binding to be
initialized before calling runApp.
A simple answer, you need to use this line, if your main function uses async keyword because you use await statement inside it.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance(); // just an example
}
Related
I wanted to user firestore in my unity proyect, so I did as in the documentation. Notice that I also do db = FirebaseFirestore.DefaultInstance;.
I put the firebase starting code in a trial monobehviour in of my scene, in which I wanted to make some database query trials.
Firebase.FirebaseApp app;
void Start() {
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {
var dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available) {
// Create and hold a reference to your FirebaseApp,
// where app is a Firebase.FirebaseApp property of your application class.
app= Firebase.FirebaseApp.DefaultInstance;
Debug.LogError("FirebaseApp succesfully created");
// Set a flag here to indicate whether Firebase is ready to use by your app.
} else {
UnityEngine.Debug.LogError(System.String.Format(
"Could not resolve all Firebase dependencies: {0}", dependencyStatus));
// Firebase Unity SDK is not safe to use here.
}
});
db = FirebaseFirestore.DefaultInstance;
}
Every time I stopped my app and re-played in the editor, unity freezed, and I had to kill the process and restart unity.
Since I commented out the: app= Firebase.FirebaseApp.DefaultInstance;
to: //app= Firebase.FirebaseApp.DefaultInstance; as I only wanted to use the database, everything is going fine.
Am I doing something wrong? Does it make sense that due to some mistake unity hangs after re-play (first play works).
On the other hand I dont understand why in the docs the code advices to store the FirebaseApp in a variable if the FirebaseApp class has got an static instance getter: public static FirebaseApp DefaultInstance { get; }
Thanks for any comment.
Scenario: I have three screen of an app that I launch based on condition. One screen is buttons with other two screen options.
It works fine on a screen where I am initialising the firebase and doing fetching and all the stuff fine. void startFirebase() async { await Firebase.initializeApp(); } . However on a second screen I am doing initialisation same way but I am encountered with this error:
No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp().
Question: How to check if it is initialised (to check if initialised on first screen and wont reinitialise on second one) and - what will happen if I initialise Firebase on both the screens or if initialised twice?
update based on first provided answer:
I am not sure but calling initilise twice does not show any error. Heres how I'm trying twice:
#override
void initState() {
startFirebase();
try{
startFirebase();
}catch(e){
print(e.toString());
}
super.initState();
}
//another way:
#override
void initState() {
startFirebase();
startFirebase();
/* try{
startFirebase();
}catch(e){
print(e.toString());
}*/
super.initState();
}
No error on run tab and app works fine.
Firebase init will fail with a different message if you attempt to do it a second time.
FirebaseApp name [DEFAULT] already exists
You can check if it's already initialized as described in this other question.
Unless you have specific needs, you should instead consider instead initializing Firebase just once globally for your main app object when it first launches, and don't worry about it again after that.
I'm trying to access chatting room Using firestore and coroutines.
fun getOwner() {
runBlocking {
var de = async(Dispatchers.IO) {
firestore.collection("Chat").document("cF7DrENgQ4noWjr3SxKX").get()
}
var result = de.await().result
}
But i get error like this :
E/AndroidRuntime: FATAL EXCEPTION: Timer-0
Process: com.example.map_fetchuser_trest, PID: 19329
java.lang.IllegalStateException: Task is not yet complete
at com.google.android.gms.common.internal.Preconditions.checkState(Unknown Source:29)
at com.google.android.gms.tasks.zzu.zzb(Unknown Source:121)
at com.google.android.gms.tasks.zzu.getResult(Unknown Source:12)
at com.example.map_fetchuser_trest.model.Repository$getOwner$1.invokeSuspend(Repository.kt:53)
How can i get chat document? when i use origin api like below, I can access chat room document.
firestore.collection("Chat").document(
"cF7DrENgQ4noWjr3SxKX"
).get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val chatDTO = task.result?.toObject(Appointment::class.java)
}
}
Task is the thing one awaits on, but you wrapped it in another layer of async. Remove the async:
fun getOwner() {
runBlocking {
var de = firestore.collection("Chat").document("cF7DrENgQ4noWjr3SxKX").get()
var result = de.await().result
}
}
However, by using runBlocking() you have shot yourself in the foot and wrote blocking code that just formally uses an async API to no good effect.
To truly benefit from it, you must have a
suspend fun getOwner() = firestore
.collection("Chat")
.document("cF7DrENgQ4noWjr3SxKX")
.get()
.await()
.result
and launch a coroutine at the place you call it from:
launch {
val owner = getOwner()
// update the GUI
}
This assumes you're calling launch from an object that is a CoroutineScope.
val db = FirebaseFirestore.getInstance()
override suspend fun saveBinToDB(bin: Bin): Result<Unit> {
lateinit var result:Result<Unit>
db.collection("bins")
.add(bin)
.addOnSuccessListener { documentReference ->
Log.d(TAG, "DocumentSnapshot written with ID: ${documentReference.id}")
result = Result.Success(Unit)
}
.addOnFailureListener { e ->
Log.w(TAG, "Error adding document", e)
result = Result.Error(Exception())
}
.await()
return result
}
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.3.7"
The usage of runBlocking{..} in the first code snippet works as follows: the runBlocking function blocks to execute the parameter lambda code (and the lambda code will suspend inside). It gives less sense.
You may want to start a coroutine with launch{..} function instead and use withContext(Dispatchers.Main){..} to have the block executed in the UI thread, e.g. to show the fetched results. You may also implement CoroutineScope in your activity class.
The first step - you need to turn the Firebase API call into a suspending function. It could be done with suspendCoroutine{..} function (there are several more functions like suspendCancellableCoroutine{..} in the kotlinx.coroutines library.
There is an integration library with Google Play Services that provides support for Firebase
https://github.com/Kotlin/kotlinx.coroutines/tree/master/integration/kotlinx-coroutines-play-services
I struggled to get await() function. Check your build.gradle should have below dependencies -
def coroutines = '1.6.4'
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines"
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
api "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$coroutines"
Now instead of Complete/Failure Listener, use await() just like used in #Marko answer -
suspend fun getDataFromFireStore() = firestore
.collection("some key")
.document("some key")
.get()
.await()
.documents
I have a function loadData that loads some text from a file:
Future<String> loadAsset() async {
return await rootBundle.loadString('assets/data/entities.json');
}
The loadString method is from Flutter SDK, and is asynchronous.
The loadAsset method is then called in another method, that must me marked as async, since loadAsset is async and I need to use await:
Future<List<Entity>> loadEntities() async {
String jsonData = await loadAsset();
return parseData(jsonData);
}
The parseData method is not async, it receives a String, parse it, and return a list of objects:
List<Entity> parseData(String jsonString) {
...
}
But since loadEntities must be marked with async, this requires that it returns a Future, but in practice, it's not a Future because since I use await, it awaits for the loadAsset method to finish, then call the parseData funcion using the result.
This easily turns into a snowball of async call, because every method that uses loadEntities must be marked as async too.
Also, I can't use loadEntities in a class constructor, because the constructor should be marked as async, which is not allowed in Dart.
Am I using the async/await pattern in Dart wrong? How could I use the loadEntities method in a class constructor?
No, async is contagious and there is no way to go back from async to sync execution.
async/await is only syntactic sugar for methodThatReturnsFuture().then(...)
Marking a method with async is only to allow you to use await inside its body. Without async you would still need to return a Future for calling code to only execute after the result of loadAsset() becomes available.
You can use the Future returned from the async call directly. This would look something like this:
class HasAsync {
HasAsync() {
asyncFunction().then((val) {
print(val);
});
}
Future<int> asyncFunction() async {
int val = await otherFunction();
return val;
}
}
You just can't use await within the non-async function.
As you've tagged this with 'flutter', I'm going to guess this is within a flutter app. If that's the case look at the docs for FutureBuilder - it might help with what you're trying to do.
I know I may be too late for you to make any use of this answer but I am writing it anyways hoping someone will find it useful. So here is my two cents.
I had the same thought process as you did when I first tried to figure out what is asynchronous programming and how it can be used.
Since the question is regarding Flutter, I will use dart to explain this.
First, let's dive in to the the basic actually the purpose of using async await in asynchronous programming.
According to the flutter documentation, the purpose of async and await keywords is to declaratively mark a function as asynchronous and use it's results.
To define an async function, add async before the function body
The await keyword works only in async functions.
Therefore, whenever you try to get an output from a function marked as asynchronous it will have no option but to return a Future. Look at the following example for more clarification.
Firstly, you have a function which will do some calculations
Secondly, you have a simple function which gets data from an API by doing a simple http get request.
finally another function which will process some data and print some values.
void processInfo(){
calculateStuff();
Future<Map> decodedData = getDataFromInternet();
getDataProcessed(decodedData);
}
so in synchronous programming this would mean that all three functions will execute one after another. But let's say the second function getDataFromInternet() is called asynchronously. A simple implementation would be like below.
Future<Map> getDataFromInternet() async {
http.Response response = await http.get(this._apiPath);
Map decodedData;
if (response.statusCode != 200)
print("invalid response. cannot proceed!");
else {
decodedData = jsonDecode(response.body);
}
return decodedData;
}
So the above function is required to return a future. The question is why?
It's simple. In this case, it's because since we want to return something and by the time the return statement is executed and the data from the 'get request' may or may not be available at that moment.
Thus, the function returns a Future type result which and either be in complete state or incomplete state.
So how can we process this result? As a matter of fact this can be done in 3 ways.
1. Method One - Handle it as a promise
So once the getDataFromInternet() function returns a Future result in this example, you need the process that future result like how you'd handle a promise in javascript. Refer the code sample below.
void getDataProcessed(Future<Map> data) {
print('getting data from future map');
data.then((d) {
print(d);
});
}
2. Method Two - mark the parent function asynchronous (contagious way)
void processInfo() async{
calculateStuff();
//now you can simply await that result and process the result rather
//than handling a Future<Map> result in this case.
//Note: it is not required to use future variable because we are awaiting
//for result
Map decodedData = await getDataFromInternet();
getDataProcessed(decodedData);
}
so in this case the getDataProcessed() function will look something like this.
void getDataProcessed(Map data) {
//this will simply print the data object which is complete result which is by
//no way is a promise object
print(data);
}
3. Method Three - Use the results from the asynchronous method in a synchronous function (non-contagious way)
In this case the processInfo() function will change slightly, i.e. the getDataProcessed() will no longer be invoked in this method and will look something like this.
void processInfo(){
calculateStuff();
getDataFromInternet();
}
Instead of invoking getDataProcessed() in processInfo() function, we can use the result from getDataFromInternet() function to invoke the getDataProcessed() function.this mean we won't have to mark processInfo() as async and we can process getDataProcessed() method after we finish executing getDataFromInternet() method. The following code sample demonstrates how to do it.
void getDataFromInternet() async {
http.Response response = await http.get(this._apiPath);
Map decodedData;
if (response.statusCode != 200)
print("invalid response. cannot proceed!");
else {
decodedData = jsonDecode(response.body);
}
//in this case, since we are awaiting for get results response and the
//function is not expected to return anything the data type passed into
//getDataProcessed() function now will be of type Map rather than being type
//Future<Map> . Thus allowing it to be a synchronous function and without
//having to handle the future objects.
getDataProcessed(decodedData);
}
void getDataProcessed(Map data) {
//this will simply print the data object which is complete result which is by
//no way is a promise object
print(data);
}
So revising back this long answer,
async/await is just the declarative way to mark asynchronous functions
when an asynchronous function is called it can be handled in 3 ways.
get the return Future and handle it like a promise with the use of 'then()' function so no need to mark the parent
function async
mark the parent function async and handle the returned object with await to force the function to wait for the result.
call the desired function with the output of the async function at the end of the async function. This will allow the main
function to continue non-dependent functions while waiting for the
results of the async function and one the async function get the
results it can go in to the other function at the end and execute it
with the data received.
then and await are different. await will stop the program there until the Future task is finished. However then will not block the program. The block within then will be executed when the Future task is finished afterwards.
If you want your program to wait for the Future task, then use await. If you want your program to continue running and the Future task do it things "in the background", then use then.
As to your problem, I suggest redesign it. Do the loading assets and other async things that are needed for the constructor elsewhere. After these tasks are completed, then call the constructor.
How modify the following code to get article data and top articles asynchronously in hack ?
class ArticleController
{
public function viewAction()
{
// how get
$article = $this->getArticleData();
$topArticles = $this->getTopArticles();
}
private function getArticleData() : array
{
// return article data from db
}
private function getTopArticles() : array
{
// return top articles from db
}
}
The warning from the async documentation page is relevant here:
There is currently basic support for async. For example, you can
currently write basic async functions that call other async functions.
However, we are currently finalizing other foundations (e.g. async
database, scheduling, and memory handling APIs) which will be required
to reach the full potential of async in production. We felt, though,
it would be useful to introduce the concept and technology of async
(even with basic functionality) in order to get developers used to the
syntax and some of the technical details.
So, the raw database queries you need to actually make use of async functions are unfortunately not available yet. The documentation linked above talks some about how async functions work in general, and includes an example of coalesced fetching, something that you can do with async functions right now.
The DB API is coming eventually, but isn't available yet, sorry!
HHVM 3.6 and newer
async functions info
The two HHVM PHP language keywords that enable async functions are async and await. async declares a function as asynchronous. await suspends the execution of an async function until the result of the asynchronous operation represented by await is available. The return value of a function that await can be used upon is an object that implements Awaitable<T>.
You have an example in the documentation (1). There is a discussion about asynchronous functions in the language specification as well (2).
It actually took me some time to realize how to use and call the asynchronous functions, so I think you will find some more info useful.
We have these two functions: foo() and bar().
async function foo(): Awaitable<void> {
print "executed from foo";
}
async function bar(int $n): Awaitable<int> {
print "executed from bar";
return $n+1;
}
Let's experiment some ways to call these two functions:
foo(); // will print "executed from foo"
bar(15); // will print "executed from bar"
$no1 = bar(15); // will print "executed from bar"
print $no1; // will output error, because $number is not currently an `int`; it is a `WaitHandle`
$no2 = bar(15)->join(); // will print "executed from bar"
print $no2; // will print 16
AsyncMysqlClient tips
The connection to a MySQL database is made with AsyncMysqlClient::connect asynchronous function which returns an ExternalThreadEventWaitHandle to an AsyncMysqlConnection.
You can perform query or queryf on an AsyncMysqlConnection. Note: the data you send to a queryf is properly escaped by the function.
A query you perform on an AsyncMysqlConnection returns either an AsyncMysqlQueryResult (when the query performs ok) or AsyncMysqlQueryErrorResult (if the query goes wrong; then you can treat errors with the mysql_error(), mysql_errno() and failureType() members of this class). Both AsyncMysqlQueryResult and AsyncMysqlQueryErrorResult extend AsyncMysqlResult abstract class.
Below is a probable implementation of your class:
class ArticleController {
private AsyncMysqlConnection $connection;
public async function viewAction(int $articleId): Awaitable<void> {
$this->connection = await AsyncMysqlClient::connect( /* connection data */ );
$article = await $this->getArticleData($articleId);
}
public async function getArticleData(int $id): Awaitable<?Vector> {
$articleDataQuery = await $this->connection->queryf("SELECT * FROM articles WHERE id %=d", $id);
if($articleDataQuery instanceof AsyncMysqlQueryErrorResult) {
throw new Exception("Error on getting data: ".$articleDataQuery->mysql_error());
}
// Considering that $id represents a unique id in your database, then
// you are going to get only one row from your database query
// so you return the first (and only) row in the query result
if($articleDataQuery->numRows() == 1) {
return $articleDataQuery->mapRowsTyped()[0];
}
return null;
}
}
P.S. I hope it is not too late for this answer and I hope it helps you. If you consider this useful, please, accept it.