日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

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

發布時間:2023/12/10 Android 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 socket工具android,Android通过socket长连接实现推送 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

工具:Android studio

軟件方法及協議:socket、protobuf

實現原理:

通過本地建立一個socket,綁定服務器IP和port,然后connect,再開啟另外線程定時心跳(注意這里的心跳不是自定義發送數據,而是采用socket本身的心跳功能sendUrgentData,否則有坑),心跳失敗則自動重連,另一方面,啟動循環從緩存獲取socket數據。

大致框架

推送實現流程圖

實現代碼:

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; //消息內容

}

總結

以上是生活随笔為你收集整理的socket工具android,Android通过socket长连接实现推送的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。