when doing instant messaging on the Android side, the pit point is how to ensure that the message is instantaneous and does not consume power. Why do you say that? The reason is that if you want to guarantee instant messaging, there are usually two mechanisms of pull or push. Pull timed polling mechanism, compared to waste server resources, push server pushing mechanism, need to maintain a long connection, the client and server are required to be relatively high (network environment, the number of servers to maintain connections, etc.), their detailed advantages and disadvantages are not described. Both of these mechanisms require the client to be active for a long time, provided that the CPU is awake, and that the android has a sleep mechanism, which ensures that the phone is dormant for the most part, reducing power consumption and prolonging the machine time. After the phone sleeps, the thread handles the paused state, so that both of the preceding methods are paused, causing the message to fail after hibernation. Some people may say that the phone has a wake-up mechanism, if it has been awakened, so that the software is a big power consumption, basically do not have a day of cell phone battery is dry light, think about half a grid before bedtime, the morning the electricity dry light is shut down, depressed mood immediately spontaneously, so this is not possible, will directly lead to the software is Instant and power consumption is contradictory, how to do? The solution is to be balanced, to ensure instant sex while minimizing power consumption. One, wake-up mechanism The phone has a sleep mechanism, it also provides a wake-up mechanism so that we can wake up our program to continue to work during hibernation. About wake-up say two classes: Alarmmanager and wakelock: alarmmanager phone's alarm mechanism, walking clock mechanism is not the same, To ensure that sleep can also be timed accurately, and wake up the program, the specific usage does not say that the Alarmmanager can wake the CPU, the program wakes up, but its wake-up time, just to ensure that it wakes the intent of the object receiving method execution, as for the method inside calls other asynchronous processing, it does not guarantee that So he usually wakes up in a shorter time, and then goes to sleep after he finishes. If you want to make sure that something outside of async is done, you'll have to apply for Wakelock to make sure the phone doesn't hibernate, or that it's half done and the phone is dormant. Here use Alarmmanager and Wakelock, put the message in the asynchronous to do, specifically how to do the later look. First talk about the alarm wake-up cycle problem, to ensure that the message immediately, of course, the shorter the better, but in order to ensure that the power saving, it can not be too frequent. Strategy One, you can use the water wave strategy, reset the alarm: Start intensive scheduling, gradually grow. such as: 30 seconds start, each increment 5 seconds, has been incremented to 25 minutes, on the fixed cycle. Strategy II, can be used at leisure busy time strategy, busy day, cycle intensive, evening leisure, long period. strategy three, alarm adjustment strategy, to ensure that the message immediately, to receive the message, re-initialize the alarm time, starting from the shortest period, to ensure that the chat status, instant. strategy four, Wakelock wake up, detect whether the phone screen is lit, to determine whether to obtain a wake-up lock, reduce the number of wakes. 1, setting alarms
Am.set (Alarmmanager.elapsed_realtime_wakeup, (Triggerattime + time), pi);
2, Alarm time optimization
public class AlarmTime {public static final Atomiclong alarmtime=new atomiclong (0); /** * Initialize alarm time, re-connect or receive message initialization */public static long initalarmtime () { Alarmtime.set (global.alarm _trigger_time); return Alarmtime.get (); } /** * Optimize alarm time, number of re-connected errors more than a certain number of times, optimize the alarm time and try to re-connect to the number of errors * 10 minutes, 30 seconds, 30 seconds,;;; arrival error number, 10 minutes;;;;; * @return * * Public Static Long optimizealarmtime () { alarmtime.set (global.alarm_trigger_optimize_time);//10 min return Alarmtime.get (); } public static long Incrementtime () { long time =alarmtime.get (); if (time==0) return Alarmtime.addandget (global.alarm_trigger_time);//default 30 seconds to start else if (time< Global.alarm_trigger_max_time)//25 minutes return Alarmtime.addandget (global.alarm_trigger_time_increment);// Increment 5 Seconds , else return time; }}
3. Wake-up mechanism
Public final class Imwakelock {private static final String TAG = IMWakeLock.class.getSimpleName (); Private WakeLock WakeLock = null; Private String tag= ""; Private PowerManager pm; Public Imwakelock (Context paramcontext,string tag) {This.tag =tag; Pm= ((PowerManager) paramcontext. Getsystemservice (Context.power_service)); WakeLock = Pm.newwakelock (powermanager.partial_wake_lock, tag); }/** * Gets the power lock, keeps the service running when the screen is off while still acquiring the CPU, */public synchronized void Acquirewakelock () {if (!pm.isscree NOn ()) {if (Null! = Wakelock&&!wakelock.isheld ()) {IMLOG.D (TAG, tag+ "@@===> get wake-up sleep lock" ); Wakelock.acquire (); }}}/** * release device Power Lock */public synchronized void Releasewakelock () {if (null! = WakeLock &A mp;& Wakelock.isheld ()) {IMLOG.D (TAG, tag+ "@@===> release wake-Up Sleep lock"); Wakelock.release (); }} public synchronized void Finalize () {if (null! = WakeLock && Wakelock.isheld ()) {Imlo G.D (TAG, tag+ "@@===> release wake-Up Sleep lock"); Wakelock.release (); } wakeLock = null; } public boolean Isscreenon () {return Pm.isscreenon (); }}
4. Waking time
private void Startapnotify () { if (this.sessionid==0| | This.ticket==null) return; if (Wakelock.isscreenon ()) { imlog.d (TAG, "[Email protected]@===> start empty request"); Apnotifythread=new Apnotifythread (This,false); } else{ Wakelock.acquirewakelock (); Apnotifythread=new Apnotifythread (this,true); } Exec=executors.newsinglethreadexecutor (); Exec.execute (apnotifythread); Exec.shutdown (); }
Wake-up mechanism is good, but if wake up, long time not to release the wake-up lock is not enough, so here to consider the message mechanism.
Second, the message collects the message to collect, uses the push and pull to combine the way, why adopt two kind of combination way? Take a look at the feature push: instant, maintain a connection, take a long time. Pull: Passive, maintain connection, short processing time. According to the phone's wake-up and sleep mechanism, can be analyzed when the push is suitable for mobile phone in-place sleep, not hibernate, maintain a long connection, to ensure instant receipt of messages. and pull suitable for mobile phone sleep state (sleep state no way to judge, only according to the screen light up no judgment, the curve to the National salvation), that is, after sleep, wake up with wake-up mechanism, pull under no message, no message to release the dormant lock, there is a message to receive messages, after the release of sleep lock, to ensure the shortest Reduce power consumption. Push logic Flowchart: Pull Logic Flowchart:
Code Processing Section:
public class Apnotifythread extends thread{private static final String TAG = ApNotifyThread.class.getSimpleName () ; protected volatile Boolean isrunning=false; protected volatile aphold.client Client; protected volatile vrvthttpclient THC; protected volatile Tprotocol protocol; protected volatile long SessionID; protected volatile String ticket; Protected final long errornum=15; protected Notifyservice Service; protected Boolean isold=false; protected Boolean isdoshortrequest=false; Public Apnotifythread (Notifyservice Service,boolean isdoshortrequest) {This.sessionid=service.getsessionid (); This.ticket=service.getticket (); This.service=service; This.isdoshortrequest=isdoshortrequest; } @Override public void Run () {IMLOG.D (TAG, "[email protected]@===> empty request starts processing threadid=" +th Read.currentthread (). GetId ()); This.isrunning=true; if (this.isdoshortrequest) {if (Shortemptyrequest () &&this.isrunning) longemptyrequ EST (); Then open the Sky Request}else{longemptyrequest (); } imlog.d (TAG, "[email protected]@===>" + (this.isold? ") Previous ":") + "Null request terminated threadid=" +thread.currentthread (). GetId ()); This.isrunning=false; }/** * Initialize * @param islongtimeout * @throws Exception * */private void init ( Boolean islongtimeout) throws exception{thc= notifyhttpclientutil.getvrvthttpclient (islongtimeout); protocol = new Tbinaryprotocol (THC); }/** * Sky request */private void Longemptyrequest () {try{this.init (t Rue); Client= New Aphold.client (protocol); for (;;) {if (! Netstatusutil.havactivenet (Imapp.getapp ())) {Imlog. D (TAG, "[email protected]@===> No Available Network"); Break } try {if (!handlemessage ()) break; } catch (Texception e) {if (!this.isrunning) break; IMLOG.D (TAG, "[email protected]@===> Request exception:" + E.getmessage ()); if (Exceptionhandler (e)) {throw new imexception ("Excessive number of connection failures", messagecode.im_exception_connect); } continue; }} imlog.d (TAG, "[email protected]@===>" + (this.isold? ") Previous ":") + "empty request normal exit"); } catch (Exception e) {imlog.d (TAG, "[email protected]@===>" + (this.isold? ") Previous ":") + "Empty request exception Exit" +e.getmessage ()); if (Exceptionhandler (e)) {//Call re-connect IMLOG.D (TAG, "[email protected]@===> call re-connect"); This.service.getDataSyncer (). SetValue (Userprofile.reconnect, "0"); }}finally{close (); }}/** * Short empty request * @return */Private Boolean shortemptyrequest () { Boolean isdolongrequest=true; try{long messagenum=0; if (! Netstatusutil.havactivenet (Imapp.getapp ())) {IMLOG.D (TAG, "[email protected]@===> No Available Network"); return false; } this.init (FALSE); Gets the number of messages apservice.client apclient = new apservice.client (protocol); This.service.getDataSyncer (). SetValue (Userprofile.lastrequesttime, String.valueof (Systemclock.elapsedrealtime () )); IMLOG.D (TAG, "[Email protected]@===>notifyid:" +notifyid.notifyid.get ()); messagenum= apclient.getnotifyMsgsize (SessionID, Ticket, NotifyID.notifyID.get ()); NotifyError.notifyErrorNum.set (0); IMLOG.D (TAG, "[email protected]@===> Get message Number:" +messagenum); if (messagenum==-1) throw new Imexception ("Session invalidation", Messagecode.im_biztips_sessioninvaild); If there is a message to receive the message if (messagenum>0&&this.isrunning) {long receivemessagenum=0 ; Client= New Aphold.client (protocol); for (;;) {if (! Netstatusutil.havactivenet (Imapp.getapp ())) {IMLOG.D (TAG, "[email protected]@===> No available Network" ); Break } if (!handlemessage ()) break; receivemessagenum++; if (receivemessagenum==messagenum)//short connection after receiving exit break; } } imlog.d (TAG, "[email protected]@===>" + (this.isold? ") Previous ":") + "empty request normal exit"); }catch (Exception e) {imlog.d (TAG, "[email protected]@===>" + (this.isold? ") Previous ":") + "Empty request exception Exit" +e.getmessage ()); if (Exceptionhandler (e)) {isdolongrequest=false; Call re-connect IMLOG.D (TAG, "[email protected]@===> call re-connect"); This.service.getDataSyncer (). SetValue (Userprofile.reconnect, "0"); }} finally{close (); This.service.releaseWakeLock (); } return isdolongrequest; }/** * Exception handling determines whether to re-connect * @param e * @return */Private Boolean Exceptionhandler (Exception e) {Boolean isreconnect=false; if (e instanceof imexception) {isreconnect=true; }else if (! ( e instanceof sockettimeoutexception) &&!(e instanceof nohttpresponseexception)) {NotifyError.notifyErrorNum.incrementAndGet (); if (NotifyError.notifyErrorNum.get () >this. ErrorNum) {isreconnect=true; NotifyError.notifyErrorNum.set (0); }}else NotifyError.notifyErrorNum.set (0); E.printstacktrace (); return isreconnect; /** * Empty request send and receive data processing * @throws Texception */Private Boolean handlemessage () throw S texception{if (!this.isrunning) return false; IMLOG.D (TAG, "[Email protected]@===>sessionid" +sessionid); Sendemptyrequestreq req = new Sendemptyrequestreq (); Req.setsessionid (SessionID); Req.setticket (ticket); Req.setnotifyid (NotifyID.notifyID.get ()); IMLOG.D (TAG, "[email protected]@===> the time the empty request cycle begins"); This.service.getDataSyncer (). SetValue (UserproFile. Lastrequesttime, String.valueof (Systemclock.elapsedrealtime ())); Client. Sendemptyrequest (req); NotifyError.notifyErrorNum.set (0); if (!this.isrunning) return false; Apnotifyimpl iface = new Apnotifyimpl (); apnotify.processor<iface> Processor = new apnotify.processor<iface> (Iface); Boolean isstop = false; while (!isstop) {try {imlog.d (TAG, "[email protected]@===> into receive data processing"); while (processor.process (protocol, protocol) = = true) {IsS top = true; Break } imlog.d (TAG, "[email protected]@===> End receiving data processing"); } catch (Texception e) {imlog.d (TAG, "[email protected]@===> Receive data Processing exception"); Isstop = true; }} imlog.d (TAG, "[Email protected]@===> the end of an empty request cycle "); if (!iface.issessionvaild) {//Backstage report session failure This.service.setSessionID (0); This.service.setTicket (NULL); return false; }//Reset the alarm this.service.getDataSyncer (). SetValue (Userprofile.alarm_ttime, "0"); return true; }/** * Close connection */private void Close () {synchronized (this) {if (THC ! = null) {Thc.shutdown (); Thc.close (); Thc=null; }} if (client! = null && client.getinputprotocol ()! = null) {Client.getinpu Tprotocol (). Gettransport (). Close (); Client.getoutputprotocol (). Gettransport (). Close (); }}/** * thread interrupt */public void interrupt () {this.isrunning=false; This.isold=true; Close (); Super.interrupt (); }/** * Determines whether it is in the running state */public Boolean isrunning () {return isrunning; }
Based on the above analysis optimization, the Android side of the instant message charge, the rest is to adjust the wake-up alarm cycle, balance the message immediacy and power consumption issues.