博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
AndroidPN系列之三--服务端Bug汇总
阅读量:6220 次
发布时间:2019-06-21

本文共 15532 字,大约阅读时间需要 51 分钟。

hot3.png

服务端重启客户端不重启就无法连接上的BUG

private void addTask(Runnable runnable) {               Log.d(LOGTAG, "addTask(runnable)...");               taskTracker.increase();               synchronized (taskList) {                   if (taskList.isEmpty() && !running) {                       running = true;                      futureTask = taskSubmitter.submit(runnable);                      if (futureTask == null) {                          taskTracker.decrease();                       }                   } else {                   //解决服务器端重启后,客户端不能成功连接androidpn服务器                   runTask();                       taskList.add(runnable);                   }               }               Log.d(LOGTAG, "addTask(runnable)... done");          }

把客户端的随机生成的UUID代码修改为自己应用的用户名密码

将org.androidpn.client.XmppManager中

final String newUsername = newRandomUUID();      final String newPassword = newRandomUUID();

修改为自己应用的用户名密码

把客户端的随机生成的UUID代码,改成把设备的id或者mac(device/mac)作为用户名,会出现重复插入的错误.

修改服务端org.androidpn.server.service.impl. UserServiceImpl这个类

public User saveUser(User user) throws UserExistsException {              try {                  //判断用户是否存在                  user = getUserByUsername(user.getUsername());                                } catch (DataIntegrityViolationException e) {                  e.printStackTrace();                  log.warn(e.getMessage());                  throw new UserExistsException("User '" + user.getUsername()                          + "' already exists!");              } catch (EntityExistsException e) { // needed for JPA                  e.printStackTrace();                  log.warn(e.getMessage());                  throw new UserExistsException("User '" + user.getUsername()                          + "' already exists!");              }catch (UserNotFoundException e) {                      return userDao.saveUser(user);              }              return user;          }

我这里采用捕获异常的方法

 

推送多次只显示最后一次的的问题:

 

修改客户端Notifier类中notify方法中的PendingIntent.getActivity方法的第二个参数每次不同就可以解决了,我是定义一个静态变量后每次+1解决。

PendingIntent contentIntent = PendingIntent.getActivity(context, notifyNum++,intent, PendingIntent.FLAG_UPDATE_CURRENT);

服务器连接不上出现xmpp connection failed xmpp502错误:

搞了半天经别别人提示才发现服务端的5222端口没开,根本ping不上

最后让服务端的人员把端口打开下解决了。

服务器中文乱码:

发送方先

String title = URLEncoder.encode("通知","UTF-8");  title = StringUtils.replace(title, "%", "@$");

接收方在org.androidpn.server.console.controller.NotificationController中

String title = ServletRequestUtils.getStringParameter(request, "title");      title = StringUtils.replace(title, "@$", "%");

已经登陆过一次,更换用户登陆但是显示登陆者还是第一次登陆的用户:

将org.androidpn.client.XmppManager中

username = sharedPrefs.getString(Constants.XMPP_USERNAME, "");      password = sharedPrefs.getString(Constants.XMPP_PASSWORD, "");

换成自己的用户名和密码,然后在程序启动service的地方判断下,因为我的应用是登陆后启动的service,所以登录需要判断下原来的service是否启动,如果已经启动了就要关掉下再开,否则无法更新用户

/**          * 启动推送相关服务          * @Description:               * @return void          */          private void startAndroidpnServer(){              serviceManager = new ServiceManager(this);              serviceManager.setNotificationIcon(R.drawable.ic_launcher);              RunningServiceInfo runningServiceInfo = ServiceUtil.isServiceRunning(MainActivity.this, "org.androidpn.client.NotificationService");              if(runningServiceInfo == null){                  serviceManager.startService();              }else{                  // 获得该Service的组件信息 可能是pkgname/servicename                  ComponentName serviceCMP = runningServiceInfo.service;                  // 设置该service的组件信息                  Intent intent = new Intent();                  intent.setComponent(serviceCMP);                  stopService(intent);                  serviceManager.startService();              }          }          /**          * 判断某个service是否启动          * @Description:           * @param mContext          * @param className          * @return              * @return 如果存在返回service否则返回null          */          public static ActivityManager.RunningServiceInfo isServiceRunning(Context mContext,String className) {                    ActivityManager activityManager = (ActivityManager)              mContext.getSystemService(Context.ACTIVITY_SERVICE);               List
serviceList = activityManager.getRunningServices(30); if (!(serviceList.size()>0)) { return null; } ActivityManager.RunningServiceInfo runningService = null; for (int i=0; i

离线推送功能:

 

主要用到org.androidpn.server.dao.,

org.androidpn.server.dao

org.androidpn.server.model

org.androidpn.server.service.impl

org.androidpn.server.service

这5个包用来存储离线数据,配置-config.xml,hibernate.cfg.xml,这里代码太多又很简单,照着它原来的例子写就好了,这里就不贴了。

修改org.androidpn.server.xmpp.push.NotificationManager,在session没连上时候将数据存入数据库

public void sendBroadcast(String apiKey, String title, String message,                  String uri) {              log.debug("sendBroadcast()...");                            //给所有注册过的用户发送消息              IQ notificationIQ = createNotificationIQ(apiKey, title, message, uri);              UserService userService = ServiceLocator.getUserService();              for (User user : userService.getUsers()) {                                    String username = user.getUsername();                                    ClientSession session = sessionManager.getSession(username);                  if (session != null && session.getPresence().isAvailable()) {                          notificationIQ.setTo(session.getAddress());                          session.deliver(notificationIQ);                  }else{                      UserPushMessageService msgService = (UserPushMessageService) ServiceLocator.getService("userPushMessageService");                      JSONObject msg = new JSONObject();                      msg.put("apiKey", apiKey);                      msg.put("username", username);                      msg.put("title", title);                      msg.put("message", message);                      msg.put("uri", uri);                                            msgService.addMsg(username, msg);                  }              }                                      }

最后在org.androidpn.server.xmpp.handler. PresenceUpdateHandler类的

public void process(Packet packet) {          ClientSession session = sessionManager.getSession(packet.getFrom());            try {              Presence presence = (Presence) packet;              Presence.Type type = presence.getType();                if (type == null) { // null == available                  if (session != null                          && session.getStatus() == Session.STATUS_CLOSED) {                      log.warn("Rejected available presence: " + presence + " - "                              + session);                      return;                  }                    if (session != null) {                      session.setPresence(presence);                                            //登录成功后发送离线消息                      NotificationManager notificationManager = new NotificationManager();                      String username = session.getUsername();                      UserPushMessageService msgService = (UserPushMessageService) ServiceLocator.getService("userPushMessageService");                      String msgsStr = msgService.getMessages(username);                      if( msgsStr != null ){                          JSONArray msgs = JSONArray.fromObject(msgsStr);                          for (int i = 0; i < msgs.size(); i++) {                              JSONObject msg = msgs.optJSONObject(i);                              notificationManager.sendNotifcationToUser(msg.optString("apiKey"),username, msg.optString("title"), msg.optString("message"), msg.optString("uri"));                          }                          msgService.removeMessages(username);                      }                                            if (!session.isInitialized()) {                          // initSession(session);                          session.setInitialized(true);                      }                  }                } else if (Presence.Type.unavailable == type) {                    if (session != null) {                      session.setPresence(presence);                  }                } else {                  presence = presence.createCopy();                  if (session != null) {                      presence.setFrom(new JID(null, session.getServerName(),                              null, true));                      presence.setTo(session.getAddress());                  } else {                      JID sender = presence.getFrom();                      presence.setFrom(presence.getTo());                      presence.setTo(sender);                  }                  presence.setError(PacketError.Condition.bad_request);                  PacketDeliverer.deliver(presence);              }            } catch (Exception e) {              log.error("Internal server error. Triggered by packet: " + packet,                      e);          }      }

客户端接收消息跳转到指定的activity:

修改org.androidpn.client.Notifier类的notify(String notificationId, String apiKey,String title,String message, String uri)方法,将 该方法中的Intent修改为自己需要的Intent即可。

客户端重复登陆的问题:

这个问题困扰我挺长时间,一开始是想在XmppManager的login前发送个消息结果有时可以有时失败很不稳定,最后决定在客户端service启动前判断该用户是否登陆,如果登陆则发送消息给最先登陆的用户告诉他他的账号被登陆了。

服务端的代码,我在org.androidpn.server.console.controller.UserController里加了个方法

/**          * 判断重复登陆          * @param request          * @param response          * @return          * @throws Exception          */          public ModelAndView checkRepetition(HttpServletRequest request,                  HttpServletResponse response) throws Exception {                            String userCode = request.getParameter("userCode");              if(userCode!=null){                  SessionManager sessmg = SessionManager.getInstance();                  for (Iterator iterator = sessmg.getSessions().iterator(); iterator.hasNext();) {                      ClientSession sess = (ClientSession) iterator.next();                      String name = sess.getUsername();                      if(userCode.equals(name)){                                                    String apiKey = Config.getString("apiKey", "");                          String title = "通知";                          String message = "您的账号在其他设备登录,如非本人操作,请注意账号安全,及时修改密码。";                          String uri = "LoginActivity";                          NotificationManager notificationManager = new NotificationManager();                          notificationManager.sendNotifcationToUser(apiKey, name, title, message, uri);                                response.getOutputStream().println("true");                                                    return null;                      }                  }              }              response.getOutputStream().println("false");              return null;      }

客户端首先在启动之前先异步访问上面的接口,如果重复登录则会发送消息给客户端,代码我这里不贴了,我用的是自己封装的AsyncTask类,贴出来你们也不能用,你们自己开个线程执行下就好了。

然后是在org.androidpn.client. Notifier的方法中加了个判断用来判断是重复登陆的消息提示,然后跳转到登陆界面,这里我延迟了30秒才提示,要是马上提示怕两个人同时登录一个账号,一个用户发现另一个用户登陆后自己又登录,这是可能第二个用户还没建立好session连接,这样就无法发送给第二个用户,讲的有点乱,就是两个用户登录一个账号的问题,用用就会发现了。

if (uri.equals("LoginActivity")) {                                    new NetWork
().setNetWorkListen( new NetWorkListen
() { Intent intent; String notificationId; String apiKey; String title; String message; String uri; Notification notification; @Override public void onPreExecute() { // TODO Auto-generated method stub } @Override public String doInBackground(String... params) { //初始化数据 notificationId = params[0]; apiKey= params[1]; title= params[2]; message= params[3]; uri= params[4]; //重复定义notification notification = new Notification(); notification.icon = getNotificationIcon(); notification.defaults = Notification.DEFAULT_LIGHTS; if (isNotificationSoundEnabled()) { notification.defaults |= Notification.DEFAULT_SOUND; } if (isNotificationVibrateEnabled()) { notification.defaults |= Notification.DEFAULT_VIBRATE; } notification.flags |= Notification.FLAG_AUTO_CANCEL; notification.when = System.currentTimeMillis(); notification.tickerText = message; try { //因为推送需要时间,所以延迟1分钟推送,保证后登陆的用户连接上 Thread.sleep(30000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } @Override public void onPostExecute(String result) { intent = new Intent(context, NotificationDetailsActivity.class); intent.putExtra(Constants.NOTIFICATION_ID, notificationId); intent.putExtra(Constants.NOTIFICATION_API_KEY, apiKey); intent.putExtra(Constants.NOTIFICATION_TITLE, title); intent.putExtra(Constants.NOTIFICATION_MESSAGE, message); intent.putExtra(Constants.NOTIFICATION_URI, uri); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent contentIntent = PendingIntent.getActivity(context, notifyNum++, intent, PendingIntent.FLAG_UPDATE_CURRENT); notification.setLatestEventInfo(context, title, message, contentIntent); notificationManager.notify(random.nextInt(), notification); } @Override public void onProgressUpdate(Integer... values) { // TODO Auto-generated method stub } }).execute( notificationId, apiKey, title, message, uri); return; }

暂时就这些了,因为公司的原因不能给大家修改后的源码,以上的代码都只给出了关键的部分,具体的还要大家自己动手去弄,有不明白的我们可以交流,我也是刚开始弄android,有不好的地方或者还有什么bug也欢迎大家指正。

转载于:https://my.oschina.net/milu6852/blog/806383

你可能感兴趣的文章
细谈WEB标准
查看>>
经典SQL
查看>>
Gitweb 安装与配置
查看>>
Microsoft.Net中数字签名技术
查看>>
iOS-iOS8模拟器设置中文键盘
查看>>
关于cocos2dx手游lua文件加密的解决方式
查看>>
分布式事务处理模型
查看>>
CSS实现限制显示的字数,超出显示"..."
查看>>
探索ASP.NET MVC5系列之~~~2.视图篇(上)---包含XSS防御和异步分部视图的处理
查看>>
MD5加密算法(转)
查看>>
Vue.2.0.5-条件渲染
查看>>
[译]AngularJS Services 获取后端数据
查看>>
scapy流量嗅探简单使用
查看>>
Hadoop Hive概念学习系列之hive的正则表达式初步(六)
查看>>
Leetcode: Combination Sum IV && Summary: The Key to Solve DP
查看>>
Hibernate整合C3P0实现连接池
查看>>
Apache vs. Nginx
查看>>
C++数值类型极限值的获取
查看>>
Bag标签之中的一个行代码实行中文分词实例3
查看>>
3295 落单的数 九章算法面试题
查看>>