最近把測試丟過來的種種BUG解決後,終於有時間去研究研究Socket通訊,再加上之前作的WiFi鏈接和熱點開啓,因而有了如今的這篇博文:建立熱點發送文件,讓另外一臺手機鏈接熱點接收文件。java
因爲Android沒有直接開啓熱點的API,因此咱們這裏採用反射。git
/** * 開啓便攜熱點 * @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是否正確。github
WiFi廣播監聽事件:json
/** * 註冊監聽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:socket
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列表:ide
/** * 掃描周圍可用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廣播發送文件列表。spa
/** * 等待接收端發送初始化完成指令,向其發送文件列表 * @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控件,經過選擇須要接收的文件,點擊「開始發送」按鈕,將發送「開始發送」指令到發送端,開啓端口進行文件接收。.net
/** * 設置接收文件列表適配器 */ 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參考瞭如下大神博文及資料: