android xmpp总结

来源:互联网 发布:360产品展示软件 编辑:程序博客网 时间:2024/05/29 18:32

         从0到现在第一版的发布,其中的问题真是只有自己知道...难得项目到一个阶段了,给自己打个分,留个tag,也好以后开发时会省点事!

         进入正题:若是再做一个基于xmpp的手机客户端,功能需要类似于qq一样时!首先要做的是了解你的服务器是什么样的!这太重要了,要不你需要考虑的数据交互问题不好处理!在项目开始的初期,要与后台人员定好传输的自定义部分,如iq包里的东西...这主要是看后台服务人员是否了解openfire或着smack之间的常用参数的传递;

         例如我就遇到了我对iq包解读时花名册只能读取一条数据,这是由于服务端在给item赋值时,没有将不同好友或者信息中,添加不同的jid,而是始终用的都是同一个jid,以至我只能解读到最后一条item。

         其次,做好XmppConnection的工具类,这个是非常重要的,你需要考虑到很多东西,如断网重连,自定义的xmpp的插件,为常用参数赋值等问题,我重点说一下断网后重连的问题吧,这个是我最头疼的地方,到现在也只是用的最费电的方式...

         首先是在登录成功后为XmppConnection添加一个ConnectionListener,用来处理出现的重连问题,在xmpp工具类中先将

static{   
        try{  
          Class.forName("org.jivesoftware.smack.ReconnectionManager");  
        }catch(Exception e){  
            e.printStackTrace();  
        }  
    }  

        这段代码加入,具体应该是asmack的问题,然后在listener中处理connectionClosedOnError的方法,用迭代实现不断的申请重新登陆,记得断网时要更改ui将用户置为下线状态,且此时他发送的消息应为离线消息... 

        当有网后,登录上后,应提交presence为available,否者发送的消息别的用户能收到,但是自己接不到他们发的message,具体还需要看情况,我当时情况是发送的message只有to:jid,并没有from:jid,据我推测应该是发的离线消息的格式,但后来我把状态包发过去之后就没有了这个问题。

        还有一点需要强调的就是,同一用户登录问题,这个可以和后台协商,具体做法就不想讲了,只是我们商定好了一个关键包,来处理让谁在线...但是做项目时要考虑这个问题,此问题还会影响到后续离线消息的处理,以及服务端用户始终在线的情况...

        再次,说一下android端的正事了。因为是一个频繁与数据打交道的项目所以就要制定好数据的存储与使用问题了,这主要是两方面,一是收到的消息要及时保存(应该这时去通知服务器,消息已经收到了),二是消息要在ui界面即时展示:

        从总体的角度把握一下,先是登录的数据问题,根据需求判断是否记住密码,自动登录!所有这样的数据应当是下次启动时还需要用到的数据,所以用SharedPreferences来保存;接下来是,好友信息,群信息,这种数据应当是时时更新的,所以用全局变量来记录,即保存在内存中(当系统长时间挂机时,再次启动后台的程序时,我们要先做判空,若好友信息为空时,即时重新登陆,防止空指针异常),或者也可以使用sql来保存这个好友信息...消息的存储就必须用sql了...(具体消息还要细分为纯文本,纯图片,图文混排)

       现在说下我的实现方式,我用来3中方式管理数据,1用一个全局变量来存储一些常用的数据,例如ip,post,username,password,List<Friend>,List<Groud>等数据,这样的数据有共同点,就是每回需要改动或者项目正常运行时,需要根据它们来修改一些ui,所有放在内存中方便调用,缺点就是空指针异常的出现几率增大了,2用sp来保存数据,例如做记录用户密码时用的就是它来保存,这种东西方便记录一下小数据,不能记录list集合这是不方便的,但是效率高点,3用sqlite来存储数据,例如message(好友信息)(群信息),但是我用了一张表处理了两种message带来的烦心事就是我需要判断消息到底属于谁的...这没少费我时间所以我推荐多一张表。

       说一下我遇到的问题吧,首先登录后进入的是好友列表界面,这就需要使用到ExpandableListView来做一个分组问题,其中又需要让开始时将子item展开用

  int groupCount = ExpandableListView.getCount();
  for (int i = 0; i < groupCount; i++) {
   fl.expandGroup(i);
  }

来处理一下,这样就可以保证一开始时候好友列表就是展开的,其次还需要群列表,聊天记录等数据的处理了,重点说一下聊天记录的处理吧,我的思路是单独做了一个表,专门用来记录每次和谁聊天的聊天记录,但是我只记录最后一条信息,用来再ui界面显示...这样做的好处是我可以在sqlite中更具数据的时间排序,这样显示时就自动排好序了...缺点还是效率的问题,即每次退出聊天界面时会瞬间卡顿,(提议更好的做法是放到内存中,等ui显示完成后再进行保存,这样下回运行时就有数据可以读取了),这个聊天记录界面还有一个消息提醒的问题要处理,这就需要判断当前用户的状态了,


    ActivityManager mActivityManager = (ActivityManager) context
      .getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> taskInfos = mActivityManager
      .getRunningTasks(1);
    if (taskInfos.size() > 0
      && !taskInfos.get(0).topActivity.getPackageName()
        .contains(getPackageName())){

//TODO

}

用这个判断是否在后台运行,这就需要给用户发送Notification提示,还有一种情况,就是用户没在聊天界面,有消息进来时我们需要提示用户,
    ActivityManager mActivityManager = (ActivityManager) context
      .getSystemService(Context.ACTIVITY_SERVICE);
      List<RunningTaskInfo> taskInfos = mActivityManager
      .getRunningTasks(1);
    if (taskInfos.size() > 0
      && taskInfos.get(0).topActivity.getPackageName()
        .contains(getPackageName())) {

//先进行后台判断,在自己建立的基础activity写一个获取当前activity 的方法getCurrentActivity(),再用activity 的信息比较是否在聊天界面
     if (getCurrentActivity().getComponentName().equals(
       getComponentName())) {

//TODO

}

这里面涉及到一个基础的activity类和一个基础的Application类,这个是用来管理所有activity以及一些常用方法的地方,

/** 获取当前处于栈顶的activity,无论其是否处于前台 */
 public static BaseActivity getCurrentActivity() {
  List<BaseActivity> copy;
  synchronized (mActivities) {
   copy = new ArrayList<BaseActivity>(mActivities);
  }
  if (copy.size() > 0) {
   return copy.get(copy.size() - 1);
  }
  return null;
 }

其中List<BaseActivity>是用来在创建基础activity时记录开启的activity的集合,用它来获取当前运行的activity,这里只是提供一种管理activity 的方法,大家可以集思广意,

         回归正题,当消息来了,我们可以用ui提示,也可以开启震动等方式提醒,这里主要就是ui的更新了,建议写一个接口,这管理消息的service中接到消息就调用接口,来刷新ui这样方便,我用的笨方法,但是也解决了我就不细讲了,,,

         重点的service来了,用来管理消息的服务,我是在登录成功后就开启的服务,用来接收消息,并初步处理消息,这里包括,推送的消息,自定义的关键消息,正常消息了,若是正常消息,就先判断消息的类型纯文本,图片还是图文,然后再判别群消息,好友消息,处理完后,将消息保存,并发送广播通知ui更新,总体上就是这么一个流程,遇到的问题就是(1)图片的处理,base64的解码,我后台是c#,我接到的base64解码不出来图片,具体是因为c#需要先将图片转成byte[],再将byte转成流利用base64转成string,再传过来,而我这面直接就能将bitmap转成base64,所以现在数据有些问题...有谁会这个问题可以告诉我一下,不胜感激!(2)说到服务就会有另一个问题,就是后台运行时,用户清理进程时服务也一同清理的问题,实测MIUI会出现这样的问题,即时开启守护线程维护service也不行,现在做法只有监听手机中常用广播判断服务,再开启但是效果不理想,谁有这方面的经验也可以告诉我一下,不胜感激!三星的系统没发现这个问题

        其他的在说一下常用的地方,一是断网,当断网后监听网络改变的广播,并作相应的处理,用户状态变成下线...再发送的消息为离线消息等,asmack自带断网重连的方法,我是在xmppconnection上加了监听,并做了重登的操作(重登之后一定要把在线状态发包,要不然发消息还是离线消息)...当登录成功后通知ui再把状态改为在线,二就是刚登陆时,iq包里包含离线消息,注意处理(具体就是把消息记录为message.db的格式记录下来,通过ui显示出来),三就是退出的处理,这里要考虑的东西有点多,1是退出,需要把service停了(不开启守护线程),解注一些广播接收者,关闭所有的activity等,2是后天运行,最开始用绑定service做的后来发现用户容易清理线程提高内存,所以后来改成守护线程了,这里说一下退回桌面的方法

        // 将activity放到后台去
        PackageManager pm = getPackageManager();
        ResolveInfo homeInfo = pm.resolveActivity(
          new Intent(Intent.ACTION_MAIN)
            .addCategory(Intent.CATEGORY_HOME),
          0);
        ActivityInfo ai = homeInfo.activityInfo;
        Intent startIntent = new Intent(
          Intent.ACTION_MAIN);
        startIntent
          .addCategory(Intent.CATEGORY_LAUNCHER);
        startIntent.setComponent(new ComponentName(
          ai.packageName, ai.name));
        startActivitySafely(startIntent);

四说一下更新,以及链接超时的问题,更新是用apk的版本号对比更新的,具体看你和服务端的沟通了,一旦更新我就用下载的方法更新apk,推荐使用afinal的jar包,还不错...

至于什么时候提示更新就要看需求了(这包括是否强制升级等问题),链接超时主要是看网络的状态,当然判断时我们还是以时间和xmpp的返回值来处理是否超时链接,这就需要子线程运行的一个判断了,需要异步处理问题,用子线程登录,当子线程登录成功后主线程需要跳转主activity,另外我在主线程用while(true)来不断监听子线程是否登录成功,并作超时判断...五就是dialog的一个小问题了,我想在弹出的dialog中的EditText中事先settext但是不成功后来发现原因了应该这样做

public void diag() {
  LayoutInflater factory = LayoutInflater.from(this);
  textEntryView = factory.inflate(
    R.layout.alert_dialog_text_entry, null);

  ip2 = (EditText) textEntryView
    .findViewById(R.id.ip_et);
  post = (EditText) textEntryView
    .findViewById(R.id.post_et);
  ip2.setText(“ip”));
  post2.setText(5222+"");
  AlertDialog dlg = new AlertDialog.Builder(this)

    .setTitle("ip设置")
    .setView(textEntryView)
    .setPositiveButton("确定", new DialogInterface.OnClickListener() {

     
     public void onClick(DialogInterface dialog, int whichButton) {
      
      ips = ip2.getText().toString().trim();

      

      if (StringUtils.isEmpty(ips)) {
       UIUtils.showToastSafe("IP不能为空!");
       return;
      } else if (StringUtils.isEmpty(post2.getText()
        .toString())) {
       UIUtils.showToastSafe("端口号不能为空!");
       return;
      } else {
       posts = Integer.valueOf(post2.getText().toString());
       SPUtils.setPrefString("IP", ips);
       SPUtils.setPrefInt("POST", posts);
       // 为了让xmpp重新更换链接的ip
       XmppConnection.getInstance().closeConnection();
      }
         }
    })
    .setNegativeButton("取消", new DialogInterface.OnClickListener() {
     public void onClick(DialogInterface dialog, int whichButton) {

     }
    }).create();
  dlg.show();

 }

这样就可在显示时就将text放在editview中了,六做记住密码功能时要特别强调一下不要只记录了密码,还要对应上用户名要不就尴尬了,还有登录的用户名的editview要加上changelistener要不然就会出现用户换了但是还可以用上一个用户的密码的问题...七其他的还有一些简单的功能需要考虑如每个对话的item可以复制粘贴(弹popupwindow,设置button来调用系统的粘贴板),还有草稿箱功能,主要考录用sp来记录未发送的消息八混淆时要将asmack除去,并将xmpp的debug模式关闭要不打包后项目不能正常运行等等吧

          项目作下来一个人要考虑的东西太多了,还必须和后台沟通好,协议制定好,但是真是成长了不少...希望多提宝贵意见毕竟自己写的东西还有许多需要改进的地方,上述问题中若有错误的地方欢迎指出,您的帮助是对我的莫大鼓励,再次感谢大牛们的技术支持...

          附上项目的展示视频(不是全部功能,后续还更改了新的东西,录屏软件吐司提示有些问题...),http://bcs.duapp.com/videobylg/media/2014-12-31_14_18_29.mp4

          http://bcs.duapp.com/videobylg/media/2014-12-31_14_07_22.mp4,源码鉴于商用,就不公开了...谢谢阅读!

       

 

0 0
原创粉丝点击