为什么调用外部 java 函数会导致我的应用程序崩溃? (亚马逊社交网络、S3)

Why does calling an external java function cause my app to crash? (Amazon SNS, S3)

我们正在组装一个使用 Amazon S3 和 SNS SDK 的 Unity 应用程序。他们自己工作正常 - 使用 SNS 只有应用程序正确注册和接收通知,使用 S3 我们可以很好地下载图像和视频。

将 SDK 合并到一个应用程序现在会导致问题,几个小时后我找到了导致崩溃的行:

GCM.cs

string regId = cls.CallStatic<string>("register",senderIds);

奇怪的是,此时 ADB logcat 没有给出任何错误,但是在此调用之前放置 return 之后,我们知道这是应用程序崩溃的地方。奇怪的是,推送注册过程在 ADB 中继续,同时显示 "This app has crashed" 消息,直到它成功完成,就好像应用程序正常启动一样...

被调用的"register"函数在AWSUnityGCMWrapper.java

public static String register(final String senderIds) {
    try {
        if (senderIds == null) {
            return "";
        }
        Activity activity = UnityPlayer.currentActivity;
        GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(activity);
        String regId = gcm.register(senderIds);
        storeRegistrationId(activity, regId);
        return regId;
    } catch (IOException e) {
        Log.e(TAG, "failed to register the to gcm");
        Log.e(TAG, "exception = " + e.getMessage());
    }
    return "";
}

尝试了 AndroidManifest.xml 的许多配置,但没有成功,除了 logcat 似乎一切正常! CallStatic returns 正确并且该过程继续进行,直到在崩溃弹出窗口上按下 'ok'...

知道为什么这个调用静态函数会导致这个问题吗?如果需要,请索取更多代码示例!

编辑:AndroidManifest.xml 如果有帮助...

<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.company.appname"
android:installLocation="preferExternal"
android:theme="@android:style/Theme.NoTitleBar"
android:versionCode="1"
android:versionName="1.0">

<uses-feature android:name="android.hardware.camera" />

<supports-screens
    android:smallScreens="true"
    android:normalScreens="true"
    android:largeScreens="true"
    android:xlargeScreens="true"
    android:anyDensity="true"/>

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- PUSH --><uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

<!-- PUSH --><permission android:name="com.company.appname.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<!-- PUSH --><uses-permission android:name="com.company.appname.permission.C2D_MESSAGE" />

<application
    android:icon="@drawable/app_icon"
    android:label="@string/app_name"
    android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
    android:debuggable="false">
    <activity android:name="com.unity3d.player.UnityPlayerNativeActivity"
              android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
        <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
        <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="false" />
    </activity>

    <activity android:name="com.unity3d.player.VideoPlayer"
              android:label="@string/app_name"
              android:screenOrientation="portrait"
              android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
    </activity>

  <!-- PUSH --><receiver
      android:name="com.company.appname.GCMBroadcastReceiver"
      android:permission="com.google.android.c2dm.permission.SEND" >
      <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
        <category android:name="com.company.appname" />
      </intent-filter>
    </receiver>

  <!-- PUSH --><service android:name="com.company.appname.GCMIntentService" />

    <!--
        To support devices using the TI S3D library for stereo mode we must 
        add the following library.
        Devices that require this are: ODG X6 
    -->
    <uses-library android:name="com.ti.s3d" android:required="false" />
    <!--
        To support the ODG R7 in stereo mode we must add the following library.
    -->
    <uses-library android:name="com.osterhoutgroup.api.ext" android:required="false" />
</application>

编辑 - AWSUnityGCMWrapper.java 使用 AsyncTask

public static String register(final String senderIds) {

    // Async Task
    _regID = "0";
    _done = false;

    new RegTask().execute(senderIds);

    while(!_done);

    return _regID;

}

public static void storeRegistrationId(Context context, String regId) {
    final SharedPreferences prefs = getGCMPreferences(context);
    int appVersion = getAppVersion(context);
    Log.i(TAG, "Saving regId on app version " + appVersion);
    SharedPreferences.Editor editor = prefs.edit();
    editor.putString(PROPERTY_REG_ID, regId);
    editor.putInt(PROPERTY_APP_VERSION, appVersion);
    editor.commit();
}

public static void setReg(String regID) {
    _done = true;
    _regID = regID;
}

public static String getReg() {
    return _regID;
}

private static class RegTask extends AsyncTask<String, Void, String> {

    String _regID;

    public RegTask() {
        super();
        _regID = "0";
    }

    @Override
    protected String doInBackground(String... params) {

        Log.i(TAG, ">>> Registering With GCM Async Task... Sender: " + params[0]);
        String senderIds = params[0];
        try {
            if (senderIds == null) {
                //_regID = "0";
                return "0";
            }
            Log.i(TAG, ">>> Trying Registration...");
            Activity activity = UnityPlayer.currentActivity;
            GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(activity);
            String regId = gcm.register(senderIds);
            Log.i(TAG, ">>> New Registration ID: " + regId);
            storeRegistrationId(activity, regId);
            //_regID = regId;
            return regId;
        } catch (IOException e) {
            Log.e(TAG, "failed to register the to gcm");
            Log.e(TAG, "exception = " + e.getMessage());
        }

        //_regID = "0";
        Log.i(TAG, ">>> Registering With GCM Async Task Done...");
        return "0";
    }

    @Override
    protected void onPostExecute(String result) {
        setReg(result);
    }
}

在Logcat

I/AWSUnityGCMWrapper(21896): >>> 正在注册 GCM 异步任务...发件人:750882817708 I/AWSUnityGCMWrapper(23338): >>> 新注册 ID: APA91bHWev8CCsbso8JE5a28J... I/AWSUnityGCMWrapper(21896):在应用程序版本 1 上保存 regId

当按下注册按钮时,应用程序仍然立即崩溃。

因此,根据我们在您 post 下的评论中的讨论,您注册 GCM 的代码应该是 运行 在 UI 线程中。也就是说,您应该将您的函数包装在 运行OnUiThread() 中,这是您通过以下方式完成的:

  1. 正在创建一个 Runnable class MyRunnable r = new MyRunnable(UnityPlayer.currentActivity, senderIds);

  2. 运行 是这样的: UnityPlayer.currentActivity.运行OnUiThread(r);

它所做的不再是崩溃,而是抛出异常(在您的日志中称为 MAIN_THREAD)。

你应该做的不是仅仅在你的 运行nable 函数中调用 .register(...) 在后台调用它,例如在 AsyncTask 中:

private void registerBackground(final String senderId) {
    new AsyncTask() {
        @Override
        protected String doInBackground(Void... params) {
            String msg = "";
            try {
                GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(UnityPlayer.currentActivity);


                String regId = gcm.register(senderId);
                storeRegistrationId(UnityPlayer.currentActivity, regId);
            } catch (IOException ex) {
                msg = "Error :" + ex.getMessage();
            }
            return msg;
        }

        @Override
        protected void onPostExecute(String msg) {
            // msg returned
        }
    }.execute(null, null, null);

这是因为 android 上的 UI 操作发生在主线程上,但网络必须在后台完成。

调用这个而不是你的注册函数,如果有帮助请告诉我。

编辑:

顺便说一句,您可能想要监听更改或以适当的方式等待它们,因为我可以看到您的静态 f-tion returns 字符串。

编辑2:

因此,在我的建议下,您已将其移至适当的异步任务。但是还有一个等待结果的问题。要做到这一点,你可以写这样的东西而不是一会儿:

Handler myHandler = new Handler() {

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case 0:
            _done = true;
            break;
        default:
            break;
        }
    }
};

也在你的任务的 onPostExecute() 中调用这个 myHandler.sendEmptyMessage(0);

然后,在您的函数中创建一个 运行nable 或另一个简单的服务员,而不是 while() 。但是现在,您可能想用 Thread.sleep(1000) 来测试它;在一段时间内减轻 UI.

上的负载

所以我决定恢复原来的状态并开始剥离 AndroidManifest.xml(其中有很多来自同事的东西似乎是多余的)。使用以下清单似乎已经解决了问题,一定是有什么冲突...

<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.company.appname"
  android:installLocation="preferExternal"
android:versionCode="1"
android:versionName="1.0">

<supports-screens
  android:smallScreens="true"
  android:normalScreens="true"
  android:largeScreens="true"
  android:xlargeScreens="true"
  android:anyDensity="true"/>

<uses-sdk android:minSdkVersion="9" />

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<permission android:name="com.amazonaws.unity.permission.C2D_MESSAGE"
  android:protectionLevel="signature" />
<uses-permission android:name="com.amazonaws.unity.permission.C2D_MESSAGE" />

<application
  android:theme="@android:style/Theme.NoTitleBar"
  android:icon="@drawable/app_icon"
  android:label="@string/app_name"
  android:debuggable="true">

<activity android:name="com.unity3d.player.UnityPlayerNativeActivity"
          android:label="@string/app_name">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
    <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
  </intent-filter>
  <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
  <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="false" />
</activity>

<receiver
     android:name="com.company.appname.GCMBroadcastReceiver"
    android:permission="com.google.android.c2dm.permission.SEND" >
  <intent-filter>
    <action android:name="com.google.android.c2dm.intent.RECEIVE" />
    <action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
    <category android:name="com.company.appname" />
  </intent-filter>
</receiver>

<service android:name="com.company.appname.GCMIntentService" />

我今天会做一些测试,并尝试找出到底是什么部分导致了问题。