Android連線熱點的Socket檔案傳輸
阿新 • • 發佈:2019-02-02
最近把測試丟過來的種種BUG解決後,終於有時間去研究研究Socket通訊,再加上以前做的WiFi連線和熱點開啟,於是有了現在的這篇博文:建立熱點發送檔案,讓另一臺手機連線熱點接收檔案。
效果圖:
兩臺裝置是如何傳輸檔案的:
- 傳送端->建立WiFi熱點
- 接收端->連線熱點
- 傳送端->傳送檔案列表
- 接收端->收到後展示檔案列表,選擇要接收的檔案傳送給傳送端
- 傳送端->傳送所選檔案
- 接收端->開始接收…
傳送端->建立WiFi熱點:
由於Android沒有直接開啟熱點的API,所以我們這裡採用反射。
/**
* 開啟便攜熱點
* @param context 上下文
* @param SSID 便攜熱點SSID
* @param password 便攜熱點密碼
* @return
*/
public static boolean openAp(Context context, String SSID, String password) {
if(TextUtils.isEmpty(SSID)) {
return false;
}
WifiManager wifiManager = (WifiManager) context.getSystemService(context.WIFI_SERVICE);
if (wifiManager.isWifiEnabled()) {
wifiManager.setWifiEnabled(false);
}
WifiConfiguration wifiConfiguration = getApConfig(SSID, password);
try {
if(wifiManager.isWifiEnabled()) {
//關閉WiFi
wifiManager.setWifiEnabled(false);
}
if(isApOn(context)) {
//關閉熱點
closeAp(context);
}
//使用反射開啟Wi-Fi熱點
Method method = wifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
method.invoke(wifiManager, wifiConfiguration, true);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
接收端->連線熱點:
連線熱點前先開啟WiFi廣播監聽事件,然後開啟WiFi,掃描周圍可用WiFi列表,拿到SSID再進行連線,最後在WiFi廣播監聽事件中比較已連線WIFI的SSID是否正確。
WiFi廣播監聽事件:
/**
* 註冊監聽WiFi操作的系統廣播
*/
private void registerWifiReceiver() {
IntentFilter filter = new IntentFilter();
//監聽WiFi開啟與關閉
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
//監聽掃描周圍可用WiFi列表結果
filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
//監聽WiFi連線與斷開
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
//註冊廣播
registerReceiver(mWifiBroadcaseReceiver, filter);
}
/**
* 反註冊WiFi相關的系統廣播
*/
private void unregisterWifiReceiver() {
if (mWifiBroadcaseReceiver != null) {
unregisterReceiver(mWifiBroadcaseReceiver);
mWifiBroadcaseReceiver = null;
}
}
WiFi廣播接收器:
public abstract class WifiBroadcaseReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if(intent != null) {
if(intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
//監聽WiFi開啟/關閉事件
int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0);
if(wifiState == WifiManager.WIFI_STATE_ENABLED) {
//WiFi已開啟
onWifiEnabled();
} else if(wifiState == WifiManager.WIFI_STATE_DISABLED) {
//WiFi已關閉
onWifiDisabled();
}
} else if(intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
WifiMgr wifiMgr = new WifiMgr(context);
List<ScanResult> scanResults = wifiMgr.getScanResults();
if(wifiMgr.isWifiEnabled() && scanResults != null && scanResults.size() > 0) {
//成功掃描
onScanResultsAvailable(scanResults);
}
} else if(intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
//網路狀態改變的廣播
NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if (info != null) {
if (info.getState().equals(NetworkInfo.State.CONNECTED)) {
//WiFi已連線
WifiMgr wifiMgr = new WifiMgr(context);
String connectedSSID = wifiMgr.getConnectedSSID();
onWifiConnected(connectedSSID);
} else if (info.getState().equals(NetworkInfo.State.DISCONNECTED)) {
//WiFi已斷開連線
onWifiDisconnected();
}
}
}
}
}
public abstract void onWifiEnabled();
public abstract void onWifiDisabled();
public abstract void onScanResultsAvailable(List<ScanResult> scanResults);
public abstract void onWifiConnected(String connectedSSID);
public abstract void onWifiDisconnected();
}
開啟和關閉WiFi:
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
/**
* 開啟WiFi
*/
public void openWifi() {
if (!mWifiManager.isWifiEnabled()) {
mWifiManager.setWifiEnabled(true);
}
}
/**
* 關閉WiFi
*/
public void closeWifi() {
if (mWifiManager.isWifiEnabled()) {
mWifiManager.setWifiEnabled(false);
}
/**
* 當前WiFi是否開啟
*/
public boolean isWifiEnabled() {
return mWifiManager.isWifiEnabled();
}
掃描周圍可用WiFi列表:
/**
* 掃描周圍可用WiFi
* @return
*/
public boolean startScan() {
if(isWifiEnabled()) {
return mWifiManager.startScan();
}
return false;
}
拿到WiFi掃描結果並且連線熱點,當接收端連線成功後,會發一個UDP廣播告知區域網內裝置連線熱點成功:
/**
* WiFi廣播接收器
*/
private WifiBroadcaseReceiver mWifiBroadcaseReceiver = new WifiBroadcaseReceiver() {
@Override
public void onWifiEnabled() {
//WiFi已開啟,開始掃描可用WiFi
mWifiMgr.startScan();
}
@Override
public void onWifiDisabled() {
//WiFi已關閉,清除可用WiFi列表
mSelectedSSID = "";
mScanResults.clear();
setupWifiAdapter();
}
@Override
public void onScanResultsAvailable(List<ScanResult> scanResults) {
//掃描周圍可用WiFi成功,設定可用WiFi列表
mScanResults.clear();
mScanResults.addAll(scanResults);
setupWifiAdapter();
}
@Override
public void onWifiConnected(String connectedSSID) {
//判斷指定WiFi是否連線成功
if (connectedSSID.equals(mSelectedSSID) && !mIsSendInitOrder) {
//連線成功
//告知傳送端,接收端初始化完畢
mHandler.sendEmptyMessage(MSG_FILE_RECEIVER_INIT_SUCCESS);
mIsSendInitOrder = true;
} else {
//連線成功的不是裝置WiFi,清除該WiFi,重新掃描周圍WiFi
LogUtils.e("連線到錯誤WiFi,正在斷開重連...");
mWifiMgr.disconnectWifi(connectedSSID);
mWifiMgr.startScan();
}
}
@Override
public void onWifiDisconnected() {
}
};
傳送端->傳送檔案列表:
當傳送端收到初始化完畢指令時,將用UDP廣播發送檔案列表。
/**
* 等待接收端傳送初始化完成指令,向其傳送檔案列表
* @param serverPort
* @throws Exception
*/
private void receiveInitSuccessOrder(int serverPort) throws Exception {
//確保WiFi連線後獲取正確IP地址
int tryCount = 0;
String localIpAddress = ApMgr.getHotspotLocalIpAddress(getContext());
while (localIpAddress.equals(Consts.DEFAULT_UNKNOW_IP) && tryCount < Consts.DEFAULT_TRY_COUNT) {
Thread.sleep(1000);
localIpAddress = ApMgr.getHotspotLocalIpAddress(getContext());
tryCount ++;
}
/** 這裡使用UDP傳送和接收指令 */
mDatagramSocket = new DatagramSocket(serverPort);
while (true) {
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
mDatagramSocket.receive(receivePacket);
String response = new String(receivePacket.getData()).trim();
if(isNotEmptyString(response)) {
LogUtils.e("接收到的訊息 -------->>>" + response);
if(response.equals(Consts.MSG_FILE_RECEIVER_INIT_SUCCESS)) {
//初始化成功指令
mHandler.sendEmptyMessage(MSG_FILE_RECEIVER_INIT_SUCCESS);
//傳送檔案列表
InetAddress inetAddress = receivePacket.getAddress();
int port = receivePacket.getPort();
//通過UDP傳送檔案列表給接收端
sendFileInfoListToFileReceiverWithUdp(inetAddress, port);
} else if(response.equals(Consts.MSG_START_SEND)) {
//開始傳送指令
initSenderServer();
} else {
//接收端發來的待發送檔案列表
parseFileInfo(response);
}
}
}
}
/**
* 通過UDP傳送檔案列表給接收端
* @param ipAddress IP地址
* @param serverPort 埠號
*/
private void sendFileInfoListToFileReceiverWithUdp(InetAddress ipAddress, int serverPort) {
if(!isEmptyList(mAllFileInfos)) {
String jsonStr = FileInfo.toJsonStr(mAllFileInfos);
DatagramPacket sendFileInfoPacket = new DatagramPacket(jsonStr.getBytes(), jsonStr.getBytes().length, ipAddress, serverPort);
try {
//傳送檔案列表
mDatagramSocket.send(sendFileInfoPacket);
LogUtils.i("傳送訊息 --------->>>" + jsonStr + "=== Success!");
mHandler.obtainMessage(MSG_SET_STATUS, "成功傳送檔案列表...").sendToTarget();
} catch (IOException e) {
e.printStackTrace();
LogUtils.i("傳送訊息 --------->>>" + jsonStr + "=== 失敗!");
}
}
}
接收端->選擇檔案,並告知傳送端開始傳送:
收到檔案列表後,接收端會將檔案列表展示在RecyclerView控制元件,通過選擇需要接收的檔案,點選“開始傳送”按鈕,將傳送“開始傳送”指令到傳送端,開啟埠進行檔案接收。
/**
* 設定接收檔案列表介面卡
*/
private void setupReceiveFilesAdapter() {
List<Map.Entry<String, FileInfo>> fileInfos = AppContext.getAppContext().getReceiverFileInfoMap();
Collections.sort(fileInfos, Consts.DEFAULT_COMPARATOR);
//設定介面卡
mReceiveFilesAdapter = new CommonAdapter<Map.Entry<String, FileInfo>>(getContext(), R.layout.item_files_selector, fileInfos) {
@Override
protected void convert(ViewHolder holder, Map.Entry<String, FileInfo> fileInfoMap, int position) {
final FileInfo fileInfo = fileInfoMap.getValue();
//檔案路徑
holder.setText(R.id.tv_item_files_selector_file_path, fileInfo.getFilePath());
//檔案大小
holder.setText(R.id.tv_item_files_selector_size, FileUtils.FormetFileSize(fileInfo.getSize()));
//檔案接收狀態
if(fileInfo.getProgress() >= 100) {
holder.setText(R.id.tv_item_files_selector_status, "接收完畢");
} else if(fileInfo.getProgress() == 0) {
holder.setText(R.id.tv_item_files_selector_status, "準備接收");
} else if(fileInfo.getProgress() < 100) {
holder.setText(R.id.tv_item_files_selector_status, "正在接收");
} else {
holder.setText(R.id.tv_item_files_selector_status, "接收失敗");
}
//檔案接收進度
ProgressBar progressBar = holder.getView(R.id.pb_item_files_selector);
progressBar.setProgress(fileInfo.getProgress());
//選中檔案
CheckBox checkBox = holder.getView(R.id.cb_item_files_selector);
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked) {
mSendFileInfos.add(fileInfo);
} else {
mSendFileInfos.remove(fileInfo);
}
//選中的檔案個數大於零才可點選底部按鈕
btnSendFileList.setEnabled(mSendFileInfos.size() > 0);
}
});
}
};
mReceiveFilesRecyclerView.setAdapter(mReceiveFilesAdapter);
//設定ListView樣式
mReceiveFilesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
//分割線
mReceiveFilesRecyclerView.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL));
}
/**
* 傳送選中的檔案列表給傳送端
*/
private void sendFileListToFileSender() {
new Thread() {
@Override
public void run() {
try {
//獲取IP地址
String serverIp = mWifiMgr.getIpAddressFromHotspot();
if(mDatagramSocket == null) {
//解決:java.net.BindException: bind failed: EADDRINUSE (Address already in use)
mDatagramSocket = new DatagramSocket(null);
mDatagramSocket.setReuseAddress(true);
mDatagramSocket.bind(new InetSocketAddress(Consts.DEFAULT_SERVER_UDP_PORT));
}
//傳送選中的檔案列表
InetAddress ipAddress = InetAddress.getByName(serverIp);
String jsonStr = FileInfo.toJsonStr(mSendFileInfos);
DatagramPacket sendPacket = new DatagramPacket(jsonStr.getBytes(), jsonStr.getBytes().length, ipAddress, Consts.DEFAULT_SERVER_UDP_PORT);
mDatagramSocket.send(sendPacket);
LogUtils.i("傳送訊息 ------->>>" + jsonStr);
//傳送開始傳送檔案指令
byte[] sendData = Consts.MSG_START_SEND.getBytes(BaseTransfer.UTF_8);
DatagramPacket sendPacket2 = new DatagramPacket(sendData, sendData.length, ipAddress, Consts.DEFAULT_SERVER_UDP_PORT);
mDatagramSocket.send(sendPacket2);
LogUtils.i("傳送訊息 ------->>>" + sendData);
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
/**
* ServerSocket啟動執行緒
*/
private class ReceiveServerRunnable implements Runnable {
@Override
public void run() {
try {
//開始接收檔案
String serverIp = mWifiMgr.getIpAddressFromHotspot();
List<Map.Entry<String, FileInfo>> fileInfoList = AppContext.getAppContext().getReceiverFileInfoMap();
Collections.sort(fileInfoList, Consts.DEFAULT_COMPARATOR);
for(final Map.Entry<String, FileInfo> fileInfoMap : fileInfoList) {
//連線傳送端,逐個檔案進行接收
final int position = fileInfoList.indexOf(fileInfoMap);
mClientSocket = new Socket(serverIp, Consts.DEFAULT_FILE_RECEIVE_SERVER_PORT);
FileReceiver fileReceiver = new FileReceiver(mClientSocket, fileInfoMap.getValue());
fileReceiver.setOnReceiveListener(new FileReceiver.OnReceiveListener() {
@Override
public void onStart() {
mHandler.obtainMessage(MSG_SET_STATUS, "開始接收"+ FileUtils.getFileName(fileInfoMap.getValue().getFilePath())).sendToTarget();
}
@Override
public void onProgress(FileInfo fileInfo, long progress, long total) {
//更新接收進度檢視
int i_progress = (int) (progress * 100 / total);
LogUtils.e("正在接收:" + fileInfo.getFilePath() + "\n當前進度:" + i_progress);
Message msg = new Message();
msg.what = MSG_UPDATE_PROGRESS;
msg.arg1 = position;
msg.arg2 = i_progress;
mHandler.sendMessage(msg);
}
@Override
public void onSuccess(FileInfo fileInfo) {
//接收成功
mHandler.obtainMessage(MSG_SET_STATUS, "檔案:" + FileUtils.getFileName(fileInfo.getFilePath()) + "接收成功").sendToTarget();
fileInfo.setResult(FileInfo.FLAG_SUCCESS);
AppContext.getAppContext().updateReceiverFileInfo(fileInfo);
Message msg = new Message();
msg.what = MSG_UPDATE_PROGRESS;
msg.arg1 = position;
msg.arg2 = 100;
mHandler.sendMessage(msg);
}
@Override
public void onFailure(Throwable throwable, FileInfo fileInfo) {
if(fileInfo != null) {
//接收失敗
mHandler.obtainMessage(MSG_SET_STATUS, "檔案:" + FileUtils.getFileName(fileInfo.getFilePath()) + "接收失敗").sendToTarget();
fileInfo.setResult(FileInfo.FLAG_FAILURE);
AppContext.getAppContext().updateReceiverFileInfo(fileInfo);
Message msg = new Message();
msg.what = MSG_UPDATE_PROGRESS;
msg.arg1 = position;
msg.arg2 = -1;
mHandler.sendMessage(msg);
}
}
});
//加入執行緒池執行
mFileReceiverList.add(fileReceiver);
AppContext.getAppContext().MAIN_EXECUTOR.execute(fileReceiver);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
檔案接收執行緒:
public class FileReceiver extends BaseTransfer implements Runnable {
/**
* 接收檔案的Socket的輸入輸出流
*/
private Socket mSocket;
private InputStream mInputStream;
/**
* 待接收的檔案資料
*/
private FileInfo mFileInfo;
/**
* 用來控制執行緒暫停、恢復
*/
private final Object LOCK = new Object();
private boolean mIsPaused = false;
/**
* 設定未執行執行緒的不執行標識
*/
private boolean mIsStop;
/**
* 該執行緒是否執行完畢
*/
private boolean mIsFinish;
/**
* 檔案接收監聽事件
*/
private OnReceiveListener mOnReceiveListener;
public FileReceiver(Socket socket, FileInfo fileInfo) {
mSocket = socket;
mFileInfo = fileInfo;
}
/**
* 設定接收監聽事件
* @param onReceiveListener
*/
public void setOnReceiveListener(OnReceiveListener onReceiveListener) {
mOnReceiveListener = onReceiveListener;
}
@Override
public void run() {
if(mIsStop) {
return;
}
//初始化
try {
if(mOnReceiveListener != null) {
mOnReceiveListener.onStart();
}
init();
} catch (Exception e) {
e.printStackTrace();
LogUtils.i("FileReceiver init() ------->>> occur expection");
if(mOnReceiveListener != null) {
mOnReceiveListener.onFailure(e, mFileInfo);
}
}
//傳送檔案實體資料
try {
parseBody();
} catch (Exception e) {
e.printStackTrace();
LogUtils.i("FileReceiver parseBody() ------->>> occur expection");
if(mOnReceiveListener != null) {
mOnReceiveListener.onFailure(e, mFileInfo);
}
}
//檔案傳輸完畢
try {
finishTransfer();
} catch (Exception e) {
e.printStackTrace();
LogUtils.i("FileReceiver finishTransfer() ------->>> occur expection");
if(mOnReceiveListener != null) {
mOnReceiveListener.onFailure(e, mFileInfo);
}
}
}
@Override
public void init() throws Exception {
if(mSocket != null) {
mInputStream = mSocket.getInputStream();
}
}
@Override
public void parseBody() throws Exception {
if(mFileInfo == null) {
return;
}
long fileSize = mFileInfo.getSize();
OutputStream fos = new FileOutputStream(FileUtils.gerateLocalFile(mFileInfo.getFilePath()));
byte[] bytes = new byte[BYTE_SIZE_DATA];
long total = 0;
int len = 0;
long sTime = System.currentTimeMillis();
long eTime = 0;
while ((len = mInputStream.read(bytes)) != -1) {
synchronized (LOCK) {
if(mIsPaused) {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//寫入檔案
fos.write(bytes, 0, len);
total = total + len;
//每隔200毫秒返回一次進度
eTime = System.currentTimeMillis();
if(eTime - sTime > 200) {
sTime = eTime;
if(mOnReceiveListener != null) {
mOnReceiveListener.onProgress(mFileInfo, total, fileSize);
}
}
}
}
//檔案接收成功
if(mOnReceiveListener != null) {
mOnReceiveListener.onSuccess(mFileInfo);
}
mIsFinish = true;
}
@Override
public void finishTransfer() throws Exception {
if(mInputStream != null) {
try {
mInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(mSocket != null && mSocket.isConnected()) {
try {
mSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 暫停接收執行緒
*/
public void pause() {
synchronized (LOCK) {
mIsPaused = true;
LOCK.notifyAll();
}
}
/**
* 恢復接收執行緒
*/
public void resume() {
synchronized (LOCK) {
mIsPaused = false;
LOCK.notifyAll();
}
}
/**
* 設定當前的接收任務不執行
*/
public void stop() {
mIsStop = true;
}
/**
* 檔案是否在接收中
* @return
*/
public boolean isRunning() {
return !mIsFinish;
}
/**
* 檔案接收監聽事件
*/
public interface OnReceiveListener {
void onStart();
void onProgress(FileInfo fileInfo, long progress, long total);
void onSuccess(FileInfo fileInfo);
void onFailure(Throwable throwable, FileInfo fileInfo);
}
}
傳送端->傳送所選檔案:
收到接收端發來的檔案列表和“開始傳送”指令後,傳送端將會把所選檔案逐個傳送給接收端。
/**
* 初始化傳送端服務,開始傳送檔案
*/
private void initSenderServer() {
mSenderServerRunnable = new SenderServerRunnable();
new Thread(mSenderServerRunnable).start();
}
/**
* 檔案傳送執行緒
*/
private class SenderServerRunnable implements Runnable {
private ServerSocket mServerSocket;
@Override
public void run() {
try {
//獲取待發送的檔案列表資料,按position索引排序
List<Map.Entry<String, FileInfo>> fileInfoList = AppContext.getAppContext().getSendFileInfoMap();
Collections.sort(fileInfoList, Consts.DEFAULT_COMPARATOR);
mServerSocket = new ServerSocket(Consts.DEFAULT_FILE_RECEIVE_SERVER_PORT);
//逐個檔案進行傳送
for(final Map.Entry<String, FileInfo> fileInfoMap : fileInfoList) {
final FileInfo fileInfo = fileInfoMap.getValue();
Socket socket = mServerSocket.accept();
FileSender fileSender = new FileSender(socket, fileInfo);
fileSender.setOnSendListener(new FileSender.OnSendListener() {
@Override
public void onStart() {
mHandler.obtainMessage(MSG_SET_STATUS, "開始傳送"+ FileUtils.getFileName(fileInfo.getFilePath())).sendToTarget();
}
@Override
public void onProgress(long progress, long total) {
//更新發送進度檢視
int i_progress = (int) (progress * 100 / total);
LogUtils.e("正在傳送:" + fileInfo.getFilePath() + "\n當前進度:" + i_progress);
Message msg = new Message();
msg.what = MSG_UPDATE_PROGRESS;
msg.arg1 = fileInfo.getPosition();
msg.arg2 = i_progress;
mHandler.sendMessage(msg);
}
@Override
public void onSuccess(FileInfo fileInfo) {
//傳送成功
mHandler.obtainMessage(MSG_SET_STATUS, "檔案:" + FileUtils.getFileName(fileInfo.getFilePath()) + "傳送成功").sendToTarget();
fileInfo.setResult(FileInfo.FLAG_SUCCESS);
AppContext.getAppContext().updateSendFileInfo(fileInfo);
Message msg = new Message();
msg.what = MSG_UPDATE_PROGRESS;
msg.arg1 = fileInfo.getPosition();
msg.arg2 = 100;
mHandler.sendMessage(msg);
}
@Override
public void onFailure(Throwable throwable, FileInfo fileInfo) {
//傳送失敗
mHandler.obtainMessage(MSG_SET_STATUS, "檔案:" + FileUtils.getFileName(fileInfo.getFilePath()) + "傳送失敗").sendToTarget();
fileInfo.setResult(FileInfo.FLAG_FAILURE);
AppContext.getAppContext().updateSendFileInfo(fileInfo);
Message msg = new Message();
msg.what = MSG_UPDATE_PROGRESS;
msg.arg1 = fileInfo.getPosition();
msg.arg2 = -1;
mHandler.sendMessage(msg);
}
});
//新增到執行緒池執行
mFileSenderList.add(fileSender);
AppContext.FILE_SENDER_EXECUTOR.execute(fileSender);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 關閉Socket連線
*/
public void closeServerSocket() {
if(mServerSocket != null) {
try {
mServerSocket.close();
mServerSocket = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
檔案傳送執行緒:
public class FileSender extends BaseTransfer implements Runnable {
/**
* 待發送的檔案資料
*/
private FileInfo mFileInfo;
/**
* 傳送檔案的Socket輸入輸出流
*/
private Socket mSocket;
private OutputStream mOutputStream;
/**
* 用來控制執行緒暫停、恢復
*/
private final Object LOCK = new Object();
private boolean mIsPause;
/**
* 該執行緒是否執行完畢
*/
private boolean mIsFinish;
/**
* 設定未執行執行緒的不執行標識
*/
private boolean mIsStop;
/**
* 檔案傳送監聽事件
*/
private OnSendListener mOnSendListener;
public FileSender(Socket socket, FileInfo fileInfo) {
mSocket = socket;
mFileInfo = fileInfo;
}
/**
* 設定傳送監聽事件
* @param onSendListener
*/
public void setOnSendListener(OnSendListener onSendListener) {
mOnSendListener = onSendListener;
}
@Override
public void run() {
if(mIsStop) {
return;
}
//初始化
try {
if(mOnSendListener != null) {
mOnSendListener.onStart();
}
init();
} catch (Exception e) {
e.printStackTrace();
LogUtils.i("FileSender init() ------->>> occur expection");
if(mOnSendListener != null) {
mOnSendListener.onFailure(e, mFileInfo);
}
}
//傳送檔案實體資料
try {
parseBody();
} catch (Exception e) {
e.printStackTrace();
LogUtils.i("FileSender parseBody() ------->>> occur expection");
if(mOnSendListener != null) {
mOnSendListener.onFailure(e, mFileInfo);
}
}
//檔案傳輸完畢
try {
finishTransfer();
} catch (Exception e) {
e.printStackTrace();
LogUtils.i("FileSender finishTransfer() ------->>> occur expection");
if(mOnSendListener != null) {
mOnSendListener.onFailure(e, mFileInfo);
}
}
}
@Override
public void init() throws Exception {
mSocket.setSoTimeout(30 * 1000);
OutputStream os = mSocket.getOutputStream();
mOutputStream = new BufferedOutputStream(os);
}
@Override
public void parseBody() throws Exception {
long fileSize = mFileInfo.getSize();
File file = new File(mFileInfo.getFilePath());
InputStream fis = new FileInputStream(file);
int len = 0;
long total = 0;
byte[] bytes = new byte[BYTE_SIZE_DATA];
long sTime = System.currentTimeMillis();
long eTime = 0;
while ((len = fis.read(bytes)) != -1) {
synchronized (LOCK) {
if(mIsPause) {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//寫入檔案
mOutputStream.write(bytes, 0, len);
total += len;
//每隔200毫秒返回一次進度
eTime = System.currentTimeMillis();
if(eTime - sTime > 200) {
sTime = eTime;
if(mOnSendListener != null) {
mOnSendListener.onProgress(total, fileSize);
}
}
}
}
//關閉Socket輸入輸出流
mOutputStream.flush();
mOutputStream.close();
//檔案傳送成功
if(mOnSendListener != null) {
mOnSendListener.onSuccess(mFileInfo);
}
mIsFinish = true;
}
@Override
public void finishTransfer() throws Exception {
if(mOutputStream != null) {
try {
mOutputStream.close();
} catch (IOException e) {
}
}
if(mSocket != null && mSocket.isConnected()) {
try {
mSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 暫停傳送執行緒
*/
public void pause() {
synchronized (LOCK) {
mIsPause = true;
LOCK.notifyAll();
}
}
/**
* 恢復傳送執行緒
*/
public void resume() {
synchronized (LOCK) {
mIsPause = false;
LOCK.notifyAll();
}
}
/**
* 設定當前的傳送任務不執行
*/
public void stop() {
mIsStop = true;
}
/**
* 檔案是否在傳送中
* @return
*/
public boolean isRunning() {
return !mIsFinish;
}
public interface OnSendListener {
void onStart();
void onProgress(long progress, long total);
void onSuccess(FileInfo fileInfo);
void onFailure(Throwable throwable, FileInfo fileInfo);
}
}
因為懶,以上列出的只是部分核心程式碼,選擇檔案的功能也沒去做,草草地在Activity中寫死了幾個檔案去上傳,具體程式碼可去Github下載執行,參見SendFilesActivity類,哈哈!
總結:
貌似第一次在部落格中貼那麼長的程式碼,關於Socket的知識還要學許多許多,而我懂的也只不過是入門的皮毛,以上Demo參考了以下大神博文及資料: