opendatacam / opendatacam-mobile

OpenDataCam mobile app for android
https://play.google.com/store/apps/details?id=com.opendatacam
MIT License
12 stars 3 forks source link

Improve assets copy to android filesystem on first install & on update #8

Open tdurand opened 3 years ago

tdurand commented 3 years ago

Problem

In order to use nodejs-mobile , you need to first copy the node.js app files to the phone filesystem, you can't start node.js directly with the APK file. (todo when the App in first installed and anytime that the node.js app is changed (each app update) )

Right now we were using the "recommended" method from nodejs-mobile, which is to ship the app + node_module folder in the assets directory and recursively copy this to the phone file system https://code.janeasystems.com/nodejs-mobile/android/node-project-folder#copy-the-nodejs-project-at-runtime-and-start-from-there .

Problem with this is that with a big node_module folder with quite a lot of dependency like OpenDataCam , this means copy pasting 24,723 Files, 4,709 Folders and 142 MB .. And on top of adding 140 MB to the app size, copying lots of small files is slow.

"Out of the box" , the first app install takes 2min30s (and each update).

Two things we are looking to improve here:

copy files faster

Turns out that there was a low hanging fruit here to improve this, instead of copy pasting all the files recursively which is slow, we create a zip at build time of the node.js app and then unzip it on the phone. ( idea from stackoverflow: https://stackoverflow.com/a/42415755 )

I've tried that : https://github.com/opendatacam/opendatacam-mobile/commit/c1fcca46665b275ba9bc64fd059edf299c12c219 and we get from 2min30s to around 20 seconds .. So this is already a huge win šŸ™Œ

Only downside is that we need to integrate that zipping on the build process. for now I'm doing it manually with this command:

zip -0 -r ../nodejs-project.zip .

Have less files (and less MBs) to copy

Thinking of having 24,723 Files, 4,709 Folders for 142Mb of files in the node_modules folder is a bit crazy.. when the client side part of the app (actually loaded in the browser) is max 5 MB .. the server side part has obviously node.js has a dependency but not even included in those 142 MB ( it is 142 MB + the 30-50 MB of node.js) ... My guess is that it might also be only 5 - 20 MB in the end...

Normally we never care about this because we do the npm install on the server and the size of the node_modules folder doesn't matter.. what matters is how much you send to the client ( see explanation here: https://github.com/vercel/next.js/discussions/14339 ) . Here we want to also reduce this to avoid.

One tool to our service for this is npm prune --production , which you can run after npm run build and delete all the node_modules dependencies that are listed as "devDependencies" in the package.json: https://github.com/opendatacam/opendatacam/blob/master/package.json#L43

Out of the box doing this already reduce a bit, down to 115 MB (121,226,417 bytes) and 22,298 Files, 4,231 Folders

What I need to do next is to improve this, by:

image

image

tdurand commented 3 years ago

Some trial and error progress notes:

Found out this project while looking into the "webpack" of node.js : https://github.com/vercel/ncc , it is from the same people doing next.js

It is supposed to be able to build your node.js app into a single index.js file (which is huge) containing all the node_modules dependencies you need.. then you just need to do: node index.js without needing the node_module folder.

This seems very much what I'm looking to do.. and I was able to make it work properly on a very simple example project that uses express: https://github.com/vercel/next.js/tree/canary/examples/custom-server .

Final app necessary files:

image

What you need to do is:

# create the .next  folder containing the client side code 
npm i
npm run build
# pack the node.js server side code in the dist/ folder
ncc build server.js
# this creates a dist/ folder with an index.js in it (18 MB with express.js)

# then you need to copy the client side code to the dist/ folder (so the index.js app can use it)
cp -r .next/ dist/

# then launching the app should work
cd dist/
node index.js

Also very important you need to disable the auto routing of next.js by hooking to next to node.js this way in your server.js file

const app = next({ useFileSystemPublicRoutes: false })

But while trying on OpenDataCam I run into a couple of issues:

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.
    at resolveDispatcher (/mnt/c/Users/tibod/Documents/GitHub/opendatacam/node_modules/react/cjs/react.development.js:1465:13)
    at useMemo (/mnt/c/Users/tibod/Documents/GitHub/opendatacam/node_modules/react/cjs/react.development.js:1520:20)
    at Provider (/mnt/c/Users/tibod/Documents/GitHub/opendatacam/node_modules/react-redux/lib/components/Provider.js:22:41)
    at processChild (/mnt/c/Users/tibod/Documents/GitHub/opendatacam/dist/index.js:470359:14)
    at resolve (/mnt/c/Users/tibod/Documents/GitHub/opendatacam/dist/index.js:470276:5)

Couldn't figure out how to fix this yet... related to ncc packing several copy of React I think in the big index.js ...

Also investigating I've seen someone trying the same thing but failing also (not for the same reason: https://github.com/vercel/next.js/issues/8574 )

I will pause investigation for now as the priority is to get a beta out.. but this ncc tool is promising.... Also I think it might be a good time to get rid of express.js if we move forward with this.. as there are lightweights alternatives now..

tdurand commented 3 years ago

Some update on this.. was able to make very good improvements on this, down to reducing the first load to 5 seconds, same as the subsequent opening of the app šŸ™Œ , and reducing also the size of the app by 20-30 MB..

This improvement was made by adding a build step to statically export the whole front-end app and only ship this to the device instead of having the whole next.js dependencies and have the node.js making the front-end app render dynamically on the device... ( reminder: the slowness didn't come from rendering the app but from copying thousands of small @babel, react etc.. files )

This comes at the cost of increasing a bit the diff between the "opendatacam mobile" branch and the normal branch: https://github.com/opendatacam/opendatacam/compare/development...mobile, documenting here some more changes for my future self

I guess in the future is it would be better to make this "webpack" for node : ncc solution work.. but in the meantime this is a good workaround that makes the experience way better.

tdurand commented 2 years ago

Dropping this here as I though about this old issue, maybe will be useful at some point šŸ˜… , next.js now has a standalone output mode that exports the necessary dependencies without needing to cherry-pick node_modules folder: https://nextjs.org/blog/next-12-1?utm_source=next-site&utm_medium=banner&utm_campaign=next-website#self-hosted-nextjs-improvements