XReconnectionManager.java 12 KB

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