文件下载

1. OkHttp下载文件(权限处理)

public static void okDownload(){
        OkHttpClient client = new OkHttpClient.Builder()
                .build();

        Request request = new Request.Builder()
                .url("http://cdn.banmi.com/banmiapp/apk/banmi_330.apk")
                .get()
                .build();

        Call call = client.newCall(request);

        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e(TAG, "onFailure: "+e.getMessage() );
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                ResponseBody body = response.body();
                InputStream inputStream = body.byteStream();
                saveFile(inputStream,sd+"/"+"abc123.apk",body.contentLength());
            }
        });
​
    }
    /**
     * 保存文件
     * @param inputStream
     * @param path
     * @param callBack
     * @param max
     */
    private static void saveFiles(InputStream inputStream, String path,  long max) {
      //读写的进度
        long count = 0;
        try {
            FileOutputStream outputStream = new FileOutputStream(new File(path));

            int length = -1;
            byte[] bys = new byte[1024*10];

            while((length = inputStream.read(bys))!=-1){
                outputStream.write(bys,0,length);

                count += length;

                Log.d(TAG, "progress: "+count  +"    max:" + max);
            }

            inputStream.close();
            outputStream.close();

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this,"下载完成",Toast.LENGTH_SHORT).show();

                    InstallUtil.installApk(MainActivity.this,string);
                }
            });
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

2.Retrofit下载:

接口

public interface DownService {
    String url = "http://cdn.banmi.com/banmiapp/apk/banmi_330.apk";
    String baseUrl = "http://cdn.banmi.com/";

    @GET("banmiapp/apk/banmi_330.apk")
    @Streaming
    Observable<ResponseBody> downLoadFile();
}

下载代码

    public static void retrofitDown() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(ApiService.mBaseUrl)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
​
        ApiService apiService = retrofit.create(ApiService.class);
        Observable<ResponseBody> download = apiService.download();
        download.subscribeOn(Schedulers.io())//io线程
                .subscribe(new Observer<ResponseBody>() {
                  //因为要写文件,所以Observer不切换到主线程
                    @Override
                    public void onSubscribe(Disposable d) {
​
                    }
​
                    @Override
                    public void onNext(ResponseBody responseBody) {
                        InputStream inputStream = responseBody.byteStream();
                        saveFiles(inputStream,path,callBack,responseBody.contentLength());
                    }
​
                    @Override
                    public void onError(Throwable e) {
                        callBack.onFail(e.getMessage());
                    }
​
                    @Override
                    public void onComplete() {
​
                    }
                });
    }

3.HttpUrlConnection下载:

private void http() {
        new Thread(){
            @Override
            public void run() {
                super.run();

                try {
                    URL url = new URL("http://cdn.banmi.com/banmiapp/apk/banmi_330.apk");
                    HttpURLConnection con = (HttpURLConnection) url.openConnection();

                    int responseCode = con.getResponseCode();
                    if (responseCode==200){
                        InputStream inputStream = con.getInputStream();
                        int max = con.getContentLength();
                        saveFile(inputStream,sd+"/"+"abc789.apk",max);
                    }

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

4.下载ok后,安装对应得apk,例如使用okhttp下载

public class InstallUtil {

    public static final int UNKNOWN_CODE = 2019;
    public static void installApk(Context context, String path) {
        if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
            startInstallO(context,path);
        }else if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
            startInstallN(context,path);
        }else {
            startInstall(context,path);
        }
    }

    /**
     *android1.x-6.x
     *@param path 文件的路径
     */
    private static void startInstall(Context context, String path) {
        Intent install = new Intent(Intent.ACTION_VIEW);
        install.setDataAndType(Uri.parse("file://" + path), "application/vnd.android.package-archive");
        install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(install);
    }

    /**
     * android7.x
     * @param path 文件路径
     */
    @RequiresApi(api = Build.VERSION_CODES.N)
    private static void startInstallN(Context context, String path) {
        //参数1 上下文, 参数2 在AndroidManifest中的android:authorities值, 参数3  共享的文件
        Uri apkUri = FileProvider.getUriForFile(context, "com.baidu.download.provider", new File(path));
        Intent install = new Intent(Intent.ACTION_VIEW);
        //由于没有在Activity环境下启动Activity,设置下面的标签
        install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        //添加这一句表示对目标应用临时授权该Uri所代表的文件
        install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        install.setDataAndType(apkUri, "application/vnd.android.package-archive");
        context.startActivity(install);
    }

    /**
     * android8.x
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    private static void startInstallO(final Context context, String path) {
        //首先用canRequestPackageInstalls()方法判断你的应用是否有这个权限
        boolean isGranted = context.getPackageManager().canRequestPackageInstalls();
        if (isGranted) startInstallN(context,path);//安装应用的逻辑(写自己的就可以)
        else new AlertDialog.Builder(context)
                .setCancelable(false)
                .setTitle("安装应用需要打开未知来源权限,请去设置中开启权限")
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface d, int w) {
                        Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
                        Activity act = (Activity) context;
                        act.startActivityForResult(intent, UNKNOWN_CODE);
                    }
                })
                .show();
    }
}

//授权 可以安装apk的权限后,回调此方法,进行安装
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        if (requestCode == UNKNOWN_CODE) {
            installApk(this, Environment.getExternalStorageDirectory() + "/a.apk");//再次执行安装流程,包含权限判等
        }
    }

内容提供者注册

  <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.baidu.download.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

//file_paths
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <root-path name="download" path="" />
    </paths>
</resources>

如果是高版本8.0以上手机,必须还要做其他要做适配

  1. 清单列表添加
//对高版本的手机访问sdcard
android:requestLegacyExternalStorage="true"
//适配9.0Https
android:usesCleartextTraffic="true"
  1. 权限添加
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

5.多线程断点续传--下载

复习RandomAccessFile

private void moreDown() {
        ThreadManager.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                try {
                    URL url = new URL(mUrl);
                    HttpURLConnection con = (HttpURLConnection) url.openConnection();
                    //1.获取要下载文件的大小
                    long contentLength = con.getContentLength();
                    Log.d(TAG, "contentLength: " + contentLength);
​
                    //2.创建与下载文件同样大小的空壳文件
                    RandomAccessFile raf = new RandomAccessFile(mDownPath, "rw");
                    raf.setLength(contentLength);
​
                    //3.计算各个线程下载的范围
                     /*
                     * 线程个数 THREAD_COUNT = 3 以3个线程为空 每个线程编号从0开始 0线程 1线程 2线程
                     * 文件长度contentLength 以11bytes为例
                     * 每个线程下载长度 blockSize = contentLength / THREAD_COUNT 11 / 3 = 3
                     * 线程threadId start下载起点 end下载的终点 0线程 0 ~ 2 1线程 3 ~ 5 2线程 6 ~ 8 9
                     * 10(contentLength - 1)
                     * 线程threadId 下载的 start = threadId * blockSize end = (threadId + 1)* blockSize - 1
                     * 如果是下载最后一部分线程end = contentLength - 1
                     */
                    long block = contentLength / THREAD_COUNT;
​
                    //4.创建指定个数的线程并给定下载范围下载
                    for (int i = 0; i < THREAD_COUNT; i++) {
                        long start = i * block;
                        long end = (i + 1) * block - 1;
​
                        if (i == THREAD_COUNT - 1) {
                            end = contentLength - 1;
                        }
​
                        //开启线程下载
                        down(i, start, end);
                    }
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
​
    }
​
    /**
     * 开启线程下载对应范围的数据
     *
     * @param threadId
     * @param start
     * @param end
     */
    private void down(final int threadId, final long start, final long end) {
        Log.d(TAG, "线程: " + threadId + ",下载范围:" + start + "--" + end);
        ThreadManager.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                try {
​
                    //记录当前线程下载位置
                    long currentPosition = start;
                    //获取sp中的下载位置,如果为0,说明没有下载过或者下载完成过
                    long position = (long) SharedPreferencesUtils.getParam(MainActivity.this,
                            "" + threadId, 0L);
​
                    if (position != 0){
                        //有以前未下载完成的,接着下载
                        currentPosition = position;
                        Log.d(TAG, "断点续传: "+threadId+",currentPosition:"+currentPosition);
                    }else {
                        Log.d(TAG, "非断点续传: "+threadId);
                    }
​
                    URL url = new URL(mUrl);
                    HttpURLConnection con = (HttpURLConnection) url.openConnection();
                    //设置请求部分数据资源:Range:bytes=10-30
                    con.setRequestProperty("Range", "bytes=" + currentPosition + "-" + end);
                    int responseCode = con.getResponseCode();
                    Log.d(TAG, "responseCode: " + responseCode);
                    //206,请求部分资源
                    if (responseCode == 206) {
                        InputStream inputStream = con.getInputStream();
                        RandomAccessFile rw = new RandomAccessFile(mDownPath, "rw");
                        //设置当前线程写入的位置
                        rw.seek(currentPosition);
​
                        //计数器,本次已下载的数据大小
                        int count = 0;
                        //本次需下载的数据总长度
                        long length = end - currentPosition;
                        byte[] bytes = new byte[2048];
                        int len;
​
                        while ((len = inputStream.read(bytes)) != -1) {
                            //写入
                            rw.write(bytes, 0, len);
                            //计数器累加
                            count += len;
                            //下载的当前位置累加
                            currentPosition+=len;
                            //将当前写入的位置保存
                            SharedPreferencesUtils.setParam(MainActivity.this,threadId+"",currentPosition);
                            Log.d(TAG, "线程: " + threadId + "总长度:" + length + "下载:" + count);

//progress显示每个线程下载进度
                        }
​
                        inputStream.close();
                        rw.close();
​
                        Log.d(TAG, "线程: " + threadId + "下载完毕");
                        //完成下载后将当前的写入位置置零
                        SharedPreferencesUtils.setParam(MainActivity.this,threadId+"",0L);
                    }
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
​
            }
        });
    }

完整代码如下:

MainActivity

public class MainActivity extends AppCompatActivity {
​
    private static final String TAG = "MainActivity";
    @BindView(R.id.btn_ok)
    Button mBtnOk;
    @BindView(R.id.btn_http)
    Button mBtnHttp;
    @BindView(R.id.pb)
    ProgressBar mPb;
    @BindView(R.id.btn_retrofit)
    Button mBtnRetrofit;
    @BindView(R.id.btn_more)
    Button mBtnMore;
    @BindView(R.id.btn_pause)
    Button mBtnPause;
    @BindView(R.id.pb2)
    ProgressBar mPb2;
    @BindView(R.id.pb3)
    ProgressBar mPb3;
    @BindView(R.id.btn_install)
    Button mBtnInstall;
    private String mUrl = "http://cdn.banmi.com/banmiapp/apk/banmi_330.apk";
    private String mPath;
    //线程数量
    private static int THREAD_COUNT = 3;
    private String mDownPath = "/storage/emulated/0/down.apk";
​
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        initPer();
    }
​
    private void initPer() {
        if (ActivityCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
​
        } else {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
        }
    }
​
    @OnClick({R.id.btn_ok, R.id.btn_http, R.id.pb, R.id.btn_retrofit, R.id.btn_more, R.id.btn_pause, R.id.pb2, R.id.pb3, R.id.btn_install})
    public void onClick(View v) {
        switch (v.getId()) {
            default:
                break;
            case R.id.btn_ok:
                okDown();
                break;
            case R.id.btn_http:
                httpDown();
                break;
            case R.id.btn_retrofit:
                retrofitDown();
                break;
            case R.id.btn_more:
                moreDown();
                break;
            case R.id.btn_install:
                installApk(mDownPath);
                break;
        }
    }
​
    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
​
    private void moreDown() {
        ThreadManager.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                try {
                    URL url = new URL(mUrl);
                    HttpURLConnection con = (HttpURLConnection) url.openConnection();
                    //1.获取要下载文件的大小
                    long contentLength = con.getContentLength();
                    Log.d(TAG, "contentLength: " + contentLength);
​
                    //2.创建与下载文件同样大小的空壳文件
                    RandomAccessFile raf = new RandomAccessFile(mDownPath, "rw");
                    raf.setLength(contentLength);
​
                    //3.计算各个线程下载的范围
                     /*
                     * 线程个数 THREAD_COUNT = 3 以3个线程为空 每个线程编号从0开始 0线程 1线程 2线程
                     * 文件长度contentLength 以11bytes为例
                     * 每个线程下载长度 blockSize = contentLength / THREAD_COUNT 11 / 3 = 3
                     * 线程threadId start下载起点 end下载的终点 0线程 0 ~ 2 1线程 3 ~ 5 2线程 6 ~ 8 9
                     * 10(contentLength - 1)
                     * 线程threadId 下载的 start = threadId * blockSize end = (threadId + 1)* blockSize - 1
                     * 如果是下载最后一部分线程end = contentLength - 1
                     */
                    long block = contentLength / THREAD_COUNT;
​
                    //4.创建指定个数的线程并给定下载范围下载
                    for (int i = 0; i < THREAD_COUNT; i++) {
                        long start = i * block;
                        long end = (i + 1) * block - 1;
​
                        if (i == THREAD_COUNT - 1) {
                            end = contentLength - 1;
                        }
​
                        //开启线程下载
                        down(i, start, end);
                    }
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
​
    }
​
    /**
     * 开启线程下载对应范围的数据
     *
     * @param threadId
     * @param start
     * @param end
     */
    private void down(final int threadId, final long start, final long end) {
        Log.d(TAG, "线程: " + threadId + ",下载范围:" + start + "--" + end);
        ThreadManager.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                try {
​
                    //记录当前线程下载位置
                    long currentPosition = start;
                    //获取sp中的下载位置,如果为0,说明没有下载过或者下载完成过
                    long position = (long) SharedPreferencesUtils.getParam(MainActivity.this,
                            "" + threadId, 0L);
​
                    if (position != 0){
                        //有以前未下载完成的,接着下载
                        currentPosition = position;
                        Log.d(TAG, "断点续传: "+threadId+",currentPosition:"+currentPosition);
                    }else {
                        Log.d(TAG, "非断点续传: "+threadId);
                    }
​
                    URL url = new URL(mUrl);
                    HttpURLConnection con = (HttpURLConnection) url.openConnection();
                    //设置请求部分数据资源:Range:bytes=10-30
                    con.setRequestProperty("Range", "bytes=" + currentPosition + "-" + end);
                    int responseCode = con.getResponseCode();
                    Log.d(TAG, "responseCode: " + responseCode);
                    //206,请求部分资源
                    if (responseCode == 206) {
                        InputStream inputStream = con.getInputStream();
                        RandomAccessFile rw = new RandomAccessFile(mDownPath, "rw");
                        //设置当前线程写入的位置
                        rw.seek(currentPosition);
​
                        //计数器,本次已下载的数据大小
                        int count = 0;
                        //本次需下载的数据总长度
                        long length = end - currentPosition;
                        byte[] bytes = new byte[2048];
                        int len;
​
                        while ((len = inputStream.read(bytes)) != -1) {
                            //写入
                            rw.write(bytes, 0, len);
                            //计数器累加
                            count += len;
                            //下载的当前位置累加
                            currentPosition+=len;
                            //将当前写入的位置保存
                            SharedPreferencesUtils.setParam(MainActivity.this,threadId+"",currentPosition);
                            Log.d(TAG, "线程: " + threadId + "总长度:" + length + "下载:" + count);
                        }
​
                        inputStream.close();
                        rw.close();
​
                        Log.d(TAG, "线程: " + threadId + "下载完毕");
                        //完成下载后将当前的写入位置置零
                        SharedPreferencesUtils.setParam(MainActivity.this,threadId+"",0L);
                    }
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
​
            }
        });
    }
​
    private void retrofitDown() {
        DownLoadUtil.retrofitDown("/storage/emulated/0/my.apk", new DownLoadUtil.ResultCallBack() {
            @Override
            public void onFail(String message) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        ToastUtil.showToast(MainActivity.this, "下载失败");
                    }
                });
​
            }
​
            @Override
            public void onProgress(long progress, long max) {
                mPb.setMax((int) max);
                mPb.setProgress((int) progress);
            }
​
            @Override
            public void onSuccess(final String path) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        ToastUtil.showToast(MainActivity.this, "下载成功");
                        mPath = path;
                        installApk(mPath);
                    }
                });
            }
        });
    }
​
    private void httpDown() {
        DownLoadUtil.httpDownload(mUrl, "/storage/emulated/0/my.apk", new DownLoadUtil.ResultCallBack() {
            @Override
            public void onFail(String message) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        ToastUtil.showToast(MainActivity.this, "下载失败");
                    }
                });
​
            }
​
            @Override
            public void onProgress(long progress, long max) {
                mPb.setMax((int) max);
                mPb.setProgress((int) progress);
            }
​
            @Override
            public void onSuccess(final String path) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        ToastUtil.showToast(MainActivity.this, "下载成功");
                        mPath = path;
                        installApk(mPath);
                    }
                });
            }
        });
    }
​
    private void okDown() {
        DownLoadUtil.okDownload(mUrl, "/storage/emulated/0/my.apk", new DownLoadUtil.ResultCallBack() {
            @Override
            public void onFail(String message) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        ToastUtil.showToast(MainActivity.this, "下载失败");
                    }
                });
​
            }
​
            @Override
            public void onProgress(long progress, long max) {
                mPb.setMax((int) max);
                mPb.setProgress((int) progress);
            }
​
            @Override
            public void onSuccess(final String path) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        ToastUtil.showToast(MainActivity.this, "下载成功");
                        mPath = path;
                        installApk(mPath);
                    }
                });
            }
        });
    }
​
    private void installApk(String path) {
        InstallUtil.installApk(this, path);
    }
​
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == InstallUtil.UNKNOWN_CODE) {
            InstallUtil.installApk(this, mPath);//再次执行安装流程,包含权限判等
        }
    }
}

DownLoadUtil类:

public class DownLoadUtil {
    private static final String TAG = "DownLoadUtil";
​
    public static void okDownload(String url, final String path, final ResultCallBack callBack){
        OkHttpClient client = new OkHttpClient.Builder()
                .build();
​
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();
        //异步调用,不用再新建线程了
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                callBack.onFail(e.getMessage());
            }
​
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                InputStream inputStream = response.body().byteStream();
                saveFiles(inputStream,path,callBack,response.body().contentLength());
            }
        });
​
    }
​
    /**
     * 保存文件
     * @param inputStream
     * @param path
     * @param callBack
     * @param max
     */
    private static void saveFiles(InputStream inputStream, String path, ResultCallBack callBack, long max) {
        File file = new File(path);
        int len;
        byte[] bytes = new byte[4096];
        //读写的进度
        long count = 0;
        try {
            //输出流
            FileOutputStream fos = new FileOutputStream(file);
            while ((len = inputStream.read(bytes)) != -1){
                fos.write(bytes,0,len);
                count+=len;
                //传递当前读写的进度
                callBack.onProgress(count,max);
                Log.d(TAG, "progress: "+count);
            }
            fos.close();
            inputStream.close();
            //完成写入
            callBack.onSuccess(path);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
​
    public static void httpDownload(final String url, final String path, final ResultCallBack callBack) {
        ThreadManager.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                try {
                    URL url1 = new URL(url);
                    HttpURLConnection conn = (HttpURLConnection) url1.openConnection();
                    int responseCode = conn.getResponseCode();
                    if (responseCode == 200){
                        InputStream inputStream = conn.getInputStream();
                        int max = conn.getContentLength();
                        saveFiles(inputStream,path,callBack,max);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    callBack.onFail(e.getMessage());
                }
            }
        });
    }
​
    public static void retrofitDown(final String path, final ResultCallBack callBack) {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(ApiService.mBaseUrl)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
​
        ApiService apiService = retrofit.create(ApiService.class);
        Observable<ResponseBody> download = apiService.download();
        download.subscribeOn(Schedulers.io())
                .subscribe(new Observer<ResponseBody>() {
                    @Override
                    public void onSubscribe(Disposable d) {
​
                    }
​
                    @Override
                    public void onNext(ResponseBody responseBody) {
                        InputStream inputStream = responseBody.byteStream();
                        saveFiles(inputStream,path,callBack,responseBody.contentLength());
                    }
​
                    @Override
                    public void onError(Throwable e) {
                        callBack.onFail(e.getMessage());
                    }
​
                    @Override
                    public void onComplete() {
​
                    }
                });
    }
​
    /**
     * 结果的回调
     */
    public interface ResultCallBack{
        void onFail(String message);
​
        void onProgress(long progress, long max);
​
        void onSuccess(String path);
    }
}

ThreadManager类

public class ThreadManager {
    private static ThreadManager mManager;
    private final ThreadPoolExecutor mExecutor;
​
    private ThreadManager(){
        mExecutor = new ThreadPoolExecutor(5,//核心线程数量,核心池的大小
                20,//线程池最大线程数
                30,//表示线程没有任务执行时最多保持多久时间会终止
                TimeUnit.SECONDS,//时间单位
                new LinkedBlockingQueue<Runnable>(),//任务队列,用来存储等待执行的任务
                Executors.defaultThreadFactory(),//线程工厂,如何去创建线程的
                new ThreadPoolExecutor.AbortPolicy());
    }
    public static ThreadManager getInstance(){
        if (mManager == null){
            synchronized (ThreadManager.class){
                if (mManager == null){
                    mManager = new ThreadManager();
                }
            }
        }
​
        return mManager;
    }
​
    /**
     * 执行任务
     */
    public void execute(Runnable runnable){
        if(runnable==null)return;
​
        mExecutor.execute(runnable);
    }
    /**
     * 从线程池中移除任务
     */
    public void remove(Runnable runnable){
        if(runnable==null)return;
​
        mExecutor.remove(runnable);
    }
​
}

InstallUtil类

public class InstallUtil {
​
    public static final int UNKNOWN_CODE = 2019;
    public static void installApk(Context context, String path) {
        if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
            startInstallO(context,path);
        }else if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
            startInstallN(context,path);
        }else {
            startInstall(context,path);
        }
    }
​
    /**
     *android1.x-6.x
     *@param path 文件的路径
     */
    public static void startInstall(Context context, String path) {
        Intent install = new Intent(Intent.ACTION_VIEW);
        install.setDataAndType(Uri.parse("file://" + path), "application/vnd.android.package-archive");
        install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(install);
    }
​
    /**
     * android7.x
     * @param path 文件路径
     */
    @RequiresApi(api = Build.VERSION_CODES.N)
    public static void startInstallN(Context context, String path) {
        //参数1 上下文, 参数2 在AndroidManifest中的android:authorities值, 参数3  共享的文件
        Uri apkUri = FileProvider.getUriForFile(context, "com.baidu.download.provider", new File(path));
        Intent install = new Intent(Intent.ACTION_VIEW);
        //由于没有在Activity环境下启动Activity,设置下面的标签
        install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        //添加这一句表示对目标应用临时授权该Uri所代表的文件
        install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        install.setDataAndType(apkUri, "application/vnd.android.package-archive");
        context.startActivity(install);
    }
​
    /**
     * android8.x
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    private static void startInstallO(final Context context, String path) {
        boolean isGranted = context.getPackageManager().canRequestPackageInstalls();
        if (isGranted) startInstallN(context,path);//安装应用的逻辑(写自己的就可以)
        else new AlertDialog.Builder(context)
                .setCancelable(false)
                .setTitle("安装应用需要打开未知来源权限,请去设置中开启权限")
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface d, int w) {
                        Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
                        Activity act = (Activity) context;
                        act.startActivityForResult(intent, UNKNOWN_CODE);
                    }
                })
                .show();
    }
}

SharedPreferencesUtils工具类:

public class SharedPreferencesUtils {
    /**
     * 保存在手机里面的文件名
     */
    private static final String FILE_NAME = "share_date";
    
    
    /**
     * 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法
     * @param context
     * @param key
     * @param object 
     */
    public static void setParam(Context context , String key, Object object){
        
        String type = object.getClass().getSimpleName();
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        
        if("String".equals(type)){
            editor.putString(key, (String)object);
        }
        else if("Integer".equals(type)){
            editor.putInt(key, (Integer)object);
        }
        else if("Boolean".equals(type)){
            editor.putBoolean(key, (Boolean)object);
        }
        else if("Float".equals(type)){
            editor.putFloat(key, (Float)object);
        }
        else if("Long".equals(type)){
            editor.putLong(key, (Long)object);
        }
        
        editor.commit();
    }
    
    
    /**
     * 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值
     * @param context
     * @param key
     * @param defaultObject
     * @return
     */
    public static Object getParam(Context context , String key, Object defaultObject){
        String type = defaultObject.getClass().getSimpleName();
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        
        if("String".equals(type)){
            return sp.getString(key, (String)defaultObject);
        }
        else if("Integer".equals(type)){
            return sp.getInt(key, (Integer)defaultObject);
        }
        else if("Boolean".equals(type)){
            return sp.getBoolean(key, (Boolean)defaultObject);
        }
        else if("Float".equals(type)){
            return sp.getFloat(key, (Float)defaultObject);
        }
        else if("Long".equals(type)){
            return sp.getLong(key, (Long)defaultObject);
        }
        
        return null;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容