Android之基于XMPP协议即时通讯软件(二)

2013博客之星投票请移步:http://vote.blog.csdn.net/blogstaritem/blogstar2013/weidi1989

昨天给大家粗略的介绍了一下我那个简单的项目:Android之基于XMPP协议即时通讯软件(一)

从今天开始,就详细展开的介绍设计思路,一是给自己做个总结,二是希望能给各位朋友一点点帮助吧!

也许和很多刚刚开始入行程序猿的朋友一样,我最初也是每开始一个新项目,只是稍微思考一下便开始写代码。一个星期后,尽管实现了部分功能,却发现自己有点乱了,因为又突然生出很多新的想法,于是又顺着思路写下去,再持续一段时间,就会发现这个项目做不下去了。代码重复很多,而且bug也是层出不穷。因此不得不推翻重新开始,浪费了很多时间。所以说,开始前的准备工作是相当重要的。

一般说来完成一个项目可以分成三步:

首先:画几天时间好好理清思路,可以在草稿纸上写写画画,列出必须实现的功能已经界面特效等等,做到胸有成竹。

其次:开始搭建框架,定义好所有功能的接口,并且注释好做什么用,这是方便团队合作的项目,个人项目也可以养成写注释的好习惯。

最后:分开实现各部分功能,并且分开调试,可以单独独立成一个应用或者单元测试模块。比如说本应用的所有界面效果都是独立成一个应用测试无误后才最后合入的。

我这个项目呢,一开始是完成了所有的界面模块工作,因为是直接从之前的项目移植过来的,稍微修改了一下,所以这部分省掉了很多时间。

重心就直接放在了功能实现这方面了。功能模块部分就是下面两个包:com.way.service和 com.way.smack

首先是定义通用接口,主要就是将第三方包asmack.jar中需要用到的东西拿出来,这个包是非常庞大的,但是我们这个小应用并不是需要实现所有的功能,所以,抽取自己需要的那部分就可以了,定义成接口,方便以后扩展需要的功能,代码如下:

[java] view plaincopyprint?/**

  • 首先定义一些接口,需要实现一些什么样的功能,

  • @author way

  • /
    public interface Smack {
    /
    *

    • 登陆
    • @param account
    •        账号 
      
    • @param password
    •        密码 
      
    • @return 是否登陆成功
    • @throws XXException
    •         抛出自定义异常,以便统一处理登陆失败的问题 
      
      */
      public boolean login(String account, String password) throws XXException;

    /**

    • 注销登陆
    • @return 是否成功
      */
      public boolean logout();

    /**

    • 是否已经连接上服务器
    • @return
      */
      public boolean isAuthenticated();

    /**

    • 添加好友
    • @param user
    •        好友id 
      
    • @param alias
    •        昵称 
      
    • @param group
    •        所在的分组 
      
    • @throws XXException
      */
      public void addRosterItem(String user, String alias, String group)
      throws XXException;  
      

    /**

    • 删除好友
    • @param user
    •        好友id 
      
    • @throws XXException
      */
      public void removeRosterItem(String user) throws XXException;

    /**

    • 修改好友昵称
    • @param user
    •        好友id 
      
    • @param newName
    •        新昵称 
      
    • @throws XXException
      */
      public void renameRosterItem(String user, String newName)
      throws XXException;  
      

    /**

    • 移动好友到新分组
    • @param user
    •        好友id 
      
    • @param group
    •        新组名 
      
    • @throws XXException
      */
      public void moveRosterItemToGroup(String user, String group)
      throws XXException;  
      

    /**

    • 重命名分组
    • @param group
    •        之前的组名 
      
    • @param newGroup
    •        新组名 
      
      */
      public void renameRosterGroup(String group, String newGroup);

    /**

    • 请求好友重新授权,用在添加好友失败时,重复添加 再次向对方发出申请
    • @param user
    •        好友id 
      
      */
      public void requestAuthorizationForRosterItem(String user);

    /**

    • 添加新分组
    • @param group
      */
      public void addRosterGroup(String group);

    /**

    • 设置当前在线状态
      */
      public void setStatusFromConfig();

    /**

    • 发送消息
    • @param user
    • @param message
      */
      public void sendMessage(String user, String message);

    /**

    • 向服务器发送心跳包,保持长连接 通过一个闹钟控制,定时发送,
      */
      public void sendServerPing();

    /**

    • 从jid中获取好友名
    • @param jid
    • @return
      */
      public String getNameForJID(String jid);
      }

/**

  • 首先定义一些接口,需要实现一些什么样的功能,

  • @author way

  • /
    public interface Smack {
    /
    *

    • 登陆
    • @param account
    •        账号
      
    • @param password
    •        密码
      
    • @return 是否登陆成功
    • @throws XXException
    •         抛出自定义异常,以便统一处理登陆失败的问题
      
      */
      public boolean login(String account, String password) throws XXException;

    /**

    • 注销登陆
    • @return 是否成功
      */
      public boolean logout();

    /**

    • 是否已经连接上服务器
    • @return
      */
      public boolean isAuthenticated();

    /**

    • 添加好友
    • @param user
    •        好友id
      
    • @param alias
    •        昵称
      
    • @param group
    •        所在的分组
      
    • @throws XXException
      */
      public void addRosterItem(String user, String alias, String group)
      throws XXException;
      

    /**

    • 删除好友
    • @param user
    •        好友id
      
    • @throws XXException
      */
      public void removeRosterItem(String user) throws XXException;

    /**

    • 修改好友昵称
    • @param user
    •        好友id
      
    • @param newName
    •        新昵称
      
    • @throws XXException
      */
      public void renameRosterItem(String user, String newName)
      throws XXException;
      

    /**

    • 移动好友到新分组
    • @param user
    •        好友id
      
    • @param group
    •        新组名
      
    • @throws XXException
      */
      public void moveRosterItemToGroup(String user, String group)
      throws XXException;
      

    /**

    • 重命名分组
    • @param group
    •        之前的组名
      
    • @param newGroup
    •        新组名
      
      */
      public void renameRosterGroup(String group, String newGroup);

    /**

    • 请求好友重新授权,用在添加好友失败时,重复添加 再次向对方发出申请
    • @param user
    •        好友id
      
      */
      public void requestAuthorizationForRosterItem(String user);

    /**

    • 添加新分组
    • @param group
      */
      public void addRosterGroup(String group);

    /**

    • 设置当前在线状态
      */
      public void setStatusFromConfig();

    /**

    • 发送消息
    • @param user
    • @param message
      */
      public void sendMessage(String user, String message);

    /**

    • 向服务器发送心跳包,保持长连接 通过一个闹钟控制,定时发送,
      */
      public void sendServerPing();

    /**

    • 从jid中获取好友名
    • @param jid
    • @return
      */
      public String getNameForJID(String jid);
      }

真正的实现就留给他的子工具类SmackImpl。昨天我说过,本应用只有一个服务:XXService,他时继承自BaseService,BaseService也是完成了一部分通用的功能,比如说通知栏消息处理等。在XXService中,我们主要是处理Activity与Smack中连接工作。每个Activity在onResume时,就与该XXService绑定,绑定之后,便可调用该Service中的所有公共函数,比如说登陆、发消息等等,然后Service再调用Smack中已经定义好的各种接口函数,即可完成与服务器的交互。没错,其实这就是MVC设计模式。

Service担当这个C的角色,Activity就是V的角色,Smack就是M了。

现在我们的Activity可以调用Service中的函数提交消息给服务器了,这是单方面的,我们同时需要获得服务器返回的信息,比如说是否登陆成功、收消息之类。这个任务就自然的落到了回调接口IConnectionStatusCallback身上了,在绑定服务成功的同时,我们注册此回调接口,下面是此接口定义的唯一一个函数。也许眼尖的朋友会问了,为什么没有收到新消息的回调,其实一开始我是加了的,后面因为将数据库改成了ContentProvider,就可以去掉了,因为收消息可以通过监听数据库动态变化嘛,对吧?这就是ContentProvider的好处所在,这个留待后续详细说,这里只是提及一下。

[java] view plaincopyprint?/**

  • 服务器回调接口

  • @author way

  • /
    public interface IConnectionStatusCallback {
    /
    *

    • 连接状态改变
    • @param connectedState
    •        连接状态,有连接、未连接、链接中三种 
      
    • @param reason
    •        连接失败的原因 
      
      */
      public void connectionStatusChanged(int connectedState, String reason);
      }

/**

  • 服务器回调接口

  • @author way

  • /
    public interface IConnectionStatusCallback {
    /
    *

    • 连接状态改变
    • @param connectedState
    •        连接状态,有连接、未连接、链接中三种
      
    • @param reason
    •        连接失败的原因
      
      */
      public void connectionStatusChanged(int connectedState, String reason);
      }
      在绑定服务成功的时候,将此回调传递给Service,比如登陆界面的ServiceConnection:

[java] view plaincopyprint?ServiceConnection mServiceConnection = new ServiceConnection() {

@Override  
public void onServiceConnected(ComponentName name, IBinder service) {  
    mXxService = ((XXService.XXBinder) service).getService();  
    //注册回调   
    mXxService.registerConnectionStatusCallback(LoginActivity.this);  
}  

@Override  
public void onServiceDisconnected(ComponentName name) {  
    mXxService.unRegisterConnectionStatusCallback();  
    mXxService = null;  
}  

};

ServiceConnection mServiceConnection = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mXxService = ((XXService.XXBinder) service).getService();
        //注册回调
        mXxService.registerConnectionStatusCallback(LoginActivity.this);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mXxService.unRegisterConnectionStatusCallback();
        mXxService = null;
    }

};

就这样,整体与服务器交互的基本框架就完成了。很简单对吧?

OK,今天就到这里了,以上仅是本人的一点心得体会,后续将详细讲解功能接口Smack的实现以及如何处理联系人和聊天记录数据库等功能,非常感谢你看到了文章末尾,话说,你是否忘记投票了呢?O(∩_∩)O哈哈~

你可能感兴趣的:(Android之基于XMPP协议即时通讯软件(二))