转载自: https://blog.csdn.net/tiancizhu/article/details/76241126
作者:TianciZhu
消息推送
一、主要是两种实现方式:
- pull:轮询,轮询时间短:耗电,耗流量。时间长,不能保证消息及时。
轮询:
Timer:WakeLock 让CPU 保持唤醒,耗电量很大
AlarmManager:管理独立的硬件时钟RTC,可以在CPU休眠的时候正常运行。在预设的时间到达之后,唤醒CPU。这样CPU可以正常休眠,只需要任务到达之后醒来一段很短的时间。极光推送就是基于此实现的。 - push:SMS、长连接。
二、长连接又包括三种方式:
- GCM:google的Gcm,容易被国内厂商阉割,而且NAT(Network AddressTranslation)容易超时,长连接无法保持,造成消息推送延迟。
- 第三方推送:友盟、极光,腾讯信鸽
- 自定义长连接:长连接、心跳和推送及时率。
保持长连接,是消息及时的重要保证。发送心跳包,如果前台检测发送失败,则重新初始化一个socket。
三、把socket链接和心跳功能都放在一个Service中,为什么要放在Service中?
一般我们这种socket几乎是跟app的生命周期一样长,甚至更长。不管在不在Service中去完成操作,我们都得开异步线程,虽然Service并不是异步操作,但是为了提升我们任务的优先级,我们最好是放在Service中,因为Service是由Android系统管理的,并且拥有比较高的优先级,线程是Java中的异步任务载体,可以说android系统不太认识线程。放在Service中可以很大程度上避免任务被回收或者关闭
四、下面是android代码的具体实现:
代码转载自:http://blog.csdn.net/u010072711/article/details/76099776
BackSevice服务:
import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Arrays;
public class BackService extends Service {
private static final String TAG = “BackService”;
/** 心跳检测时间 /
private static final long HEART_BEAT_RATE = 3 * 1000;
/* 主机IP地址 /
private static final String HOST = “192.168.1.104”;
/* 端口号 /
public static final int PORT = 9800;
/* 消息广播 /
public static final String MESSAGE_ACTION = “org.feng.message_ACTION”;
/* 心跳广播 */
public static final String HEART_BEAT_ACTION = “org.feng.heart_beat_ACTION”;
private long sendTime = 0L;
/** 弱引用 在引用对象的同时允许对垃圾对象进行回收 */
private WeakReference<Socket> mSocket;
private ReadThread mReadThread;
private IBackService.Stub iBackService = new IBackService.Stub() {
@Override
public boolean sendMessage(String message) throws RemoteException {
return sendMsg(message);
}
};
@Override
public IBinder onBind(Intent arg0) {
return (IBinder) iBackService;
}
@Override
public void onCreate() {
super.onCreate();
new InitSocketThread().start();
}
// 发送心跳包
private Handler mHandler = new Handler();
private Runnable heartBeatRunnable = new Runnable() {
@Override
public void run() {
if (System.currentTimeMillis() - sendTime >= HEART_BEAT_RATE) {
boolean isSuccess = sendMsg("");// 就发送一个\r\n过去, 如果发送失败,就重新初始化一个socket
if (!isSuccess) {
mHandler.removeCallbacks(heartBeatRunnable);
mReadThread.release();
releaseLastSocket(mSocket);
new InitSocketThread().start();
Log.d("ztf"," 发送失败,就重新初始化一个socket" );
}
}
mHandler.postDelayed(this, HEART_BEAT_RATE);
}
};
public boolean sendMsg(String msg) {
if (null == mSocket || null == mSocket.get()) {
return false;
}
Socket soc = mSocket.get();
try {
if (!soc.isClosed() && !soc.isOutputShutdown()) {
OutputStream os = soc.getOutputStream();
String message = msg + "\r\n";
os.write(message.getBytes());
os.flush();
sendTime = System.currentTimeMillis();// 每次发送成功数据,就改一下最后成功发送的时间,节省心跳间隔时间
Log.i(TAG, "发送成功的时间:" + sendTime);
} else {
return false;
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
// 初始化socket
private void initSocket() throws UnknownHostException, IOException {
Socket socket = new Socket(HOST, PORT);
mSocket = new WeakReference<Socket>(socket);
mReadThread = new ReadThread(socket);
mReadThread.start();
mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);// 初始化成功后,就准备发送心跳包
}
// 释放socket
private void releaseLastSocket(WeakReference<Socket> mSocket) {
try {
if (null != mSocket) {
Socket sk = mSocket.get();
if (!sk.isClosed()) {
sk.close();
}
sk = null;
mSocket = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
class InitSocketThread extends Thread {
@Override
public void run() {
super.run();
try {
initSocket();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class ReadThread extends Thread {
private WeakReference<Socket> mWeakSocket;
private boolean isStart = true;
public ReadThread(Socket socket) {
mWeakSocket = new WeakReference<Socket>(socket);
}
public void release() {
isStart = false;
releaseLastSocket(mWeakSocket);
}
@SuppressLint("NewApi")
@Override
public void run() {
super.run();
Socket socket = mWeakSocket.get();
if (null != socket) {
try {
InputStream is = socket.getInputStream();
byte[] buffer = new byte[1024 * 4];
int length = 0;
while (!socket.isClosed() && !socket.isInputShutdown()
&& isStart && ((length = is.read(buffer)) != -1)) {
if (length > 0) {
String message = new String(Arrays.copyOf(buffer,
length)).trim();
Log.i(TAG, "收到服务器发送来的消息:"+message);
// 收到服务器过来的消息,就通过Broadcast发送出去
if (message.equals("ok")) {// 处理心跳回复
Intent intent = new Intent(HEART_BEAT_ACTION);
sendBroadcast(intent);
} else {
// 其他消息回复
Intent intent = new Intent(MESSAGE_ACTION);
intent.putExtra("message", message);
sendBroadcast(intent);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Activity:
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
private static final String TAG = “MainActivity”;
private Intent mServiceIntent;
private IBackService iBackService;
private TextView tv;
private EditText et;
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initData();
}
private void initViews() {
tv = (TextView) findViewById(R.id.resule_text);
et = (EditText) findViewById(R.id.content_edit);
btn = (Button) findViewById(R.id.send);
}
private void initData() {
mServiceIntent = new Intent(this, BackService.class);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String string = et.getText().toString().trim();
Log.i(TAG, string);
try {
Log.i(TAG, "是否为空:" + iBackService);
if (iBackService == null) {
Toast.makeText(getApplicationContext(),
"没有连接,可能是服务器已断开", Toast.LENGTH_SHORT).show();
} else {
boolean isSend = iBackService.sendMessage(string);
Toast.makeText(MainActivity.this,
isSend ? "success" : "fail", Toast.LENGTH_SHORT)
.show();
et.setText("");
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
@Override
protected void onStart() {
super.onStart();
bindService(mServiceIntent, conn, BIND_AUTO_CREATE);
// 开始服务
registerReceiver();
}
@Override
protected void onResume() {
super.onResume();
// 注册广播 最好在onResume中注册
// registerReceiver();
}
@Override
protected void onPause() {
super.onPause();
// 注销广播 最好在onPause上注销
unregisterReceiver(mReceiver);
// 注销服务
unbindService(conn);
}
// 注册广播
private void registerReceiver() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BackService.HEART_BEAT_ACTION);
intentFilter.addAction(BackService.MESSAGE_ACTION);
registerReceiver(mReceiver, intentFilter);
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// 消息广播
if (action.equals(BackService.MESSAGE_ACTION)) {
String stringExtra = intent.getStringExtra("message");
tv.setText(stringExtra);
} else if (action.equals(BackService.HEART_BEAT_ACTION)) {// 心跳广播
tv.setText("正常心跳");
}
}
};
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// 未连接为空
iBackService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 已连接
iBackService = IBackService.Stub.asInterface(service);
}
};
}
aidl文件:
interface IBackService{
boolean sendMessage(String message);
}
补充知识:
五、android系统的推送和iOS的推送有什么区别
首先我们必须知道,所有的推送功能必须有一个客户端和服务器的长连接,因为推送是由服务器主动向客户端发送消息,如果客户端和服务器之间不存在一个长连接那么服务器是无法来主动连接客户端的。因而推送功能都是基于长连接的基础是上的。
IOS长连接是由系统来维护的,也就是说苹果的IOS系统在系统级别维护了一个客户端和苹果服务器的长链接,IOS上的所有应用上的推送都是先将消息推送到苹果的服务器然后将苹果服务器通过这个系统级别的长连接推送到手机终端上,这样的的几个好处为:
在手机终端始终只要维护一个长连接即可,而且由于这个长连接是系统级别的不会出现被杀死而无法推送的情况。
省电,不会出现每个应用都各自维护一个自己的长连接。
安全,只有在苹果注册的开发者才能够进行推送,等等。
android的长连接是由每个应用各自维护的,但是google也推出了和苹果技术架构相似的推送框架,C2DM,云端推送功能,但是由于google的服务器不在中国境内,其他的原因你懂的。所以导致这个推送无法使用,android的开发者不得不自己去维护一个长链接,于是每个应用如果都24小时在线,那么都得各自维护一个长连接,这种电量和流量的消耗是可想而知的。虽然国内也出现了各种推送平台,但是都无法达到只维护一个长连接这种消耗的级别。
作者:TianciZhu
来源:CSDN
原文:https://blog.csdn.net/tiancizhu/article/details/76241126