I have made a game and released it on iOS and now I am trying to build it for Android and I can’t get it to run at all. I am using a 2013 Nexus 7 (the newest one) to test it on. When I run it I see the splash screen and then the screen just goes black. The app doesn’t crash, it just sits there showing a black screen. To test I have swapped out the first composer scene (“menu”) with a hello world scene that I have successful run standalone so I know the problem (or at least the first problem) is in my main file. See below the log output from the android device and my main file (apologies - its pretty messy because I am doing ads and notifications etc in there as well). Appreciate any help you can give me!
Log:
V/Corona (19307): > Class.forName: network.LuaLoader
V/Corona (19307): < Class.forName: network.LuaLoader
V/Corona (19307): Loading via reflection: network.LuaLoader
I/Corona (19307): Platform: Nexus 7 / ARM Neon / 4.3 / Adreno ™ 320 / OpenGL ES 3.0 V@14.0 AU@ (CL@) / 2014.2393
V/Corona (19307): > Class.forName: CoronaProvider.licensing.google.LuaLoader
V/Corona (19307): < Class.forName: CoronaProvider.licensing.google.LuaLoader
V/Corona (19307): Loading via reflection: CoronaProvider.licensing.google.LuaLoader
I/Corona (19307): [Lua::RuntimeDispatchEvent()] WARNING: This function is deprecated. Use Lua::DispatchRuntimeEvent() instead.
Main File:
display.setStatusBar(display.HiddenStatusBar) local composer = require( "composer") if "Android" == system.getInfo( "platformName" ) then return true else require('audio') if audio.supportsSessionProperty then audio.setSessionProperty(audio.MixMode, audio.AmbientMixMode) end end require( "ice" ) composer.multiTouch = require("dmc\_multitouch") --was composer.MultiTouch system.activate("multitouch") local gameNetwork = require "gameNetwork" local ads = require( "ads" ) local thisRate = ice:loadBox( "myRate" ); local analytics = require "analytics" local facebook = require( "facebook" ) local widget = require( "widget" ) local connection = require( "connection" ) if ( system.getInfo( "platformName" ) == "Android" ) then local store = require( "plugin.google.iap.v3" ) elseif ( system.getInfo( "platformName" ) == "iPhone OS" ) then local store = require( "store" ) end customVar = {} composer.volumeSwitch = thisRate:retrieve( "volumeSwitch" ); composer.currScene = composer.getSceneName("current") composer.gameOver = false composer.pushMessage = false composer.noAdsAdClicked = false composer.run = false composer.textScaleFactor = 2 composer.restart = false composer.firstRun = true composer.appSuspend = false composer.autoRestartNum = 0 composer.showingAd = false composer.reboot = false composer.popup = false composer.newStoreItem = false local chooseAd = 0 local cancelNotify local noAdsAd local adPlaceholder --[[if ( system.getInfo("environment") =="simulator" ) then local function monitorMem() collectgarbage() print( "MemUsage: " .. collectgarbage("count") ) local textMem = system.getInfo( "textureMemoryUsed" ) / 1000000 print( "TexMem: " .. textMem ) end Runtime:addEventListener("enterFrame", monitorMem) end]] function DidReceiveRemoteNotification(message, additionalData, isFirstOpen) analytics.logEvent( "Recieved GameThrive Push Notification" ) if (additionalData and additionalData.newStoreItem) then composer.pushMessage = message composer.newStoreItem = true end end local launchArgs = ... local GameThrive = require ( "plugin.GameThrivePushNotifications" ) GameThrive.HandleLaunchArgs(launchArgs, "###############", DidReceiveRemoteNotification) local mainGroup = display.newGroup() -- Main function local function main() io.output():setvbuf('no') analytics.init( "###############" ) facebook.publishInstall( "##############") --print(1/display.contentScaleX, 1/display.contentScaleY) composer.difficulty = thisRate:retrieve( "difficulty" ) if composer.difficulty == nil then composer.difficulty = 2 end composer.arLevel = thisRate:retrieve( "arLevel" ) if composer.arLevel == nil then composer.arLevel = 40 end composer.pause = thisRate:retrieve( "savedpause" ) if composer.pause == nil then composer.pause = false end composer.volumeSwitch = thisRate:retrieve( "volumeSwitch" ) if composer.volumeSwitch == nil then composer.volumeSwitch = true end composer.removeAdsIAP = thisRate:retrieve( "removeAdsIAP" ) if composer.removeAdsIAP == nil then composer.removeAdsIAP = false end composer.extraLifeIAP = thisRate:retrieve( "extraLifeIAP" ) if composer.extraLifeIAP == nil then composer.extraLifeIAP = false end composer.blueThemeIAP = thisRate:retrieve( "blueThemeIAP" ) if composer.blueThemeIAP == nil then composer.blueThemeIAP = false end customVar.blueThemeSelected = thisRate:retrieve( "blueThemeSelected" ) if customVar.blueThemeSelected == nil then customVar.blueThemeSelected = false end composer.purpleThemeIAP = thisRate:retrieve( "purpleThemeIAP" ) if composer.purpleThemeIAP == nil then composer.purpleThemeIAP = false end customVar.purpleThemeSelected = thisRate:retrieve( "purpleThemeSelected" ) if customVar.purpleThemeSelected == nil then customVar.purpleThemeSelected = false end composer.pinkThemeIAP = thisRate:retrieve( "pinkThemeIAP" ) if composer.pinkThemeIAP == nil then composer.pinkThemeIAP = false end customVar.pinkThemeSelected = thisRate:retrieve( "pinkThemeSelected" ) if customVar.pinkThemeSelected == nil then customVar.pinkThemeSelected = false end customVar.originalThemeSelected = thisRate:retrieve( "originalThemeSelected" ) if customVar.originalThemeSelected == nil then customVar.originalThemeSelected = false end if customVar.originalThemeSelected then customVar.selectedTheme = "Green" elseif customVar.blueThemeSelected then customVar.selectedTheme = "Blue" elseif customVar.purpleThemeSelected then customVar.selectedTheme = "Purple" elseif customVar.pinkThemeSelected then customVar.selectedTheme = "Pink" end local highestLevel = thisRate:retrieve( "highestLevel" ) if highestLevel == nil then highestLevel = 1 end composer.notificationOn = thisRate:retrieve( "notificationOn" ) if composer.notificationOn == nil then composer.notificationOn = false end customVar.numGames = thisRate:retrieve( "numGames" ) if customVar.numGames == nil then customVar.numGames = 0 end highScore = thisRate:retrieve( "highScore" ) if highScore == nil then highScore = 0 end lifeTimeBlocks = thisRate:retrieve( "lifeTimeBlocks" ) if lifeTimeBlocks == nil then lifeTimeBlocks = 0 end composer.showStartOptions = thisRate:retrieve( "showStartOptions" ) if composer.showStartOptions == nil then composer.showStartOptions = true end composer.switchState = thisRate:retrieve( "switchState" ) if composer.switchState == nil then composer.switchState = false end composer.comma = function(amount) local formatted = tostring(amount) while true do formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2') if (k==0) then break end end return formatted end local function switchOn() composer.showStartOptions = false thisRate:store( "showStartOptions",composer.showStartOptions ) composer.switchState = true thisRate:store( "switchState",composer.switchState ) thisRate:save() end local function switchOff() composer.showStartOptions = true thisRate:store( "showStartOptions",composer.showStartOptions ) composer.switchState = false thisRate:store( "switchState",composer.switchState ) thisRate:save() end composer.toggleStartOptions = function( event ) local switch = event.target if switch.isOn then switch:setState( { isOn=true, isAnimated=true, onComplete=switchOff } ) print(switch.isOn) else switch:setState( { isOn=false, isAnimated=true, onComplete=switchOn } ) print(switch.isOn) end end composer.localNotify = function() if composer.notificationOn then system.cancelNotification( ) composer.notificationOn = false thisRate:store("notificationOn",composer.notificationOn) thisRate:save() end local futureTime = 0 local randMessageNum = math.random(1,4 ) local badgeNum = 0 local runNotify = function() if randMessageNum \< 4 then randMessageNum = randMessageNum + 1 else randMessageNum = 1 end local randMessage futureTime = futureTime + (5 \* 24 \* 60 \* 60 ) -- 5 Days composer.notificationOn = true thisRate:store("notificationOn",composer.notificationOn) thisRate:save() customVar.numGames = thisRate:retrieve( "numGames" ) if customVar.numGames == nil then customVar.numGames = 0 end highScore = thisRate:retrieve( "highScore" ) if highScore == nil then highScore = 0 end lifeTimeBlocks = thisRate:retrieve( "lifeTimeBlocks" ) if lifeTimeBlocks == nil then lifeTimeBlocks = 0 end if customVar.numGames \> 0 then if randMessageNum == 1 then randMessage = "Play Jump Game again to see if you can beat your high score of "..composer.comma(highScore) elseif randMessageNum == 2 then randMessage = "The Jump Game blocks are getting lonely... You've jumped on "..composer.comma(lifeTimeBlocks).." of them already but they want more!" elseif randMessageNum == 3 then randMessage = "You've played "..composer.comma(customVar.numGames).." games of Jump Game. Time for one more?" elseif randMessageNum == 4 then if highestLevel == 1 then randMessage = "You haven't made it passed level 1 on Jump Game yet. Play now and get to the elevator blocks on level 2!" else randMessage = "You've made it to level "..highestLevel.." on Jump Game. Play now and see if you can get to level "..(highestLevel+1).."!" end end else if randMessageNum == 1 or randMessageNum == 3 then randMessage = "The Jump Game blocks are offended that you haven't finished a game yet. Come jump on them to make them feel better!" elseif randMessageNum == 2 or randMessageNum == 4 then randMessage = "You haven't made it passed level 1 on Jump Game yet. Play now and get to the elevator blocks on level 2!" end end if ( system.getInfo( "platformName" ) == "iPhone OS" ) then badgeNum = badgeNum +1 local options = { alert = randMessage, badge = badgeNum, sound = "alarm.caf", custom = { msg = "Alarm" } } local notificationID = system.scheduleNotification( futureTime, options ) else local options = { alert = randMessage, sound = "alarm.caf", custom = { msg = "Alarm" } } local notificationID = system.scheduleNotification( futureTime, options ) end end composer.timerStash.timer027 = timer.performWithDelay( 100, runNotify ) composer.timerStash.timer027 = timer.performWithDelay( 100, runNotify ) composer.timerStash.timer027 = timer.performWithDelay( 100, runNotify ) composer.timerStash.timer027 = timer.performWithDelay( 100, runNotify ) return true end noAdsAdGroup = display.newGroup( ) mainGroup:insert( noAdsAdGroup ) local function onRemoveAdsAd(event) if "clicked" == event.action then local i = event.index if 1 == i then analytics.logEvent( "Clicked NoAdsAd Banner, Yes" ) if store.isActive then if store.canMakePurchases then composer.showLoading() if ( store.availableStores.apple ) then store.purchase( {"01removeAds"} ) elseif ( store.availableStores.google ) then store.purchase( "01removeAds" ) end else local alert = native.showAlert( "Jump Game","In-app purchases are disabled in your device settings. Please enable and try again from the Jump Game menu settings.", { "Ok" }, addListeners ) return true end else local alert = native.showAlert( "Jump Game","You are not connected to the App Store. Please check your internet connection and try again from the Jump Game menu settings.", { "Ok" }, addListeners ) return true end elseif 2 == i then analytics.logEvent( "Clicked NoAdsAd Banner, No" ) return true end end end local function removeAdsAd(event) analytics.logEvent( "Clicked NoAdsAd Banner" ) local alert = native.showAlert( "Jump Game","Do you want remove all ads from Jump Game?", { "Yes Please!", "No thanks", }, onRemoveAdsAd ) return true end local function onNoAdsAdEvent(self) if composer.currScene ~= "game" then removeAdsAd() end end adPlaceholder = display.newRect( 0, display.contentHeight-100, display.contentWidth, 100 ) adPlaceholder:setFillColor( 0.1,0.1,0.1 ) adPlaceholder.anchorX = 0 adPlaceholder.anchorY = 0 adPlaceholder.alpha = 1 noAdsAdGroup:insert(adPlaceholder) noAdsAd = widget.newButton { id = "noAdsAd", defaultFile = "noAds.png", overFile = "noAdsOver.png", width = 640, height = 100, onRelease = onNoAdsAdEvent, } noAdsAd.x = display.contentWidth/2; noAdsAd.y = display.contentHeight-48 noAdsAd.name = "noAdsAd" noAdsAdGroup:insert(noAdsAd) noAdsAdGroup.alpha = 0 local function adListener4( event ) if ( event.isError ) and composer.removeAdsIAP == false then analytics.logEvent( "AdMob Error, Switching to NoAdsAd Banner" ) print("Switch to NoAdAd") noAdsAdGroup.alpha = 1 else analytics.logEvent( "Showing AdMob Banner" ) end return true end ads.init( "admob", "################" , adListener4 ) local function adListener2( event ) if ( event.isError ) and composer.removeAdsIAP == false then --composer.currScene ~= "game" and if composer.currScene ~= "game" then analytics.logEvent( "iAd Error, Switching to Admob Banner" ) --ads.hide() print("Switch to admob") ads:setCurrentProvider( "admob" ) ads.show( "banner", { x=0, y=display.contentHeight-50, appId="####################", testMode=false } ) else analytics.logEvent( "iAd Error, Switching to NoAdsAd Banner" ) noAdsAdGroup.alpha = 1 end else analytics.logEvent( "Showing iAd Banner" ) end return true end ads.init( "iads", "###########", adListener2 ) local function adListener( event ) if ( event.type == "adStart" and event.isError ) and composer.removeAdsIAP == false then analytics.logEvent( "Vungle Error, No Ad Served" ) elseif ( event.type == "adEnd" ) then analytics.logEvent( "Showing Vungle Fullscreen" ) customVar.time2 = 0 thisRate:store("time2",customVar.time2); thisRate:save(); end return true end ads.init( "vungle", "#############", adListener ) composer.showAd = function() if composer.removeAdsIAP == false then noAdsAdGroup.alpha = 0 ads.hide() ads:setCurrentProvider( "vungle" ) ads.show( "interstitial" ) else return true end end composer.showAd4 = function() if composer.removeAdsIAP == false then noAdsAdGroup.alpha = 0 ads.hide() ads:setCurrentProvider( "admob" ) ads.show( "banner", { x=0, y=display.contentHeight-50, appId="##################", testMode=false } ) else return true end end composer.showAd2 = function() if composer.removeAdsIAP == false then noAdsAdGroup.alpha = 0 ads.hide() ads:setCurrentProvider( "iads" ) ads.show( "banner", { x=0, y=display.contentHeight-50 } ) else return true end end composer.hideAd = function() ads.hide() noAdsAdGroup.alpha = 0 end composer.loadAd = function() ads.load( "interstitial", { appId="################", testMode=false } ) end composer.isAdLoaded = function() ads.isLoaded("interstitial") end -- function to listen for system events local function onSystemEvent( event ) if event.type == "applicationStart" then if ( system.getInfo( "platformName" ) == "iPhone OS" ) then gameNetwork.init( "gamecenter" native.setProperty( "applicationIconBadgeNumber", 0 ) end composer.localNotify() if composer.removeAdsIAP == false then if ( system.getInfo( "platformName" ) == "Android" ) then composer.showAd4() elseif ( system.getInfo( "platformName" ) == "iPhone OS" ) then composer.showAd2() end end return true elseif event.type == "applicationResume" then composer.localNotify() if composer.removeAdsIAP == false then if ( system.getInfo( "platformName" ) == "Android" ) then composer.showAd4() elseif ( system.getInfo( "platformName" ) == "iPhone OS" ) then composer.showAd2() end end return true end end Runtime:addEventListener( "system", onSystemEvent ) local function myUnhandledErrorListener( event ) analytics.logEvent( "Unhandled Error", { errorMessage=event.errorMessage, stackTrace=event.stackTrace } ) end Runtime:addEventListener("unhandledError", myUnhandledErrorListener) local spinner local loadingOverlay local function loadingOverlayEvent(event) return true end loadingOverlay = display.newRect( display.contentWidth/2, display.contentHeight/2, display.contentWidth, display.contentHeight ) loadingOverlay.alpha = 0 loadingOverlay:setFillColor(0,0,0) mainGroup:insert(loadingOverlay) local options = { width = 128, height = 128, numFrames = 1, sheetContentWidth = 128, sheetContentHeight = 128 } local spinnerSingleSheet = graphics.newImageSheet( "loading.png", options ) -- Create the widget spinner = widget.newSpinner { width = 112, height = 112, sheet = spinnerSingleSheet, startFrame = 1, deltaAngle = 10, incrementEvery = 20 } spinner.alpha = 0 spinner.x = display.contentWidth/2 spinner.y = display.contentHeight/2 composer.showLoading = function() display.getCurrentStage():setFocus( loadingOverlay ) loadingOverlay:addEventListener( "touch", loadingOverlayEvent ) transition.to (loadingOverlay, {time=500, alpha=0.6} ) transition.to (spinner, {time=500, alpha=1} ) spinner:start() end composer.endLoading = function() transition.to (loadingOverlay, {time=500, alpha=0} ) transition.to (spinner, {time=500, alpha=0} ) spinner:stop() loadingOverlay:removeEventListener( "touch", loadingOverlayEvent ) display.getCurrentStage():setFocus( nil ) end isDebug = true local options = { effect = "fade", time = 500, } composer.gotoScene("menu", options) return true end composer.timerStash = {} composer.trans = {} composer.gt = {} composer.cancelAllTimers = function() local k, v for k,v in pairs(composer.timerStash) do timer.cancel( v ) v = nil; k = nil end composer.timerStash = nil composer.timerStash = {} end composer.pauseAllTimers = function() local k, v for k,v in pairs(composer.timerStash) do timer.pause( v ) end end composer.resumeAllTimers = function() local k, v for k,v in pairs(composer.timerStash) do timer.resume( v ) end end -- composer.pauseAllTransitions = function() local k, v for k,v in pairs(composer.trans) do transition.pause( v ) end end composer.cancelAllTransitions = function() local k, v for k,v in pairs(composer.trans) do transition.cancel( v ) v = nil; k = nil end composer.trans = nil composer.trans = {} end --cancel all gtweens composer.cancelAllTweens = function() local k, v for k,v in pairs(composer.gt) do v:pause(); v = nil; k = nil end composer.gt = nil composer.gt = {} end main()