因为客户端可能会多次发起连接请求,所以当此处文件传输完成后(不管成功或失败),都需要重新 startService

,让服务再次堵塞等待客户端的连接请求

FileTransfer 包含三个字段,MD5码值用于校验文件的完整性,fileLength

是为了用于计算文件的传输进度

public class FileTransfer implements Serializable {

//文件路径

private String

filePath;

//文件大小

private long

fileLength;

//MD5码

private String md5;

···

}

为了将文件传输进度发布到外部界面,所以除了需要启动Service外,界面还需要绑定Service,此处就需要用到一个更新文件传输状态的接口

public

interface OnProgressChangListener {

//当传输进度发生变化时

void

onProgressChanged(FileTransfer fileTransfer, int

progress);

//当传输结束时

void

onTransferFinished(File file);

}

因此,需要将 progressChangListener

作为参数传给  WifiServerService

,并在进度变化时更新进度对话框

private

WifiServerService.OnProgressChangListener progressChangListener =

new WifiServerService.OnProgressChangListener() {

@Override

public

void onProgressChanged(final FileTransfer fileTransfer, final int

progress) {

runOnUiThread(new Runnable()

{

@Override

public void run() {

progressDialog.setMessage("文件名: " + new

File(fileTransfer.getFilePath()).getName());

progressDialog.setProgress(progress);

progressDialog.show();

}

});

}

@Override

public

void onTransferFinished(final File file) {

runOnUiThread(new Runnable()

{

@Override

public void run() {

progressDialog.cancel();

if (file

!= null && file.exists()) {

openFile(file.getPath());

}

}

});

}

};

四、客户端加入群组并发起文件传输请求

文件发送界面 SendFileActivity 需要实现

DirectActionListener 接口 首先,需要先注册P2P广播,以便获取周边设备信息以及连接状态

@Override

protected

void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_send_file);

initView();

mWifiP2pManager =

(WifiP2pManager)

getSystemService(Context.WIFI_P2P_SERVICE);

mChannel =

mWifiP2pManager.initialize(this, getMainLooper(), this);

broadcastReceiver = new

DirectBroadcastReceiver(mWifiP2pManager, mChannel,

this);

registerReceiver(broadcastReceiver,

DirectBroadcastReceiver.getIntentFilter());

}

通过

discoverPeers 方法搜索周边设备,回调函数用于通知方法是否调用成功

mWifiP2pManager.discoverPeers(mChannel, new

WifiP2pManager.ActionListener() {

@Override

public

void onSuccess() {

showToast("Success");

}

@Override

public

void onFailure(int reasonCode) {

showToast("Failure");

loadingDialog.cancel();

}

});

当搜索结束后,系统就会触发

WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION 广播,此时就可以调用

requestPeers 方法获取设备列表信息,此处用 RecyclerView 展示列表,在

onPeersAvailable 方法刷新列表

mWifiP2pManager.requestPeers(mChannel, new

WifiP2pManager.PeerListListener() {

@Override

public

void onPeersAvailable(WifiP2pDeviceList peers) {

mDirectActionListener.onPeersAvailable(peers.getDeviceList());

}

});

@Override

public

void onPeersAvailable(Collection wifiP2pDeviceList) {

Log.e(TAG, "onPeersAvailable

:" + wifiP2pDeviceList.size());

this.wifiP2pDeviceList.clear();

this.wifiP2pDeviceList.addAll(wifiP2pDeviceList);

deviceAdapter.notifyDataSetChanged();

loadingDialog.cancel();

}

之后,通过点击事件选中群主(服务器端)设备,通过 connect

方法请求与之进行连接

private void connect() {

WifiP2pConfig config = new WifiP2pConfig();

if

(config.deviceAddress != null && mWifiP2pDevice != null)

{

config.deviceAddress =

mWifiP2pDevice.deviceAddress;

config.wps.setup =

WpsInfo.PBC;

showLoadingDialog("正在连接 " +

mWifiP2pDevice.deviceName);

mWifiP2pManager.connect(mChannel, config, new

WifiP2pManager.ActionListener() {

@Override

public void onSuccess() {

Log.e(TAG,

"connect onSuccess");

}

@Override

public void onFailure(int reason) {

showToast("连接失败 " + reason);

dismissLoadingDialog();

}

});

}

}

此处依然无法通过函数函数来判断连接结果,需要依靠系统发出的

WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION

方法来获取到连接结果,在此处可以通过 requestConnectionInfo

获取到组连接信息,信息最后通过 onConnectionInfoAvailable

方法传递出来,在此可以判断当前设备是否为群主,获取群组IP地址

@Override

public void

onConnectionInfoAvailable(WifiP2pInfo wifiP2pInfo) {

dismissLoadingDialog();

wifiP2pDeviceList.clear();

deviceAdapter.notifyDataSetChanged();

btn_disconnect.setEnabled(true);

btn_chooseFile.setEnabled(true);

Log.e(TAG,

"onConnectionInfoAvailable");

Log.e(TAG,

"onConnectionInfoAvailable groupFormed: " +

wifiP2pInfo.groupFormed);

Log.e(TAG,

"onConnectionInfoAvailable isGroupOwner: " +

wifiP2pInfo.isGroupOwner);

Log.e(TAG,

"onConnectionInfoAvailable getHostAddress: " +

wifiP2pInfo.groupOwnerAddress.getHostAddress());

StringBuilder stringBuilder = new StringBuilder();

if

(mWifiP2pDevice != null) {

stringBuilder.append("连接的设备名:");

stringBuilder.append(mWifiP2pDevice.deviceName);

stringBuilder.append("\n");

stringBuilder.append("连接的设备的地址:");

stringBuilder.append(mWifiP2pDevice.deviceAddress);

}

stringBuilder.append("\n");

stringBuilder.append("是否群主:");

stringBuilder.append(wifiP2pInfo.isGroupOwner ? "是群主" :

"非群主");

stringBuilder.append("\n");

stringBuilder.append("群主IP地址:");

stringBuilder.append(wifiP2pInfo.groupOwnerAddress.getHostAddress());

tv_status.setText(stringBuilder);

if

(wifiP2pInfo.groupFormed && !wifiP2pInfo.isGroupOwner)

{

this.wifiP2pInfo =

wifiP2pInfo;

}

}

至此服务器端和客户端已经通过 Wifi P2P

连接在了一起,客户端也获取到了服务器端的IP地址,在选取好待发送的文件后就可以主动发起对服务器端的连接请求了

发起选取文件的方法

Intent intent = new

Intent(Intent.ACTION_GET_CONTENT);

intent.setType("*

public class WifiClientTask extends AsyncTask

{

private

ProgressDialog progressDialog;

private

FileTransfer fileTransfer;

private

static final int PORT = 4786;

private

static final String TAG = "WifiClientTask";

public

WifiClientTask(Context context, FileTransfer fileTransfer)

{

this.fileTransfer =

fileTransfer;

progressDialog = new

ProgressDialog(context);

progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

progressDialog.setCancelable(false);

progressDialog.setCanceledOnTouchOutside(false);

progressDialog.setTitle("正在发送文件");

progressDialog.setMax(100);

}

@Override

protected

void onPreExecute() {

progressDialog.show();

}

@Override

protected

Boolean doInBackground(String... strings) {

fileTransfer.setMd5(Md5Util.getMd5(new

File(fileTransfer.getFilePath())));

Log.e(TAG, "文件的MD5码值是:" +

fileTransfer.getMd5());

Socket socket =

null;

OutputStream outputStream =

null;

ObjectOutputStream

objectOutputStream = null;

InputStream inputStream =

null;

try {

socket = new Socket();

socket.bind(null);

socket.connect((new

InetSocketAddress(strings[0], PORT)), 10000);

outputStream =

socket.getOutputStream();

objectOutputStream = new

ObjectOutputStream(outputStream);

objectOutputStream.writeObject(fileTransfer);

inputStream = new FileInputStream(new

File(fileTransfer.getFilePath()));

long fileSize =

fileTransfer.getFileLength();

long total = 0;

byte buf[] = new byte[512];

int len;

while ((len = inputStream.read(buf)) != -1)

{

outputStream.write(buf, 0, len);

total +=

len;

int

progress = (int) ((total * 100) / fileSize);

publishProgress(progress);

Log.e(TAG,

"文件发送进度:" + progress);

}

outputStream.close();

objectOutputStream.close();

inputStream.close();

socket.close();

outputStream = null;

objectOutputStream = null;

inputStream = null;

socket = null;

Log.e(TAG, "文件发送成功");

return true;

} catch (Exception e)

{

Log.e(TAG, "文件发送异常 Exception: " +

e.getMessage());

} finally {

if (outputStream != null) {

try

{

outputStream.close();

} catch

(IOException e) {

e.printStackTrace();

}

}

if (objectOutputStream != null) {

try

{

objectOutputStream.close();

} catch

(IOException e) {

e.printStackTrace();

}

}

if (inputStream != null) {

try

{

inputStream.close();

} catch

(IOException e) {

e.printStackTrace();

}

}

if (socket != null) {

try

{

socket.close();

} catch

(Exception e) {

e.printStackTrace();

}

}

}

return false;

}

@Override

protected

void onProgressUpdate(Integer... values) {

progressDialog.setProgress(values[0]);

}

@Override

protected

void onPostExecute(Boolean aBoolean) {

progressDialog.cancel();

Log.e(TAG, "onPostExecute: "

+ aBoolean);

}

}

五、校验文件完整性

传输文件的完整性主要是通过计算文件的MD5码值来保证了,在发送文件前,即在 WifiClientTask 的

doInBackground 方法中进行计算,将MD5码值赋给 FileTransfer 模型,通过如下方法计算得到

public class Md5Util {

public

static String getMd5(File file) {

InputStream inputStream =

null;

byte[] buffer = new

byte[2048];

int numRead;

MessageDigest md5;

try {

inputStream = new

FileInputStream(file);

md5 =

MessageDigest.getInstance("MD5");

while ((numRead = inputStream.read(buffer)) >

0) {

md5.update(buffer, 0, numRead);

}

inputStream.close();

inputStream = null;

return md5ToString(md5.digest());

} catch (Exception e)

{

return null;

} finally {

if (inputStream != null) {

try

{

inputStream.close();

} catch

(IOException e) {

e.printStackTrace();

}

}

}

}

private

static String md5ToString(byte[] md5Bytes) {

StringBuilder hexValue = new

StringBuilder();

for (byte b : md5Bytes)

{

int val = ((int) b) & 0xff;

if (val < 16) {

hexValue.append("0");

}

hexValue.append(Integer.toHexString(val));

}

return

hexValue.toString();

}

}

因为客户端会将 FileTransfer

传给服务器端,所以服务器端在文件传输结束后,可以重新计算文件的MD5码值,进行对比以判断文件是否完整

android文件搜索广播,Android 实现无网络传输文件相关推荐

  1. 吊炸天之Android 如何实现无网络传输文件

    热文导读 | 点击标题阅读 欢迎加入Java和Android架构知识星球 Android大厂面试题锦集附答案(BAT TMD JD 小米) 程序员如何进阶成为大神? 作者:叶应是叶 原文:http:/ ...

  2. Android 实现无网络传输文件(2)

    在我的上一篇文章:Android 实现无网络传输文件,我介绍了通过 Wifi Direct(Wifi 直连)实现 Android 设备之间进行文件传输的方法,可以在无移动网络的情况下实现点对点的文件传 ...

  3. oracle asm和文件系统,Oracle工具之--ASM与文件系统及跨网络传输文件

    Oracle工具之--ASM与文件系统及跨网络传输文件 Oracle DBMS_FILE_TRANSFER可以实现文件系统和ASM磁盘组之间实现文件传输及ASM磁盘组之间跨网络的传输. DBMS_FI ...

  4. Oracle工具之--ASM与文件系统及跨网络传输文件

    Oracle工具之--ASM与文件系统及跨网络传输文件   Oracle DBMS_FILE_TRANSFER可以实现文件系统和ASM磁盘组之间实现文件传输及ASM磁盘组之间跨网络的传输. DBMS_ ...

  5. Java 文件传输小工具,网络传输文件,内网传输

    文件传输小工具,网络传输文件,内网传输 换了一台电脑,想把一台电脑的资料放到另一太电脑上,由于没有太大的U盘就动手用Java写了个小工具.有需要的朋友下载使用! 运行环境 运行环境需要安装JDK,并配 ...

  6. android加载网页pdf,android 一行代码搞定加载网络 pdf 文件

    之前写过一篇Android打开本地pdf文件的文章,最后总结的时候说,后面一定要拓展库,让其也能打开网络的的pdf文件.今天终于可以兑现承诺了.frok一份代码github.com/JoanZapat ...

  7. 文件搜索工具android,Search Everything下载

    在PC系统上,又一款很知名的本地文件搜索工具,名称叫做Everything,而在android平台上,也有一款如此功能的应用,它的名字叫 Search Everything,十分小巧,但是其功能却是十 ...

  8. android连路由器 mtu,解决app无网络问题,将宽带路由器MTU从1500修改成1480

    问题描述 移动宽带路由器MTU=1500时某些app无网络,改成1480就解决了,是什么原因造成的? 正文 出问题的APP,使用手机默认MTU =1500来发送TCP报文.到达宽带路由器WAN出口时, ...

  9. Android允许应用程序使用Http明文网络传输

    自Android9.0系统开始,应用程序默认只允许使用HTTPS类型的网络请求,HTTP类型的网络请求因为有安全隐患默认不再支持.为了让应用程序支持HTTP请求,可以做如下设置: 1.在res/xml ...

最新文章

  1. Tomcat配置虚拟路径,使上传文件与服务器及工程文件分离开
  2. asin c语言中 返回值范围_大学C语言考试易错知识点总结
  3. Storyboard.storyboard could not be opened. Could not read the archive.
  4. 有关fwrite语句的用法
  5. 保险的现金价值是什么意思?
  6. 模拟任务调度算法 C语言 【留学生作业】
  7. 怎么利用微博进行营销?
  8. 常见英文咒语与低俗词汇一览表(应避免使用)
  9. 功率因数 matlab,基于Matlab的功率因数校正电路的仿真分析
  10. 北京消费扶贫双创中心启用 2000余种特色产品长期展销
  11. 计算机如何磁盘整理,如何整理磁盘碎片让Win7电脑运行更快?
  12. java金额三位一撇方法_Java数据格式化问题
  13. 锂离子电池电量计的选择
  14. 商汤科技发布新一代SensePass工业级可视人脸识别门禁全新升级
  15. dhcp snooping华为_华为交换机DHCP snooping
  16. 【03yy and triangle】
  17. Tomcat的8005、8009,8080端口解释
  18. CSS布局:CSS三大特性、盒子模型
  19. 【系统分析师之路】面向对象开发方法,Coad方法、Booch方法和OMT方法及UML
  20. 高中必备学习软件_有哪些适合高中生的学习软件?

热门文章

  1. python3_04.循环函数
  2. ASP.NET Aries 入门开发教程7:DataGrid的行操作(主键操作区)
  3. Javascript 调用百度地图不显示
  4. 部署scrapy爬虫
  5. 克鲁斯卡尔重构树小结
  6. linux系统编程之(一) 信号量
  7. Android java传递int类型数组给C
  8. 转载 漫谈机器学习中的距离和相似性度量方法
  9. vc建立一个窗口的流程
  10. 转:OWASP发布Web应用程序的十大安全风险