我们正在构建一个使用 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() 中,您可以这样做:
创建 Runnable 类 MyRunnable r = new MyRunnable(UnityPlayer.currentActivity, senderIds);
像这样运行: 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/