Adobe-Runtime-Support icon indicating copy to clipboard operation
Adobe-Runtime-Support copied to clipboard

1 minute app loading times on some Android devices

Open dampgnat opened this issue 2 years ago • 47 comments

It's likely this isn't technically a bug, but it's becoming a big problem.

Over the last 8 months my app has been live, regular content updates have accumulated APK size to 185 MB (IPA 135 MB) and so naturally the load times have also grown.

Latest app load times:

  • Samsung S21 5G - within a few seconds
  • Google Pixel 4a - 30 seconds
  • Redmi Note 10 Pro - 30 seconds
  • Nokia G20 - 60 seconds
  • Samsung Galaxy J7 Prime - 60 seconds

60 seconds is a lonnnnng time staring at a silent black screen, and unsurprisingly a lot of those users are assuming it has failed to load.

Firstly, I'm curious as to why the load time differs so much, especially when the Nokia G20 is only a year old. All assets are included as SWCs and so must be loaded when the app loads. One developer suggested this might be audio files being unpacked. My audio SWCs are 30 MB of MP3s files. Image/animation SWCs are 55 MB.

Secondly, if nothing can be done to reduce the load time, do you know if it's possible to load assets as SWFs at runtime while retaining their class names? (iOS strips SWFs of all script including class names, so I can't even play runtime loaded sound effects).

Observed using various AIR SDKs over the last 2 years up to 33.1.1.821

dampgnat avatar Jun 23 '22 10:06 dampgnat

We've had some luck with reducing load times on Android using an old Flash trick for preloaders -- if you add a frame into the final main swf, the SWCs (and all the rest of the code and content) are moved onto the second frame, which greatly improved app startup times for us. This was years ago, but a 60MB app with tons of content from SWCs used to take 20 seconds to start on some Samsung tablets, but by adding that frame in the beginning it dropped to only 2 or 3 seconds to start up.

We were using the Flex compiler in FlashDevelop (though it was all AS3 and SWCs), and based it on one of their preloader templates where it uses the [Frame] meta tag to add an extra frame before the rest of the content. We were originally just doing this so we could display a "loading" screen, but it also made a huge difference with reducing the startup time!

FliplineStudios avatar Jun 23 '22 12:06 FliplineStudios

Hi

We've just been looking at this sort of thing for a couple of other companies, seems like a fairly common theme! so I would be very interested to know what is actually happening during this start-up time!

@FliplineStudios thanks for this info, we can have a look further to see how this idea of adding a 'preloader' into a first frame would actually happen in practice. But then, when you say it "improved app startup times", did it actually just disguise the start-up time by displaying a picture (static? or animated?), or did it really mean that your 'main' ActionScript class constructor was being called more quickly? I don't suppose you have any such SWF that's had this [Frame] injected into it so that we can see what it actually did?

We were starting to look into the idea of kicking off a separate display list within the runtime to display a preloader SWF whilst the main SWF then loaded in the background but I'm wondering whether the simpler option would be to have this just treat it like a first frame...

We can try to replicate this and see what's taking the time ... @dampgnat if there's any chance we can get your SWF file (or an APK or AAB build with aab-debug or apk-debug) we can try profiling it a little more (and can look to see whether the SWF tag ordering has got anything to do with it....)

thanks

ajwfrost avatar Jun 23 '22 13:06 ajwfrost

@FliplineStudios thanks for the workaround suggestion!! Curious to know if it just offsets the loading time as Andrew suspects. As it happens I'm also using the Flex compiler in Flash Develop, and I also used [Frame(factoryClass="Preloader")] when developing for web, but removed it for mobile. Certainly sounds worth a try! Did you have to change Flash/Animate SWC export settings to Export classes in frame 2?

@ajwfrost good to know I'm not the only one experiencing this. I wish I could send you a build, but my client security probably wouldn't allow it. I'll see if there's a way I can provide a less sensitive version for you to analyse.

dampgnat avatar Jun 23 '22 15:06 dampgnat

@ajwfrost It actually did improve load times -- Originally we were just setting it up so we could show a loading screen instead of a black screen, and were surprised to see such a big reduction in loading times in addition to that. When we first set it up, this was quite a few years back testing on a Galaxy Tab 3 I believe, but it would normally just stick on a black screen for around 20 seconds before showing the app's content. After adding the extra frame with a loading message, it would instead sit on a black screen for 1 or 2 seconds, then show our loading screen for 1 second or so, then go right into the main content. At the time we figured that the combination of trying to initialize the app/AIR along with the actual SWF's content was just very taxing on the device, but by splitting it up into two separate frames -- where on the first frame it just has to worry about starting up without any of the SWF's actual assets, then on the second frame it initializes all of the SWF content -- it made a big difference.

@dampgnat Nice, that's what we're using in FlashDevelop too -- I can't remember if we modified any of the usual preloader code in that Preloader.as file aside from just showing some text or an image, possibly just setting up stage.align and stage.scaleMode if they weren't already in there normally. We kept everything the same with our SWCs from Animate -- everything with linkage is still Export in First Frame, and just that preloader/Frame tag does all of the work of actually moving it to the second frame. When checking out one of our files in SWF Investigator I'm seeing about 10 classes in ABC Block 0, and all of the rest are moved to ABC Block 1 (around 7000 classes from our code and all of our assets in SWCs with linkage). Hopefully it will make a difference for your startup time too!

FliplineStudios avatar Jun 23 '22 15:06 FliplineStudios

We have the same problem. It is important to say that if the user tap on the screen in this load process the ANR happens.

https://github.com/airsdk/Adobe-Runtime-Support/issues/1831

megajogos avatar Jun 23 '22 17:06 megajogos

Hey @FliplineStudios @ajwfrost after implementing the preloader it would appear that loading time is unaffected. But curiously it's also taken a performance hit. When testing on PC, the SWF runs at half frame rate in Debug, but the correct FPS in Release. When testing on Android, the app runs at about a third frame rate (Release).

Perhaps I've done something wrong, but traces show that the preloader is being used. (In Flashdevelop I had to add the Main class to Additional Compiler Options (-frame start Main), and set the Preloader as the document class for it to work.)

dampgnat avatar Jun 23 '22 17:06 dampgnat

@dampgnat Very strange! In our projects we still have Main.as as the main document class, and have [Frame(factoryClass = "Preloader")] in that file before the class declaration (haven't changed anything in Additional Compiler Options). Aside from adding some text on screen, our Preloader.as is set up the same as what's generated in FlashDevelop's "AS3 Project with Preloader" template, where it checks for currentFrame == totalFrames on enterFrame, and then creates a new instance of Main after that...

FliplineStudios avatar Jun 23 '22 18:06 FliplineStudios

Ok scrap that, we're finding that it does now seem to be loading much faster @FliplineStudios (my old Nexus 5X is shockingly inconsistent) On a Pixel 4a - loading time has gone from an average of 25 seconds to 3! But it then gets stuck. Will continue my investigations...

dampgnat avatar Jun 23 '22 20:06 dampgnat

It seems that [Frame(factoryClass = "Preloader")] does not work anymore https://stackoverflow.com/questions/16122820/does-asc-2-0-recognize-frame-metadata-tags-ex-for-preloader-factoryclass but instead use Additional Compiler Argument (-frame=AnyFrameLabelYouWant,YourMainClass)

cleverbeapps avatar Jun 24 '22 07:06 cleverbeapps

Hi

@cleverbeapps yes I found that the ASC 2.0 was ignoring it completely, but with the Flex compiler from 4.16 it worked.. thanks for the hints re. the arguments for the newer compiler!

This is quite an interesting one, looking at what's going on under the hood. The inclusion of the preloader will mean that as soon as the first 'frame' is reached, the content will start displaying, and this content is just using the preloader class as if the whole application was the preloader. I'm actually a bit surprised that this works as I thought it was all single-threaded, but it looks like the file is loaded in chunks via a background thread. So the runtime will continue to process the SWF as it comes in, until it's fully loaded at which point the preloader is complete.

The challenge I then see though is that the preloader (or at least, the template here from FlashDevelop) is doing an 'addChild' with a new symbol which is the actual main document class. Which means that (a) the main document class can't access the stage within it's constructor, and (b) there is an extra set of objects in the hierarchy which can lead to scaling/resizing issues when things like orientation changes happen.

I would actually have expected there to be the initial frame containing the preloader, then a second frame containing the main application class, so the preloader doesn't have to add the main app as a child. But, the preloader is set as the "main" application class (SymbolClass tag, ID of 0) which means it's associated with the overall root timeline. Will have to see if we can craft a SWF that has a preloader on frame one added via PlaceObject, and then a main app class added just in the second frame...

@dampgnat just in terms of your debugging here, are you using Scout? as that may help particularly if there are exceptions being thrown internally that are causing it to stop..

thanks

ajwfrost avatar Jun 24 '22 09:06 ajwfrost

@ajwfrost Hi Andrew. I have tried (IntellijIdea IDE):

  1. in Additional Compiler Options added -frame=ContentFrame,Main
  2. changed my main application class link to my custom Preloader.as (from https://gist.github.com/aajanki/751852)
  3. in that Preloader.as on line 33 (nextFrame()) - application is stopped and have no response. I have tried goToAndStop("ContentFrame"), but nothing changed.
  4. I have added breakpoint in Main constructor class, but it never fired

Could you share how to get it work? Thanks.

cleverbeapps avatar Jun 24 '22 10:06 cleverbeapps

@cleverbeapps for some reason I had to move my Main class into my domain and include that to make it work.

Additional Compiler Options: -frame start com.company.project.Main

EDIT I've since discovered that this wasn't necessary.

In my Preloader.as I wait until all bytes are loaded and then:

var mainClass:Class = getDefinitionByName("com.company.project.Main") as Class;
addChild(new mainClass() as DisplayObject);

@ajwfrost I have used Scout quite a bit in the past but only for isolating performance bottlenecks. My client's SDK makes it difficult to do debug builds. The device that did run the APK without freezing had focus problems when reopening it (no touch response), perhaps that's related to the stage access issue you mentioned? I'm going to see if I can remove some stuff and then send you an APK as you requested. Do you have an upload link?

dampgnat avatar Jun 24 '22 11:06 dampgnat

@cleverbeapps in your preloader code, you say it's hanging on nextFrame() - but I'm not sure why you'd want to change frames here at all... in the FlashDevelop template it calls stop() there...

@dampgnat interesting, I think there are some issues with the display list ending up unnecessarily complicated. I just tried changing the preloader to:

		private function startup():void 
		{
			var s : Stage = stage;
			var mainClass:Class = getDefinitionByName("Main") as Class;
			s.addChild(new mainClass() as DisplayObject);
			s.removeChild(this);
		}

which I think simplifies it a bit...

For uploads: https://transfer.harman.com/requests/dp5JuDJjFPJ0donrpcPemS thanks

ajwfrost avatar Jun 24 '22 11:06 ajwfrost

@ajwfrost Good catch about the stage access, I forgot that was also a factor in this setup! @dampgnat Not sure if you're accessing the stage in your Main.as constructor (or in classes it's initializing) and that's what causing it get stuck, but in our apps our Main.as constructor only has this:

if (stage) mainStartup();
else addEventListener(Event.ADDED_TO_STAGE, mainStartup);

And then the mainStartup function removes that event listener. This way, anything in Main.as or the rest of your project won't actually start running until the Preloader adds Main to the stage, and it has access to the stage.

FliplineStudios avatar Jun 24 '22 13:06 FliplineStudios

@FliplineStudios heheh... that ol' chestnut :) Yeah, that's in my code, and it's working fine on some Android devices, but not others. Still investigating...

dampgnat avatar Jun 24 '22 13:06 dampgnat

@ajwfrost I added your simplification, thanks!

While investigating, I've made a discovery (potentially related to this topic) regarding the performance issues mentioned above. Test builds on PC (be it a mobile or desktop project, on a very fast pc) perform worse when building a Debug SWF if the -swf-version=38 or above. (Release runs at full speed)

But when I run with the preloader, the fps really plummets. (Release runs at full speed)

I guess it's to be expected that Debug mode would naturally run a bit slower than Release, but to the extent of affecting movieclip playback on a modern pc? And only if using swf version 38 or above? But more to the point - the fact it performs this badly when adding the preloader might actually be useful information to you in this investigation? It runs fine in Release mode or using swf versions below 38.

dampgnat avatar Jun 24 '22 16:06 dampgnat

@dampgnat Interesting, I know we've stuck with using swf-version 37 in our own projects, because 38 was when Adobe switched PC builds to use Direct3D 11 instead of Direct3D 9, and for some reason AIR thinks all of my PCs don't support DirectX 11 so it falls back to software rendering. That's crazy that it gets even worse with the Preloader setup!

FliplineStudios avatar Jun 24 '22 17:06 FliplineStudios

@FliplineStudios hey, are you suggesting that AIR now harnesses the GPU for PC builds?? Or is that for stage3D/Starling only?

dampgnat avatar Jun 24 '22 17:06 dampgnat

@dampgnat I'm not entirely sure, at least it doesn't seem exactly the same as GPU mode on mobile -- but I do notice a performance increase on PC when setting renderMode to gpu/direct even with classic display list vector graphics, compared to cpu renderMode being sluggish.

FliplineStudios avatar Jun 24 '22 18:06 FliplineStudios

Please anyone knows how to do that inside Animate? How to specify this custom preloader?

megajogos avatar Jun 24 '22 18:06 megajogos

@megajogos honestly, using of Adobe Animate for AIR development is kind of rudimental, like Flash Builder. Maybe I am wrong, but it is pain to work with that IDE etc. From me, I can suggest switching to IntellijIDEA, and life will be much easier.

cleverbeapps avatar Jun 24 '22 18:06 cleverbeapps

We don't use Animate as IDE, but our CI uses Animate to build the swf and ADT to build the apps and we have almost 40 apps, so this CI is very important.

@ajwfrost do you know how to specify this custom preloader inside the Animate?

megajogos avatar Jun 24 '22 19:06 megajogos

@megajogos if you're using Animate anyway, which will generate a SWF with a main application already based on MovieClip, then you can set this up manually I would think? So just add a keyframe to the very start of your main timeline and set the document class to be the preloader .as file...

ajwfrost avatar Jun 25 '22 05:06 ajwfrost

@dampgnat belated response (I wrote it yesterday but forgot to hit the 'comment' button!)

I was going to ask, what renderMode are you running it in? cpu would always use cpu, but auto or gpu (or direct) would try to set up Direct3D, and in general it tries with Direct3D 11 now .. but yes the fallback appears to be to cpu rendering rather than to Direct3D 9.

Not sure why the use of a preloader makes any difference... we can look into that.

ajwfrost avatar Jun 25 '22 05:06 ajwfrost

@ajwfrost could you please give a link to ASC2.0 documentation that describes all compiler arguments, if there any?

cleverbeapps avatar Jun 25 '22 07:06 cleverbeapps

@ajwfrost interesting, my desktop project appears to have been running with GPU anyway, as my application descriptor didn't specify a renderMode and it clearly runs slower if I specify "cpu".

As a side note, I had no idea the application descriptor file was used when building/testing the SWF (I use Flashdevelop). I assumed that was only for packaging (like when packaging for mobile).

I've discovered that Flashdevelop (or flashplayer_32_sa_debug.exe) is what's causing the massive performance hit when running in Debug mode (much worse with preloader), as performance returns to normal when I close Flashdevelop while testing the SWF. The debugger clearly doesn't like swf version 38 and above.

Regarding the APK loading time issue - have there been any interesting findings with the APKs I sent?

dampgnat avatar Jun 28 '22 10:06 dampgnat

Hi @ajwfrost were the APKs I sent of any use? Or do you also need the SWF for analysis? The loading time issue has become a blocker for our client, so we're very keen to find a way a forward.

dampgnat avatar Jul 08 '22 09:07 dampgnat

Hi @dampgnat - apologies for the delay, we've had some other issues taking up a bit too much time to check on this. We're actually looking into building a preloader type capability into the runtime which should then help with masking the start-up but also depending on how it works, it may improve the overall time per the comments earlier here..

We can pull the SWF out of the APK though :-) so we have everything we need I think now.

thanks!

ajwfrost avatar Jul 08 '22 16:07 ajwfrost

Thanks for the update @ajwfrost! Fingers crossed that avoids the side effects caused by the current preloader workaround. Let me know if you need beta SDKs tested this end.

dampgnat avatar Jul 11 '22 09:07 dampgnat

Hi @ajwfrost I noticed 33.1.1.926 was released last week but with no mention of this issue. Is there a visible road-map for planned fixes? We're trying to manage client expectations on this.

dampgnat avatar Aug 01 '22 10:08 dampgnat