看了https://blog.csdn.net/baidu_34928905/article/details/88388082的文章,感觉不错,就自己试了一下,现在把代码放出来,以备后用
在使用时,我们先要做的准备是
一、权限 在AndroidManifest.xml中加入
<!-- 允许访问网络 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 访问电话状态 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 允许程序写入外部存储,如SD卡上写文件 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 允许程序读取外部存储 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!--震动权限 -->
<uses-permission android:name="android.permission.VIBRATE"></uses-permission>
这里要注意的是,对于文件的读写权限,你要在页面中动态的申请,这里可以根据自己使用的不同方法自己确定
//权限的设置
myUntils.JudgePermission(this,this,"您拒绝了读取文件的功能,会造成软件无法更新下载,并且会影响到维修与巡更功能!",Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE);
二、我们知道,绝大多数国产Android App都会内置一个更新功能,也就是把新版本的APK放在服务器上,通过接口获取更新信息并下载,然后进行安装。虽然这种行为被Google严厉禁止,但身处这种环境下还是得妥协的。在android7.0之后,我们不能直接
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
而是要进行如下处理:
1、AndroidManifest中增加
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
2、res下新建xml/provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
好了,准备工作完成,开始我们的代码。
DownAPKService.java
public class DownAPKService extends Service {
private final int NotificationID = 0X10000;
private NotificationManager mNotificationManager = null;
private NotificationCompat.Builder builder;
//文件保存路径(如果有sd卡就保存sd卡,如果没有sd卡就保存到手机包名下的路径)
private String Apk_dir = "";
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
//创建保存路径
initAPKDir();
}
/**
* 创建保存路径
*/
private void initAPKDir() {
/*
* 创建路径的时候一定要用[/],不能使用[\],但是创建文件夹加文件的时候可以使用[\].
* [/]符号是Linux系统路径分隔符,而[\]是windows系统路径分隔符 Android内核是Linux.
*/
if(isHasSdcard())
{
//保存到sd卡
Apk_dir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/VersionChecker/";
}else{
//保存到app的包名路径下
Apk_dir = getApplicationContext().getFilesDir().getAbsolutePath() + "/VersionChecker/";
}
File destDir = new File(Apk_dir);
if(!destDir.exists())
{
destDir.mkdirs();
}
}
/**
* 判断是否插入sd卡
* @return true 有 false 无
*/
private boolean isHasSdcard() {
String status = Environment.getExternalStorageState();
return status.equals(Environment.MEDIA_MOUNTED);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 接收Intent传来的参数:
// 文件下载路径
String APK_URL = intent.getStringExtra("apk_url");
String APK_Name = intent.getStringExtra("apk_name");
DownFile(APK_URL, Apk_dir, APK_Name);
return super.onStartCommand(intent, flags, startId);
}
private void DownFile(String apk_url,String apk_path, String apk_name) {
OkGo.<File>get(apk_url)
.tag(this)
.execute(new FileCallback(apk_path,apk_name) {
@Override
public void onStart(Request<File, ? extends Request> request) {
super.onStart(request);
Log.e("DownAPKService.java(onStart)", "行数: 98 开始下载文件");
String id = "my_channel_01";
String name = "渠道的名字";
mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
// 针对Android 8.0版本对于消息栏的限制,需要加入channel渠道这一概念
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
//Android 8.0以上
NotificationChannel mChannel = new NotificationChannel(id,name,NotificationManager.IMPORTANCE_LOW);
mNotificationManager.createNotificationChannel(mChannel);
builder = new NotificationCompat.Builder(getApplicationContext());
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setTicker("正在下载新版本");
builder.setContentTitle(getApplicationName());
builder.setContentText("正在下载,请稍后...");
builder.setNumber(0);
builder.setChannelId(id);
builder.setAutoCancel(true);
}else{
builder = new NotificationCompat.Builder(getApplicationContext());
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setTicker("正在下载新版本");
builder.setContentTitle(getApplicationName());
builder.setContentText("正在下载,请稍后...");
builder.setNumber(0);
builder.setAutoCancel(true);
}
mNotificationManager.notify(NotificationID,builder.build());
}
@Override
public void onSuccess(Response<File> response) {
Log.e("DownAPKService.java(onSuccess)", "行数: 138 下载完成");
Intent installIntent = new Intent(Intent.ACTION_VIEW);
System.out.println(response.body().getPath());
Uri uri = Uri.fromFile(new File(response.body().getPath()));
installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
installIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri uri1 = FileProvider.getUriForFile(getApplicationContext(), getApplicationContext().getPackageName() + ".provider", response.body());
installIntent.setDataAndType(uri1, "application/vnd.android.package-archive");
}else{
installIntent.setDataAndType(uri, "application/vnd.android.package-archive");
}
PendingIntent mPendingIntent = PendingIntent.getActivity(DownAPKService.this, 0, installIntent, 0);
builder.setContentText("下载完成,请点击安装");
builder.setContentIntent(mPendingIntent);
mNotificationManager.notify(NotificationID, builder.build());
// 震动提示
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
assert vibrator != null;
vibrator.vibrate(250L);// 参数是震动时间(long类型)
stopSelf();
startActivity(installIntent);// 下载完成之后自动弹出安装界面
mNotificationManager.cancel(NotificationID);
}
@Override
public void onError(Response<File> response) {
super.onError(response);
System.out.println("文件下载失败");
mNotificationManager.cancel(NotificationID);
Log.e("DownAPKService.java(onError)", "行数: 171 err:" + response.message());
}
@Override
public void downloadProgress(Progress progress) {
super.downloadProgress(progress);
int percent = (int)Math.floor(progress.fraction * 100);
int totalS = Long.valueOf(progress.totalSize).intValue();
builder.setProgress(totalS, percent, false);
if (percent < 0) {//遏制有负数的情况
percent = 0;
}
builder.setContentInfo(percent + "%");
mNotificationManager.notify(NotificationID, builder.build());
}
});
}
/**
* @return
* @Description 获取当前应用的名称
*/
private String getApplicationName() {
PackageManager packageManager = null;
ApplicationInfo applicationInfo = null;
try {
packageManager = getApplicationContext().getPackageManager();
applicationInfo = packageManager.getApplicationInfo(getPackageName(), 0);
} catch (PackageManager.NameNotFoundException e) {
applicationInfo = null;
}
String applicationName = (String) packageManager.getApplicationLabel(applicationInfo);
return applicationName;
}
@Override
public void onDestroy() {
super.onDestroy();
stopSelf();
}
}
mainactivity.java中调用
String apk_url = "http://XXXXXX/home.apk";
Intent intent = new Intent();
intent.putExtra("apk_url",apk_url);
intent.putExtra("apk_name","home.apk");
intent.setClass(MainActivity.this,DownAPKService.class);
startService(intent);
最后,你要在Androidmanifest.xml中注册这个service
<service android:name=".services.DownAPKService"/>