編輯:關於Android編程
因為運營商有一個NAT超時:因為IP v4的IP量有限,運營商分配給手機終端的IP是運營商內網的IP,手機要連接Internet,就需要通過運營商的網關做一個網絡地址轉換(Network Address Translation,NAT)。簡單的說運營商的網關需要維護一個外網IP、端口到內網IP、端口的對應關系,以確保內網的手機可以跟Internet的服務器通訊,大部分移動無線網絡運營商都在鏈路一段時間沒有數據通訊時,會淘汰NAT表中的對應項,造成鏈路中斷。
所以我們需要間隔一定的時間發送一個數據包來保證當前的TCP連接保持有效,這就是所謂的心跳包
智能心跳實際上就是動態的探測到最大的NAT超時時間,然後選定合適的心跳間隔區間去發送心跳包,同時在網絡狀況發生變化的時候能夠動態的調整心跳間隔時間;如果心跳間隔不合適,例如心跳間隔過短,那麼可能導致頻繁的喚醒手機發送心跳包,增加耗電,心跳間隔過長,可能導致這條TCP連接已經無效但是無法及時的檢測到,只能等待下一個心跳包發送的時候才能感知到,所以會導致消息接收延遲,所以探測到一個合適的心跳間隔是非常重要的,把耗電和消息接收及時性綜合折中來取得一個最佳的體驗
public abstract class HeartbeatScheduler { protected int timeout = 20000; protected int minHeart = 60; protected int maxHeart = 300; protected int step = 30; protected volatile boolean started = false; protected volatile long heartbeatSuccessTime; protected volatile int currentHeartType; public static final String HEART_TYPE_TAG = "heart_type"; public static final int UNKNOWN_HEART = 0, SHORT_HEART = 1, PROBE_HEART = 2, STABLE_HEART = 3, REDUNDANCY_HEART = 4; protected PendingIntent createPendingIntent(Context context, int requestCode, int heartType) { Intent intent = new Intent(); intent.setPackage(context.getPackageName()); intent.setAction(SyncAction.HEARTBEAT_REQUEST); intent.putExtra(HEART_TYPE_TAG, heartType); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT); return pendingIntent; } protected void set(int minHeart, int maxHeart, int step) { this.minHeart = minHeart; this.maxHeart = maxHeart; this.step = step; SyncLogUtil.i("set minMax:" + minHeart + ",maxHeart:" + maxHeart + ",step:" + step); } protected boolean isStarted() { return started; } protected abstract boolean isStabled(); protected void setCurrentHeartType(int currentHeartType) { this.currentHeartType = currentHeartType; SyncLogUtil.i("set current heart type:" + currentHeartType); } protected int getTimeout() { return timeout; } protected void setTimeout(int timeout) { this.timeout = timeout; } protected long getHeartbeatSuccessTime() { return heartbeatSuccessTime; } protected void setHeartbeatSuccessTime(long heartbeatSuccessTime) { this.heartbeatSuccessTime = heartbeatSuccessTime; } protected abstract void start(Context context); protected abstract void stop(Context context); protected abstract void clear(Context context); protected abstract void adjustHeart(Context context, boolean success); protected abstract void startNextHeartbeat(Context context, int heartType); protected abstract void resetScheduledHeart(Context context); protected abstract void receiveHeartbeatFailed(Context context); protected abstract void receiveHeartbeatSuccess(Context context); protected abstract int getCurHeart(); }
public class WatchHearbeatScheduler extends HeartbeatScheduler { private class Heartbeat { AtomicInteger heartbeatStabledSuccessCount = new AtomicInteger(0); // 心跳連續成功次數 AtomicInteger heartbeatFailedCount = new AtomicInteger(0); // 心跳連續失敗次數 int successHeart; int failedHeart; int curHeart = 270; AtomicBoolean stabled = new AtomicBoolean(false); } private int curMaxHeart = maxHeart; private int curMinHeart = minHeart; private int maxFailedCount = 5; private int maxSuccessCount = 20; private volatile String networkTag; private int requestCode = 700; private MapheartbeatMap = new HashMap<>(); private List successHeartList = new ArrayList<>(); protected WatchHearbeatScheduler() { } @Override protected void start(Context context) { started = true; networkTag = NetUtil.getNetworkTag(context); alarm(context); SyncLogUtil.i("start heartbeat,networkTag:" + networkTag); } @Override protected void stop(Context context) { heartbeatSuccessTime = 0; started = false; networkTag = null; currentHeartType = UNKNOWN_HEART; for (Map.Entry entry : heartbeatMap.entrySet()) { Heartbeat heartbeat = entry.getValue(); heartbeat.heartbeatStabledSuccessCount.set(0); heartbeat.heartbeatFailedCount.set(0); } cancel(context); SyncLogUtil.d("stop heartbeat..."); } @Override protected void setCurrentHeartType(int currentHeartType) { this.currentHeartType = currentHeartType; } @Override protected void set(int minHeart, int maxHeart, int step) { super.set(minHeart, maxHeart, step); curMaxHeart = maxHeart; curMinHeart = minHeart; } @Override protected boolean isStabled() { Heartbeat heartbeat = getHeartbeat(); return heartbeat.stabled.get(); } @TargetApi(Build.VERSION_CODES.KITKAT) public void alarm(Context context) { AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Heartbeat heartbeat = getHeartbeat(); boolean stabled = heartbeat.stabled.get(); int heart; if (stabled) { heart = heartbeat.curHeart - 10; if (heart < minHeart) { heart = minHeart; } heart = heart * 1000; } else { heart = heartbeat.curHeart * 1000; } int heartType = stabled ? STABLE_HEART : PROBE_HEART; PendingIntent pendingIntent = createPendingIntent(context, requestCode, heartType); int sdk = Build.VERSION.SDK_INT; if (sdk >= Build.VERSION_CODES.KITKAT) { alarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + heart, pendingIntent); } else { alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + heart, pendingIntent); } SyncLogUtil.i("start heartbeat,curHeart [" + heartbeat.curHeart + "],heart [" + heart + "],requestCode:" + requestCode + ",stabled:" + stabled); } private void cancel(Context context) { Heartbeat heartbeat = getHeartbeat(); int heartType = heartbeat.stabled.get() ? STABLE_HEART : PROBE_HEART; AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); PendingIntent pendingIntent = createPendingIntent(context, requestCode, heartType); alarmManager.cancel(pendingIntent); SyncLogUtil.d("cancel heartbeat,requestCode:" + requestCode); } @Override public void startNextHeartbeat(Context context, int heartType) { alarm(context); } @Override public void resetScheduledHeart(Context context) { alarm(context); } private void addSuccessHeart(Integer successHeart) { if (!successHeartList.contains(successHeart)) { if (successHeartList.size() > 10) { successHeartList.remove(0); } successHeartList.add(successHeart); SyncLogUtil.i("add successHeart:" + successHeart); } SyncLogUtil.i("successHeartList:" + successHeartList); } private void removeSuccessHeart(Integer successHeart) { successHeartList.remove(Integer.valueOf(successHeart)); SyncLogUtil.i("successHeartList:" + successHeartList); } @Override protected void adjustHeart(Context context, boolean success) { if (currentHeartType == REDUNDANCY_HEART) { SyncLogUtil.d("redundancy heart,do not adjustHeart..."); return; } Heartbeat heartbeat = getHeartbeat(); if (success) { onSuccess(heartbeat); } else { onFailed(heartbeat); } SyncLogUtil.i("after success is [" + success + "] adjusted,heartbeat.curHeart:" + heartbeat.curHeart + ",networkTag:" + networkTag); } private void onSuccess(Heartbeat heartbeat) { heartbeat.successHeart = heartbeat.curHeart; curMinHeart = heartbeat.curHeart; addSuccessHeart(heartbeat.successHeart); heartbeat.heartbeatFailedCount.set(0); if (heartbeat.stabled.get()) { int count = heartbeat.heartbeatStabledSuccessCount.incrementAndGet(); SyncLogUtil.i("heartbeatStabledSuccessCount:" + heartbeat.heartbeatStabledSuccessCount.get()); if (count >= maxSuccessCount) { maxSuccessCount += 20; SyncLogUtil.i("maxSuccessCount:" + maxSuccessCount); Integer successHeart = selectMinSuccessHeart(heartbeat.curHeart); if (successHeart != null) { heartbeat.curHeart = successHeart; } else { heartbeat.stabled.set(false); curMaxHeart = maxHeart; heartbeat.curHeart = (curMinHeart + curMaxHeart) / 2; SyncLogUtil.i("curHeart = (" + curMinHeart + " + " + curMaxHeart + ") / 2 = " + heartbeat.curHeart); } } } else { heartbeat.curHeart = (curMinHeart + curMaxHeart) / 2; SyncLogUtil.i("curHeart = (" + curMinHeart + " + " + curMaxHeart + ") / 2 = " + heartbeat.curHeart); } if (heartbeat.curHeart >= maxHeart) { heartbeat.curHeart = maxHeart; heartbeat.stabled.set(true); SyncLogUtil.i("探測達到最大心跳adjust stabled:" + heartbeat.stabled.get()); } else if (curMaxHeart - curMinHeart < 10) { if (!heartbeat.stabled.get()) { heartbeat.curHeart = curMinHeart; } heartbeat.stabled.set(true); SyncLogUtil.i("二分法探測盡頭adjust stabled:" + heartbeat.stabled.get()); } SyncLogUtil.i("curHeart:" + heartbeat.curHeart + ",curMinHeart:" + curMinHeart + ",curMaxHeart:" + curMaxHeart); } private void onFailed(Heartbeat heartbeat) { removeSuccessHeart(heartbeat.curHeart); heartbeat.failedHeart = heartbeat.curHeart; heartbeat.heartbeatStabledSuccessCount.set(0); curMaxHeart = heartbeat.curHeart; int count = heartbeat.heartbeatFailedCount.incrementAndGet(); SyncLogUtil.i("heartbeatFailedCount:" + count); if (maxSuccessCount > 20) { maxSuccessCount -= 20; } if (heartbeat.stabled.get()) { if (count > maxFailedCount) { Integer successHeart = selectMaxSuccessHeart(heartbeat.curHeart); if (successHeart != null) { heartbeat.curHeart = successHeart; } else { heartbeat.stabled.set(false); curMinHeart = minHeart; heartbeat.curHeart = (curMinHeart + curMaxHeart) / 2; SyncLogUtil.i("curHeart = (" + curMaxHeart + " + " + curMinHeart + ") / 2 = " + heartbeat.curHeart); } } else { SyncLogUtil.i("continue retry heartbeat.curHeart:" + heartbeat.curHeart + ",stabled:" + heartbeat.stabled.get()); } } else { if (count > maxFailedCount) { heartbeat.curHeart = (curMinHeart + curMaxHeart) / 2; SyncLogUtil.i("curHeart = (" + curMaxHeart + " + " + curMinHeart + ") / 2 = " + heartbeat.curHeart); } else { SyncLogUtil.i("continue retry heartbeat.curHeart:" + heartbeat.curHeart + ",stabled:" + heartbeat.stabled.get()); } } if (curMaxHeart - curMinHeart < 10) { if (!heartbeat.stabled.get()) { curMinHeart = minHeart; } SyncLogUtil.i("二分法探測達到瓶頸" + ",curHeart:" + heartbeat.curHeart); SyncLogUtil.i("curMinHeart:" + curMinHeart + ",curMaxHeart:" + curMaxHeart); } SyncLogUtil.i("curHeart:" + heartbeat.curHeart + ",curMinHeart:" + curMinHeart + ",curMaxHeart:" + curMaxHeart); } private Integer selectMaxSuccessHeart(int curHeart) { Collections.sort(successHeartList, new Comparator () { @Override public int compare(Integer lhs, Integer rhs) { return rhs.compareTo(lhs); } }); SyncLogUtil.i("successHeartList:" + successHeartList); for (Integer heart : successHeartList) { if (curHeart >= heart) { continue; } else { return heart; } } return null; } private Integer selectMinSuccessHeart(int curHeart) { Collections.sort(successHeartList, new Comparator () { @Override public int compare(Integer lhs, Integer rhs) { return lhs.compareTo(rhs); } }); SyncLogUtil.i("successHeartList:" + successHeartList); for (Integer heart : successHeartList) { if (curHeart >= heart) { continue; } else { return heart; } } return null; } private Heartbeat getHeartbeat() { Heartbeat heartbeat = heartbeatMap.get(networkTag); if (heartbeat == null) { heartbeat = new Heartbeat(); heartbeatMap.put(networkTag, heartbeat); } return heartbeat; } @Override protected void receiveHeartbeatFailed(Context context) { adjustHeart(context, false); } @Override protected void receiveHeartbeatSuccess(Context context) { adjustHeart(context, true); alarm(context); } @Override protected void clear(Context context) { stop(context); heartbeatMap.clear(); successHeartList.clear(); curMinHeart = minHeart; curMaxHeart = maxHeart; SyncLogUtil.d("clear heartbeat..."); } @Override protected int getCurHeart() { Heartbeat heartbeat = getHeartbeat(); return heartbeat.curHeart; } }
使用ImageView會遇到的問題 在Android應用中,都少不了圖片的顯示,ImageView,輪播圖,ViewPager等等,很多
1 Content Provider組件簡介Content Provider組件是Android應用的重要組件之一,管理對數據的訪問,主要用於不同的應用程序之間實現數據共
不少玩家會想到將圖片拷貝到電腦中,然後用PS等工具去標注編輯加工,然在再發送到朋友圈或社交平台。那麼,轉來轉去,是不是也不太方便呢?其實Android手機也
Android特效專輯(十二)——仿支付寶咻一咻功能實現波紋擴散特效,精細小巧的View 先來看看這個效果 這是我的在Only上添加的效果,說實話