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

标签 android amazon-web-services unity-game-engine push-notification google-cloud-messaging

我们正在构建一个使用 Amazon S3 和 SNS SDK 的 Unity 应用程序。它们本身工作得很好 - 通过 SNS,只有应用程序可以正确注册并接收通知,通过 S3,我们可以很好地下载图像和视频。

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

GCM.cs

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

奇怪的是,ADB logcat 此时没有给出任何错误,但在调用之前和之后输入返回值后,就知道这是应用程序崩溃的地方。奇怪的是,推送注册过程在 ADB 中继续,同时显示“此应用程序已崩溃”消息,直到成功完成,就像应用程序正常启动一样......

被调用的“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 正确返回,并且该过程仍然继续,直到在崩溃弹出窗口上按下“确定”...

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

编辑: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>

编辑 - 使用 AsyncTask 的 AWSUnityGCMWrapper.java

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

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

最佳答案

因此,根据我们在您帖子下的评论中的讨论,您注册 GCM 的代码应该在 UI 线程中运行。也就是说,您应该将函数包装在 runOnUiThread() 中,您可以这样做:

  1. 创建 Runnable 类 MyRunnable r = new MyRunnable(UnityPlayer.currentActivity, senderIds);

  2. 像这样运行: UnityPlayer.currentActivity.runOnUiThread(r);

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

您应该做的不是仅仅在可运行函数中调用 .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 返回字符串。

编辑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);

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

关于android - 为什么调用外部 java 函数会导致我的应用程序崩溃? (亚马逊 SNS、S3),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33762218/

相关文章:

android - Robotium 能否测试应用内计费、社交分享、 Intent 等功能?

Android Studio Gradle 构建失败。错误 :Cause: peer not authenticated

amazon-web-services - 成功 sls 删除后,所有 sls 命令上都是 "Stack with id X does not exist"

c# - Unity 中的 3D 物理 compass (处理旋转)

c# - 统一生成对象

android - 是否可以将 VoiP 调用转发到 GSM

Android:为什么不让每个方法都同步?

amazon-web-services - CloudFormation 查看非事件/已删除的更改集

ios - AWS Cognito 用户池 - "Value null at ' userName' 未能满足约束 : Member must not be null"- iOS

unity-game-engine - 四元数 * Vector3 * 距离