由於項目需求有推送的功能,技術總監又不想用第三方極光推送,所以在同事的幫助下搭建了簡單的socket通訊。
首先服務端 java搭建
AndriodService.java
package com.android.net;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class AndroidService {
// // 定義保存所有的Socket
// public static List<Socket> socketList = new ArrayList<Socket>();
//
// public static void main(String[] args) throws IOException {
// ServerSocket server = new ServerSocket(3000);
// while (true) {
// Socket s = server.accept();
// socketList.add(s);
// // 每當客戶端連接之後啓動一條ServerThread線程爲該客戶端服務
// new Thread(new ServiceThreada(s)).start();
//
// }
// }
public static void main(String[] args) throws IOException {
ServerSocket serivce = new ServerSocket(3000);
while (true) {
// 等待客戶端連接
Socket socket = serivce.accept();
String ip = socket.getInetAddress().toString();
System.out.println(System.currentTimeMillis() + "用戶IP:" + ip
+ " 鏈接了 ");
new Thread(new AndroidRunable(socket)).start();
}
}
}
AndroidRunable .java
package com.android.net;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
public class AndroidRunable implements Runnable {
Socket socket = null;
public AndroidRunable(Socket socket) {
this.socket = socket;
}
public void run() {
// 向android客戶端輸出hello worild
String line = null;
InputStream input;
OutputStream output;
String str = "hello world!";
try {
// 向客戶端發送信息
output = socket.getOutputStream();
input = socket.getInputStream();
// BufferedReader bff = new BufferedReader(
// new InputStreamReader(input));
// output.write(str.getBytes("gbk"));
// output.flush();
//半關閉socket
// socket.shutdownOutput();
// 獲取客戶端的信息
System.out.println(Thread.class.getName());
ObjectOutputStream oos=new ObjectOutputStream(output);
ObjectInputStream ois = new ObjectInputStream(input);
while (true) {
Object object = ois.readObject();
System.out.println(object);
if (object instanceof byte[]) {
byte[] buff = (byte[]) object;
System.out.print(new String(buff, "gbk"));
}
oos.writeObject("收到消息");
}
// 關閉輸入輸出流
// output.close();
// bff.close();
// input.close();
// socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
因爲推送打開服務端要一直保持在線,所以就不關了,反正項目中的服務端不是你搭,關鍵在客戶端是否正常。
客戶端 Andriod項目
首先我要知道客戶端收到服務端信息是什麼類型,其次要怎麼辦,怎麼執行。
我這個項目是 服務端發送過來一個json 客戶端解析得到要到數據,以廣播的形式發送通知。
上代碼
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Application;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
/**
* socket 封裝類
* 客戶端讀流線程
* ClientReadThead clientReadThead;
* 客戶端流線程
* ClientWriteThread clientWriteThread;
* @author onebot
*
*/
public class ClientSocket {
ClientReadThead clientReadThead;
ClientWriteThread clientWriteThread;
private static ClientSocket reClient;
private ClientSocket() {
// TODO Auto-generated constructor stub
}
private ClientSocket(Application context) {
// TODO Auto-generated constructor stub
new MyThread(context).start();
}
public static ClientSocket reSocket(Application context) {
if (reClient != null) {
reClient.close();
reClient = null;
}
if (reClient == null) {
synchronized (ClientSocket.class.getName()) {
if (reClient == null) {
reClient = new ClientSocket(context);
}
}
}
return reClient;
}
class MyThread extends Thread {
Application context;
public MyThread(Application context) {
this.context = context;
}
@Override
public void run() {
// 定義消息
Message msg = new Message();
msg.what = 0x11;
Bundle bundle = new Bundle();
bundle.clear();
try {
// 連接服務器 並設置連接超時爲5秒
Socket socket = new Socket();
socket.connect(new InetSocketAddress("服務端地址",端口號),
5000);
clientWriteThread = new ClientWriteThread(socket);
clientWriteThread.start();
clientReadThead = new ClientReadThead(socket, context);
clientReadThead.start();
timers();
} catch (Exception e) {
bundle.putString("msg", "連接異常,再次請求連接");
msg.setData(bundle);
e.printStackTrace();
// 發送消息 修改UI線程中的組件
try {
sleep(5000);
reSocket(context);
} catch (Exception e2) {
// TODO: handle exception
}
}
}
}
private void timers() {
Timer timer = new Timer();
timer.schedule(new TimerTasks(), 1000 * 60, 5 * 1000 * 60);
// 獲得鬧鐘管理器
// AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
// 設置任務執行計劃
// am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000 * 60, 5 *
// 1000 * 60, sender);//從firstTime纔開始執行,每隔5秒再執行
}
class TimerTasks extends TimerTask {
@Override
public void run() {
// TODO Auto-generated method stub
// 位置
}
}
//客戶端寫消息給服務器(位置消息)
public void writeMessage(Object ob) {
if (clientWriteThread != null) {
clientWriteThread.writeMessage(ob);
} else {
throw new RuntimeException("you need transfer method reSocket");
}
}
private void close() {
// TODO Auto-generated method stub
if (clientWriteThread != null) {
clientWriteThread.close();
clientWriteThread = null;
}
if (clientReadThead != null) {
clientReadThead.close();
clientReadThead = null;
}
System.gc();
}
//
public static void closeGc() {
reClient.close();
reClient = null;
System.gc();
}
}
ClientReadThead.java
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.net.Socket;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
/**
* 客戶端讀流線程
* @author onebot
*
*/
public class ClientReadThead extends Thread {
private Socket socket;
private ObjectInputStream ois;
private boolean tag = true;
private int len = -1;
private byte[] b;
public Application context;
public ClientReadThead(Socket socket, Application context) {
// TODO Auto-generated constructor stub
try {
this.context = context;
this.socket = socket;
this.ois = new ObjectInputStream(socket.getInputStream());
b = new byte[1024 * 1024 * 5];
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
tag = false;
}
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
try {
while (tag) {
synchronized (this) {
Object ob = ois.readObject();
dealMessage(ob);
}
}
close();
} catch (Exception e) {
// TODO: handle exception
tag = false;
ClientSocket.reSocket(context);
e.printStackTrace();
}
}
private void dealMessage(Object ob) {
// TODO Auto-generated method stub
// 廣播
Intent intent = new Intent();
intent.setAction("com.box");
Log.e("=========", "走沒走");
if (ob instanceof String) {
intent.putExtra("msg", ob.toString());
} else if (ob instanceof Serializable) {
Serializable s = (Serializable) ob;
intent.putExtra("msg", s);
}
context.sendOrderedBroadcast(intent, null); // 有序廣播發送
// Toast.makeText(context, "發送廣播成功", Toast.LENGTH_SHORT).show();
System.out.println(ob);
}
public void close() {
try {
ois.close();
ois = null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
ClientWriteThread .java
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
/**
* 客戶端寫流線程
*/
public class ClientWriteThread extends Thread {
private boolean tag = true;
private final static String TAG = "com.xiao.hei.Thread.WriteThread";
private Object o;
private Socket socket;
private ObjectOutputStream os;
public ClientWriteThread(Socket socket) {
// TODO Auto-generated constructor stub
try {
this.socket = socket;
this.os = new ObjectOutputStream(socket.getOutputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
tag = false;
}
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
try {
while (tag) {
synchronized (this) {
if (o == null)
wait();
else {
os.writeObject(o);
o = null;
}
}
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
synchronized public void writeMessage(Object o) {
System.out.println(o);
this.o = o;
notify();
}
public synchronized void close() {
try {
notify();
tag = false;
os.close();
socket.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}
廣播 SocketReceiver.java
import java.io.Serializable;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Builder;
import android.util.Log;
import android.widget.RemoteViews;
public class ClientReceiver extends BroadcastReceiver {
public static final String LOGIN_BOX = "com.box";
public static final String LOGIN_USER = "";
public static final String LOGIN_LOACTION = "";
public static final String MSG_SERVICE = "";
/** Notification構造器 */
NotificationCompat.Builder mBuilder;
/** Notification的ID */
int notifyId = 100;
/** Notification管理 */
public NotificationManager mNotificationManager;
public Context context;
public PushBean push;
public String MsgContent;
public int MsgType;
public String MsgImgpath;
@SuppressLint("NewApi")
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
// String action = intent.getAction();
System.out.println("接受到廣播");
this.context = context;
initNotify();
mNotificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);// 獲取系統通知的服務
push = new PushBean();
Object msg = intent.getExtras().get("msg");
Log.e(ClientReceiver.class.getName(), msg.toString());
if (msg instanceof String) {
deal_string(msg.toString());
} else if (msg instanceof Serializable) {
deal_serializable((Serializable) msg);
}
}
private void deal_serializable(Serializable serializable) {
// showIntentActivityNotify(serializable.toString());
shwoNotify(serializable.toString());
System.out.println("11111");
}
// / {"type":1,"msg":{}} type 類型 0 1 2 3 4 5 6
private void deal_string(String string) {
// try {
// JSONObject jo = new JSONObject(string);
// PushBean push = new PushBean();
// push.setContent(jo.getString("content"));
//
// push.setType(jo.getInt("type"));
// push.setImg_path(jo.getString("img"));
//
//
// MsgContent = push.getContent();
// MsgType=push.getType();
// MsgImgpath=push.getImg_path();
//
// if(MsgType==1){
// //類型1的通知
//
// }
// if(MsgType==2){
// //發送登錄後的類型2的通知
//
// }
// if(MsgType==3){
// //發送附件有網吧的類型3的通知
//
// {
//
shwoNotify(string);
}
// } catch (JSONException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//
// showIntentActivityNotify(string);
//}
public PendingIntent getDefalutIntent(int flags) {
PendingIntent pendingIntent = PendingIntent.getActivity(context, 1,
new Intent(), flags);
return pendingIntent;
}
public void showIntentActivityNotify(String text) {
// Notification.FLAG_ONGOING_EVENT --設置常駐
// Flag;Notification.FLAG_AUTO_CANCEL 通知欄上點擊此通知後自動清除此通知
// notification.flags = Notification.FLAG_AUTO_CANCEL;
// //在通知欄上點擊此通知後自動清除此通知
try {
// mBuilder = new NotificationCompat.Builder(context);
mBuilder.setAutoCancel(true)
// 點擊後讓通知將消失
.setContentTitle("奇葩葩").setContentText(text)
.setSmallIcon(R.drawable.ico_qpp).setTicker("有新的更新");
// 點擊的意圖ACTION是跳轉到Intent
Intent resultIntent = new Intent(context, MainActivity.class);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pendingIntent);
mNotificationManager.notify(notifyId, mBuilder.build());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 初始化通知欄
* */
private void initNotify() {
mBuilder = new NotificationCompat.Builder(context);
mBuilder.setContentTitle("<span style="font-family: Arial, Helvetica, sans-serif;">QPP</span>")
.setContentText("<span style="font-family: Arial, Helvetica, sans-serif;">QPP</span>有新的更新")
.setContentIntent(
getDefalutIntent(Notification.FLAG_AUTO_CANCEL))
// .setNumber(number)//顯示數量
.setTicker("測試通知來啦")// 通知首次出現在通知欄,帶上升動畫效果的
.setWhen(System.currentTimeMillis())// 通知產生的時間,會在通知信息裏顯示
.setPriority(Notification.PRIORITY_DEFAULT)// 設置該通知優先級
.setAutoCancel(true)// 設置這個標誌當用戶單擊面板就可以讓通知將自動取消
.setOngoing(false)// ture,設置他爲一個正在進行的通知。他們通常是用來表示一個後臺任務,用戶積極參與(如播放音樂)或以某種方式正在等待,因此佔用設備(如一個文件下載,同步操作,主動網絡連接)
.setDefaults(Notification.DEFAULT_VIBRATE)// 向通知添加聲音、閃燈和振動效果的最簡單、最一致的方式是使用當前的用戶默認設置,使用defaults屬性,可以組合:
// Notification.DEFAULT_ALL Notification.DEFAULT_SOUND 添加聲音 //
// requires VIBRATE permission
.setSmallIcon(R.drawable.ic_launcher);
}
public void shwoNotify(Object ob) {
// 先設定RemoteViews
RemoteViews view_custom = new RemoteViews(context.getPackageName(),
R.layout.view_custom);
// 設置對應IMAGEVIEW的ID的資源圖片
view_custom.setImageViewResource(R.id.custom_icon, R.drawable.ico_qpp);
// view_custom.setInt(R.id.custom_icon,"setBackgroundResource",R.drawable.icon);
view_custom.setTextViewText(R.id.tv_custom_title, "QPP");
view_custom.setTextViewText(R.id.tv_custom_content,
"附近的網咖" + ob.toString());
// view_custom.setTextViewText(R.id.tv_custom_time,
// String.valueOf(System.currentTimeMillis()));
// 設置顯示
// view_custom.setViewVisibility(R.id.tv_custom_time, View.VISIBLE);
// view_custom.setLong(R.id.tv_custom_time,"setTime",
// System.currentTimeMillis());//不知道爲啥會報錯,過會看看日誌
// 設置number
// NumberFormat num = NumberFormat.getIntegerInstance();
// view_custom.setTextViewText(R.id.tv_custom_num,
// num.format(this.number));
mBuilder = new Builder(context);
mBuilder.setContent(view_custom)
.setContentIntent(
getDefalutIntent(Notification.FLAG_AUTO_CANCEL))
.setWhen(System.currentTimeMillis())// 通知產生的時間,會在通知信息裏顯示
.setTicker("有新的消息 ").setPriority(Notification.PRIORITY_DEFAULT)// 設置該通知優先級
.setAutoCancel(true).setOngoing(false)// 不是正在進行的 true爲正在進行
// 效果和.flag一樣
.setSmallIcon(R.drawable.ico_qpp);
// 點擊的意圖ACTION是跳轉到Intent
Intent resultIntent = new Intent(context, MainActivity.class);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pendingIntent);
// mNotificationManager.notify(notifyId, mBuilder.build());
Notification notify = mBuilder.build();
notify.contentView = view_custom;
// Notification notify = new Notification();
// notify.icon = R.drawable.icon;
// notify.contentView = view_custom;
// notify.contentIntent =
// getDefalutIntent(Notification.FLAG_AUTO_CANCEL);
mNotificationManager.notify(notifyId, notify);
}
}
import android.app.Application;
public class BaseApplication extends Application{
public static ClientSocket clientSocket;
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
<span style="white-space:pre"> </span>//初始化
clientSocket = ClientSocket.reSocket(this);
}
}
清單文件的配置 註冊廣播 打開網絡權限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.umeng.message.example"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="22" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:name="com.umeng.message.example.BaseApplication"
android:theme="@style/AppTheme" >
<activity
android:name="com.umeng.message.example.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name="com.umeng.message.example.ClientReceiver"
>
<intent-filter android:priority="1000"
>
<action android:name="com.box"/>
</intent-filter>
</receiver>
</application>
</manifest>
在app中使用調用
如果需要客戶端登錄後發送消息給服務端 服務端纔開啓對用戶的推送 那麼
BaseActivity
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
public class BaseActivity extends Activity {
/** Notification管理 */
public NotificationManager mNotificationManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
initService();
}
/**
* 初始化要用到的系統服務
*/
private void initService() {
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
/**
* 清除當前創建的通知欄
*/
public void clearNotify(int notifyId){
mNotificationManager.cancel(notifyId);//刪除一個特定的通知ID對應的通知
// mNotification.cancel(getResources().getString(R.string.app_name));
}
/**
* 清除所有通知欄
* */
public void clearAllNotify() {
mNotificationManager.cancelAll();// 刪除你發的所有通知
}
/**
* @獲取默認的pendingIntent,爲了防止2.3及以下版本報錯
* @flags屬性:
* 在頂部常駐:Notification.FLAG_ONGOING_EVENT
* 點擊去除: Notification.FLAG_AUTO_CANCEL
*/
public PendingIntent getDefalutIntent(int flags){
PendingIntent pendingIntent= PendingIntent.getActivity(this, 1, new Intent(), flags);
return pendingIntent;
}
}
MainActivity
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Builder;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RemoteViews;
import android.widget.TextView;
import encryption.stringUtil.Util;
import encryption.util.NumUtil;
public class MainActivity extends BaseActivity {
/** Notification構造器 */
NotificationCompat.Builder mBuilder;
/** Notification的ID */
int notifyId = 100;
String buffer = "";
TextView txt1;
Button send;
EditText ed1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initNotify();
txt1 = (TextView) findViewById(R.id.txt1);
send = (Button) findViewById(R.id.send);
ed1 = (EditText) findViewById(R.id.ed1);
send.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// if (clientWriteThread != null)
// clientWriteThread.writeMessage("================");
// else
// System.err.println("null============ ");//發送消息到服務端
BaseApplication.clientSocket.writeMessage("================");
}
});
}
<span style="white-space:pre"> </span>//調用的方法
public void ClientWriteMsg(Object ob){
BaseApplication.clientSocket.writeMessage(ob);
}
}
友情提示代碼中的通知 請初始化再用。
代碼都在上面 就不發demo了 客戶端是封裝好了的,有斷線重連的過程。