应用冻结、中断和强制关闭 "Uncaught remote exception!"
App freezing, breaking and forcing to close by "Uncaught remote exception!"
我一直在使用 react-native
和 socket.io
开发套接字应用程序。
我使用 AccountManager
和 Authenticator
将用户凭证存储在 Android。
在 Android 中,我收听了 error
(又名 onError
)事件。
当套接字连接意外关闭时error
事件被触发。
如果 error
包含 EngineIOException: websocket error
文本,我将启动一个处理程序 postDelayed 以再次连接到套接字服务器。
不幸的是,应用程序在 运行 postDelayed
代码时冻结。然后一个对话框 windows 出现,其中包括 App isn't responding - Do you want to close it? - [Wait] [OK]
.
如果我按下 [OK]
按钮,几秒钟后会出现另一个消息框。
我试了一整天,但找不到解决方案。
有没有机会解决这个问题?
补充信息
- 我试图在
Android
上实现典型的 Autheticator
。我有 Authenticator.java
、AuthenticatorActivity.java
、AuthenticatorService.java
和 APIServerAuthenticate.java
文件来连接套接字服务器。我使用 Activity
和 Intent
而没有 Android View
classes。
- 它始终停止
getAuthToken()
方法的 Bundle results = future.getResult();
行。永远不要传递到下一行。我发现通过使用 Log.d()
方法。
ADB logcat 在应用程序崩溃后显示以下消息。
I/WindowState( 394): WIN DEATH: Window{52a4aef8 u0 com.rnnativemodules/com.rnnativemodules.MainActivity}
D/dalvikvm( 394): GC_CONCURRENT freed 1912K, 26% free 10997K/14820K, paused 2ms+2ms, total 104ms
W/EGL_genymotion( 690): eglSurfaceAttrib not implemented
D/MobileDataStateTracker( 394): default: setPolicyDataEnable(enabled=true)
I/qtaguid ( 394): Failed write_ctrl(s 0 10083) res=-1 errno=1
W/NetworkManagementSocketTagger( 394): setKernelCountSet(10083, 0) failed with errno -1
I/qtaguid ( 394): Failed write_ctrl(s 1 10023) res=-1 errno=1
W/NetworkManagementSocketTagger( 394): setKernelCountSet(10023, 1) failed with errno -1
W/InputMethodManagerService( 394): Got RemoteException sending setActive(false) notification to pid 16331 uid 10083
D/Sensors ( 394): Client connection accepted (187)
E/Genymotion( 394): Could not open '/sys/class/power_supply/genymotion_fake_path/present'
D/dalvikvm(16391): Late-enabling CheckJNI
I/ActivityManager( 394): Start proc com.rnnativemodules for service com.rnnativemodules/com.customemodule.AuthenticatorService: pid=16391 uid=10083 gids={50083, 3003, 1015, 1028}
W/dalvikvm(16391): Exception Ljava/lang/RuntimeException; thrown while initializing Lcom/facebook/react/bridge/ReactBridge;
W/dalvikvm(16391): Exception Ljava/lang/ExceptionInInitializerError; thrown while initializing Lcom/facebook/react/bridge/NativeMap;
E/JavaBinder(16391): *** Uncaught remote exception! (Exceptions are not yet supported across processes.)
E/JavaBinder(16391): java.lang.ExceptionInInitializerError
E/JavaBinder(16391): at com.facebook.react.bridge.NativeMap.<clinit>(NativeMap.java:22)
E/JavaBinder(16391): at com.customemodule.APIServerAuthenticate.signIn(APIServerAuthenticate.java:56)
E/JavaBinder(16391): at com.customemodule.Authenticator.getAuthToken(Authenticator.java:79)
E/JavaBinder(16391): at android.accounts.AbstractAccountAuthenticator$Transport.getAuthToken(AbstractAccountAuthenticator.java:196)
E/JavaBinder(16391): at android.accounts.IAccountAuthenticator$Stub.onTransact(IAccountAuthenticator.java:113)
E/JavaBinder(16391): at android.os.Binder.execTransact(Binder.java:388)
E/JavaBinder(16391): at dalvik.system.NativeStart.run(Native Method)
E/JavaBinder(16391): Caused by: java.lang.RuntimeException: SoLoader.init() not yet called
E/JavaBinder(16391): at com.facebook.soloader.SoLoader.assertInitialized(SoLoader.java:234)
E/JavaBinder(16391): at com.facebook.soloader.SoLoader.loadLibrary(SoLoader.java:169)
E/JavaBinder(16391): at com.facebook.react.bridge.ReactBridge.staticInit(ReactBridge.java:39)
E/JavaBinder(16391): at com.facebook.react.bridge.ReactBridge.<clinit>(ReactBridge.java:31)
E/JavaBinder(16391): ... 7 more
W/dalvikvm(16391): threadid=9: thread exiting with uncaught exception (group=0xa4bff648)
E/AndroidRuntime(16391): FATAL EXCEPTION: Binder_1
E/AndroidRuntime(16391): java.lang.ExceptionInInitializerError
E/AndroidRuntime(16391): at com.facebook.react.bridge.NativeMap.<clinit>(NativeMap.java:22)
E/AndroidRuntime(16391): at com.customemodule.APIServerAuthenticate.signIn(APIServerAuthenticate.java:56)
E/AndroidRuntime(16391): at com.customemodule.Authenticator.getAuthToken(Authenticator.java:79)
E/AndroidRuntime(16391): at android.accounts.AbstractAccountAuthenticator$Transport.getAuthToken(AbstractAccountAuthenticator.java:196)
E/AndroidRuntime(16391): at android.accounts.IAccountAuthenticator$Stub.onTransact(IAccountAuthenticator.java:113)
E/AndroidRuntime(16391): at android.os.Binder.execTransact(Binder.java:388)
E/AndroidRuntime(16391): at dalvik.system.NativeStart.run(Native Method)
E/AndroidRuntime(16391): Caused by: java.lang.RuntimeException: SoLoader.init() not yet called
E/AndroidRuntime(16391): at com.facebook.soloader.SoLoader.assertInitialized(SoLoader.java:234)
E/AndroidRuntime(16391): at com.facebook.soloader.SoLoader.loadLibrary(SoLoader.java:169)
E/AndroidRuntime(16391): at com.facebook.react.bridge.ReactBridge.staticInit(ReactBridge.java:39)
E/AndroidRuntime(16391): at com.facebook.react.bridge.ReactBridge.<clinit>(ReactBridge.java:31)
E/AndroidRuntime(16391): ... 7 more
我的 class 喜欢以下内容:
public class SocketIO {
final String TAG = "!NativeModules";
final String TAG2 = this.getClass().getSimpleName();
final String ERR_TOKEN = "ERR_TOKEN";
final String ERR_NO_AUTH_HEADER = "ERR_NO_AUTH_HEADER";
private final String SERVER_URL = "http://192.168.1.25:3443";
private final String mAccountType = "api.awesome.mobile";
private final String mAuthTokenType = "FULL_ACCESS";
private final String[] mAuthToken = new String[1];
Boolean areEventsRegistered = false;
// TODO: mActivity and mContext must be changed with Service's Activity and Context
private Activity mActivity;
private ReactApplicationContext mContext;
public SocketIO(ReactApplicationContext context) {
this.mContext = context;
}
private Socket mSocket;
{
try {
IO.Options opts = new IO.Options();
opts.reconnectionDelay = 2000;
opts.reconnectionDelayMax = 14000;
// opts.randomizationFactor = 0.8;
mSocket = IO.socket(SERVER_URL, opts);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
public Socket getSocket() {
return mSocket;
}
/**
* This method is used for to prevent register the same Listener multiple times.
*/
private void registerEvents () {
if (areEventsRegistered) {
return;
}
// Setting the request header!
mSocket.io().on(Manager.EVENT_TRANSPORT, onTransport);
mSocket.on(Socket.EVENT_CONNECT, onConnect);
mSocket.on(Socket.EVENT_DISCONNECT, onDisconnect);
mSocket.on(Socket.EVENT_ERROR, onError);
mSocket.on(Socket.EVENT_CONNECT_ERROR, onConnectError);
mSocket.on(Socket.EVENT_RECONNECT, onReconnect);
mSocket.on(Socket.EVENT_RECONNECT_ERROR, onReconnectError);
areEventsRegistered = true;
}
private void deregisterEvents () {
if (!areEventsRegistered) {
return;
}
mSocket.io().off(Manager.EVENT_TRANSPORT, onTransport);
mSocket.off(Socket.EVENT_CONNECT, onConnect);
mSocket.off(Socket.EVENT_DISCONNECT, onDisconnect);
mSocket.off(Socket.EVENT_ERROR, onError);
mSocket.off(Socket.EVENT_CONNECT_ERROR, onConnectError);
mSocket.off(Socket.EVENT_RECONNECT, onReconnect);
mSocket.off(Socket.EVENT_RECONNECT_ERROR, onReconnectError);
areEventsRegistered = false;
}
public void connect (Activity activity) {
mActivity = activity;
WritableMap results = getAuthToken(mAccountType, mAuthTokenType);
if (results.hasKey(AccountManager.KEY_AUTHTOKEN)) {
mAuthToken[0] = results.getString(AccountManager.KEY_AUTHTOKEN);
registerEvents();
mSocket.connect();
} else {
/**
* ERROR HANDLING
*/
String errorCode = results.getString(AccountManager.KEY_ERROR_CODE);
String errorMessage = results.getString(AccountManager.KEY_ERROR_MESSAGE);
Log.d(TAG, TAG2 + " ---> connect - errorCode: " + errorCode);
/**
* If the error code E_FAILED_TO_CONNECT it means the server is down or cannot
* be reached at the moment. Try again after 30 seconds.
*/
if (errorCode.equals(APIServerAuthenticate.ERROR_FAILED_TO_CONNECT_CODE)) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
connect(mActivity);
}
}, 30000);
}
}
}
public void disconnect (final Activity activity) {
mActivity = activity;
deregisterEvents();
mSocket.disconnect();
}
private Emitter.Listener onTransport = new Emitter.Listener() {
@Override
public void call(Object... args) {
Transport transport = (Transport)args[0];
transport.on(Transport.EVENT_REQUEST_HEADERS, new Emitter.Listener() {
@Override
public void call(Object... args) {
@SuppressWarnings("unchecked")
Map<String, List<String>> headers = (Map<String, List<String>>)args[0];
if (mAuthToken[0] != null) {
String bearer = "Bearer " + mAuthToken[0];
headers.put("Authorization", Arrays.asList(bearer));
}
}
}).on(Transport.EVENT_RESPONSE_HEADERS, new Emitter.Listener() {
@Override
public void call(Object... args) {
}
});
}
};
private Emitter.Listener onConnect = new Emitter.Listener() {
@Override
public void call(final Object... args) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
};
private Emitter.Listener onError = new Emitter.Listener() {
@Override
public void call(final Object... args) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
// Important!
disconnect(mActivity);
String error = args[0].toString();
if (error.equals(ERR_TOKEN)) {
/**
* If the token sent is invalid the auth server returns ERR_TOKEN error.
* Because of that remove the current authtoken from AccountManager cache.
* Then try to connect again.
*/
if (invalidateAuthToken(mAccountType, mAuthTokenType)) {
connect(mActivity);
}
}
else if (error.contains("EngineIOException: websocket error")) {
/**
* If the socket connection is closed unexpectedly.
* Socket returns an error as "io.socket.engineio.client.EngineIOException:
* websocket error". Try again to connect to the server after 30 seconds.
*/
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
connect(mActivity);
}
}, 30000);
return;
}
}
});
}
};
private Emitter.Listener onConnectError = new Emitter.Listener() {
@Override
public void call(final Object... args) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
};
private Emitter.Listener onReconnect = new Emitter.Listener() {
@Override
public void call(final Object... args) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
};
private Emitter.Listener onReconnectError = new Emitter.Listener() {
@Override
public void call(final Object... args) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
};
/**
* Get the authorization token from the account manager
* @param accountType
* @param authTokenType
*/
public WritableMap getAuthToken(String accountType, String authTokenType) {
AccountManager mAccountManager = AccountManager.get(mContext);
// Finished flag
final boolean[] isFinished = new boolean[1];
final WritableMap response = new WritableNativeMap();
final Account availableAccounts[] = mAccountManager.getAccountsByType(accountType);
if (availableAccounts.length == 0) {
response.putString(AccountManager.KEY_ERROR_CODE, AccountManagerModule.ERROR_NO_ACCOUNT_CODE);
response.putString(AccountManager.KEY_ERROR_MESSAGE, AccountManagerModule.ERROR_NO_ACCOUNT_MESSAGE);
isFinished[0] = true;
} else {
final Account account = availableAccounts[0];
// TODO: activity below must be changed with service activity `getCurrentActivity()``
final AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(account, authTokenType, null, mActivity, null, null);
new Thread(new Runnable() {
@Override
public void run() {
try {
Bundle results = future.getResult();
final String authToken = results.getString(AccountManager.KEY_AUTHTOKEN);
// If no error message
if (results.getString(AccountManager.KEY_ERROR_MESSAGE) == null) {
if (authToken == null) {
response.putString(AccountManager.KEY_ERROR_CODE, AccountManagerModule.ERROR_NO_TOKEN_CODE);
response.putString(AccountManager.KEY_ERROR_MESSAGE, AccountManagerModule.ERROR_NO_TOKEN_MESSAGE);
isFinished[0] = true;
} else {
response.putString(AccountManager.KEY_AUTHTOKEN, results.getString(AccountManager.KEY_AUTHTOKEN));
isFinished[0] = true;
}
} else {
response.putString(AccountManager.KEY_ERROR_CODE, results.getString(AccountManager.KEY_ERROR_CODE));
response.putString(AccountManager.KEY_ERROR_MESSAGE, results.getString(AccountManager.KEY_ERROR_MESSAGE));
isFinished[0] = true;
}
} catch (Exception e) {
e.printStackTrace();
response.putString(AccountManager.KEY_ERROR_CODE, AccountManagerModule.ERROR_EXCEPTION_CODE);
response.putString(AccountManager.KEY_ERROR_MESSAGE, e.getMessage());
isFinished[0] = true;
}
}
}).start();
}
//wait for response
while (isFinished[0] == false) {
try{
Thread.sleep(150);
} catch (Exception e) {}
}
return response;
}
/**
* Removes an auth token from the AccountManager's cache. Does nothing if the auth token is not currently in the cache.
* Applications must call this method when the auth token is found to have expired or otherwise become invalid for
* authenticating requests. The AccountManager does not validate or expire cached auth tokens otherwise.
*
* @param accountType
* @param authTokenType
*/
public boolean invalidateAuthToken(String accountType, String authTokenType) {
final AccountManager mAccountManager = AccountManager.get(mContext);
// Finished flag
final boolean[] isFinished = new boolean[1];
final boolean[] response = new boolean[1];
response[0] = false;
// getting the first relevant account!
Account accounts[] = mAccountManager.getAccountsByType(accountType);
if(accounts.length == 0) {
// throw new Exception(AccountManagerModule.ERROR_NO_ACCOUNT_MESSAGE);
Log.d(TAG, TAG2 + " ! " + AccountManagerModule.ERROR_NO_ACCOUNT_MESSAGE);
} else {
final Account account = accounts[0];
final AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(account, authTokenType, null, mActivity, null,null);
new Thread(new Runnable() {
@Override
public void run() {
try {
Bundle bundle = future.getResult();
final String authtoken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
mAccountManager.invalidateAuthToken(account.type, authtoken);
response[0] = true;
isFinished[0] = true;
} catch (Exception e) {
e.printStackTrace();
isFinished[0] = true;
}
}
}).start();
}
//wait for finishing the ops.
while (isFinished[0] == false) {
try{
Thread.sleep(100);
} catch (Exception e) {}
}
return response[0];
}
}
当通过用户交互启动上面的代码时,new Thread
或 AsynkTask
与用于休眠 Thread
的 while
循环一起正常工作。
但是,当 运行 系统交互代码时,应用程序被冻结。未响应。
我的解决方法是将具有异步任务或创建新线程的代码(在本例中为 getAuthToken()
内容)移动到相关方法中。
然后我删除了 while
循环以等待异步结果。我在异步部分指导了我的应用程序。
对我有用。
我一直在使用 react-native
和 socket.io
开发套接字应用程序。
我使用 AccountManager
和 Authenticator
将用户凭证存储在 Android。
在 Android 中,我收听了 error
(又名 onError
)事件。
当套接字连接意外关闭时error
事件被触发。
如果 error
包含 EngineIOException: websocket error
文本,我将启动一个处理程序 postDelayed 以再次连接到套接字服务器。
不幸的是,应用程序在 运行 postDelayed
代码时冻结。然后一个对话框 windows 出现,其中包括 App isn't responding - Do you want to close it? - [Wait] [OK]
.
如果我按下 [OK]
按钮,几秒钟后会出现另一个消息框。
我试了一整天,但找不到解决方案。
有没有机会解决这个问题?
补充信息
- 我试图在
Android
上实现典型的Autheticator
。我有Authenticator.java
、AuthenticatorActivity.java
、AuthenticatorService.java
和APIServerAuthenticate.java
文件来连接套接字服务器。我使用Activity
和Intent
而没有 AndroidView
classes。 - 它始终停止
getAuthToken()
方法的Bundle results = future.getResult();
行。永远不要传递到下一行。我发现通过使用Log.d()
方法。
ADB logcat 在应用程序崩溃后显示以下消息。
I/WindowState( 394): WIN DEATH: Window{52a4aef8 u0 com.rnnativemodules/com.rnnativemodules.MainActivity}
D/dalvikvm( 394): GC_CONCURRENT freed 1912K, 26% free 10997K/14820K, paused 2ms+2ms, total 104ms
W/EGL_genymotion( 690): eglSurfaceAttrib not implemented
D/MobileDataStateTracker( 394): default: setPolicyDataEnable(enabled=true)
I/qtaguid ( 394): Failed write_ctrl(s 0 10083) res=-1 errno=1
W/NetworkManagementSocketTagger( 394): setKernelCountSet(10083, 0) failed with errno -1
I/qtaguid ( 394): Failed write_ctrl(s 1 10023) res=-1 errno=1
W/NetworkManagementSocketTagger( 394): setKernelCountSet(10023, 1) failed with errno -1
W/InputMethodManagerService( 394): Got RemoteException sending setActive(false) notification to pid 16331 uid 10083
D/Sensors ( 394): Client connection accepted (187)
E/Genymotion( 394): Could not open '/sys/class/power_supply/genymotion_fake_path/present'
D/dalvikvm(16391): Late-enabling CheckJNI
I/ActivityManager( 394): Start proc com.rnnativemodules for service com.rnnativemodules/com.customemodule.AuthenticatorService: pid=16391 uid=10083 gids={50083, 3003, 1015, 1028}
W/dalvikvm(16391): Exception Ljava/lang/RuntimeException; thrown while initializing Lcom/facebook/react/bridge/ReactBridge;
W/dalvikvm(16391): Exception Ljava/lang/ExceptionInInitializerError; thrown while initializing Lcom/facebook/react/bridge/NativeMap;
E/JavaBinder(16391): *** Uncaught remote exception! (Exceptions are not yet supported across processes.)
E/JavaBinder(16391): java.lang.ExceptionInInitializerError
E/JavaBinder(16391): at com.facebook.react.bridge.NativeMap.<clinit>(NativeMap.java:22)
E/JavaBinder(16391): at com.customemodule.APIServerAuthenticate.signIn(APIServerAuthenticate.java:56)
E/JavaBinder(16391): at com.customemodule.Authenticator.getAuthToken(Authenticator.java:79)
E/JavaBinder(16391): at android.accounts.AbstractAccountAuthenticator$Transport.getAuthToken(AbstractAccountAuthenticator.java:196)
E/JavaBinder(16391): at android.accounts.IAccountAuthenticator$Stub.onTransact(IAccountAuthenticator.java:113)
E/JavaBinder(16391): at android.os.Binder.execTransact(Binder.java:388)
E/JavaBinder(16391): at dalvik.system.NativeStart.run(Native Method)
E/JavaBinder(16391): Caused by: java.lang.RuntimeException: SoLoader.init() not yet called
E/JavaBinder(16391): at com.facebook.soloader.SoLoader.assertInitialized(SoLoader.java:234)
E/JavaBinder(16391): at com.facebook.soloader.SoLoader.loadLibrary(SoLoader.java:169)
E/JavaBinder(16391): at com.facebook.react.bridge.ReactBridge.staticInit(ReactBridge.java:39)
E/JavaBinder(16391): at com.facebook.react.bridge.ReactBridge.<clinit>(ReactBridge.java:31)
E/JavaBinder(16391): ... 7 more
W/dalvikvm(16391): threadid=9: thread exiting with uncaught exception (group=0xa4bff648)
E/AndroidRuntime(16391): FATAL EXCEPTION: Binder_1
E/AndroidRuntime(16391): java.lang.ExceptionInInitializerError
E/AndroidRuntime(16391): at com.facebook.react.bridge.NativeMap.<clinit>(NativeMap.java:22)
E/AndroidRuntime(16391): at com.customemodule.APIServerAuthenticate.signIn(APIServerAuthenticate.java:56)
E/AndroidRuntime(16391): at com.customemodule.Authenticator.getAuthToken(Authenticator.java:79)
E/AndroidRuntime(16391): at android.accounts.AbstractAccountAuthenticator$Transport.getAuthToken(AbstractAccountAuthenticator.java:196)
E/AndroidRuntime(16391): at android.accounts.IAccountAuthenticator$Stub.onTransact(IAccountAuthenticator.java:113)
E/AndroidRuntime(16391): at android.os.Binder.execTransact(Binder.java:388)
E/AndroidRuntime(16391): at dalvik.system.NativeStart.run(Native Method)
E/AndroidRuntime(16391): Caused by: java.lang.RuntimeException: SoLoader.init() not yet called
E/AndroidRuntime(16391): at com.facebook.soloader.SoLoader.assertInitialized(SoLoader.java:234)
E/AndroidRuntime(16391): at com.facebook.soloader.SoLoader.loadLibrary(SoLoader.java:169)
E/AndroidRuntime(16391): at com.facebook.react.bridge.ReactBridge.staticInit(ReactBridge.java:39)
E/AndroidRuntime(16391): at com.facebook.react.bridge.ReactBridge.<clinit>(ReactBridge.java:31)
E/AndroidRuntime(16391): ... 7 more
我的 class 喜欢以下内容:
public class SocketIO {
final String TAG = "!NativeModules";
final String TAG2 = this.getClass().getSimpleName();
final String ERR_TOKEN = "ERR_TOKEN";
final String ERR_NO_AUTH_HEADER = "ERR_NO_AUTH_HEADER";
private final String SERVER_URL = "http://192.168.1.25:3443";
private final String mAccountType = "api.awesome.mobile";
private final String mAuthTokenType = "FULL_ACCESS";
private final String[] mAuthToken = new String[1];
Boolean areEventsRegistered = false;
// TODO: mActivity and mContext must be changed with Service's Activity and Context
private Activity mActivity;
private ReactApplicationContext mContext;
public SocketIO(ReactApplicationContext context) {
this.mContext = context;
}
private Socket mSocket;
{
try {
IO.Options opts = new IO.Options();
opts.reconnectionDelay = 2000;
opts.reconnectionDelayMax = 14000;
// opts.randomizationFactor = 0.8;
mSocket = IO.socket(SERVER_URL, opts);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
public Socket getSocket() {
return mSocket;
}
/**
* This method is used for to prevent register the same Listener multiple times.
*/
private void registerEvents () {
if (areEventsRegistered) {
return;
}
// Setting the request header!
mSocket.io().on(Manager.EVENT_TRANSPORT, onTransport);
mSocket.on(Socket.EVENT_CONNECT, onConnect);
mSocket.on(Socket.EVENT_DISCONNECT, onDisconnect);
mSocket.on(Socket.EVENT_ERROR, onError);
mSocket.on(Socket.EVENT_CONNECT_ERROR, onConnectError);
mSocket.on(Socket.EVENT_RECONNECT, onReconnect);
mSocket.on(Socket.EVENT_RECONNECT_ERROR, onReconnectError);
areEventsRegistered = true;
}
private void deregisterEvents () {
if (!areEventsRegistered) {
return;
}
mSocket.io().off(Manager.EVENT_TRANSPORT, onTransport);
mSocket.off(Socket.EVENT_CONNECT, onConnect);
mSocket.off(Socket.EVENT_DISCONNECT, onDisconnect);
mSocket.off(Socket.EVENT_ERROR, onError);
mSocket.off(Socket.EVENT_CONNECT_ERROR, onConnectError);
mSocket.off(Socket.EVENT_RECONNECT, onReconnect);
mSocket.off(Socket.EVENT_RECONNECT_ERROR, onReconnectError);
areEventsRegistered = false;
}
public void connect (Activity activity) {
mActivity = activity;
WritableMap results = getAuthToken(mAccountType, mAuthTokenType);
if (results.hasKey(AccountManager.KEY_AUTHTOKEN)) {
mAuthToken[0] = results.getString(AccountManager.KEY_AUTHTOKEN);
registerEvents();
mSocket.connect();
} else {
/**
* ERROR HANDLING
*/
String errorCode = results.getString(AccountManager.KEY_ERROR_CODE);
String errorMessage = results.getString(AccountManager.KEY_ERROR_MESSAGE);
Log.d(TAG, TAG2 + " ---> connect - errorCode: " + errorCode);
/**
* If the error code E_FAILED_TO_CONNECT it means the server is down or cannot
* be reached at the moment. Try again after 30 seconds.
*/
if (errorCode.equals(APIServerAuthenticate.ERROR_FAILED_TO_CONNECT_CODE)) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
connect(mActivity);
}
}, 30000);
}
}
}
public void disconnect (final Activity activity) {
mActivity = activity;
deregisterEvents();
mSocket.disconnect();
}
private Emitter.Listener onTransport = new Emitter.Listener() {
@Override
public void call(Object... args) {
Transport transport = (Transport)args[0];
transport.on(Transport.EVENT_REQUEST_HEADERS, new Emitter.Listener() {
@Override
public void call(Object... args) {
@SuppressWarnings("unchecked")
Map<String, List<String>> headers = (Map<String, List<String>>)args[0];
if (mAuthToken[0] != null) {
String bearer = "Bearer " + mAuthToken[0];
headers.put("Authorization", Arrays.asList(bearer));
}
}
}).on(Transport.EVENT_RESPONSE_HEADERS, new Emitter.Listener() {
@Override
public void call(Object... args) {
}
});
}
};
private Emitter.Listener onConnect = new Emitter.Listener() {
@Override
public void call(final Object... args) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
};
private Emitter.Listener onError = new Emitter.Listener() {
@Override
public void call(final Object... args) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
// Important!
disconnect(mActivity);
String error = args[0].toString();
if (error.equals(ERR_TOKEN)) {
/**
* If the token sent is invalid the auth server returns ERR_TOKEN error.
* Because of that remove the current authtoken from AccountManager cache.
* Then try to connect again.
*/
if (invalidateAuthToken(mAccountType, mAuthTokenType)) {
connect(mActivity);
}
}
else if (error.contains("EngineIOException: websocket error")) {
/**
* If the socket connection is closed unexpectedly.
* Socket returns an error as "io.socket.engineio.client.EngineIOException:
* websocket error". Try again to connect to the server after 30 seconds.
*/
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
connect(mActivity);
}
}, 30000);
return;
}
}
});
}
};
private Emitter.Listener onConnectError = new Emitter.Listener() {
@Override
public void call(final Object... args) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
};
private Emitter.Listener onReconnect = new Emitter.Listener() {
@Override
public void call(final Object... args) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
};
private Emitter.Listener onReconnectError = new Emitter.Listener() {
@Override
public void call(final Object... args) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
};
/**
* Get the authorization token from the account manager
* @param accountType
* @param authTokenType
*/
public WritableMap getAuthToken(String accountType, String authTokenType) {
AccountManager mAccountManager = AccountManager.get(mContext);
// Finished flag
final boolean[] isFinished = new boolean[1];
final WritableMap response = new WritableNativeMap();
final Account availableAccounts[] = mAccountManager.getAccountsByType(accountType);
if (availableAccounts.length == 0) {
response.putString(AccountManager.KEY_ERROR_CODE, AccountManagerModule.ERROR_NO_ACCOUNT_CODE);
response.putString(AccountManager.KEY_ERROR_MESSAGE, AccountManagerModule.ERROR_NO_ACCOUNT_MESSAGE);
isFinished[0] = true;
} else {
final Account account = availableAccounts[0];
// TODO: activity below must be changed with service activity `getCurrentActivity()``
final AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(account, authTokenType, null, mActivity, null, null);
new Thread(new Runnable() {
@Override
public void run() {
try {
Bundle results = future.getResult();
final String authToken = results.getString(AccountManager.KEY_AUTHTOKEN);
// If no error message
if (results.getString(AccountManager.KEY_ERROR_MESSAGE) == null) {
if (authToken == null) {
response.putString(AccountManager.KEY_ERROR_CODE, AccountManagerModule.ERROR_NO_TOKEN_CODE);
response.putString(AccountManager.KEY_ERROR_MESSAGE, AccountManagerModule.ERROR_NO_TOKEN_MESSAGE);
isFinished[0] = true;
} else {
response.putString(AccountManager.KEY_AUTHTOKEN, results.getString(AccountManager.KEY_AUTHTOKEN));
isFinished[0] = true;
}
} else {
response.putString(AccountManager.KEY_ERROR_CODE, results.getString(AccountManager.KEY_ERROR_CODE));
response.putString(AccountManager.KEY_ERROR_MESSAGE, results.getString(AccountManager.KEY_ERROR_MESSAGE));
isFinished[0] = true;
}
} catch (Exception e) {
e.printStackTrace();
response.putString(AccountManager.KEY_ERROR_CODE, AccountManagerModule.ERROR_EXCEPTION_CODE);
response.putString(AccountManager.KEY_ERROR_MESSAGE, e.getMessage());
isFinished[0] = true;
}
}
}).start();
}
//wait for response
while (isFinished[0] == false) {
try{
Thread.sleep(150);
} catch (Exception e) {}
}
return response;
}
/**
* Removes an auth token from the AccountManager's cache. Does nothing if the auth token is not currently in the cache.
* Applications must call this method when the auth token is found to have expired or otherwise become invalid for
* authenticating requests. The AccountManager does not validate or expire cached auth tokens otherwise.
*
* @param accountType
* @param authTokenType
*/
public boolean invalidateAuthToken(String accountType, String authTokenType) {
final AccountManager mAccountManager = AccountManager.get(mContext);
// Finished flag
final boolean[] isFinished = new boolean[1];
final boolean[] response = new boolean[1];
response[0] = false;
// getting the first relevant account!
Account accounts[] = mAccountManager.getAccountsByType(accountType);
if(accounts.length == 0) {
// throw new Exception(AccountManagerModule.ERROR_NO_ACCOUNT_MESSAGE);
Log.d(TAG, TAG2 + " ! " + AccountManagerModule.ERROR_NO_ACCOUNT_MESSAGE);
} else {
final Account account = accounts[0];
final AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(account, authTokenType, null, mActivity, null,null);
new Thread(new Runnable() {
@Override
public void run() {
try {
Bundle bundle = future.getResult();
final String authtoken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
mAccountManager.invalidateAuthToken(account.type, authtoken);
response[0] = true;
isFinished[0] = true;
} catch (Exception e) {
e.printStackTrace();
isFinished[0] = true;
}
}
}).start();
}
//wait for finishing the ops.
while (isFinished[0] == false) {
try{
Thread.sleep(100);
} catch (Exception e) {}
}
return response[0];
}
}
当通过用户交互启动上面的代码时,new Thread
或 AsynkTask
与用于休眠 Thread
的 while
循环一起正常工作。
但是,当 运行 系统交互代码时,应用程序被冻结。未响应。
我的解决方法是将具有异步任务或创建新线程的代码(在本例中为 getAuthToken()
内容)移动到相关方法中。
然后我删除了 while
循环以等待异步结果。我在异步部分指导了我的应用程序。
对我有用。