首先下载问文件需要在AndroidManifest.xml里添加SD卡读写权限,下面两个权限:
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
android:name="android.permission.READ_EXTERNAL_STORAGE" />
下载app还需要一个下载帮助类,文件的读写流类;此类外界调用时需要传人Handler,主要负责app下载完成时通知外界做安装操作,此外还添加了安卓的系统通知栏Notification,负责在系统通知栏上显示下在进度,由于Android对通知栏做了重构,Notification只能安卓16以上的api调用,16以下用NotificationCompat,如果下兼容api16以下自行查阅NotificationCompat使用添加兼容,此外Android8.0对Notification的调用还与8.0以下的不一样!(如下看代码!)如果有同学不需要系统通知栏显示进度可自行屏蔽Notification的代码。也可以删了Notification的代码自己写一个Dialog进度条去显示进度!
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.util.Log; import com.qianjinjia.zhishan.R; import com.qianjinjia.zhishan.dialog.ProgressBarDialog; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.text.NumberFormat; /** * Created by Administrator on 2018/3/9. */ public class DownFileHelper { Handler handler; Context mContext; NotificationManager mNotifyManager; Notification.Builder builder; public DownFileHelper(Context mContext, Handler handler) { this.handler = handler; this.mContext = mContext; } /** * 下载最新版本的apk * * @param path apk下载地址 */ public void downFile(final String path) { mNotifyManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); Bitmap btm = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.app_icon);//可以换成你的app的logo if (Build.VERSION.SDK_INT >= 26) { //创建 通知通道 channelid和channelname是必须的(自己命名就好) NotificationChannel channel = new NotificationChannel("1", "Channel1", NotificationManager.IMPORTANCE_DEFAULT); channel.enableLights(true);//是否在桌面icon右上角展示小红点 channel.setLightColor(Color.GREEN);//小红点颜色 channel.setShowBadge(true); //是否在久按桌面图标时显示此渠道的通知 mNotifyManager.createNotificationChannel(channel); builder = new Notification.Builder(mContext, "1"); //设置通知显示图标、文字等 builder.setSmallIcon(R.mipmap.app_logo)//可以换成你的app的logo .setLargeIcon(btm) .setTicker("正在下载") .setContentTitle("我的app") .setAutoCancel(true) .build(); mNotifyManager.notify(1, builder.build()); } else { builder = new Notification.Builder(mContext); builder.setSmallIcon(R.mipmap.app_logo)//可以换成你的app的logo .setLargeIcon(btm) .setTicker("正在下载") .setContentTitle("我的app") .setAutoCancel(true)//可以滑动删除通知栏 .build(); mNotifyManager.notify(1, builder.build()); } new Thread() { public void run() { try { URL url = new URL(path); HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setReadTimeout(5000); con.setConnectTimeout(5000); con.setRequestProperty("Charset", "UTF-8"); con.setRequestMethod("GET"); if (con.getResponseCode() == 200) { int length = con.getContentLength();// 获取文件大小 InputStream is = con.getInputStream(); FileOutputStream fileOutputStream = null; if (is != null) { //对apk进行保存 File file = new File(Environment.getExternalStorageDirectory() .getPath(), "your_app_name.apk"); fileOutputStream = new FileOutputStream(file); byte[] buf = new byte[1024]; int ch; int process = 0; NumberFormat numberFormat = NumberFormat.getInstance(); // 设置精确到小数点后2位 numberFormat.setMaximumFractionDigits(2); String result; while ((ch = is.read(buf)) != -1) { fileOutputStream.write(buf, 0, ch); process += ch; //更新进度条 result = numberFormat.format((float) process / (float) length * 100); builder.setContentText("下载进度:" + result + "%"); builder.setProgress(length, process, false); mNotifyManager.notify(1, builder.build()); } } if (fileOutputStream != null) { fileOutputStream.flush(); fileOutputStream.close(); } //apk下载完成,使用Handler()通知安装apk builder.setProgress(length, length, false); builder.setContentText("已经下载完成"); mNotifyManager.notify(1, builder.build()); mNotifyManager.cancelAll(); handler.sendEmptyMessage(0); } } catch (Exception e) { e.printStackTrace(); } } }.start(); } }
安装app的实现类,其中Android8.0安装时需要检测和申请app可安装未知来源权限允许,在AndroidManifest.xml添加
package com.qianjinjia.zhishan.helper; import android.Manifest; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.support.v4.app.ActivityCompat; import android.support.v4.content.FileProvider; import com.qianjinjia.zhishan.BuildConfig; import com.qianjinjia.zhishan.dialog.ConfirmDialog; import java.io.File; import java.io.IOException; /** * Created by Administrator on 2018/3/13. */ public class InstallApk { Activity context; public InstallApk(Activity context) { this.context = context; } public void installApk(File apkFile) { Intent intent = new Intent(Intent.ACTION_VIEW); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { boolean b = context.getPackageManager().canRequestPackageInstalls(); if (b) { intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri contentUri = FileProvider.getUriForFile(context.getApplicationContext(), BuildConfig.APPLICATION_ID+".fileProvider", apkFile); intent.setDataAndType(contentUri, "application/vnd.android.package-archive"); context.startActivity(intent); } else { //请求安装未知应用来源的权限 ConfirmDialog confirmDialog =new ConfirmDialog(context); confirmDialog.setStyle("安装权限","Android8.0安装应用需要打开未\n知来源权限,请去设置中开启权限", "去设置","取消"); confirmDialog.setClicklistener(new ConfirmDialog.ClickListenerInterface() { @Override public void doConfirm() { String[] mPermissionList = new String[]{Manifest.permission.REQUEST_INSTALL_PACKAGES}; ActivityCompat.requestPermissions(context, mPermissionList, 2); } }); confirmDialog.show(); } } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri contentUri = FileProvider.getUriForFile(context.getApplicationContext(), BuildConfig.APPLICATION_ID+".fileProvider", apkFile); intent.setDataAndType(contentUri, "application/vnd.android.package-archive"); context.startActivity(intent); } else { intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } } } }
还需要个权限检测和权限申请的帮助类
package com.qianjinjia.zhishan.helper; import android.app.Activity; import android.content.pm.PackageManager; import android.os.Build; import android.support.annotation.RequiresApi; import android.support.v4.app.ActivityCompat; /** * Created by Administrator on 2018/3/14. */ //权限检测和申请帮助类 public class PermissionHelper { Activity activity; public PermissionHelper(Activity activity) { this.activity = activity; } /** * 第 1 步: 检查是否拥有指定的所有权限 */ public boolean checkPermissionAllGranted(String[] permissions) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { for (String permission : permissions) { if (ActivityCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) { // 只要有一个权限没有被授予, 则直接返回 false return false; } } return true; } return true; } /** * 第 2 步: 请求权限 */ // 一次请求多个权限, 如果其他有权限是已经授予的将会自动忽略掉 public void requestPermissionAllGranted(String[] permissions, int i) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { ActivityCompat.requestPermissions(activity, permissions, i); } } }
然后在Activity里负责是否更新逻辑和下载所需要的权限申请和检测以及安装app的操作
public class MainActivity extends BaseActivityimplements MainContract.View { Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 0: new InstallApk(MainActivity.this) .installApk(new File(Environment.getExternalStorageDirectory(), "your_app_name.apk")); break; } } }; @Override protected int getLayout() { return R.layout.activity_main; } //这里是请求服务的是否更新接口返回数据的处理,你可以结合你自己后端的处理 @Override public void updateonSuccess(UpdateBean bean) { if (Integer.valueOf(bean.getData().getAndroid_version_index()) > SystemTool.getAppVersionCode(MyApplication._context)) { url = bean.getData().getAndroid_download_url(); if (bean.getData().getAndroid_is_update() == 2) { //更新版本 String today = DateUtils.getNowTime(); String days = (String) SpUtils.getParam(getApplicationContext(), "day", ""); if (!days.equals(today)) { updateDialog(bean); } } else { //强制更新版本 forcedUpdateDialog(bean); } } } private void forcedUpdateDialog(UpdateBean bean) { aButtonDialog.setStyle("版本更新", bean.getData().getUpdate_desc(), "立即更新"); aButtonDialog.setClicklistener(new AButtonDialog.ClickListenerInterface() { @Override public void doConfirm() { permissionsCheckAndDownload(); } }); aButtonDialog.setOnKeyListener(new DialogInterface.OnKeyListener() { @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { aButtonDialog.dismiss(); android.os.Process.killProcess(android.os.Process.myPid()); //获取PID System.exit(0); //常规java、c#的标准退出法,返回值为0代表正常退出 } return false; } }); aButtonDialog.show(); } private void updateDialog(UpdateBean bean) { confirmDialog.setStyle("版本更新", bean.getData().getUpdate_desc(), "立即更新", "取消"); confirmDialog.setClicklistener(new ConfirmDialog.ClickListenerInterface() { @Override public void doConfirm() { permissionsCheckAndDownload(); } }); confirmDialog.setOnCancelClickListener(new ConfirmDialog.OnCancelClickListener() { @Override public void onCancelClick() { String today = DateUtils.getNowTime(); SpUtils.put(getApplicationContext(), "day", today); } }); confirmDialog.show(); } private void permissionsCheckAndDownload() { if (Build.VERSION.SDK_INT >= 23) { permissionsCheck(); } else { new DownFileHelper(MainActivity.this, handler) .downFile(url); } } private void permissionsCheck() { if (!permissionHelper.checkPermissionAllGranted(mPermissionList)) { permissionHelper.requestPermissionAllGranted(mPermissionList, 1); } else { new DownFileHelper(MainActivity.this, handler) .downFile(url); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case 1: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { new DownFileHelper(MainActivity.this, handler) .downFile(url); } else { //不给读写权限处理 } break; case 2: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { new InstallApk(MainActivity.this) .installApk(new File(Environment.getExternalStorageDirectory(), "your_app_name.apk")); } else { Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES); startActivityForResult(intent, 10012); } break; } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case 10012: Log.d("resultCode", resultCode + ""); if (Build.VERSION.SDK_INT >= 26) { boolean b = getPackageManager().canRequestPackageInstalls(); if (b) { new InstallApk(MainActivity.this) .installApk(new File(Environment.getExternalStorageDirectory(), "qianjinjia.apk")); } else { final AButtonDialog aButton = new AButtonDialog(MainActivity.this); aButton.setStyle("您未打开未知来源\n权限不能及时更新", "知道了"); aButton.setClicklistener(new AButtonDialog.ClickListenerInterface() { @Override public void doConfirm() { } }); aButton.setOnKeyListener(new DialogInterface.OnKeyListener() { @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { aButton.dismiss(); android.os.Process.killProcess(android.os.Process.myPid()); //获取PID System.exit(0); //常规java、c#的标准退出法,返回值为0代表正常退出 } return false; } }); aButton.show(); } } break; default: break; } } }