标签:
几个问题:
这个问题其实很好验证,按如下步骤做一个实验:
步骤
下载一个TCP调试软件(做为一个TCP Server);

写一个Android平台的TCP Client, 用一个Timer来向Server发送心跳;
关闭手机屏幕,让手机休眠;
结论
熄灭屏幕后,很快你就会发现心跳没了,但是连接还在。证明了CPU休眠了但连接不会断。
同样做一个实验来验证收到数据包是否能唤醒设备
步骤
Tcp连接建立后, 让手机进入休眠;
打开手机屏幕, 看Client端是否收到消息;
结论
看到Server端发送过去的消息被打印在了屏幕上。
基于Netty网络框架
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener,TcpClient.TcpClientListener {private EditText edtMain;private Button btnConnect;private TcpClient tcpClient;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);edtMain = (EditText) findViewById(R.id.edt_main);btnConnect = (Button) findViewById(R.id.btn_Connect);tcpClient = new TcpClient();edtMain.append("onCreate<<<<<<<<<<<<<<<<<");//Heart beatnew Timer().schedule(new TimerTask() {@Overridepublic void run() {final CharSequence time = DateFormat.format("hh:mm:ss", System.currentTimeMillis());if (tcpClient.isConnected()) {tcpClient.send((time + "\r\n").toString().getBytes());}runOnUiThread(new Runnable() {@Overridepublic void run() {edtMain.append(time + "\n");}});}}, 0, 2000);btnConnect.setOnClickListener(this);tcpClient.setTcpClentListener(this);}@Overrideprotected void onDestroy() {edtMain.append("onDestroy>>>>>>>>>>>>>>>>");super.onDestroy();}@Overridepublic void onClick(View v) {tcpClient.connect("10.1.1.86", 3008);}@Overridepublic void received(final byte[] msg) {runOnUiThread(new Runnable() {@Overridepublic void run() {edtMain.append("received ============= " + new String(msg));}});}@Overridepublic void sent(byte[] sentData) {}@Overridepublic void connected() {runOnUiThread(new Runnable() {@Overridepublic void run() {btnConnect.setText("Connected");}});}@Overridepublic void disConnected(int code, String msg) {runOnUiThread(new Runnable() {@Overridepublic void run() {btnConnect.setText("Connect");}});}}
TcpClient.java
public class TcpClient {//Disconnect codepublic static final int ERR_CODE_NETWORK_INVALID = 0;public static final int ERR_CODE_TIMEOUT = 1;public static final int ERR_CODE_UNKNOWN = 2;public static final int ERR_CODE_DISCONNECTED = 3;private EventLoopGroup mGroup;private Bootstrap mBootstrap;private TcpClientListener mListener;private ChannelFuture mChannelFuture;private boolean isConnected = false;public TcpClient() {mGroup = new NioEventLoopGroup();mBootstrap = new Bootstrap();mBootstrap.group(mGroup).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast("decoder", new FrameDecoder());ch.pipeline().addLast("handler", new MsgHandler());}});}public boolean isConnected() {return isConnected;}/*** 建立连接* @author swallow* @createTime 2016/2/16* @lastModify 2016/2/16* @param host Server host* @param port Server port* @return*/public void connect(final String host, final int port) {try {mChannelFuture = mBootstrap.connect(host, port);// mChannelFuture.sync();// mChannelFuture.channel().closeFuture();} catch (Exception e) {mGroup.shutdownGracefully();if (mListener != null)mListener.disConnected(ERR_CODE_TIMEOUT, e.getMessage());e.printStackTrace();}}/*** 断开连接* @author swallow* @createTime 2016/1/29* @lastModify 2016/1/29* @param* @return*/public void disConnect() {if (mChannelFuture == null)return;if (!mChannelFuture.channel().isActive())return;mChannelFuture.channel().close();mChannelFuture.channel().closeFuture();mGroup.shutdownGracefully();}/*** 消息发送* @param* @return* @author swallow* @createTime 2016/1/29* @lastModify 2016/1/29*/public void send(final byte[] data) {final ByteBuf byteBuf = Unpooled.copiedBuffer(data);mChannelFuture.channel().writeAndFlush(byteBuf).addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {if (mListener != null)mListener.sent(data);}});}/*** 设置TcpClient监听* @author swallow* @createTime 2016/1/29* @lastModify 2016/1/29* @param* @return*/public void setTcpClentListener(TcpClientListener listener) {this.mListener = listener;}/*** @className: FrameDecoder* @classDescription: Decode to frames* @author: swallow* @createTime: 2015/11/23*/private class FrameDecoder extends ByteToMessageDecoder {@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {byte[] data = new byte[in.readableBytes()];in.readBytes(data);out.add(data);// // 不够4个字节则不予解析// if (in.readableBytes() < 4) {// return;// }// in.markReaderIndex(); // mina in.mark();//// // 获取帧长度// byte[] headers = new byte[4];// in.readBytes(headers);// int frameLen = Utils.bytesToInt(headers);// if (frameLen > in.readableBytes()) {// in.resetReaderIndex();// return;// }//// //获取一个帧// byte[] data = new byte[frameLen];// in.readBytes(data);// out.add(data);}}/*** @className: MsgHandler* @classDescription: 经过FrameDecoder的消息处理器* @author: swallow* @createTime: 2015/11/23*/private class MsgHandler extends SimpleChannelInboundHandler<byte[]> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, byte[] msg) throws Exception {if (mListener != null)mListener.received(msg);}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {if (mListener != null){isConnected = true;mListener.connected();}}@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {if (mListener != null){isConnected = false;mListener.disConnected(ERR_CODE_DISCONNECTED, "The connection is disconnected!");}}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {super.exceptionCaught(ctx, cause);}}/*** @className: TcpClientListener* @classDescription: The client listener* @author: swallow* @createTime: 2015/11/25*/public interface TcpClientListener {void received(byte[] msg);void sent(byte[] sentData);void connected();void disConnected(int code, String msg);}}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="cn.ecpark.devicesleep.MainActivity"><Buttonandroid:id="@+id/btn_Connect"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:text="Connect" /><ScrollViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:layout_above="@+id/btn_Connect"><EditTextandroid:id="@+id/edt_main"android:layout_width="match_parent"android:layout_height="match_parent"android:editable="false" /></ScrollView></RelativeLayout>
如果一开始就对Android手机的硬件架构有一定的了解,设计出的应用程序通常不会成为待机电池杀手,而要设计出正确的通信机制与通信协议也并不困难。但如果不去了解而盲目设计,可就没准了。
首先Android手机有两个处理器,一个叫Application Processor(AP),一个叫Baseband Processor(BP)。AP是ARM架构的处理器,用于运行Linux+Android系统;BP用于运行实时操作系统(RTOS),通讯协议栈运行于BP的RTOS之上。非通话时间,BP的能耗基本上在5mA左右,而AP只要处于非休眠状态,能耗至少在50mA以上,执行图形运算时会更高。另外LCD工作时功耗在100mA左右,WIFI也在100mA左右。一般手机待机时,AP、LCD、WIFI均进入休眠状态,这时Android中应用程序的代码也会停止执行。
Android为了确保应用程序中关键代码的正确执行,提供了Wake Lock的API,使得应用程序有权限通过代码阻止AP进入休眠状态。但如果不领会Android设计者的意图而滥用Wake Lock API,为了自身程序在后台的正常工作而长时间阻止AP进入休眠状态,就会成为待机电池杀手。比如前段时间的某应用,比如现在仍然干着这事的某应用。
首先,完全没必要担心AP休眠会导致收不到消息推送。通讯协议栈运行于BP,一旦收到数据包,BP会将AP唤醒,唤醒的时间足够AP执行代码完成对收到的数据包的处理过程。其它的如Connectivity事件触发时AP同样会被唤醒。那么唯一的问题就是程序如何执行向服务器发送心跳包的逻辑。你显然不能靠AP来做心跳计时。Android提供的Alarm Manager就是来解决这个问题的。Alarm应该是BP计时(或其它某个带石英钟的芯片,不太确定,但绝对不是AP),触发时唤醒AP执行程序代码。那么Wake Lock API有啥用呢?比如心跳包从请求到应答,比如断线重连重新登陆这些关键逻辑的执行过程,就需要Wake Lock来保护。而一旦一个关键逻辑执行成功,就应该立即释放掉Wake Lock了。两次心跳请求间隔5到10分钟,基本不会怎么耗电。除非网络不稳定,频繁断线重连,那种情况办法不多。
网上有说使用AlarmManager,因为AlarmManager 是Android 系统封装的用于管理 RTC 的模块,RTC (Real Time Clock) 是一个独立的硬件时钟,可以在 CPU 休眠时正常运行,在预设的时间到达时,通过中断唤醒 CPU。
以上原文链接
ym_research_android_suspend_network
标签:
原文地址:http://www.cnblogs.com/swallowzhang/p/5466588.html