socket工具android,Android通过socket长连接实现推送

工具:Android studio

软件方法及协议:socket、protobuf

实现原理:

通过本地建立一个socket,绑定服务器IP和port,然后connect,再开启另外线程定时心跳(注意这里的心跳不是自定义发送数据,而是采用socket本身的心跳功能sendUrgentData,否则有坑),心跳失败则自动重连,另一方面,启动循环从缓存获取socket数据。

大致框架

0776dac9e3a3

推送实现流程图

实现代码:

public class QpushClient implements Runnable {

protected static volatile QpushClient mInstance;//volatile可保证可见性和有序性

protected Handler mHandler;

protected InetSocketAddress mAddress;

String mIMEI;

protected String TAG = "QpushClient";

//socket连接的超时时间

private final int CONNECT_TIME_OUT = 5 * 1000;

//巡检周期

private final int CHECK_PERIOD = 2 * 1000;

//连接尝试间隔时间

private final int CONNECT_PERIOD = 30 * 1000;

private final int HEARTBEART_PERIOD = 30 * 1000;

//若连接失败或响应失败,则尝试次数为9,若仍无效,则不再尝试

private final int CONNECT_TRY_TIMES = 9;

private final int SEND_MSG_TYPE_HEARTBEAT = 1; //心跳包

private final int SEND_MSG_TYPE_SOCKET_LOGIN = 2; //发送socket登录包

//连接尝试次数

private int mConnectCount;

Socket mClientSocket;

String mHost;

int mPort;

//设置是否去读取数据

boolean isStartRecieveMsg = false;

//开启心跳检测

boolean isKeepHeartBeat = false;

BufferedReader mReader;

ScheduledExecutorService executor;//定位定时器

HeartBeatTask mHeartBeatTask;

private QpushClient(Handler handler) {

mHandler = handler;

}

public static QpushClient getInstance(Handler handler) {

if (mInstance == null) {

synchronized(QpushClient.class){ //线程安全,所以加锁

if(mInstance == null){

mInstance = new QpushClient(handler);

}}

}

return mInstance;

}

public void init(String host, int port,String imei) {

mHost = host;

mPort = port;

mIMEI = imei;

new Thread(this).start();

isStartRecieveMsg = true;

isKeepHeartBeat = true;

}

@Override

public void run() {

mAddress = new InetSocketAddress(getIP(mHost), mPort);

//尝试连接,若未连接,则设置尝试次数

while (mConnectCount < CONNECT_TRY_TIMES) {

connect();

if (!mClientSocket.isConnected()) {

mConnectCount++;

sleep(CONNECT_PERIOD);

} else {

mConnectCount = 0;//连接上,则恢复置0

break;

}

}

if (mClientSocket.isConnected()) {

keepHeartBeat();

recvProtobufMsg();

// recvStringMsg();

}

}

private void connect() {

try {

if (mClientSocket == null) {

mClientSocket = new Socket();

}

mClientSocket.connect(mAddress, CONNECT_TIME_OUT);

Driver.HeartBeat heartbeat = Driver.HeartBeat.newBuilder().setImei(mIMEI).build();

Driver.ClientMessage socketLogin = Driver.ClientMessage.newBuilder().setType(1).setHeartBeat(heartbeat).build();

sendMsg(socketLogin,SEND_MSG_TYPE_SOCKET_LOGIN);

} catch (IOException e) {

e.printStackTrace();

Log.e(TAG, "连接失败 mClientSocket.connect fail ,ip=" + mAddress.getHostName() + ";port=" + mAddress.getPort() + ";detail:" + e.getMessage());

}

}

/**

* 心跳维护

*/

private void keepHeartBeat() {

//设置心跳频率,启动心跳

if (isKeepHeartBeat) {

if (mHeartBeatTask == null) {

mHeartBeatTask = new HeartBeatTask();

}

try {

if (executor != null) {

executor.shutdownNow();

executor = null;

}

executor = Executors.newScheduledThreadPool(1);

executor.scheduleAtFixedRate(

mHeartBeatTask,

1000, //initDelay

HEARTBEART_PERIOD, //period

TimeUnit.MILLISECONDS);

} catch (Exception ex) {

ex.printStackTrace();

}

}

}

/**

* @param message

* @param type 1=login;2=心跳;

*/

public void sendMsg(String message, int type) {

PrintWriter writer;

try {

writer = new PrintWriter(new OutputStreamWriter(mClientSocket.getOutputStream(), "UTF-8"), true);

writer.println(message);

Log.e(TAG, "sendMsg Socket.isClosed()=" + mClientSocket.isClosed() + ";connect=" + mClientSocket.isConnected());

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

switch (type) {

case SEND_MSG_TYPE_HEARTBEAT:

mHandler.obtainMessage(QpushService.PUSH_TYPE_PROTO_DATA, "发送心跳异常").sendToTarget();

break;

}

}

}

/**

* @param message

* @param type 1=login;2=心跳;

*/

public void sendMsg(Driver.ClientMessage message, int type) {

try {

message.writeTo(mClientSocket.getOutputStream());

Log.e(TAG, "sendMsg success");

} catch (IOException e) {

// TODO Auto-generated catch block

if (type == SEND_MSG_TYPE_HEARTBEAT) {

//心跳失败

Log.e(TAG, "心跳失败");

if (mClientSocket.isClosed()) {

connect();

}

} else {

Log.e(TAG, "发送数据失败");

}

e.printStackTrace();

}

}

/**

* 不断的检测是否有服务器推送的数据过来

*/

public void recvStringMsg() {

while (mClientSocket != null && mClientSocket.isConnected() && !mClientSocket.isClosed()) {

try {

mReader = new BufferedReader(new InputStreamReader(mClientSocket.getInputStream(), "UTF-8"));

String data = mReader.readLine();

Log.e(TAG, "recvStringMsg data=" + data);

} catch (IOException e) {

e.printStackTrace();

} catch (Exception ex) {

ex.printStackTrace();

}

sleep(2000);

}

sleep(CHECK_PERIOD);

}

/**

* 不断的检测是否有服务器推送的数据过来

*/

public void recvProtobufMsg() {

while (isStartRecieveMsg) {

try {

byte[] resultByte = recvByteMsg(mClientSocket.getInputStream());

if (resultByte != null) {

Driver.ClientMessage retMsg = Driver.ClientMessage.parseFrom(resultByte);

mHandler.obtainMessage(QpushService.PUSH_TYPE_PROTO_DATA, retMsg).sendToTarget();

} else {

Log.e(TAG, "resultByte is null");

}

} catch (IOException e) {

e.printStackTrace();

} catch (Exception ex) {

ex.printStackTrace();

}

sleep(5 * 1000);

}

}

/**

* 接收server的信息

*

* @return

*/

public byte[] recvByteMsg(InputStream inpustream) {

try {

byte len[] = new byte[1024];

int count = inpustream.read(len);

byte[] temp = new byte[count];

for (int i = 0; i < count; i++) {

temp[i] = len[i];

}

return temp;

} catch (Exception localException) {

localException.printStackTrace();

}

return null;

}

class HeartBeatTask implements Runnable {

@Override

public void run() {

//执行发送心跳

try {

mClientSocket.sendUrgentData(65);

} catch (IOException e) {

e.printStackTrace();

try {

Log.e(TAG, "socket心跳异常,尝试断开,重连");

mClientSocket.close();

mClientSocket = null;

//然后尝试重连

connect();

} catch (IOException e1) {

e1.printStackTrace();

}

}

Log.e(TAG, "发送心跳,Socket.isClosed()=" + mClientSocket.isClosed() + ";connect" + mClientSocket.isConnected());

}

}

/**

* 通过域名获取IP

*

* @param domain

* @return

*/

public String getIP(String domain) {

String IPAddress = "";

InetAddress ReturnStr1 = null;

try {

ReturnStr1 = java.net.InetAddress.getByName(domain);

IPAddress = ReturnStr1.getHostAddress();

} catch (UnknownHostException e) {

e.printStackTrace();

Log.e(TAG, "获取IP失败" + e.getMessage());

}

return IPAddress;

// return "192.168.3.121";

}

private void sleep(long sleepTime) {

try {

Thread.sleep(sleepTime);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

/**

* 销毁socket

*/

public void onDestory() {

if (mClientSocket != null) {

try {

mClientSocket.close();

} catch (IOException e) {

e.printStackTrace();

}

mClientSocket = null;

}

}

/*

* Ready for use.

*/

public void close() {

try {

if (mClientSocket != null && !mClientSocket.isClosed())

mClientSocket.close();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

QpushService类:主要负责启动socket客户端及管理消息

public class QpushService extends Service {

//数据推送,不显示到通知栏

final static int PUSH_TYPE_DATA = 1;

final static int PUSH_TYPE_PROTO_DATA = 2;

Handler mHandler;

String TAG = "QpushService";

QpushClient mClient;

@Override

public void onCreate() {

Log.e(TAG, "onCreate");

initHandler();

super.onCreate();

}

/**

* 初始化handler,用于接收推送的消息

*/

private void initHandler() {

mHandler = new Handler() {

@Override

public void handleMessage(Message msg) {

switch (msg.what) {

case PUSH_TYPE_DATA:

Log.e(TAG, "PUSH_TYPE_DATA");

String data = (String) msg.obj;

ToastUtil.showShort(QpushService.this.getApplicationContext(), data);

case PUSH_TYPE_PROTO_DATA:

Driver.ClientMessage clientMessage = (Driver.ClientMessage) msg.obj;

Log.e(TAG, "PUSH_TYPE_PROTO_DATA");

Intent intentCancelRighr = new Intent();

intentCancelRighr.setAction(PushReceiverAction.PUSH_ACTION);

intentCancelRighr.putExtra("data", clientMessage);

getApplicationContext().sendBroadcast(intentCancelRighr);

break;

}

}

};

}

@Nullable

@Override

public IBinder onBind(Intent intent) {

Log.e(TAG, "onBind");

return null;

}

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

mClient = QpushClient.getInstance(mHandler);

mClient.init(HttpUrls.SOCKET_HOST, HttpUrls.SOCKET_PORT, AppUtils.getPesudoUniqueID());

return super.onStartCommand(intent, flags, startId);

}

@Override

public void onDestroy() {

Log.e(TAG, "onDestroy");

super.onDestroy();

mClient.onDestory();

}

}

PushReceiver类:接收service消息的广播,运行的主进程,防止接收不到数据

public class PushReceiver extends BroadcastReceiver {

Context mContext;

String TAG = "PushReceiver";

@Override

public void onReceive(Context context, Intent intent) {

mContext = context;

if (intent.getAction().equals(PushReceiverAction.PUSH_ACTION)) {

//推送的通知消息

Driver.ClientMessage clientMessage = (Driver.ClientMessage) intent.getSerializableExtra("data");

if (clientMessage != null) {

switch (clientMessage.getType()) {

case 1: //socket登录,不做处理

Log.e(TAG, "socket登录消息,sid=" + clientMessage.getHeartBeat().getSid());

MyApplication.sid = clientMessage.getHeartBeat().getSid();

// showNotification("socket","登录成功",1);

break;

case 2://通知告知被迫取消领取红包资格

Log.e(TAG, "取消抢红包资格,title=" + clientMessage.getLogOut().getTitle() + ";message=" + clientMessage.getLogOut().getContent());

ToastUtil.showShort(mContext, "被迫取消抢红包资格");

Intent intentCancelRighr = new Intent();

intentCancelRighr.setAction(PushReceiverAction.CANCEL_REDPACKET_RIGHT);

intentCancelRighr.putExtra("data", clientMessage);

context.sendBroadcast(intentCancelRighr);

MyApplication.mLoginStatus = 1;

showNotification(clientMessage.getLogOut().getTitle(), clientMessage.getLogOut().getContent(), 1);

break;

case 3: //领取红包/未领到红包消息

Intent intentGo = new Intent();

intentGo.setAction(clientMessage.getRedPacket().getResult() == 1 ? PushReceiverAction.GET_REDPACKET_ACTION : PushReceiverAction.DONT_GET_REDPACKET_ACTION);

intentGo.putExtra("data", clientMessage);

context.sendBroadcast(intentGo);

Log.e(TAG, "红包消息,content=" + clientMessage.getRedPacket().getContent());

showNotification(clientMessage.getRedPacket().getTitle(), clientMessage.getRedPacket().getContent(), 2);

break;

}

} else {

Log.e(TAG, "clientMessage is null");

}

}

}

/**

* 在状态栏显示通知

*/

@SuppressWarnings("deprecation")

private void showNotification(String title, String content, int type) {

// 创建一个NotificationManager的引用

NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(android.content.Context

.NOTIFICATION_SERVICE);

NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext);

builder.setContentTitle(title)

.setContentText(content)

.setTicker(title)

// .setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL))//点击通知栏设置意图

.setWhen(System.currentTimeMillis())

.setPriority(Notification.PRIORITY_HIGH)

// .setAutoCancel(true)

.setOngoing(false)//ture,设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接)

.setSmallIcon(R.drawable.logo);

Notification notification = builder.build();

Log.e(TAG, "isSoundOn:" + SharePreferUtils.isSoundOn(mContext));

if (SharePreferUtils.isSoundOn(mContext) && type == 2) { //type==红包消息

notification.sound = Uri.parse("android.resource://" + mContext.getPackageName() + "/" + R.raw.diaoluo_da);

}

Intent notificationIntent;

if (type == 2) {

//进入我的资产

notificationIntent = HomeActivity.toWitchFragmentOfHome(mContext, Constants.TOWITCHOFHOME_MYASSETS);

} else {

//进入主页流量球

notificationIntent = HomeActivity.toWitchFragmentOfHome(mContext, Constants.TOWITCHOFHOME_BALL);

}

Log.e(TAG, "type=" + type);

// 点击该通知后要跳转的Activity

PendingIntent contentItent = PendingIntent.getActivity(mContext, 0, notificationIntent, 0);

notification.contentIntent = contentItent;

notification.flags = Notification.FLAG_AUTO_CANCEL;

// 把Notification传递给NotificationManager

notificationManager.notify(type, notification);

}

}

Driver类:该类是通过proto编译出来的,代码量比较大,Driver.proto文件贴下来,Driver.java文件我附上下载链接,编译教程见:protobuf在Android推送中的使用方法(比json、xml都要好的数据结构)

Driver.proto文件:

syntax = "proto3";

//编译教程:http://www.jianshu.com/p/8036003cb849

message ClientMessage

{

int32 type = 1; //类型1=socket登录;2=取消抢红包资格;3=领取红包消息

HeartBeat heartBeat = 2; //socket登录

LogOut logOut = 3; //取消抢红包资格消息

RedPacket redPacket = 4; //领取&未抢到红包消息

}

message HeartBeat

{

string sid = 1; //服务ID

string imei = 2; //手机唯一编号imei

}

message LogOut

{

string sid = 1; //服务ID

int32 result = 2; //1成功,2失败

string title = 3; //消息标题

string content = 4; //消息内容

}

message RedPacket

{

string sid = 1; //服务ID

int32 result = 2; //1成功,2失败

int32 price = 3; //红包金额单位分

string title = 4; //消息标题

string content = 5; //消息内容

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/431275.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一行代码为UITextField添加收键盘功能

iOS开发中收键盘是十分常用的功能&#xff0c;只需一行代码即可为ViewController添加工具条收键盘功能 更重要的是使用catogory&#xff0c;无代码污染。 代码 // UITextFieldkeyboard.h // TextFieldKeyBord // // Created by luo.h on 15/10/8. // Copyright © 2015…

excel一列求和_【excel每日提升】Excel周日不排班!

【新朋友】点击标题下面蓝色字“王俊东“关注。 【老朋友】点击右上角&#xff0c;转发或分享本页面内容。excel系列课程excel特效系列课程&#xff01;第1节&#xff1a;Excel有公式的单元格标记颜色&#xff0c;很简单&#xff01;第2节&#xff1a;Excel删除空行&#xff0…

android 减速动画,Android View Animation

概述可译为视图动画&#xff0c;分为缩放动画平移动画渐变动画旋转动画Android系统中定义了一个抽象类Animation来定义这种视图动画&#xff0c;它的具体子类如下表&#xff1a;动画名称对应的子类xml中标签描述缩放动画ScaleAnimation< scale />S平移动画TranslateAnima…

excel排名_Excel案例:比赛中,如何实时显示排名

其实今天的重点&#xff0c;是要借这个常规比赛记分与公布的案例&#xff0c;介绍一下数组函数&#xff01;今天的案例&#xff0c;最关键的地方只用了一次数组函数就搞定了&#xff01;------分-----割-----线-----下面进入案例&#xff1a;常见的比赛&#xff0c;有很评委一起…

node 存储过程_用Node.js操作跨平台数据库Firebird

FireBirdFirebird是一个跨平台的关系数据库系统&#xff0c;目前能够运行在Windows、linux和各种Unix操作系统上&#xff0c;提供了大部分SQL-99标准的功能。它既能作为多用户环境下的数据库服务器运行&#xff0c;也提供嵌入式数据库的实现。Firebird脱胎于Borland公司的开源版…

鸿蒙是内核名字,华为徐直军:鸿蒙只是内核的名字,是媒体给误解成操作系统...

集微网消息(文/Jimmy)&#xff0c;华为轮值董事徐直军表示鸿蒙操作系统名字是媒体取得&#xff0c;华为并没打算取这个名字。他解释称&#xff0c;本来鸿蒙这个是用于华为内部一个内核的名字&#xff0c;只是挂在市场监管总局那里注册了。然后不知道哪个媒体好朋友看到了这个&a…

手机进程设置多少个最好_安卓手机难逃卡顿宿命?打开4个系统设置,秒变新机般流畅...

近两年安卓手机的进步无疑的巨大的&#xff0c;不论是界面美观性、系统流畅度&#xff0c;还是功能的丰富性都有很大的进步&#xff0c;但是针对安卓的旧设备&#xff0c;用了两三年依旧开始变得卡顿&#xff0c;难道安卓手机真的难逃卡顿的宿命吗&#xff1f;虽然不能解决&…

html5新增的js,HTML5新增属性data-*和js/jquery之间的交互及注意事项

html的data-*属性是HTML5的新属性&#xff0c;用的时候在IE8html的data-*属性是HTML5的新属性&#xff0c;用来自定义属性&#xff0c;以在JS 中控制&#xff0c;用不会出问题&#xff0c;就是用js去获得的时候会有问题&#xff0c; 不过用Jquery的$(element).attr("data-…

韦东山 IMX6ULL和正点原子_GPIO和Pinctrl子系统的使用在100ASK_IMX6ULL上机实验

来源&#xff1a;百问网作者&#xff1a;韦东山本文字数&#xff1a;924&#xff0c;阅读时长&#xff1a;3分钟阅读本节前先阅读&#xff1a;Pinctrl子系统重要概念GPIO子系统重要概念基于GPIO子系统的LED驱动程序1.1 确定引脚并生成设备树节点NXP公司对于IMX6ULL芯片&#xf…

php foreach 收邮件_php机制本身不提供多线程的操作,有了swoole就不一样了

多个任务同时执行比如&#xff0c;我们要对已知的用户数据进行判断&#xff0c;是否需要发送邮件和短信&#xff0c;如果需要发送则发送。不使用多进程时&#xff0c;我们首先判断是否发送邮件&#xff0c;如果需要则发送&#xff1b;然后再判断是否需要发送短信&#xff0c;如…

C#中的interface

接口&#xff08;interface&#xff09; 接口泛指实体把自己提供给外界的一种抽象化物&#xff08;可以为另一实体&#xff09;&#xff0c;用以由内部操作分离出外部沟通方法&#xff0c;使其能被修改内部而不影响外界其他实体与其交互的方式。 接口实际上是一个约定: 如&…

网易不进垃圾箱html,腾讯QQ、网易126、163邮箱发送邮件进入垃圾箱及收不到邮件怎么办?...

近期有用户反馈在云奇付(www.361ser.com)会员中心通过腾讯QQ邮箱、网易126、163邮箱绑定账号及安全验证无法正常收取或收不到云奇付发送的邮件怎么办&#xff1f;其实这是因为QQ邮箱、126邮箱、163邮箱等其它邮箱都有反垃圾邮件机制&#xff0c;很多企业网站的邮件都被误拦截到…

java技术分享主题_Java开发入门:适合新手练手的Java项目(附源码下载)

Java作为一门古老的语言&#xff0c;已有20年左右的历史&#xff0c;这在发展日新月异的技术圈可以说是一个神话。虽然不少人曾抱怨Java语言就像老太太的裹脚布&#xff0c;又臭又长&#xff0c;有时写了500行都不能表达程序员的意图。但从市场上的招聘现状看&#xff0c;Java工…

uni app 调用网络打印机_uni-app 的使用体验总结

[实践] uni-app 的使用总结最近使用 uni-app 的感受。使用体验没用之前以为真和 Vue 一样&#xff0c;用了之后才知道。有点类似 Vue 和 小程序结合的感觉。写类似小程序的标签&#xff0c;有着小程序和 Vue 的生命周期钩子。对比 uni-app 文档和微信小程序的文档&#xff0c;不…

html 微信发送给朋友,H5链接分享给微信好友,显示标题、描述、缩略图

效果展示image.png1.公众号设置1.1公众号需要经过认证&#xff0c;具有分享接口功能image.png1.2填写正确的域名(域名需要经过备案)image.png1.3 保存好AppID、AppSecret、设置好ip白名单(非常重要)image.png2.php代码(需要有php环境下&#xff0c;放到域名目录下)2.1展示下目录…

python怎么创建变量_python怎么创建变量

变量(variable)是学习python初始时&#xff0c;就会接触到的一个新的知识点&#xff0c;也是一个需要熟知的概念。python是一种动态类型语言&#xff0c;在赋值的执行中可以绑定不同类型的值&#xff0c;这个过程叫做变量赋值操作&#xff0c;赋值同时确定了变量类型。在python…

nginx 学习笔记【持续更新...】

1.如果在安装过程中出现以下错误 需要安装pcre库 解决方案&#xff1a;yum install pcre 2.如果nginx启动提示端口被占用,则停止该端口的服务再启动nginx,一般为httpd服务 解决方案&#xff1a;service httpd stop 3.nginx下php编译 ./configure --prefix/usr/local/fastphp …

python怎么放音乐_python怎么播放音乐

Python播放音乐要使用pygame模块 首先安装pygame模块&#xff1a;pip install pygame 方法实现&#xff1a;def play_music(self): filepath r"E:\music\消愁.mp3"; pygame.mixer.init() # 加载音乐 pygame.mixer.music.load(filepath) pygame.mixer.music.play(sta…

怎么用计算机打分数,电脑excel中分数怎么打出来(图解excel分数输入法)

在日常工作中&#xff0c;我们经常需要在excel中输入分数&#xff0c;默认情况下&#xff0c;当我们直接输入分数&#xff0c;比如输入“9/16” 回车后&#xff0c;会发现系统显示的却是日期数据&#xff0c;如下图所示&#xff0c;那么分数该怎么输入呢&#xff1f;今天就来和…

word 7桌面上的计算机图标是,怎么设置win7系统桌面图标都变成word图标的处理办法...

随着电脑的使用率越来越高&#xff0c;我们有时候可能会遇到win7系统桌面图标都变成word图标问题&#xff0c;如果我们遇到了win7系统桌面图标都变成word图标的问题&#xff0c;要怎么处理win7系统桌面图标都变成word图标呢&#xff1f;我们只用  双击打开桌面上的计算机文件…