XReconnectionManager.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. package com.xzjmyk.pm.activity.xmpp;
  2. import android.content.Context;
  3. import android.text.TextUtils;
  4. import android.util.Log;
  5. import com.alibaba.fastjson.JSON;
  6. import com.alibaba.fastjson.JSONObject;
  7. import com.xzjmyk.pm.activity.MyApplication;
  8. import com.xzjmyk.pm.activity.bean.LoginAuto;
  9. import com.xzjmyk.pm.activity.helper.LoginHelper;
  10. import com.xzjmyk.pm.activity.sp.UserSp;
  11. import com.xzjmyk.pm.activity.ui.erp.util.LogUtil;
  12. import com.xzjmyk.pm.activity.util.DeviceInfoUtil;
  13. import com.xzjmyk.pm.activity.volley.Result;
  14. import org.jivesoftware.smack.AbstractConnectionListener;
  15. import org.jivesoftware.smack.XMPPConnection;
  16. import org.jivesoftware.smack.XMPPException.StreamErrorException;
  17. import org.jivesoftware.smack.packet.StreamError;
  18. import org.jivesoftware.smack.tcp.XMPPTCPConnection;
  19. import org.jivesoftware.smack.util.StringUtils;
  20. import java.io.DataOutputStream;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.net.HttpURLConnection;
  24. import java.net.URL;
  25. import java.util.Random;
  26. public class XReconnectionManager extends AbstractConnectionListener {
  27. // Holds the connection to the server
  28. private XMPPConnection mConnection;
  29. private boolean isReconnectionAllowed = false;
  30. private Thread mReconnectionThread;
  31. boolean mIsNetWorkActive;
  32. // Holds the state of the reconnection
  33. boolean doReconnecting = false;
  34. Context mContext;
  35. public XReconnectionManager(Context context, XMPPConnection connection, boolean reconnectionAllowed, boolean isNetWorkActive) {
  36. mContext = context;
  37. mConnection = connection;
  38. mConnection.addConnectionListener(this);
  39. isReconnectionAllowed = reconnectionAllowed;
  40. mIsNetWorkActive = isNetWorkActive;
  41. }
  42. /**
  43. * Returns true if the reconnection mechanism is enabled.
  44. *
  45. * @return true if automatic reconnections are allowed.
  46. */
  47. private boolean isReconnectionAllowed() {
  48. return doReconnecting && mIsNetWorkActive && !mConnection.isConnected() && isReconnectionAllowed;
  49. }
  50. public void setNetWorkState(boolean isNetWorkActive) {
  51. mIsNetWorkActive = isNetWorkActive;
  52. if (mIsNetWorkActive) {// 网络状态变为可用
  53. LogUtil.d("Xmpp","isReconnectionAllowed:"+isReconnectionAllowed()
  54. +" user:"+mConnection.getUser());
  55. if (isReconnectionAllowed()) {
  56. reconnect();
  57. }
  58. } else {
  59. if (mReconnectionThread != null && mReconnectionThread.isAlive()) {
  60. mReconnectionThread.interrupt();
  61. }
  62. }
  63. }
  64. /**
  65. * Starts a reconnection mechanism if it was configured to do that. The algorithm is been executed when the first connection error is detected.
  66. * <p/>
  67. * The reconnection mechanism will try to reconnect periodically in this way:
  68. * <ol>
  69. * <li>First it will try 6 times every 10 seconds.
  70. * <li>Then it will try 10 times every 1 minute.
  71. * <li>Finally it will try indefinitely every 5 minutes.
  72. * </ol>
  73. */
  74. private synchronized void reconnect() {
  75. if (this.isReconnectionAllowed()) {
  76. // Since there is no thread running, creates a new one to attempt
  77. // the reconnection.
  78. // avoid to run duplicated reconnectionThread -- fd: 16/09/2010
  79. if (mReconnectionThread != null && mReconnectionThread.isAlive()){
  80. LogUtil.d("Xmpp","mReconnectionThread 线程已存在");
  81. return;
  82. }else{
  83. LogUtil.d("Xmpp","mReconnectionThread 新线程:"+mConnection.getUser());
  84. }
  85. mReconnectionThread = new Thread() {
  86. private int mRandomBase = new Random().nextInt(11) + 5; // between 5 and 15 seconds
  87. /**
  88. * Holds the current number of reconnection attempts
  89. */
  90. private int attempts = 0;
  91. /**
  92. * Returns the number of seconds until the next reconnection attempt.
  93. *
  94. * @return the number of seconds until the next reconnection attempt.
  95. */
  96. private int timeDelay() {
  97. attempts++;
  98. if (attempts > 13) {
  99. return mRandomBase * 6 * 5; // between 2.5 and 7.5
  100. // minutes (~5 minutes)
  101. }
  102. if (attempts > 7) {
  103. return mRandomBase * 6; // between 30 and 90 seconds (~1
  104. // minutes)
  105. }
  106. return mRandomBase; // 10 seconds
  107. }
  108. /**
  109. * The process will try the reconnection until the connection succeed or the user cancel it
  110. */
  111. public void run() {
  112. // 重新连接之前,先检查Token状态
  113. int checkTokenStatus = 0;
  114. while (isReconnectionAllowed() && checkTokenStatus == 0) {
  115. checkTokenStatus = syncCheckToken();
  116. if (checkTokenStatus == 0) {// 表示检查失败,继续循环检查
  117. LogUtil.d("Token","0:表示检查失败,继续循环检查");
  118. try {
  119. Thread.sleep(5000);
  120. } catch (InterruptedException e) {
  121. e.printStackTrace();
  122. }
  123. } else if (checkTokenStatus == 1) {// 表示检查成功Token过期(或出现不能继续请求Token状态的异常),停止重新登陆
  124. doReconnecting = false;
  125. LogUtil.d("Token","1:表示检查成功Token过期");
  126. //TODO 关闭自动登录 可能出现账号异常情况
  127. //mConnection.login();
  128. // conflict();
  129. } else if (checkTokenStatus == 2) {// 2、表示检查成功,Token没有改变,可以继续下面的重新登陆
  130. LogUtil.d("Token","2:表示检查成功,Token没有改变,可以继续下面的重新登陆");
  131. break;
  132. }
  133. }
  134. while (isReconnectionAllowed()) {
  135. // Makes a reconnection attempt
  136. try {
  137. if (isReconnectionAllowed()) {
  138. mConnection.connect();
  139. }
  140. } catch (Exception e) {
  141. notifyReconnectionFailed(e);// Fires the failed reconnection notification
  142. }
  143. int remainingSeconds = timeDelay();
  144. while (isReconnectionAllowed() && remainingSeconds > 0) {
  145. try {
  146. Thread.sleep(1000);
  147. remainingSeconds--;
  148. notifyAttemptToReconnectIn(remainingSeconds);
  149. } catch (InterruptedException e1) {
  150. // Notify the reconnection has failed
  151. notifyReconnectionFailed(e1);
  152. }
  153. }
  154. }
  155. }
  156. };
  157. mReconnectionThread.setName("Smack XReconnectionManager");
  158. mReconnectionThread.setDaemon(true);
  159. mReconnectionThread.start();
  160. }
  161. }
  162. /**
  163. * 在检查Token的时候,发现冲突了
  164. */
  165. private void conflict() {
  166. ((CoreService) mContext).logout();
  167. LoginHelper.broadcastConflict(mContext);
  168. }
  169. /**
  170. * 检查Token状态,并返回int值<br/>
  171. * 0、表示检查失败,继续循环检查 <br/>
  172. * 1、表示检查成功Token过期(或出现不能继续请求Token状态的异常),停止重新登陆<br/>
  173. * 2、表示检查成功,Token没有改变,可以继续下面的重新登陆<br/>
  174. *
  175. * @return
  176. */
  177. private int syncCheckToken() {// 同步网络请求Token
  178. if (CoreService.DEBUG) {
  179. Log.d(CoreService.TAG, "开始重新登陆前的 Token 状态检查");
  180. String requestUrl = MyApplication.getInstance().getConfig().USER_LOGIN_AUTO;
  181. if (requestUrl == null) {
  182. return 1;
  183. }
  184. HttpURLConnection httpConn = null;
  185. DataOutputStream out = null;
  186. InputStream is = null;
  187. try {
  188. URL url = new URL(requestUrl);
  189. httpConn = (HttpURLConnection) url.openConnection();
  190. httpConn.setDoOutput(true);
  191. httpConn.setDoInput(true);
  192. httpConn.setConnectTimeout(5 * 1000);
  193. httpConn.setReadTimeout(5 * 1000);
  194. httpConn.setRequestMethod("POST");
  195. httpConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
  196. out = new DataOutputStream(httpConn.getOutputStream());
  197. // 参数
  198. String access_token = MyApplication.getInstance().mAccessToken;
  199. if (TextUtils.isEmpty(access_token)) {
  200. access_token = UserSp.getInstance(mContext).getAccessToken(null);
  201. }
  202. if (TextUtils.isEmpty(access_token)) {
  203. return 1;
  204. }
  205. String serial = DeviceInfoUtil.getDeviceId(mContext);
  206. if (TextUtils.isEmpty(serial)) {
  207. return 1;
  208. }
  209. if (CoreService.DEBUG) {
  210. Log.d(CoreService.TAG, "requestUrl:" + requestUrl);
  211. Log.d(CoreService.TAG, "access_token:" + access_token);
  212. Log.d(CoreService.TAG, "serial:" + serial);
  213. }
  214. StringBuilder sb = new StringBuilder();
  215. sb.append("access_token=" + access_token + "&");
  216. //String user = ((XMPPTCPConnection) mConnection).getDirectUser();
  217. LogUtil.d("wang",JSON.toJSONString(mConnection));
  218. String user =mConnection.getUser();
  219. Log.d("wang", "user..." + user);
  220. if (user == null) {
  221. return 1;
  222. }
  223. sb.append("userId=" + StringUtils.parseName(user) + "&");
  224. sb.append("serial=" + serial);
  225. out.write(sb.toString().getBytes("UTF-8"));
  226. out.flush();
  227. int statusCode = httpConn.getResponseCode();
  228. if (statusCode != 200) {
  229. return 0;
  230. }
  231. is = httpConn.getInputStream();
  232. if (is == null) {
  233. return 0;
  234. }
  235. StringBuffer buffer = new StringBuffer();
  236. int len = -1;
  237. byte[] data = new byte[1024];
  238. try {
  239. while ((len = is.read(data)) != -1) {
  240. buffer.append(new String(data, 0, len));
  241. }
  242. } catch (IOException e) {
  243. e.printStackTrace();
  244. }
  245. String result = buffer.toString();
  246. if (CoreService.DEBUG) {
  247. Log.d(CoreService.TAG, "检查状态result:" + result);
  248. }
  249. if (TextUtils.isEmpty(result)) {
  250. Log.d("wang", "result==null");
  251. return 0;
  252. }
  253. try {
  254. JSONObject jsonObject = JSON.parseObject(result);
  255. int resultCode = jsonObject.getIntValue(Result.RESULT_CODE);
  256. if (resultCode != 1) {
  257. return 0;
  258. }
  259. LoginAuto loginAuto = JSON.parseObject(jsonObject.getString(Result.DATA), LoginAuto.class);
  260. LogUtil.d("Token","loginAuto:"+JSON.toJSONString(loginAuto));
  261. if (loginAuto != null) {// 判断时候要继续重新登陆
  262. int tokenExists = loginAuto.getTokenExists();// 1=令牌存在、0=令牌不存在
  263. int serialStatus = loginAuto.getSerialStatus();// 1=没有设备号、2=设备号一致、3=设备号不一致
  264. if (serialStatus == 2) {// 设备号一致,说明没有切换过设备
  265. if (tokenExists == 1) {// Token存在,
  266. return 2;
  267. } else {// Token 不存在
  268. return 1;
  269. }
  270. } else {// 设备号不一致,那么就是切换过手机
  271. return 1;
  272. }
  273. } else {
  274. return 0;
  275. }
  276. } catch (Exception e) {
  277. e.printStackTrace();
  278. }
  279. } catch (Exception e) {
  280. e.printStackTrace();
  281. } finally {
  282. try {
  283. if (out != null) {
  284. out.close();
  285. }
  286. if (is != null) {
  287. is.close();
  288. }
  289. } catch (IOException e) {
  290. e.printStackTrace();
  291. }
  292. if (httpConn != null) {
  293. httpConn.disconnect();
  294. }
  295. }
  296. }
  297. return 0;
  298. }
  299. private void notifyReconnectionFailed(Exception exception) {
  300. if (isReconnectionAllowed()) {
  301. LogUtil.d("xmppLogs","notifyReconnectionFailed");
  302. // for (ConnectionListener listener : mConnection.getConnectionListeners()) {
  303. // listener.reconnectionFailed(exception);
  304. // }
  305. }
  306. }
  307. private void notifyAttemptToReconnectIn(int seconds) {
  308. if (isReconnectionAllowed()) {
  309. LogUtil.d("xmppLogs","notifyAttemptToReconnectIn");
  310. // for (ConnectionListener listener : mConnection.getConnectionListeners()) {
  311. // listener.reconnectingIn(seconds);
  312. // }
  313. }
  314. }
  315. void release() {
  316. doReconnecting = false;
  317. if (mReconnectionThread != null && mReconnectionThread.isAlive()) {
  318. mReconnectionThread.interrupt();
  319. }
  320. }
  321. @Override
  322. public void connectionClosed() {
  323. doReconnecting = false;
  324. }
  325. @Override
  326. public void connectionClosedOnError(Exception e) {
  327. LogUtil.d("reconnect","<<connectionClosedOnError>> mConnection:"+((XMPPTCPConnection)mConnection).getUser()
  328. +" isAuthenticated():"+mConnection.isAuthenticated());
  329. doReconnecting = true;
  330. if (e instanceof StreamErrorException) {// 有人重复登陆
  331. StreamErrorException xmppEx = (StreamErrorException) e;
  332. StreamError error = xmppEx.getStreamError();
  333. String reason = error.getCode();
  334. if ("conflict".equals(reason)) {// 发出下线通知
  335. if (CoreService.DEBUG)
  336. Log.d(CoreService.TAG, "异常断开,有另外设备登陆啦");
  337. conflict();
  338. doReconnecting = false;
  339. return;
  340. }
  341. }
  342. // 因为其他原因导致下线,那么就开始重连
  343. if (this.isReconnectionAllowed()) {
  344. if (CoreService.DEBUG)
  345. Log.d(CoreService.TAG, "异常断开,开始重连");
  346. this.reconnect();
  347. }
  348. }
  349. public XMPPConnection getmConnection() {
  350. return mConnection;
  351. }
  352. }