1、添加依赖
// okhttp
implementation 'com.squareup.okhttp3:okhttp:3.12.0'
// retrofit
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0' // 转换器,请求结果转换成 Model
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' // 配合 Rxjava 使用
// Rxjava
implementation 'io.reactivex.rxjava2:rxjava:2.2.4'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
2、添加权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
3、获取 SD 卡权限和 SD 卡根目录
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initSD();
}
private void initSD() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) ==
PackageManager.PERMISSION_GRANTED) {
openSD();
}else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 100 && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openSD();
}
}
private void openSD() {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
sd = Environment.getExternalStorageDirectory();
}
}
4、OkHttp
private void loadFileByOk() {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.build();
Request request = new Request.Builder()
.url("http://cdn.banmi.com/banmiapp/apk/banmi_330.apk")
.build();
Call call = okHttpClient.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 {
InputStream inputStream = response.body().byteStream();
// 获取要下载文件的大小
long max = response.body().contentLength();
//保存文件
saveFiles(inputStream, sd+"/"+"abc123.apk", max);
}
});
}
5、Retrofit
private void loadFileByRetrofit() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(HttpService.url)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
HttpService httpService = retrofit.create(HttpService.class);
httpService.getData()
.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();
long max = responseBody.contentLength();
saveFiles(inputStream, sd+"/"+"abc456.apk", max);
}
@Override
public void onError(Throwable e) {
Log.e(TAG, "onError: " + e.getMessage());
}
@Override
public void onComplete() {
}
});
}
6、HttpURLConnection
private void loadFileByHttp() {
new Thread(new Runnable() {
@Override
public void run() {
try {
URL url = new URL("http://cdn.banmi.com/banmiapp/apk/banmi_330.apk");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
int i = connection.getResponseCode();
if (i == 200){
InputStream inputStream = connection.getInputStream();
int max = connection.getContentLength();
saveFiles(inputStream, sd+"/"+"abc789.jpk", max);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
7、下载文件( saveFiles() 方法 )
/**
* 保存文件
* @param inputStream
* @param string 文件保存的路径
* @param max
*/
private void saveFiles(InputStream inputStream, final String string, long max) {
// ProgressBar 设置最大值
pb.setMax((int) max);
// 读写的进度
int count = 0;
try {
// 输出流
FileOutputStream fileOutputStream = new FileOutputStream(new File(string));
byte[] bytes = new byte[1024];
int len = 0;
while ((len=inputStream.read(bytes)) != -1) {
fileOutputStream.write(bytes,0,len);
// 传递当前读写的进度
count += len;
Log.e(TAG, "saveFiles: " + count + " max: " + max);
pb.setProgress(count);
}
fileOutputStream.close();
inputStream.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();
}
}
8、InstallUtil 类( 可以实现下载完应用后跳出是否安装的界面 )
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.RequiresApi;
import android.support.v4.content.FileProvider;
import java.io.File;
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);
}
}
/**
*android 1.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);
}
/**
* android 7.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);
}
/**
* android 8.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();
}
}
9、HttpService
public interface HttpService {
String url="http://cdn.banmi.com/banmiapp/";
@GET("apk/banmi_330.apk")
Observable<ResponseBody> getData();
}
10、ProgressBar
<ProgressBar
android:id="@+id/pb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
style="?android:progressBarStyleHorizontal"/>