美文网首页
线程池上传(下载)文件的简单使用以及APK的安装

线程池上传(下载)文件的简单使用以及APK的安装

作者: 君袅 | 来源:发表于2019-02-19 10:35 被阅读0次

思路:

  • 服务器处理多线程问题
    1.因为服务器是要很多人访问的,因此里面一定要用多线程来处理,不然只能一个人一个人的访问,那还叫Y啥服务
    2,拿上面这个文件上传的例子来说,它将每个连接它的用户封装到线程里面去,把用户要执行的操作定义到 run 方法里面一个用户拿一个线程,拿到线程的就自己去执行,如果有其它用户来的时候,再给新来的用户分配一个新的线程 这样就完成了服务器处理多线程的问题3. 在服务器与客户端互传数据时,我们要特别注意的是,防止两个程序造成 死等的状态,
    一般原因有以下: 1.客户端向数据端发送数据时,当发送的是字符时,第次以一行来发送,而服务端在读取的时候,也是以 一行来读取,readLine()而发送的时候往往只是发送换行以行的内容,而不能发换行也发送过去, 那么服务端在读取的时候就不读取不到换行 ,那么 readLine() 就不会停止 2. 客户端发送数据时,如果处理的是用 字符流 或是缓冲流的话,一定要记得刷新流,不然的话,数据就会发不出来 3.在用IO 读取文件里面的数据然后发送到服务端时,当家读取文件 while(in.read()) 读取文件结束时,而在 服务端的接收程序while(sin.read())不会接到一个发送完毕的提示,所以会一直等待下去,所以我们在处理这个问题的时候,还要将其发送一个文件读取结束的标志,告诉接收端文件已经读取完结,不要再等待了 而socket 里面给我们封装了shutdownInput,shutdownOutput 两个操作,此可以关闭 流,两样也可以起到告诉 接收方文件传送完毕的效果

什么是线程:

线程是操作系统运行的最小单元;进程里包含了多个线程,他们处理不同的任务,组成了一个应用或者一个系统的整体逻辑。
Thread.yield():让步,当一个线程执行yield()方法,证明该线程执行让步,让其他线程有可能的获取资源运行。
Thead.join(): 加入,当一个线程执行join(),证明该线程执行加入操作,会终止当前正在运行的线程,开始执行join的线程。
Thread 优先级:Thread.currentThread().setPrority(value);value=Max_prority;Norm_priority;Min_priority;三种不同的优先等级;
Thread.setDaemon(boolean);设置当前线程为后台线程,后台线程要在start之前调用才有效。后台线程,是指程序运行的时候在后台提供一种通用服务的线程,且这种线程并不属于程序中不可或缺的部分;只要有任何非后台线程在运行,程序就不会终止。
sleep和wait的区别
最大的不同是在等待时 wait 会释放锁,而 sleep 一直持有锁。wait 通常被用于线程间交互,sleep 通常被用于暂停执行。

实现线程的方式

1.继承Thread
2.实现Runable接口
3.实现Callable,和Runable的区别是有回调方法。
区别 :实现Runable接口扩展性更好,因为继承只能单向继承

(一)上传

1.上传线程池工具类

public class HttpUtils {
    static String result="";

    private static final String TAG = "HttpUtils";
    /**
     * HttpUrlConnection 实现文件上传
     * @param params       普通参数
     * @param fileFormName 文件在表单中的键 key=img  file =文件流
     * @param uploadFile   上传的文件
     * @param newFileName  文件在表单中的值(服务端获取到的文件名)
     * @param urlStr       上传服务器地址url
     * @throws IOException 10
     */

    public static String uploadForm(Map<String, String> params, String fileFormName, File uploadFile,
                                    String newFileName, String urlStr) throws IOException {

        if (newFileName == null || newFileName.trim().equals("")) {
            newFileName = uploadFile.getName();
        }
        StringBuilder sb = new StringBuilder();
        if (params != null) {
            for (String key : params.keySet()) {
                sb.append("Content-Disposition: form-data; name=\"" + key + "\"" + "\r\n");
                sb.append("\r\n");
                sb.append(params.get(key) + "\r\n");
            }
        }

        sb.append("--" + BOUNDARY + "\r\n");
        sb.append("Content-Disposition: form-data; name=\"" + fileFormName + "\"; filename=\"" + newFileName + "\""
                + "\r\n");
        sb.append("Content-Type: application/octet-stream" + "\r\n");// 如果服务器端有文件类型的校验,必须明确指定ContentType
        sb.append("\r\n");
        byte[] headerInfo = sb.toString().getBytes("UTF-8");
        byte[] endInfo = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("UTF-8");
        URL url = new URL(urlStr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        // 设置传输内容的格式,以及长度
        conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
        conn.setRequestProperty("Content-Length",
                String.valueOf(headerInfo.length + uploadFile.length() + endInfo.length));
        conn.setDoOutput(true);
        OutputStream out = conn.getOutputStream();
        InputStream in = new FileInputStream(uploadFile);
        //写入的文件长度
        int count = 0;
        //文件的总长度
        int available = in.available();
        // 写入头部 (包含了普通的参数,以及文件的标示等)
        out.write(headerInfo);
        // 写入文件
        byte[] buf = new byte[1024];
        int len;
        while ((len = in.read(buf)) != -1) {
            out.write(buf, 0, len);
            count += len;
            int progress = count * 100 / available;
            Log.d(TAG, "上传进度: " + progress + " %");
//            updateProgress(progress);
        }
        // 写入尾部
        out.write(endInfo);
        in.close();
        out.close();
        if (conn.getResponseCode() == 200) {
            System.out.println("文件上传成功");
            result = stream2String(conn.getInputStream());
            Log.d(TAG, "uploadForm: " + result);
        }

        return result;
    }

    // 分割符,自己定义即可
    private static final String BOUNDARY = "----WebKitFormBoundaryT1HoybnYeFOGFlBR";

    public static String stream2String(InputStream is) {
        int len;
        byte[] bytes = new byte[1024];
        StringBuffer sb = new StringBuffer();
        try {
            while ((len = is.read(bytes)) != -1) {
                sb.append(new String(bytes, 0, len));
            }
            is.close();
            return sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }
}

2.创建一个新线程调用工具类

public class UploadFile  implements Runnable {
    String filePath;
    String uploadurl;

    public UploadFile(String filePath, String uploadurl) {
        this.filePath = filePath;   //上传路径
        this.uploadurl = uploadurl;  //上传的url
    }

    @Override
    public void run() {
        /**
         * HttpUrlConnection 实现文件上传
         * @param params       普通参数
         * @param fileFormName 文件在表单中的键 key=img  file =文件流
         * @param uploadFile   上传的文件
         * @param newFileName  文件在表单中的值(服务端获取到的文件名)
         * @param urlStr       上传服务器地址url
         * @throws IOException 10
         */

        // do something --上传
        HashMap<String,String>  map = new HashMap<>();
        map.put("key","imgs");
        File file = new File(filePath);
        try {
            String result= HttpUtils.uploadForm(map,"file",file,file.getName(),uploadurl);

            if (!TextUtils.isEmpty(result)&&uploadListener!=null){

                uploadListener.uploadSuccess(result);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }


//    接口回调
    UploadListener uploadListener;

    public void setUploadListener(UploadListener uploadListener){
        this.uploadListener = uploadListener;
    }
    public interface UploadListener{
        void uploadSuccess(String result);
    }
}

3.在Activity中实现并获取到要上传的文件

   private void uploadFile() {
        String string = Environment.getExternalStorageDirectory() + File.separator + "aa.jpg";
        File file = new File(string);   //获取到文件
        if(file.exists()){
            Log.d("zll", "uploadFile: "+true);
        }
        //1.创建线程池
        //2.把线程给线程池
        ExecutorService executorService = Executors.newCachedThreadPool();  //创建性线程
        UploadFile uploadFile = new UploadFile(string, url);  //获取到新线程的类
        uploadFile.setUploadListener(new UploadFile.UploadListener() {  //通过接口回调将实现新线程的方法
            @Override
            public void uploadSuccess(final String result) {
                Log.d("zll", "uploadSuccess: "+result);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tv.setText(result);
                    }
                });
            }
        });
        executorService.execute(uploadFile);  //开始执行
        executorService.shutdown();  //结束
    }
}

(二)下载安装的配置

权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>

在application之间配置(清单文件中)

<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.day11a" //到自己的包
android:grantUriPermissions="true"
android:exported="false">

<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_path" />
</provider>

生成一个xml

<resources xmlns:android="http://schemas.android.com/apk/res/android">
<paths>
<external-path path="" name="download"/>
</paths>
</resources>

(三).下载

1.创建线程池下载工具类

public class DownLoadUtils {
    //默认下载文件保存路径
    private final String DEFAULT_TARGET_FOLDER_PATH = Environment.getExternalStorageDirectory() + File.separator;
    //下载文件保存的目标路径(包括文件名)
    private static String targetFilePathAndName;
    //最大可开启的线程数
    public static final int MAX_THREAD_NUMBER = 15;
    //下载文件的URL地址
    public static String sourcePath;

    //下载所需开启的线程数,默认为5个
    private int threadNumber = 5;

    //还在下载的线程数量
    public static int restTask;
    //下载文件总大小
    private int fileSize;
    //每个线程负责下载的文件块大小
    private int partSize;
    static Context context;
    /**
     * 开始一次下载
     * @param sourcePath 目标URL
     * @param targetFilePath 目标保存路径
     * @param threadNumber 开启的线程数
     * @param fileName 保存的文件名
     * @return 开启任务成功否
     */
    public boolean start( Context context, String sourcePath,  String targetFilePath, int threadNumber,   String fileName) throws IOException {
        this.sourcePath = sourcePath;
        this.context = context;
        this.targetFilePathAndName = targetFilePath == null ? DEFAULT_TARGET_FOLDER_PATH
                + (fileName == null ? System.currentTimeMillis() : fileName) :
                targetFilePath + (fileName == null ? System.currentTimeMillis() : fileName);
        this.threadNumber = threadNumber < 0 || threadNumber > MAX_THREAD_NUMBER ? this.threadNumber : threadNumber;
        this.restTask = this.threadNumber;


        HttpURLConnection conn = getConnection();
        fileSize = conn.getContentLength();
        conn.disconnect();

        RandomAccessFile file = new RandomAccessFile(targetFilePathAndName, "rw");
        file.setLength(fileSize);
        file.close();

        partSize = fileSize / threadNumber + 1;
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < threadNumber; i++) {
            int startPos = i * partSize;

            executorService.execute(new ThreadTask(startPos,partSize,targetFilePathAndName));
        }
        executorService.shutdown();
        return true;
    }

    public static HttpURLConnection getConnection() throws IOException {
        URL url = new URL(DownLoadUtils.sourcePath);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(10*1000);
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
        conn.setRequestProperty("Accept-Language", "zh-CN");
        conn.setRequestProperty("Referer", DownLoadUtils.sourcePath);
        conn.setRequestProperty("Charset", "UTF-8");
        conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
        conn.setRequestProperty("Connection", "Keep-Alive");
        conn.setRequestProperty("Accept-Encoding", "identity");
//        conn.connect();
        return conn;
    }

    //普通安装
    public static void installAPK( ) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        //版本在7.0以上是不能直接通过uri访问的
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
            File file = new File(targetFilePathAndName);
            // 由于没有在Activity环境下启动Activity,设置下面的标签
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            //参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致   参数3  共享的文件
            Uri apkUri = FileProvider.getUriForFile(context, "com.example.day11a", file);
            //添加这一句表示对目标应用临时授权该Uri所代表的文件
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");   //改包
        } else {
            intent.setDataAndType(Uri.fromFile(new File(targetFilePathAndName)),
                    "application/vnd.android.package-archive");
        }
        context.startActivity(intent);
    }
}

2.创建线程并完成对文件的下载

import static com.example.day11a.DownLoadUtils.restTask;//这个resttask是文件的总大小调用的是工具类里的

public class ThreadTask implements Runnable{

    private int partSize;
    //当前线程的下载位置
    private int startPos;
    //当前下载保存到的目标文件(块)
    private RandomAccessFile currentPart;
    //当前已下载数据大小
    private int currentDownLoaded;

    public ThreadTask(int startPos, int partSize,String targetFilePathAndName) throws IOException {
        this.startPos = startPos;
        this.partSize = partSize;

        currentPart = new RandomAccessFile(targetFilePathAndName, "rw");
        currentPart.seek(startPos);
    }


    @Override
    public void run() {
        doHttpTask();
    }

    private static final String TAG = "ThreadTask";
    private void doHttpTask() {
        try {
            HttpURLConnection connection = DownLoadUtils.getConnection();
            InputStream in = connection.getInputStream();
            skipFully(in, startPos);
            byte[] bytes = new byte[8*1024];
            int hasRead;
            while ((currentDownLoaded < partSize) && (hasRead = in.read(bytes)) > 0) {
                currentPart.write(bytes, 0, hasRead);
                currentDownLoaded += hasRead;

                Log.d(TAG, "doHttpTask: thread="+Thread.currentThread().getName()+"--"+currentDownLoaded);
            }
            currentPart.close();
            in.close();
            connection.disconnect();

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            restTask--;
            if (restTask == 0){
                new DownLoadUtils().installAPK();
                Log.d(TAG, "doHttpTask: 下载完成");
            }

        }

    }

    /**
     * 从输入流中从起点开始跳过指定长度
     *
     * @param in    输入流
     * @param bytes 要跳过的字节数
     * @throws IOException
     */
    public final void skipFully(InputStream in, long bytes) throws IOException {
        long len;
        while (bytes > 0) {
            len = in.skip(bytes);
            bytes -= len;
        }
    }
}

3.在Activiry中使用

    public void onClick(View v) {
        switch (v.getId()) {
            default:
                break;
            case R.id.click:
                loadFile();
                break;
            case R.id.click2:

                installApk();
                break;
        }
    }

    private void installApk() {      //安装的方法

        String  targetFilePathAndName = Environment.getExternalStorageDirectory() + File.separator+"UnknowApp-1.0.apk";

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        //版本在7.0以上是不能直接通过uri访问的
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            File file = new File(targetFilePathAndName);
            // 由于没有在Activity环境下启动Activity,设置下面的标签
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            //参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致   参数3  共享的文件
            Uri apkUri = FileProvider.getUriForFile(MainActivity.this, "com.example.morethreadloadfile", file); //改包
            //添加这一句表示对目标应用临时授权该Uri所代表的文件
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        } else {
            intent.setDataAndType(Uri.fromFile(new File(targetFilePathAndName)),
                    "application/vnd.android.package-archive");
        }
        startActivity(intent);
    }
    

    //UnknowApp-1.0.apk
    private String apk_url = "http://yun918.cn/study/public/res/UnknowApp-1.0.apk";

    private void loadFile() {    //下载的方法

        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    DownLoadUtils downLoadUtils = new DownLoadUtils();
                    downLoadUtils.start(MainActivity.this, apk_url, null, 2, "UnknowApp-1.0.apk");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

相关文章

网友评论

      本文标题:线程池上传(下载)文件的简单使用以及APK的安装

      本文链接:https://www.haomeiwen.com/subject/zacxyqtx.html