Android茄子快传项目源码

源码简介

  Android茄子快传项目源码

茄子快传是一款文件传输使用,信任咱们都很了解这款使用,应该很多人用过用来文件的传输。它有两个中心的功用:

端到端的文件传输

Web端的文件传输

这两个中心的功用咱们详细来剖析一下!

端到端的文件传输

所谓的端到端的文件传输是指使用端发送到使用端(这儿的使用端指Android使用端),这种文件传输办法是文件发送端和文件接纳端有必要装置使用。

效果图

文件发送方

 Android茄子快传项目源码  Android茄子快传项目源码  Android茄子快传项目源码  Android茄子快传项目源码

文件接纳方_1

简略的文件传输的话,咱们能够用蓝牙,wifi直连,ftp这几种办法来进行文件的传输。可是:

蓝牙传输的话,速度太慢,而且要配对。相对比较费事。
wifi直连差不多跟蓝牙相同,可是速率很快,也要配对。
ftp能够完成文件的批量传输,可是没有文件的缩略图。

开端剖析这个项目的时分就想着经过自界说协议的Socket的通讯来完成,自界说的协议包含header + body的自界说协议, header部分包含了文件的信息(长度,巨细,文件途径,缩略图), body部分便是文件。现在完成这一功用。(后序:后边开发《网页传》功用的时分,能够考虑这两个中心的功用都能用在Android架起微型Http服务器来完成。这是后话了。)

流程图

 Android茄子快传项目源码

端到端的流程图

编码完成

两部设备文件传输是需要在一个局域网的条件下的,只需文件发送方衔接上文件接纳方的热门(建立了一个局域网),这样文件发送方和文件接纳方就在一个局域网里边,咱们才能够进行Socket通讯。这是一个大前提!

初始化条件 — Ap(热门)和Wifi的管理, 文件的扫描

对Android的Ap(热门)和Wifi的一些操作都封装在下面两个类:

WifiMgr.java

APMgr.java

关于热门和Wifi的操作都是依据WifiManager来操作的。所以要像操作WifiManeger是有必要要一些权限的。有必要在AndroidManifest.xml清单文件里边声明权限:

    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

文件接纳端翻开热门而且装备热门的代码:

        //1.初始化热门
        WifiMgr.getInstance(getContext()).disableWifi();
        if(ApMgr.isApOn(getContext())){
            ApMgr.disableAp(getContext());
        }

        //热门相关的播送
        mWifiAPBroadcastReceiver = new WifiAPBroadcastReceiver() {
            @Override
            public void onWifiApEnabled() {
                Log.i(TAG, "======>>>onWifiApEnabled !!!");
                if(!mIsInitialized){
                    mUdpServerRuannable = createSendMsgToFileSenderRunnable();
                    AppContext.MAIN_EXECUTOR.execute(mUdpServerRuannable);
                    mIsInitialized = true;

                    tv_desc.setText(getResources().getString(R.string.tip_now_init_is_finish));
                    tv_desc.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            tv_desc.setText(getResources().getString(R.string.tip_is_waitting_connect));
                        }
                    }, 2*1000);
                }
            }
        };
        IntentFilter filter = new IntentFilter(WifiAPBroadcastReceiver.ACTION_WIFI_AP_STATE_CHANGED);
        registerReceiver(mWifiAPBroadcastReceiver, filter);

        ApMgr.isApOn(getContext()); // check Ap state :boolean
        String ssid = TextUtils.isNullOrBlank(android.os.Build.DEVICE) ? Constant.DEFAULT_SSID : android.os.Build.DEVICE;
        ApMgr.configApState(getContext(), ssid); // change Ap state :boolean

关于类WifiAPBroadcastReceiver是热门的一个播送类,最终一行代码是装备指定称号的热门,这儿是以设备称号作为热门的称号。

文件发送端发送文件,文件发送端首先要挑选要发送的文件,然后即将挑选的文件存储起来,这儿我是用了一个HashMap将发送的文件存储起来,key是文件的途径,value是FileInfo目标。

以下是扫描手机存储盘上面的文件列表的代码:

    /**
     * 存储卡获取 指定后缀名文件 
     * @param context
     * @param extension 
     * @return
     */
    public static List<FileInfo> getSpecificTypeFiles(Context context, String[] extension){
        List<FileInfo> fileInfoList = new ArrayList<FileInfo>();

        //内存卡文件的Uri
        Uri fileUri= MediaStore.Files.getContentUri("external");
        //挑选列,这儿只挑选了:文件途径和含后缀的文件名
        String[] projection=new String[]{
                MediaStore.Files.FileColumns.DATA, MediaStore.Files.FileColumns.TITLE
        };

        //结构挑选条件句子
        String selection="";
        for(int i=0;i<extension.length;i++)
        {
            if(i!=0)
            {
                selection=selection+" OR ";
            }
            selection=selection+ MediaStore.Files.FileColumns.DATA+" LIKE '%"+extension[i]+"'";
        }
        //按时间降序条件
        String sortOrder = MediaStore.Files.FileColumns.DATE_MODIFIED;

        Cursor cursor = context.getContentResolver().query(fileUri, projection, selection, null, sortOrder);
        if(cursor != null){
            while (cursor.moveToNext()){
                try{
                    String data = cursor.getString(0);
                    FileInfo fileInfo = new FileInfo();
                    fileInfo.setFilePath(data);

                    long size = 0;
                    try{
                        File file = new File(data);
                        size = file.length();
                        fileInfo.setSize(size);
                    }catch(Exception e){

                    }
                    fileInfoList.add(fileInfo);
                }catch (Exception e){
                    Log.i("FileUtils", "------>>>" + e.getMessage());
                }

            }
        }
        Log.i(TAG, "getSize ===>>> " + fileInfoList.size());
        return fileInfoList;
    }

留意**:这儿扫描的FileInfo目标仅仅扫描了文件途径filePath, 还有文件的巨细size。
FileInfo的其他特点到文件传输的时分再二次获取,获取FileInfo的其他特点都在FileUtils这个东西类里边了。

文件发送端翻开wifi扫描热门而且衔接热门的代码:

        if(!WifiMgr.getInstance(getContext()).isWifiEnable()) {//wifi未翻开的状况,翻开wifi
            WifiMgr.getInstance(getContext()).openWifi();
        }

        //开端扫描
        WifiMgr.getInstance(getContext()).startScan();
        mScanResultList = WifiMgr.getInstance(getContext()).getScanResultList();
        mScanResultList = ListUtils.filterWithNoPassword(mScanResultList);

        if(mScanResultList != null){
            mWifiScanResultAdapter = new WifiScanResultAdapter(getContext(),mScanResultList);
            lv_result.setAdapter(mWifiScanResultAdapter);
            lv_result.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    //单击选中指定的网络
                    ScanResult scanResult = mScanResultList.get(position);
                    Log.i(TAG, "###select the wifi info ======>>>" + scanResult.toString());

                    //1.衔接网络
                    String ssid = Constant.DEFAULT_SSID;
                    ssid = scanResult.SSID;
                    WifiMgr.getInstance(getContext()).openWifi();
                    WifiMgr.getInstance(getContext()).addNetwork(WifiMgr.createWifiCfg(ssid, null, WifiMgr.WIFICIPHER_NOPASS));

                    //2.发送UDP告诉信息到 文件接纳方 敞开ServerSocketRunnable
                    mUdpServerRuannable = createSendMsgToServerRunnable(WifiMgr.getInstance(getContext()).getIpAddressFromHotspot());
                    AppContext.MAIN_EXECUTOR.execute(mUdpServerRuannable);
                }
            });
        }

关于ListUtils.filterWithNoPassword是将扫描的成果进行过滤,过滤掉有密码的扫描成果。

lv_result.setOnItemClickListener回调的办法是衔接指定的热门来构成一个局域网。文件传输的大前提条件就现已构成了。

到这儿文件发送端和文件接纳端的初始化环境也就建立起来了。

文件传输模块

文件传输模块的中心代码就只需4个类,Transferable, BaseTransfer, FileSender, FileReceiver。

Transferable是接口。

BaseTransfer, FileSender, FileReceiver是类。

关于文件发送端,每一个文件发送对应一个FileSender,而关于文件接纳端,每一个文件的接纳对应一个FileReceiver。
而FileSender,FileReceiver是承继自 抽象类BaseTransfer的。 BaseTransfer是完成了Transferable接口。

下面是4个类图的联系:

这儿写图片描绘

在Transferable接口中界说了4个办法,别离是初始化,解析头部,解析主体,完毕。解析头部和解析主体别离对应上面说的自界说协议的header和body。初始化是为每一次文件传输做初始化作业,而完毕是为每一次文件传输做完毕作业,比方封闭一些资源流,Socket等等。

而BaseTransfer就仅仅完成了Transferable, 里边封装了一些常量。没有完成详细的办法,详细的完成是FileSender,FileReceiver。

代码概况:

Transferable
BaseTransfer
FileSender
FileReceiver

总结

端到端的文件传输就剖析到这儿,首要是Ap热门的操作,Wifi的操作,Socket通讯来完成文件的传输。可是这儿的Socket用到的不是异步IO,是同步IO。所以会引起堵塞。比方在FileSender中的暂停文件传输pause办法调用之后,会引起FileReceiver中文件传输的堵塞。假如你对异步IO有爱好,你也能够去完成一下。

关于端对端的中心代码都是在 io.github.mayubao.kuaichuan.core 包下面。
这是我在github上面的项目链接 https://github.com/mayubao/KuaiChuan

web端的文件传输

所谓的Web端的文件传输是指文件发送端作为一个Http服务器,供给文件接纳端来下载。这种文件传输办法是文件发送端有必要装置使用,而文件接纳端只需要有浏览器即可。

效果图

文件发送端

敞开Http服务器

文件接纳端

文件接纳端浏览器拜访

在android使用端架起微型Http服务器来完成文件的传输。这儿能够用ftp来完成,为什么不必ftp呢?由于没有缩略图,这是要点!

web端的文件传输的中心要点:

文件发送端热门的敞开(参阅端对端的热门操作类 APMgr.java)
文件发送端架起Http服务器。

Android端的Http服务器

Android上微型Http服务器(Socket完成),结合上面的效果图剖析。首要处理三种Http url的恳求办法就行了,由上面的文件接纳端的效果图能够看出来(文件接纳端是去拜访文件发送端的Http服务器),大致能够分为三种链接:

Index主页链接 http://hostname:port
Image链接 http://hostname:port/image/xxx.xxx
Download链接 http://hostname:port/download/xxx.xxx

这儿写图片描绘

下面用Socket来完成在Android上面的微型Http服务器的。

关于Http协议,我简略的描绘一下Http协议。关于Http协议,便是”恳求-回复(呼应)”的这种通讯形式。客户端宣布恳求,服务器依据恳求,回来一个回复(呼应)给客户端。

Http恳求的大致分为四个部分:

恳求行
恳求头
空行
恳求实体

Http呼应的大致分为四个部分:

状况行
呼应头
空行
呼应实体

Http恳求(POST恳求)的示例:

POST /image/index.html HTTP/1.1
Host: 127.0.0.1:7878
Connection: keep-alive
Content-Length: 247
Cache-Control: no-cache
Origin: chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcm
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryLIr5t1rdtuD8Ztuw
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6

------WebKitFormBoundaryLIr5t1rdtuD8Ztuw
Content-Disposition: form-data; name="username"

mayubao
------WebKitFormBoundaryLIr5t1rdtuD8Ztuw
Content-Disposition: form-data; name="username"

123456
------WebKitFormBoundaryLIr5t1rdtuD8Ztuw--

1.恳求行(恳求办法 + uri + http版别)

POST /image/index.html HTTP/1.1

2.恳求头

Host: 127.0.0.1:7878
Connection: keep-alive
Content-Length: 247
Cache-Control: no-cache
Origin: chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcm
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryLIr5t1rdtuD8Ztuw
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6

3.空行

4.恳求实体(关于GET恳求一般没有恳求实体)

------WebKitFormBoundaryLIr5t1rdtuD8Ztuw
Content-Disposition: form-data; name="username"

mayubao
------WebKitFormBoundaryLIr5t1rdtuD8Ztuw
Content-Disposition: form-data; name="username"

123456
------WebKitFormBoundaryLIr5t1rdtuD8Ztuw--

Http呼应的示例:

HTTP/1.0 200 OK 
Cache-Control:public, max-age=86400
Content-Length:235
Content-Type:image/png
Date:Wed, 21 Dec 2016 08:20:54 GMT

恳求实体

1.状况行(Http版别 + 状况 + 描绘)

HTTP/1.0 200 OK 

2.呼应头

HTTP/1.0 200 OK 
Cache-Control:public, max-age=86400
Content-Length:235
Content-Type:image/png
Date:Wed, 21 Dec 2016 08:20:54 GMT

3.空行

4.呼应实体

上面仅仅简略的叙说了一下Http一般的恳求-呼应流程,还有对应恳求,呼应的结构。假如你想进一步了解http协议,请暗里自行了解。

回到咱们的要点 AndroidMicroServer:
AndroidMicroServer是Http服务器的中心类,还有相关到其他的类,有IndexUriResHandler,ImageUriResHandler, DowloadUriResHandler。是AndroidMicroServer依据不同的Uri格局分配给指定的Handler去处理的。

UML的剖析图如下:

AndroidMicroServer剖析

下面是AndroidMicroServer的源码:

/**
 * The micro server in Android
 * Created by mayubao on 2016/12/14.
 * Contact me 345269374@qq.com
 */
public class AndroidMicroServer {

    private static final String TAG = AndroidMicroServer.class.getSimpleName();

    /**
     * the server port
     */
    private int mPort;

    /**
     * the server socket
     */
    private ServerSocket mServerSocket;

    /**
     *  the thread pool which handle the incoming request
     */
    private ExecutorService mThreadPool = Executors.newCachedThreadPool();

    /**
     * uri router handler
     */
    private List<ResUriHandler> mResUriHandlerList = new ArrayList<ResUriHandler>();

    /**
     * the flag which the micro server enable
     */
    private boolean mIsEnable = true;

    public AndroidMicroServer(int port){
        this.mPort = port;
    }

    /**
     * register the resource uri handler
     * @param resUriHandler
     */
    public void resgisterResUriHandler(ResUriHandler resUriHandler){
        this.mResUriHandlerList.add(resUriHandler);
    }

    /**
     * unresigter all the resource uri hanlders
     */
    public void unresgisterResUriHandlerList(){
        for(ResUriHandler resUriHandler : mResUriHandlerList){
            resUriHandler.destroy();
            resUriHandler = null;
        }
    }

    /**
     * start the android micro server
     */
    public void start(){
        mThreadPool.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    mServerSocket = new ServerSocket(mPort);

                    while(mIsEnable){
                        Socket socket = mServerSocket.accept();
                        hanlderSocketAsyn(socket);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * stop the android micro server
     */
    public void stop(){
        if(mIsEnable){
            mIsEnable = false;
        }

        //release resource
        unresgisterResUriHandlerList();

        if(mServerSocket != null){
            try {
//                mServerSocket.accept(); //fuck ! fix the problem, block the main thread
                mServerSocket.close();
                mServerSocket = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * handle the incoming socket
     * @param socket
     */
    private void hanlderSocketAsyn(final Socket socket) {
        mThreadPool.submit(new Runnable() {
            @Override
            public void run() {
                //1. auto create request object by the parameter socket
                Request request = createRequest(socket);

                //2. loop the mResUriHandlerList, and assign the task to the specify ResUriHandler
                for(ResUriHandler resUriHandler : mResUriHandlerList){
                    if(!resUriHandler.matches(request.getUri())){
                        continue;
                    }

                    resUriHandler.handler(request);
                }
            }
        });

    }

    /**
     * create the requset object by the specify socket
     *
     * @param socket
     * @return
     */
    private Request createRequest(Socket socket) {
        Request request = new Request();
        request.setUnderlySocket(socket);
        try {
            //Get the reqeust line
            SocketAddress socketAddress = socket.getRemoteSocketAddress();
            InputStream is = socket.getInputStream();
            String requestLine = IOStreamUtils.readLine(is);
            SLog.i(TAG, socketAddress + "requestLine------>>>" + requestLine);
            String requestType = requestLine.split(" ")[0];
            String requestUri = requestLine.split(" ")[1];

//            requestUri = URLDecoder.decode(requestUri, "UTF-8");

            request.setUri(requestUri);

            //Get the header line
            String header = "";
            while((header = IOStreamUtils.readLine(is)) != null){
                SLog.i(TAG, socketAddress + "header------>>>" + requestLine);
                String headerKey = header.split(":")[0];
                String headerVal = header.split(":")[1];
                request.addHeader(headerKey, headerVal);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return request;
    }

}

AndroidMicroServer首要有两个办法:

start (Http服务器的敞开)
stop (Http服务器的封闭,首要用来封闭ServerSocket和反注册UriResHandler)

start办法 是Http服务器的进口

关于start办法:

    /**
     * start the android micro server
     */
    public void start(){
        mThreadPool.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    mServerSocket = new ServerSocket(mPort);

                    while(mIsEnable){
                        Socket socket = mServerSocket.accept();
                        hanlderSocketAsyn(socket);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

敞开一个线程去履行ServerSocket, while循环去接纳每一个进来的Socket。 而hanlderSocketAsyn(socket)是异步处理每一个进来的socket。

    /**
     * handle the incoming socket
     * @param socket
     */
    private void hanlderSocketAsyn(final Socket socket) {
        mThreadPool.submit(new Runnable() {
            @Override
            public void run() {
                //1. auto create request object by the parameter socket
                Request request = createRequest(socket);

                //2. loop the mResUriHandlerList, and assign the task to the specify ResUriHandler
                for(ResUriHandler resUriHandler : mResUriHandlerList){
                    if(!resUriHandler.matches(request.getUri())){
                        continue;
                    }

                    resUriHandler.handler(request);
                }
            }
        });
    }

    /**
     * create the requset object by the specify socket
     *
     * @param socket
     * @return
     */
    private Request createRequest(Socket socket) {
        Request request = new Request();
        request.setUnderlySocket(socket);
        try {
            //Get the reqeust line
            SocketAddress socketAddress = socket.getRemoteSocketAddress();
            InputStream is = socket.getInputStream();
            String requestLine = IOStreamUtils.readLine(is);
            SLog.i(TAG, socketAddress + "requestLine------>>>" + requestLine);
            String requestType = requestLine.split(" ")[0];
            String requestUri = requestLine.split(" ")[1];

//            //处理URL中文乱码的问题
//            requestUri = URLDecoder.decode(requestUri, "UTF-8");

            request.setUri(requestUri);

            //Get the header line
            String header = "";
            while((header = IOStreamUtils.readLine(is)) != null){
                SLog.i(TAG, socketAddress + "header------>>>" + requestLine);
                String headerKey = header.split(":")[0];
                String headerVal = header.split(":")[1];
                request.addHeader(headerKey, headerVal);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return request;
    }

关于每一个进来的Socket:

经过createRequest(socket)来创立一个Request目标,对应一个Http Request目标。在createRequest(socket)中怎么去从socket中去读取每一行呢?关于每一个Http恳求的每一行都是以’\r\n’字节结束的。只需判别读取字节省的时分判别接连的两个字节是以’\r\n’结束的便是一行结束的标识。概况请检查IOStreamUtils.java
依据恳求行的path,分配给对应的Uri处理目标去处理,而所对应uri怎么获取,是从Socket的Inputsream读取Http Request的恳求行中读取出来的。关于ResUriHandler,是一个接口。首要依据恳求行的uri 分配给对应的ResUriHandler去处理。 ResUriHandler的完成类是对应给出呼应的处理类。

留意:可参阅上面的UML的类

资源下载此资源仅限νìρ下载,请先

如遇到链接失效请提交工单处理。

【下载提示】

1. 本站30000+源码及视频教程,除了热门商业代售区源码及课程外,只要有下载按钮的,终/身νìρ都可以免费下载。

2. 本站源码及教程来自30多个渠道采购,资源描述为转载资源站点内容,本站没有精力一一测试,可能搭建失败。

3. 本站开通数十站点会/员,资源过多,大部分无法亲自测试,源码有可能存在缺\\\\陷或者不完整的风险,仅供参考&研究。确认购买视为接受该风险,由于源码具有可复\\\\制性,不接受任何理由退\\\\款!!!

4. 本站使用在线支付,付款完毕后,积分自动到账。

5. 充积分比例:1:1。

6. 所有源码包含安装教程与否,请仔细观看资源描述。

7. 所有源码不提供代安装搭建,如有疑问请提提交工单。

资源下载
下载需要:νìρ专享
此资源仅对νìρ开放下载

如遇到链接失效请提交工单处理。

【下载提示】

1. 本站30000+源码及视频教程,除了热门商业代售区源码及课程外,只要有下载按钮的,终/身νìρ都可以免费下载。

2. 本站源码及教程来自30多个渠道采购,资源描述为转载资源站点内容,本站没有精力一一测试,可能搭建失败。

3. 本站开通数十站点会/员,资源过多,大部分无法亲自测试,源码有可能存在缺\\\\陷或者不完整的风险,仅供参考&研究。确认购买视为接受该风险,由于源码具有可复\\\\制性,不接受任何理由退\\\\款!!!

4. 本站使用在线支付,付款完毕后,积分自动到账。

5. 充积分比例:1:1。

6. 所有源码包含安装教程与否,请仔细观看资源描述。

7. 所有源码不提供代安装搭建,如有疑问请提提交工单。

 Android茄子快传项目源码原文链接:https://www.qwzy8.com/28875.html

广告位招租

评论0

请先

           
1,如有问题请前往用户中心提交工单,12小时内回复!
2,投稿优质资源可获得最长本站置顶广告位推荐,收益100%归作者所有,可提现!
3,欢迎发布其他站点购买的各类源码教程资源,支持置换本站各类资源!
没有账号? 注册  忘记密码?