为什么调用外部 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() 中,这是您通过以下方式完成的:
正在创建一个 Runnable class
MyRunnable r = new MyRunnable(UnityPlayer.currentActivity, senderIds);
运行 是这样的:
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" />
我今天会做一些测试,并尝试找出到底是什么部分导致了问题。
我们正在组装一个使用 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() 中,这是您通过以下方式完成的:
正在创建一个 Runnable class MyRunnable r = new MyRunnable(UnityPlayer.currentActivity, senderIds);
运行 是这样的: 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" />
我今天会做一些测试,并尝试找出到底是什么部分导致了问题。