就最近的形势来看,似乎是不差的。变革即意味着机遇,确实如此。我觉得我似乎在朝着自己的计划慢慢靠近。我想很快就会有确切的答案吧。我静静地等着。

         

        最近两天一直在研究Android 信息推送的事情。学习了AndroidPN这个框架,虽然最后不会用这个,项目还是要自己搭,但终究有值得借鉴的地方,特此把学习的内容总结在这里。

 

        AndroidPN(Android Push Notification)是韩国人思密达共享的一个简单的框架,基于XMPP协议,实现了信息推送服务。

        一:安装测试运行

        1,下载客户端和服务器端程序。http://sourceforge.net/projects/androidpn/files/  程序10年11月上传后就没有再更改过,可能已经被原分享者放弃了思密达。

        2,打开客户端程序下的raw/androidpn.properties文件,配置客户端信息,将xmppHost配置成10.0.2.2,xmppPort=5222,5222 是服务器的xmpp服务监听端口。

        3,运行androidpn-server-0.5.0\bin\run.bat启动服务器,从浏览器访问http://127.0.0.1:7070/

在模拟器中运行客户端。

        4,从Web端向客户端发消息。

        效果如下:

android isp Android isp fpnr_android isp

android isp Android isp fpnr_Android_02


       

        友情提示:

        1,如果出现运行run.bat一闪而过,无法访问http://127.0.0.1:7070/的情况,请配置好Java环境变量。

        2,客户端运行后后提示“Application unfortunately Stopped”,有可能是引用的asmack.jar包的问题,重新引用,然后Clean,重新Build Project。祝大家好运。

         二:源代码学习

        在项目中我是写客户端的,所以我主要学习了客户端的源码。现总结如下:
 
         1,程序入口DemoAppActivity开启服务:

// Start the service
ServiceManager serviceManager = new ServiceManager(this);
serviceManager.setNotificationIcon(R.drawable.notification);//设置消息的图标
serviceManager.startService();

 在实例化ServiceManage的过程中,做了以下工作,加载 res/raw/androidpn.properties 配置文件中的参数信息,并将其保存在SharedPreferences中。然后调用startService()开启服务。

 

 

public void startService() {
        Thread serviceThread = new Thread(new Runnable() {
            @Override
            public void run() {
                Intent intent = NotificationService.getIntent();
                context.startService(intent);
            }
        });
        serviceThread.start();
}

startService()方法开启了一个子线程去启动真正的信息推送服务NotificationService。对于Service,作者思密达在OnCreate()的时候做了很多工作,OnCreate()方法在服务被创建时调用,且只会被调用一次。因此多次的start()并不会产生多个实例。在OnCreate()方法中,作者获取了一个很重要的参数deviceId设备ID。可是后面并没有使用它。

 

 

telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
// Get deviceId
deviceId = telephonyManager.getDeviceId();

 获得设备ID后,

 

 

xmppManager = new XmppManager(this);
taskSubmitter.submit(new Runnable() {
public void run() {
   NotificationService.this.start();
   }
});

实例化NotificationService类时会做以下工作:

 

public NotificationService() {
        notificationReceiver = new NotificationReceiver();
        connectivityReceiver = new ConnectivityReceiver(this);
        phoneStateListener = new PhoneStateChangeListener(this);
        executorService = Executors.newSingleThreadExecutor();
        taskSubmitter = new TaskSubmitter(this);
        taskTracker = new TaskTracker(this);
}

BroadcastReceiver类。PhoneStateChangeListener继承了PhoneStateListener,用于监听手机状态变化。Executors.newSingleThreadExecutor()返回一个线程池(这个线程池只有一个线程),这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去。

 

NotificationService.start()方法

private void start() {
        Log.d(LOGTAG, "start()...");
        registerNotificationReceiver();
        registerConnectivityReceiver();
        // Intent intent = getIntent();
        // startService(intent);
        xmppManager.connect();
}

notificationReceiver来接收广播,registerConnectivityReceiver()方法注册了connectivityReceiver来监听网络连接状况。然后调用xmappManager的connect方法。

 

继续跟踪xmappManager的connect方法又执行了哪些工作

 

public void connect() {
        Log.d(LOGTAG, "connect()...");
        submitLoginTask();
}

 调用了submitLoginTask()方法,顾名思义,提交登录任务。

 

private void submitLoginTask() {
        Log.d(LOGTAG, "submitLoginTask()...");
        submitRegisterTask();
        addTask(new LoginTask());
}

 提交登录任务中,又提交了一个注册任务,同时将新建的登录任务添加到任务集合中并交由 TaskTracker 来对添加的任务进行监视。继续根据在登录任务中执行的工作:

(1)如果连接没有经过身份验证,根据username和password执行登录操作,然后为连接添加各种监听机制。执行刚刚添加的任务runTask()(2)如果连接通过身份验证,直接执行任务runTask();

 

private LoginTask() {
            this.xmppManager = XmppManager.this;
        }

        public void run() {
            Log.i(LOGTAG, "LoginTask.run()...");

            if (!xmppManager.isAuthenticated()) {
                Log.d(LOGTAG, "username=" + username);
                Log.d(LOGTAG, "password=" + password);

                try {
                    xmppManager.getConnection().login(
                            xmppManager.getUsername(),
                            xmppManager.getPassword(), XMPP_RESOURCE_NAME);
                    Log.d(LOGTAG, "Loggedn in successfully");

                    // connection listener
                    if (xmppManager.getConnectionListener() != null) {
                        xmppManager.getConnection().addConnectionListener(
                                xmppManager.getConnectionListener());
                    }

                    // packet filter
                    PacketFilter packetFilter = new PacketTypeFilter(
                            NotificationIQ.class);
                    // packet listener
                    PacketListener packetListener = xmppManager
                            .getNotificationPacketListener();
                    connection.addPacketListener(packetListener, packetFilter);

                    xmppManager.runTask();

                } catch (XMPPException e) {
                    Log.e(LOGTAG, "LoginTask.run()... xmpp error");
                    Log.e(LOGTAG, "Failed to login to xmpp server. Caused by: "
                            + e.getMessage());
                    String INVALID_CREDENTIALS_ERROR_CODE = "401";
                    String errorMessage = e.getMessage();
                    if (errorMessage != null
                            && errorMessage
                                    .contains(INVALID_CREDENTIALS_ERROR_CODE)) {
                        xmppManager.reregisterAccount();
                        return;
                    }
                    xmppManager.startReconnectionThread();

                } catch (Exception e) {
                    Log.e(LOGTAG, "LoginTask.run()... other error");
                    Log.e(LOGTAG, "Failed to login to xmpp server. Caused by: "
                            + e.getMessage());
                    xmppManager.startReconnectionThread();
                }

            } else {
                Log.i(LOGTAG, "Logged in already");
                xmppManager.runTask();
            }

        }
    }

 

 

 

 

具体的监听和接下来的操作会在下篇进行分析。