Categories
discuss

Existing 3-function callback to Kotlin Coroutines

I have a general question with a specific example: I’d like to use Kotlin coroutine magic instead of callback hell in Android when taking a picture.

manager.openCamera(cameraId, object : CameraDevice.StateCallback() {
    override fun onOpened(openedCameraDevice: CameraDevice) {
        println("Camera onOpened")
        // even more callbacks with openedCameraDevice.createCaptureRequest()....
    }

    override fun onDisconnected(cameraDevice: CameraDevice) {
        println("Camera onDisconnected")
        cameraDevice.close()
    }
    ...

How would I convert that to something less ugly? Is it possible to take an average callback with three or so functions, and turn it into a promise-chain by designating the primary flow as the promise-result path? And if so, should/do I use coroutines to make it async?

I’d love something with async and .await that would result in

manager.open(cameraId).await().createCaptureRequest()

I’m trying to do it through something like the following, but I don’t think I’m using CompletableDeferred right!

suspend fun CameraManager.open(cameraId:String): CameraDevice {
    val response = CompletableDeferred<CameraDevice>()
    this.openCamera(cameraId, object : CameraDevice.StateCallback() {
        override fun onOpened(cameraDevice: CameraDevice) {
            println("camera onOpened $cameraDevice")
            response.complete(cameraDevice)
        }

        override fun onDisconnected(cameraDevice: CameraDevice) {
            response.completeExceptionally(Exception("Camera onDisconnected $cameraDevice"))
            cameraDevice.close()
        }

        override fun onError(cameraDevice: CameraDevice, error: Int) {
            response.completeExceptionally(Exception("Camera onError $cameraDevice $error"))
            cameraDevice.close()
        }
    }, Handler())
    return response.await()
}

Answer

In this particular case you can use a general approach to convert a callback-based API to a suspending function via suspendCoroutine function:

suspend fun CameraManager.openCamera(cameraId: String): CameraDevice? =
    suspendCoroutine { cont ->
        val callback = object : CameraDevice.StateCallback() {
            override fun onOpened(camera: CameraDevice) {
                cont.resume(camera)
            }

            override fun onDisconnected(camera: CameraDevice) {
                cont.resume(null)
            }

            override fun onError(camera: CameraDevice, error: Int) {
                // assuming that we don't care about the error in this example
                cont.resume(null) 
            }
        }
        openCamera(cameraId, callback, null)
    }

Now, in your application code you can just do manager.openCamera(cameraId) and get a reference to CameraDevice if it was opened successfully or null if it was not.

Categories
discuss

Surprised error while building apps “Couldn’t find outer class” after update to gradle3.1.0-beta1(Android Studio 3.1 Beta1)

run: ./gradlew clean assembleDebug

Exception in thread "main" java.lang.NullPointerException: Couldn't find outer class xxxxx/SomeClass$someMethod$1$1$2 of xxxx/SomeClass$someMethod$1$1$2$1

execution failed for task ':app:transformClassesWithDesugarForDevDebug'.

issue

Same problem https://www.reddit.com/r/androiddev/comments/7u8tug/android_studio_31_beta_1_is_available/

Answer

workaround solved: https://issuetracker.google.com/issues/72750890#comment10

a bug is also filled at JB.

Categories
discuss

How to keep multiple requests separate in Nodejs / Expressjs

I am developing a NodeJS / ExpressJS application on my computer. I have node running locally. I have a single page web app. When it needs information it makes ajax requests with jQuery.

The problem is when I have multiple requests for different sets of data. Node/express will start processing the first request and then the second request comes in before the first request has been fulfilled, it sends the response to the second request from the first request instead of sending it to the first request like it is suppose to. If I put a pause ( using an alert ) in my app so that is slows it down so the next request doesn’t get sent until the first request was fulfilled, everything works fine.

I don’t understand why this is happening. I thought node / express was suppose to be able to handle this and keep the requests separate.

Also, I get a “Can’t set headers after they are sent” error in node because it is apparently merging the requests….

Here is what happens

ajax request 1 -> server
ajax request 2 -> server
ajax request 3 -> server

server -> request1 ( no response )
server -> request2 ( request 1's data)
server -> request3 ( request 2's data)
server for request3 --> error: header's already sent

I am using Google Chrome 63 with jQuery 3.3.1 on a Windows 10 machine running Node 8.9.4 and Express 4.16.2

My work-around is to chain the ajax requests so that each request doesn’t get called until the prior request has received a response from the server. But I shouldn’t have to do that…

Here is the relevant server code:

var mysql = require("mysql");
var express = require('express');
var app = express();

var DEBUG = true;

var request = null;
var response = null;

var currentDataRowParser = null;
var con = mysql.createConnection(config);

function ParseMySqlRowData(rowData)
{
    if (DEBUG) console.log("ParseMySqlRowData");

    return JSON.stringify(rowData);
}

var ParseMySqlRowsDatatoResponse = function (err, rows)
{
    if (DEBUG) console.log("ParseMySqlRowsDatatoResponse");

    var MySQLRows;

    try
    {
        if (!err)
        {
            MySQLRows = "[";
            for (var i = 0; i < rows.length; i++)
            {
                if (i > 0)
                    MySQLRows += ", ";

                MySQLRows += currentDataRowParser(rows[i]);
            }

            MySQLRows += "]";

            if (DEBUG) console.log("write rows");
            if (DEBUG) console.log(MySQLRows);
            response.send(MySQLRows);
            response.end();
        }
    }
    catch (ex)
    {
        if (DEBUG) console.log("ParseMySQLRowsDatatoResponse: ERROR");
        if (DEBUG) console.log(ex);
    }
};

var GetQueryData = function (query, dataRowParser, parseDataCallbackFunction)
{
    if (DEBUG) console.log("GetQueryData");

    currentDataRowParser = dataRowParser;

    if (parseDataCallbackFunction == null || parseDataCallbackFunction == undefined)
        parseDataCallbackFunction = ParseDataCallback;

    try
    {
        if (DEBUGQUERY)
        {
            console.log("before query");
            console.log(con.query(query, parseDataCallbackFunction));
            console.log("after query");
            console.log(query.sql);
            DEBUGQUERY = false;
        }
        else
        {
            con.query(query, parseDataCallbackFunction);
        }
    }
    catch (ex)
    {
        console.log(ex);
    }
};

app.post('/getdata', function(req, res)
{
    request = req;
    response = res;
    var query;
    switch (request.body.loaddata)
    {
    case "dataset1":
        query = "SELECT * FROM table1 WHERE key='" + request.body.key + "'";
        GetQueryData(query,ParseMySqlRowData,ParseMySqlRowsDatatoResponse);
        break;
    case "dataset2":
        query = "SELECT * FROM table2 WHERE key='" + request.body.key + "'";
        GetQueryData(query,ParseMySqlRowData,ParseMySqlRowsDatatoResponse);
        break;
    case "dataset3":
        query = "SELECT * FROM table3 WHERE key='" + request.body.key + "'";
        GetQueryData(query,ParseMySqlRowData,ParseMySqlRowsDatatoResponse);
        break;

    }
};

Answer

You cannot store req and res in global or module-level variables. When a second request comes in, it will immediately overwrite your globals and it will mix up the data for the various requests.

Does’t node separate each request instance?

Yes, there is a separate request instance, but not a separate global or module-level namespace. So, when you assign the req into the global space, you are overwriting the previous one and your code will then use the wrong one.

It is very helpful to have the request and response as a global variable. Otherwise I would have to be passing them all over the place.

You HAVE to pass them to lower level functions that need them. That’s how you keep each request separate from the others. ANY function that needs to operate on req or res should be passed those variables so it knows exactly which ones to operate on.

node.js has a shared global and module-level namespace. So, all requests in flight at the same time use that same namespace. The ONLY data that should ever be stored there is data that you specifically want to be shared between requests (such as session state, for example). Individual request or response objects should never be stored in a shared variable.


A more common way to code your type of code is to call a function like your GetQueryData() and have it return the data (likely via a promise) and then you send the response in the original request handler. Then, you don’t have to pass req or res down multiple levels at all. Your helper functions just fetch data. The request handlers fetch data, then send the response. It’s often a better encapsulation of functionality.


Here’s one way to restructure your code along the lines described above.

  1. GetQueryData() returns a promise that fulfills with the data
  2. ParseMySqlRowsData() just returns a parsed result or null if a parsing error
  3. app.post() just gets the data (via a promise) and then sends the appropriate response.
  4. There’s no global req or res and there’s no need to pass them anywhere.

Here’s the code:

var mysql = require("mysql");
var express = require('express');
var app = express();

var DEBUG = true;

var currentDataRowParser = null;
var con = mysql.createConnection(config);

function ParseMySqlRowData(rowData) {
    if (DEBUG) console.log("ParseMySqlRowData");

    return JSON.stringify(rowData);
}

var ParseMySqlRowsData = function(err, rows) {
    if (DEBUG) console.log("ParseMySqlRowsDatatoResponse");

    var MySQLRows;

    try {
        if (!err) {
            MySQLRows = "[";
            for (var i = 0; i < rows.length; i++) {
                if (i > 0)
                    MySQLRows += ", ";

                MySQLRows += currentDataRowParser(rows[i]);
            }

            MySQLRows += "]";

            if (DEBUG) console.log("write rows");
            if (DEBUG) console.log(MySQLRows);
            return MySQLRows;
        }
    } catch (ex) {
        if (DEBUG) console.log("ParseMySQLRowsDatatoResponse: ERROR");
        if (DEBUG) console.log(ex);
        return null;
    }
};

var GetQueryData = function(query, dataRowParser, parseDataCallbackFunction) {
    return new Promise((resolve, reject) =>{
        if (DEBUG) console.log("GetQueryData");

        let currentDataRowParser = dataRowParser;

        if (parseDataCallbackFunction == null || parseDataCallbackFunction == undefined)
            parseDataCallbackFunction = ParseDataCallback;

        try {
            if (DEBUGQUERY) {
                console.log("before query");
                console.log(con.query(query, parseDataCallbackFunction));
                console.log("after query");
                console.log(query.sql);
                DEBUGQUERY = false;
            } else {
                con.query(query, function(err, rows) {
                    if (err) {
                        reject(err);
                    } else {
                        let result = parseDataCallbackFunction(rows);
                        if (result) {
                            resolve(result);
                        } else {
                            reject(new Error("ParseMySqlRowsData error"));
                        }
                    }
                });
            }
        } catch (ex) {
            console.log(ex);
            reject(new Error("GetQueryData error"));
        }
    });
};

app.post('/getdata', function(req, res) {
    var query;
    let p;
    switch (request.body.loaddata) {
        case "dataset1":
            query = "SELECT * FROM table1 WHERE key='" + request.body.key + "'";
            p = GetQueryData(query, ParseMySqlRowData, ParseMySqlRowsData);
            break;
        case "dataset2":
            query = "SELECT * FROM table2 WHERE key='" + request.body.key + "'";
            p = GetQueryData(query, ParseMySqlRowData, ParseMySqlRowsData);
            break;
        case "dataset3":
            query = "SELECT * FROM table3 WHERE key='" + request.body.key + "'";
            p = GetQueryData(query, ParseMySqlRowData, ParseMySqlRowsData);
            break;
        default:
            p = Promise.reject(new Error("Invalid request.body.loaddata"));
            break;
    }
    p.then(data => {
        res.send(data);
    }).catch(err => {
        console.log(err);
        res.sendStatus(500);
    });
};

P.S. I see you still have a module-level variable you should not have: currentDataRowParser. That needs to be replaced by passing the appropriate parser to ParseMySqlRowsData() as an argument, not using a module-level shared variable. I will leave that as an excercise for you to fix. Shared variables for operating on a specific request state are a bad idea in ANY server programming and specifically in node.js programming. Keep your request-specific state in your function arguments or on the req or res object itself. That’s how you prevent overwritting the data from one request with the data from another while they are being processed.

Categories
discuss

PWA Service worker notification click

i’m trying do show a notification and do something when it’s clicked.

    try {
    navigator.serviceWorker.getRegistration()
        .then(reg => {
            reg.showNotification("Guarda il videoclip!", {
                body: "clicca qua!",
                icon: "images/she_is_fire.png",
                vibrate: [100, 50, 100],
                tag: 'sample',
                actions: [{ action: 'explore', title: 'Guarda', icon: 'images/she_is_fire.png' }],
            });
            self.addEventListener('notificationclick', function(event) {
                event.notification.close();
                window.open("https://youtu.be/PAvHeRGZ_lA");
            }, false);
        })
        .catch(err => alert('Service Worker registration error: ' + err));

} catch (err) {
    alert('Notification API error: ' + err);
}

I added the eventlistener but it never gets fired.

What am i doing wrong?

Answer

The correct place for handling clicks of Service Worker-based notifications is in Service Worker. You need to move your listener – the part with self.addEventListener('notificationclick', ...) – to your Service Worker code.

Note that you don’t have window access there. To navigate to the URL, you’d need to use clients.openWindow instead.

So your app-side code will only have the reg.showNotification call and your handler in the Service Worker will look like this:

self.addEventListener('notificationclick', function (event) {
  event.notification.close();
  clients.openWindow("https://youtu.be/PAvHeRGZ_lA");
});
Categories
discuss

React native fbsdk issue – Task :react-native-fbsdk:compileDebugJavaWithJavac FAILED

I’m having issues building my android folder with react-native run-android after installing the fbsdk library and following all the steps to properly link it. Below is my error message.

I already tried to change my default sdk version from 23 to 27.0.1 which allowed me to have a more detailed error message since before I would only get failed to build aapp.

Any idea how to fix this? It works properly on ios.

> Task :react-native-fbsdk:compileDebugJavaWithJavac FAILED
/Users/hugohyz/code/hugoh1995/dogtime_react/DogtimeReactInit/node_modules/react-native-fbsdk/android/src/main/java/com/facebook/reactnative/androidsdk/FBAppEventsLoggerModule.java:209: error: cannot find symbol
     @ReactMethod(isBlockingSynchronousMethod = true)
                                                ^
  symbol:   method isBlockingSynchronousMethod()
  location: @interface ReactMethod
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: /Users/hugohyz/code/hugoh1995/dogtime_react/DogtimeReactInit/node_modules/react-native-fbsdk/android/src/main/java/com/facebook/reactnative/androidsdk/Utility.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':react-native-fbsdk:compileDebugJavaWithJavac'.
> Compilation failed; see the compiler error output for details.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 1s
76 actionable tasks: 1 executed, 75 up-to-date
Could not install the app on the device, read the error above for details.
Make sure you have an Android emulator running or a device connected and have
set up your Android development environment:
https://facebook.github.io/react-native/docs/android-setup.html

Answer

Ok so I managed to fix the fbsdk related issue. Apparently I was missing the following code inside android/build.graddle

allprojects {
    repositories {
      ...

      maven {
          // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
          url "$rootDir/../node_modules/react-native/android"
        }
      ...

I’m now faced with another issue tho so I’ll open another thread.

It’s important not to replace existing maven when adding new maven to the repositories apparently.

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..