LibGDX Android 只申请一次 运行

LibGDX Android Application only running once

我无法弄清楚为什么我的 LibGDX 游戏在 Android.

上仅 运行 成功一次

假设游戏还没有安装在设备上,当我 运行 从 eclipse 应用程序时,游戏 运行 没问题。关闭游戏后,并尝试通过在我的 phone 上打开游戏(不是通过 eclipse)再次 运行 游戏,它似乎永远挂在这个屏幕上:

根据我的测试,我发现我的 Android 启动器代码中的 none 被执行,.onCreate() 从未被调用。 LogCat 显示没有抛出任何错误,应用程序似乎只加载了 LibGDX 库,然后什么也没有。这是整个 LogCat 输出:http://i.imgur.com/5DQ5sHY.png 在 LogCat 中唯一让我大吃一惊的是:

Launch timeout has expired, giving up wake lock!
Activity idle timeout for ActivityRecord

有时清除设备上游戏的缓存和数据似乎可以暂时解决问题,但并非总是如此。

这是Android启动器class:

package com.ifs_studios.cheekychameleon.android;

import java.util.HashMap;

import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.Toast;

import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdSize;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.InterstitialAd;
import com.google.android.gms.analytics.GoogleAnalytics;
import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.Logger.LogLevel;
import com.google.android.gms.analytics.Tracker;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.games.Games;
import com.google.android.gms.plus.Plus;
import com.ifs_studios.cheekychameleon.ActionResolver;
import com.ifs_studios.cheekychameleon.CheekyChameleon;

/**
 * MemoryBlox Android Launcher.
 * 
 * @author BleedObsidian (Jesse Prescott)
 */
public class AndroidLauncher extends AndroidApplication implements ActionResolver, ConnectionCallbacks, OnConnectionFailedListener {
    /**
     * Banner Advert AdMob key.
     */
    private static final String BANNER_ADMOB_UNIT_ID= "XXXXX";

    /**
     * Fullscreen Advert AdMob key.
     */
    private static final String FULLSCREEN_ADMOB_UNIT_ID= "XXXXX";

    /**
     * Google API Client.
     */
    private GoogleApiClient googleApiClient;

    /**
     * If currently resolving a sign in error.
     */
    private boolean isResolvingSignInError;

    /**
     * Global Leaderboard ID.
     */
    private static final String LEADERBOARD_ID = "XXXXX";

    /**
     * Unique request resolve error ID.
     */
    private static final int REQUEST_RESOLVE_ERROR = 1267;

    /**
     * Unique request for achievements ID.
     */
    private static final int REQUEST_ACHIEVEMENTS = 1268;

    /**
     * Unique request for leaderboards ID.
     */
    private static final int REQUEST_LEADERBOARD = 1269;

    /**
     * Google Analytics Trackers.
     */
    private HashMap<TrackerName, Tracker> trackers = new HashMap<TrackerName, Tracker>();

    /**
     * Google Analytics App Tracker.
     */
    private Tracker appTracker;

    /**
     * Google Analytics Global Tracker.
     */
    private Tracker globalTracker;

    /**
     * Google Analytics Ecommerce Tracker.
     */
    private Tracker ecommerceTracker;

    /**
     * If the game has been started.
     */
    private boolean isStarted = false;

    /**
     * Advertisement View.
     */
    protected AdView adView;

    /**
     * Interstitial Advertisement.
     */
    protected InterstitialAd interstitialAd;

    /**
     * Game View.
     */
    protected View gameView;

    @Override
    protected void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        this.requestWindowFeature(Window.FEATURE_NO_TITLE);

        this.setTitle("Cheeky Chameleon");

        this.appTracker = this.getTracker(TrackerName.APP_TRACKER);
        this.globalTracker = this.getTracker(TrackerName.GLOBAL_TRACKER);
        this.ecommerceTracker = this.getTracker(TrackerName.ECOMMERCE_TRACKER);

        this.appTracker.enableAdvertisingIdCollection(true);
        this.globalTracker.enableAdvertisingIdCollection(true);
        this.ecommerceTracker.enableAdvertisingIdCollection(true);

        AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
        config.useAccelerometer = false;
        config.useCompass = false;
        config.useWakelock = true;
        config.hideStatusBar = true;
        config.useImmersiveMode = true;

        RelativeLayout layout = new RelativeLayout(this);

        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        this.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);

        this.createInterstitialAd();

        this.createAdView();
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
        params.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
        layout.addView(this.adView, params);

        this.createGameView(config);
        layout.addView(this.gameView);

        this.setContentView(layout);
        this.startAdvertising();
    }

    @Override
    public void onStart() {
        super.onStart();
        GoogleAnalytics.getInstance(this).reportActivityStart(this);
        GoogleAnalytics.getInstance(this).getLogger()
        .setLogLevel(LogLevel.INFO);

        this.googleApiClient = new GoogleApiClient.Builder(this)
        .addApi(Plus.API).addScope(Plus.SCOPE_PLUS_LOGIN)
        .addApi(Games.API).addScope(Games.SCOPE_GAMES)
        .addConnectionCallbacks(this)
        .addOnConnectionFailedListener(this)
        .build();
    }

    @Override
    public void onStop() {
        super.onStop();
        System.out.println("Stop");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        GoogleAnalytics.getInstance(this).reportActivityStop(this);
        this.googleApiClient.disconnect();
        System.out.println("Destroy");
    }

    /**
     * Create AdView.
     * 
     * @return AdView.
     */
    private void createAdView() {
        this.adView = new AdView(this);
        this.adView.setAdSize(AdSize.SMART_BANNER);
        this.adView.setAdUnitId(AndroidLauncher.BANNER_ADMOB_UNIT_ID);
        this.adView.setId(12398);
        this.adView.setBackgroundColor(Color.WHITE);
    }

    /**
     *  Create fullscreen AdView.
     */
    private void createInterstitialAd() {
        this.interstitialAd = new InterstitialAd(AndroidLauncher.this);
        this.interstitialAd.setAdUnitId(AndroidLauncher.FULLSCREEN_ADMOB_UNIT_ID);

        AdRequest adRequest = new AdRequest.Builder().build();
        this.interstitialAd.loadAd(adRequest);
    }

    @Override
    public void loadInterstitialAdvert() {
        AdRequest adRequest = new AdRequest.Builder().build();
        interstitialAd.loadAd(adRequest);
    }

    /**
     * Create GameView.
     * 
     * @param config AndroidApplicationConfiguration.
     * @return View.
     */
    private void createGameView(AndroidApplicationConfiguration config) {
        this.gameView = this.initializeForView(new CheekyChameleon(this), config);

        this.gameView.setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);

        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        params.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
        params.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
        params.addRule(RelativeLayout.ABOVE, adView.getId());
        this.gameView.setLayoutParams(params);
    }

    /**
     * Start Advertising.
     * 
     * @param adView AdView.
     */
    private void startAdvertising() {
        AdRequest.Builder adRequestBuilder = new AdRequest.Builder();
           AdRequest adRequest = adRequestBuilder.build();
           adView.setAdListener(new AdListener() {
               @Override
               public void onAdFailedToLoad(int error) {
                    System.out.println("Error: " + error);
               }
           });
           adView.loadAd(adRequest);

    }

    @Override
    public void setTrackerScreenName(String name) {
        this.globalTracker.setScreenName(name);
        this.globalTracker.send(new HitBuilders.AppViewBuilder().build());
        System.out.println("Tracking screen: " + name);
    }

    /**
     * Get google analytics tracker.
     * 
     * @param trackerName TrackerName.
     * @return Tracker.
     */
    public synchronized Tracker getTracker(TrackerName trackerName) {
        if (!this.trackers.containsKey(trackerName)) {
            GoogleAnalytics analytics = GoogleAnalytics.getInstance(this);

            Tracker tracker = (trackerName == TrackerName.APP_TRACKER) ? analytics
                    .newTracker(R.xml.app_tracker)
                    : (trackerName == TrackerName.GLOBAL_TRACKER) ? analytics
                            .newTracker(R.xml.global_tracker) : analytics
                            .newTracker(R.xml.ecommerce_tracker);
            tracker.enableAdvertisingIdCollection(true);
            this.trackers.put(trackerName, tracker);
        }

        return this.trackers.get(trackerName);
    }


    /**
     * Enum used to identify the tracker that needs to be used for tracking.
     * 
     * A single tracker is usually enough for most purposes. In case you do need
     * multiple trackers, storing them all in Application object helps ensure
     * that they are created only once per application instance.
     */
    public enum TrackerName {
        APP_TRACKER, // Tracker used only in this app.
        GLOBAL_TRACKER, // Tracker used by all the apps from a company. eg: roll-up tracking.
        ECOMMERCE_TRACKER, // Tracker used by all ecommerce transactions from a company.
    }


    @Override
    public void displayInterstitialAdvert() {
        if(interstitialAd.isLoaded()) {
            this.interstitialAd.show();
        } else {
            AdRequest adRequest = new AdRequest.Builder().build();
            this.interstitialAd.loadAd(adRequest);
            this.interstitialAd.show();
        }
    }

    @Override
    public void signInGooglePlayServices() {
        if(!this.isResolvingSignInError) {
            this.googleApiClient.connect();
        }
    }

    @Override
    public void signOutGooglePlayServices() {
        this.googleApiClient.disconnect();
    }

    @Override
    public boolean isSignedInGooglePlayServices() {
        return this.googleApiClient.isConnected();
    }

    @Override
    public void displayAchievements() {
        if(this.googleApiClient.isConnected()) {
            this.startActivityForResult(Games.Achievements.getAchievementsIntent(this.googleApiClient), AndroidLauncher.REQUEST_ACHIEVEMENTS);
        } else {
            this.googleApiClient.connect();
        }
    }

    @Override
    public void displayLeaderboards() {
        if(this.googleApiClient.isConnected()) {
            this.startActivityForResult(Games.Leaderboards.getLeaderboardIntent(this.googleApiClient,
                    AndroidLauncher.LEADERBOARD_ID), AndroidLauncher.REQUEST_LEADERBOARD);
        } else {
            this.googleApiClient.connect();
        }
    }

    @Override
    public void submitScore(int score) {
        if(this.googleApiClient.isConnected()) {
            System.out.println("Submiting score: " + score);
            Games.Leaderboards.submitScore(this.googleApiClient, AndroidLauncher.LEADERBOARD_ID, score);
        }
    }

    @Override
    public void onConnectionFailed(ConnectionResult result) {
        if(this.isResolvingSignInError) {
            return;
        } else if (result.hasResolution()){
            try {
                this.isResolvingSignInError = true;
                result.startResolutionForResult(this, AndroidLauncher.REQUEST_RESOLVE_ERROR);
                System.out.println("Failed to connect to Google Play Services, Resolving");
            } catch(SendIntentException exception) {
                this.googleApiClient.connect();
            }
        } else {
            GooglePlayServicesUtil.showErrorDialogFragment(result.getErrorCode(), this, AndroidLauncher.REQUEST_RESOLVE_ERROR);
            this.isResolvingSignInError = false;
            System.out.println("Failed to connect to Google Play Services: " + result.getErrorCode());
        }
    }

    @Override
    public void onConnected(Bundle bundle) {
        System.out.println("Connected to Google Play Services");
    }

    @Override
    public void onConnectionSuspended(int number) {
        System.out.println("Google Play Services connection suspended");
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == AndroidLauncher.REQUEST_RESOLVE_ERROR) {
            this.isResolvingSignInError = false;

            if (resultCode == RESULT_OK) {
                if (!this.googleApiClient.isConnecting() &&
                        !this.googleApiClient.isConnected()) {
                    this.googleApiClient.connect();
                }
            } else {
                System.out.println("Sign in failed: " + resultCode);
            }
        } else if(requestCode == AndroidLauncher.REQUEST_ACHIEVEMENTS) {
            if (resultCode != RESULT_OK && resultCode != RESULT_CANCELED) {
                Toast.makeText(this, "Failed to show achivements", Toast.LENGTH_SHORT).show();
                System.out.println("Failed to display achievements.");
            }
        }
    }
}

这是我的 AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ifs_studios.cheekychameleon.android"
    android:versionCode="29"
    android:versionName="0.4.1" >

    <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="21" />

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="com.android.vending.BILLING" />
    <uses-permission android:name="android.permission.VIBRATE"/>
    <uses-permission android:name="android.permission.VIBRATE"/>
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
    <uses-permission android:name="com.google.android.providers.gsf.permission.WRITE_GSERVICES"/>


    <application
        android:allowBackup="false"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name">
        <activity
            android:name="com.ifs_studios.cheekychameleon.android.AndroidLauncher"
            android:label="@string/app_name" 
            android:screenOrientation="portrait"
            android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name="com.google.android.gms.ads.AdActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>

        <meta-data android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />
        <meta-data
        android:name="com.google.android.gms.analytics.globalConfigResource"
        android:resource="@xml/global_tracker" />
        <meta-data android:name="com.google.android.gms.games.APP_ID"
        android:value="@string/app_id" />
    </application>
</manifest>

根据 LogCat 的输出,我不知道问题出在哪里。我已经在 Xperia U 和 Xperia Z1 上对此进行了测试,但问题仍然存在。感谢您的帮助。

编辑 - 额外信息:

我正在使用 LibGDX (1.5.4)

的最新版本

经过几天的紧张,我终于解决了这个问题。在主线程上执行回溯后,我发现程序在尝试加载 Google Analytics 跟踪器时停止了。这是线程回溯:

at com.google.android.gms.analytics.ae.getLogger(Unknown Source)
at com.google.android.gms.analytics.ae.W(Unknown Source)
at com.google.android.gms.analytics.z$a.f(Unknown Source) at com.google.android.gms.analytics.n.a(Unknown Source)
at com.google.android.gms.analytics.n.x(Unknown Source)
at com.google.android.gms.analytics.GoogleAnalytics.eZ(Unknown Source)
at com.google.android.gms.analytics.GoogleAnalytics.(Unknown Source)
at com.google.android.gms.analytics.GoogleAnalytics.(Unknown Source)
at com.google.android.gms.analytics.GoogleAnalytics.getInstance(Unknown Source)

然后我对这个结果做了一些研究。这似乎是尝试从 xml 文件加载跟踪器时导致的错误。此处更详细地讨论了此错误:

为了防止这个错误,我不再通过删除 AndroidManifest.xml:

中的这一行来从相应的 xml 文件中加载跟踪器
<meta-data 
        android:name="com.google.android.gms.analytics.globalConfigResource"
        android:resource="@xml/analytics_global_config" />

问题已解决,谢谢大家。