[转]Android 应用自动更新及6.0,7.0,8.0适配安装

原贴:https://www.jianshu.com/p/ea42040c7acejava

原贴:https://www.jianshu.com/p/ea42040c7aceandroid

原贴:https://www.jianshu.com/p/ea42040c7aceapi

 

在线更新分为如下几个步骤:并发

1, 经过接口获取线上版本号,versionCode 2, 比较线上的versionCode 和本地的versionCode,弹出更新窗口 3, 下载APK文件(文件下载) 4,安装APK

首先建立 UpdateDownloadListener 接口类对下载的不一样状态回调:app

/** * @desciption: 下载不一样状态接口回调 */
public interface UpdateDownloadListener { /** * 下载请求开始回调 */
    void onStart(); /** * 请求成功,下载前的准备回调 * * @param contentLength 文件长度 * @param downloadUrl 下载地址 */
    void onPrepared(long contentLength, String downloadUrl); /** * 进度更新回调 * * @param progress 进度 * @param downloadUrl 下载地址 */
    void onProgressChanged(int progress, String downloadUrl); /** * 下载暂停回调 * * @param progress 进度 * @param completeSize 已下载的文件大小 * @param downloadUrl 下载地址 */
    void onPaused(int progress, int completeSize, String downloadUrl); /** * 下载完成回调 * * @param completeSize 已下载的文件大小 * @param downloadUrl 下载地址 */
    void onFinished(int completeSize, String downloadUrl); /** * 下载失败回调 */
    void onFailure(); }

而后建立 UpdateDownloadRequest 类负责处理文件的下载和线程间的通讯:dom

/** * @desciption: 负责处理文件的下载和线程间的通讯 */
public class UpdateDownloadRequest implements Runnable { /** * 开始下载的位置 */
    private int startPos = 0; /** * 下载地址 */
    private String downloadUrl; /** * 文件保存路径 */
    private String localFilePath; /** * 事件回调 */
    private UpdateDownloadListener mDownloadListener; private DownloadResponseHandler mDownloadHandler; /** * 下载标志位 */
    private boolean isDownloading = false; /** * 文件长度 */
    private long mContentLength; public UpdateDownloadRequest(String downloadUrl, String localFilePath, UpdateDownloadListener downloadListener) { this.downloadUrl = downloadUrl; this.localFilePath = localFilePath; mDownloadListener = downloadListener; mDownloadHandler = new DownloadResponseHandler(); isDownloading = true; } @Override public void run() { try { makeRequest(); } catch (IOException e) { if (mDownloadHandler != null) { mDownloadHandler.sendFailureMessage(FailureCode.IO); } } catch (InterruptedException e) { if (mDownloadHandler != null) { mDownloadHandler.sendFailureMessage(FailureCode.Interrupted); } } } /** * 创建链接 */
    private void makeRequest() throws IOException, InterruptedException { if (!Thread.currentThread().isInterrupted()) { try { URL url = new URL(downloadUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(5000); connection.setRequestMethod("GET"); connection.setRequestProperty("Range", "bytes=" + startPos + "-"); connection.setRequestProperty("Connection", "Keep-Alive"); //阻塞当前线程
 connection.connect(); mContentLength = connection.getContentLength(); if (!Thread.currentThread().isInterrupted()) { if (mDownloadHandler != null) { //完成文件下载,取得与远程文件的流
 mDownloadHandler.sendResponseMessage(connection.getInputStream()); } } } catch (IOException e) { if (!Thread.currentThread().isInterrupted()) { throw e; } } } } public boolean isDownloading() { return isDownloading; } public void stopDownloading() { isDownloading = false; } /** * 下载过程当中全部可能出现的异常状况 */
    public enum FailureCode { //  UnknownHost, Socket, SocketTimeout, ConnectTimeout, IO, HttpResponse, JSON, Interrupted } /** * 下载文件,并发送消息和回调接口 */
    public class DownloadResponseHandler { protected static final int SUCCESS_MESSAGE = 0; protected static final int FAILURE_MESSAGE = 1; protected static final int START_MESSAGE = 2; protected static final int FINISH_MESSAGE = 3; protected static final int NETWORK_OFF = 4; protected static final int PROGRESS_CHANGED = 5; protected static final int PAUSED_MESSAGE = 6; private Handler mHandler; private int mCompleteSize = 0; private int progress = 0; public DownloadResponseHandler() { if (Looper.myLooper() != null) { mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); handleSelfMessage(msg); } }; } } /** * 发送暂停消息 */
        private void sendPausedMessage() { sendMessage(obtainMessage(PAUSED_MESSAGE, null)); } /** * 发送失败消息 */
        protected void sendFailureMessage(FailureCode failureCode) { sendMessage(obtainMessage(FAILURE_MESSAGE, new Object[]{failureCode})); } private void sendMessage(Message message) { if (mHandler != null) { mHandler.sendMessage(message); } else { handleSelfMessage(message); } } private Message obtainMessage(int responseMessage, Object response) { Message msg; if (mHandler != null) { msg = mHandler.obtainMessage(responseMessage, response); } else { msg = Message.obtain(); msg.what = responseMessage; msg.obj = response; } return msg; } private void handleSelfMessage(Message message) { Object[] response; switch (message.what) { default: break; case FAILURE_MESSAGE: response = (Object[]) message.obj; handleFailureMessage((FailureCode) response[0]); break; case PROGRESS_CHANGED: response = (Object[]) message.obj; handleProgressChangedMessage((Integer) response[0]); break; case PAUSED_MESSAGE: handlePausedMessage(); break; case FINISH_MESSAGE: onFinish(); break; } } /** * 失败消息的处理逻辑 */
        protected void handleFailureMessage(FailureCode failureCode) { onFailure(failureCode); } /** * 进度改变消息的处理逻辑 */
        protected void handleProgressChangedMessage(int progress) { mDownloadListener.onProgressChanged(progress, ""); } /** * 暂停消息的处理逻辑 */
        protected void handlePausedMessage() { mDownloadListener.onPaused(progress, mCompleteSize, ""); } /** * 外部接口完成的回调 */
        public void onFinish() { mDownloadListener.onFinished(mCompleteSize, ""); } /** * 外部接口失败的回调 */
        public void onFailure(FailureCode failureCode) { mDownloadListener.onFailure(); } /** * 文件下载方法,发送各类类型的事件 */
        public void sendResponseMessage(InputStream inputStream) { //文件读写流
            RandomAccessFile randomAccessFile = null; mCompleteSize = 0; try { byte[] buffer = new byte[1024]; int length = -1; int limit = 0; randomAccessFile = new RandomAccessFile(localFilePath, "rwd"); randomAccessFile.seek(startPos); boolean isPaused = false; while ((length = inputStream.read(buffer)) != -1) { if (isDownloading) { randomAccessFile.write(buffer, 0, length); mCompleteSize += length; if ((startPos + mCompleteSize) < (mContentLength + startPos)) { progress = (int) (Float.parseFloat(getToPointFloatStr( (float) (startPos + mCompleteSize) / (mContentLength + startPos))) * 100); //限制notification更新频率
                            if (limit % 30 == 0 || progress == 100) { //在子线程中读取流数据,后转发到主线程中去。
 sendProgressChangedMessage(progress); } } limit++; } else { isPaused = true; sendPausedMessage(); break; } } stopDownloading(); if (!isPaused) { sendFinishMessage(); } } catch (IOException e) { sendPausedMessage(); stopDownloading(); e.printStackTrace(); } finally { try { if (inputStream != null) { inputStream.close(); } if (randomAccessFile != null) { randomAccessFile.close(); } } catch (IOException e) { stopDownloading(); e.printStackTrace(); } } } /** * 数字格式化 */
        private String getToPointFloatStr(float value) { DecimalFormat format = new DecimalFormat("0.00"); return format.format(value); } /** * 发送进度改变消息 */
        private void sendProgressChangedMessage(int progress) { sendMessage(obtainMessage(PROGRESS_CHANGED, new Object[]{progress})); } /** * 发送完成消息 */
        protected void sendFinishMessage() { sendMessage(obtainMessage(FINISH_MESSAGE, null)); } } }

而后建立 UpdateManager 类下载调度管理器,调用UpdateDownloadRequest:ide

/** * @desciption: 下载调度管理器,调用UpdateDownloadRequest */
public class UpdateManager { /** * 线程池 */
    private ExecutorService mExecutorService; private UpdateDownloadRequest mDownloadRequest; private UpdateManager() { //建立cache线程池
        mExecutorService = Executors.newCachedThreadPool(); } public static UpdateManager getInstance() { return Holder.INSTANCE; } public void startDownload(String downloadUrl, String localFilePath, UpdateDownloadListener listener) { if (mDownloadRequest != null && mDownloadRequest.isDownloading()) { return; } checkLocalFilePath(localFilePath); mDownloadRequest = new UpdateDownloadRequest(downloadUrl, localFilePath, listener); Future<?> future = mExecutorService.submit(mDownloadRequest); new WeakReference<Future<?>>(future); } /** * 检查文件路径 */
    private void checkLocalFilePath(String localFilePath) { File path = new File(localFilePath.substring(0, localFilePath.lastIndexOf("/") + 1)); File file = new File(localFilePath); if (!path.exists()) { path.mkdirs(); } if (!file.exists()) { try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } } public interface InstallPermissionListener { /** * 权限申请成功 */
        void permissionSuccess(); /** * 权限申请失败 */
        void permissionFail(); } private static class Holder { private static final UpdateManager INSTANCE = new UpdateManager(); } }

最后经过 Service 启动下载,建立 UpdateService 类:oop

/** * @desciption: 应用更新组件入口,用来启动下载器并更新Notification */
public class UpdateService extends Service { public static final String APK_URL="apk_url"; /** * 文件存放路径 */
    private String filePath; /** * 文件下载地址 */
    private String apkUrl; private NotificationUtils mNotificationUtils; @Override public void onCreate() { super.onCreate(); filePath = Environment.getExternalStorageDirectory() + "/videobusiness/videobusiness.apk"; mNotificationUtils = new NotificationUtils(this); } @Override public int onStartCommand(Intent intent, int flags, int startId) { apkUrl = intent.getStringExtra(APK_URL); notifyUser(getString(R.string.update_download_start), 0); startDownload(); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent intent) { return null; } private void notifyUser(String content, int progress) { mNotificationUtils.sendNotificationProgress(getString(R.string.app_name), content, progress, progress >= 100 ? getContentIntent() : PendingIntent.getActivity(this, 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT)); } private void startDownload() { UpdateManager.getInstance().startDownload(apkUrl, filePath, new UpdateDownloadListener() { @Override public void onStart() { } @Override public void onPrepared(long contentLength, String downloadUrl) { } @Override public void onProgressChanged(int progress, String downloadUrl) { notifyUser(getString(R.string.update_download_processing), progress); } @Override public void onPaused(int progress, int completeSize, String downloadUrl) { notifyUser(getString(R.string.update_download_failed), progress); deleteApkFile(); //中止服务自身
 stopSelf(); } @Override public void onFinished(int completeSize, String downloadUrl) { notifyUser(getString(R.string.update_download_finish), 100); //中止服务自身
 stopSelf(); startActivity(getInstallApkIntent()); } @Override public void onFailure() { notifyUser(getString(R.string.update_download_failed), 0); deleteApkFile(); //中止服务自身
 stopSelf(); } }); } private PendingIntent getContentIntent() { return PendingIntent.getActivity(this, 0, getInstallApkIntent(), PendingIntent.FLAG_UPDATE_CURRENT); } private Intent getInstallApkIntent() { File apkFile = new File(filePath); // 经过Intent安装APK文件
        final Intent installIntent = new Intent(Intent.ACTION_VIEW); installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); installIntent.addCategory(Intent.CATEGORY_DEFAULT); //兼容7.0
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { Uri apkUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", apkFile); installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive"); //兼容8.0
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { boolean hasInstallPermission = this.getPackageManager().canRequestPackageInstalls(); if (!hasInstallPermission) { InstallPermissionActivity.sListener = new UpdateManager.InstallPermissionListener() { @Override public void permissionSuccess() { installApk(); } @Override public void permissionFail() { Toast.makeText(UpdateService.this, "受权失败,没法安装应用", Toast.LENGTH_LONG).show(); } }; Intent intent = new Intent(this, InstallPermissionActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); return intent; } } } else { installIntent.setDataAndType(Uri.parse("file://" + apkFile.getPath()), "application/vnd.android.package-archive"); } return installIntent; } /** * 8.0 权限获取后的安装 */
    private void installApk() { File apkFile = new File(filePath); // 经过Intent安装APK文件
        Intent installIntent = new Intent(Intent.ACTION_VIEW); installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); installIntent.addCategory(Intent.CATEGORY_DEFAULT); Uri apkUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", apkFile); installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive"); startActivity(installIntent); } /** * 删除无用apk文件 */
    private void deleteApkFile() { File apkFile = new File(filePath); if (apkFile.exists() && apkFile.isFile()) { apkFile.delete(); } } }

咱们是以通知的形式更新下载进度条,下面是封装的 Notification 类:ui

/** * @desciption: 通知管理 */
public class NotificationUtils extends ContextWrapper { public static final String CHANNEL_ID = "default"; private static final String CHANNEL_NAME = "Default Channel"; private static final String CHANNEL_DESCRIPTION = "this is default channel!"; private NotificationManager mManager; public NotificationUtils(Context base) { super(base); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { createNotificationChannel(); } } @TargetApi(Build.VERSION_CODES.O) private void createNotificationChannel() { NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT); //是否绕过请勿打扰模式
 channel.canBypassDnd(); //闪光灯
        channel.enableLights(true); //锁屏显示通知
 channel.setLockscreenVisibility(VISIBILITY_SECRET); //闪关灯的灯光颜色
 channel.setLightColor(Color.RED); //桌面launcher的消息角标
 channel.canShowBadge(); //是否容许震动
        channel.enableVibration(true); //获取系统通知响铃声音的配置
 channel.getAudioAttributes(); //获取通知取到组
 channel.getGroup(); //设置可绕过 请勿打扰模式
        channel.setBypassDnd(true); //设置震动模式
        channel.setVibrationPattern(new long[]{100, 100, 200}); //是否会有灯光
 channel.shouldShowLights(); getManager().createNotificationChannel(channel); } private NotificationManager getManager() { if (mManager == null) { mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); } return mManager; } /** * 发送通知 */
    public void sendNotification(String title, String content) { NotificationCompat.Builder builder = getNotification(title, content); getManager().notify(1, builder.build()); } private NotificationCompat.Builder getNotification(String title, String content) { NotificationCompat.Builder builder = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID); } else { builder = new NotificationCompat.Builder(getApplicationContext()); builder.setPriority(PRIORITY_DEFAULT); } //标题
 builder.setContentTitle(title); //文本内容
 builder.setContentText(content); //小图标
 builder.setSmallIcon(R.mipmap.ic_launcher); //设置点击信息后自动清除通知
        builder.setAutoCancel(true); return builder; } /** * 发送通知 */
    public void sendNotification(int notifyId, String title, String content) { NotificationCompat.Builder builder = getNotification(title, content); getManager().notify(notifyId, builder.build()); } /** * 发送带有进度的通知 */
    public void sendNotificationProgress(String title, String content, int progress, PendingIntent intent) { NotificationCompat.Builder builder = getNotificationProgress(title, content, progress, intent); getManager().notify(0, builder.build()); } /** * 获取带有进度的Notification */
    private NotificationCompat.Builder getNotificationProgress(String title, String content, int progress, PendingIntent intent) { NotificationCompat.Builder builder = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID); } else { builder = new NotificationCompat.Builder(getApplicationContext()); builder.setPriority(PRIORITY_DEFAULT); } //标题
 builder.setContentTitle(title); //文本内容
 builder.setContentText(content); //小图标
 builder.setSmallIcon(R.mipmap.ic_launcher); //设置大图标,未设置时使用小图标代替,拉下通知栏显示的那个图标 //设置大图片 BitmpFactory.decodeResource(Resource res,int id) 根据给定的资源Id解析成位图
 builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)); if (progress > 0 && progress < 100) { //一种是有进度刻度的(false),一种是循环流动的(true) //设置为false,表示刻度,设置为true,表示流动
            builder.setProgress(100, progress, false); } else { //0,0,false,能够将进度条隐藏
            builder.setProgress(0, 0, false); builder.setContentText("下载完成"); } //设置点击信息后自动清除通知
        builder.setAutoCancel(true); //通知的时间
 builder.setWhen(System.currentTimeMillis()); //设置点击信息后的跳转(意图)
 builder.setContentIntent(intent); return builder; } }

使用Service 在manifest中注册this

<!--服务-->
<service android:name=".service.update.UpdateService"/>
使用方式
Intent intent = new Intent(mContext, UpdateService.class); //传递apk下载地址
intent.putExtra(UpdateService.APK_URL, apkurl); mContext.startService(intent);

7.0 适配

在Android 7.0上,对文件的访问权限做出了修改,不能在使用file://格式的Uri 访问文件 ,Android 7.0提供 FileProvider,应该使用这个来获取apk地址,而后安装apk。以下进行简单的适配:

(1) 在res 目录下,新建一个xml 文件夹,在xml 下面建立一个文件provider_paths文件:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external" path="" />
    <external-files-path name="Download" path="" />
</paths>

(2) 在AndroidManifest.xml清单文件中申明Provider:

<!-- Android 7.0 照片、APK下载保存路径-->
 <provider android:name="android.support.v4.content.FileProvider" android:authorities="packgeName.fileProvider" android:exported="false" android:grantUriPermissions="true">
     <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" />
     </provider>

(3) Android 7.0上的文件地址获取:

Uri uri = FileProvider.getUriForFile(context, "packageName.fileProvider", new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), apkFile));

注意:把上面的 packageName 换成你本身的包名,把 apkFile 换成你本身的 apk 所在的文件。 File apkFile = new File(filePath); filePath 是apk存放路径。

适配Android 8.0:未知来源的应用权限

Android8.0以上,未知来源的应用是不能够经过代码来执行安装的(在sd卡中找找到apk,手动安装是能够的),未知应用安装权限的开关被除掉,取而代之的是未知来源应用的管理列表,须要列表里面开启你的应用的未知来源的安装权限。Google这么作是为了防止一开始正经的应用后来开始经过升级来作一些不合法的事情,侵犯用户权益。

1) 在清单文件中申明权限:REQUEST_INSTALL_PACKAGES

<!--8.0请求安装包权限-->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>

(2) 在代码中判断用户是否已经受过权限了,若是已经受权,能够直接安装,若是没有受权,则跳转到受权列表,让用户开启未知来源应用安装权限,开启后,再安装应用。

//兼容8.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { boolean hasInstallPermission = this.getPackageManager().canRequestPackageInstalls(); if (!hasInstallPermission) { InstallPermissionActivity.sListener = new UpdateManager.InstallPermissionListener() { @Override public void permissionSuccess() { installApk(); } @Override public void permissionFail() { Toast.makeText(UpdateService.this, "受权失败,没法安装应用", Toast.LENGTH_LONG).show(); } }; Intent intent = new Intent(this, InstallPermissionActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); return intent; } }
由于受权时须要弹框提示,咱们用一个Activity来代理建立了一个Activity:InstallPermissionActivity 来申请权限,用户点击设置后,跳转到权限设置界面,而后咱们再onActivityResult 里判断是都受权成功。
InstallPermissionActivity 代码以下:
/** * @desciption: 兼容Android 8。0 APP 在线更新,权限申请界面 */
public class InstallPermissionActivity extends BaseActivity { public static final int INSTALL_PACKAGES_REQUEST_CODE = 1; public static UpdateManager.InstallPermissionListener sListener; private AlertDialog mAlertDialog; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 弹窗
        if (Build.VERSION.SDK_INT >= 26) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.REQUEST_INSTALL_PACKAGES}, INSTALL_PACKAGES_REQUEST_CODE); } else { finish(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { default: break; case INSTALL_PACKAGES_REQUEST_CODE: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (sListener != null) { sListener.permissionSuccess(); finish(); } } else { //startInstallPermissionSettingActivity();
 showDialog(); } break; } } private void showDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.app_name); builder.setMessage("为了正常升级 xxx APP,请点击设置按钮,容许安装未知来源应用,本功能只限用于 xxx APP版本升级"); builder.setPositiveButton("设置", new DialogInterface.OnClickListener() { @RequiresApi(api = Build.VERSION_CODES.O) @Override public void onClick(DialogInterface dialogInterface, int i) { startInstallPermissionSettingActivity(); mAlertDialog.dismiss(); } }); builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { if (sListener != null) { sListener.permissionFail(); } mAlertDialog.dismiss(); finish(); } }); mAlertDialog = builder.create(); mAlertDialog.setCancelable(false); mAlertDialog.show(); } @RequiresApi(api = Build.VERSION_CODES.O) private void startInstallPermissionSettingActivity() { //注意这个是8.0新API
        Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, 1); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == INSTALL_PACKAGES_REQUEST_CODE && resultCode == RESULT_OK) { // 受权成功
            if (sListener != null) { sListener.permissionSuccess(); } } else { // 受权失败
            if (sListener != null) { sListener.permissionFail(); } } finish(); } @Override protected void onDestroy() { super.onDestroy(); sListener = null; } }

注意:当经过Intent 跳转到未知应用受权列表的时候,必定要加上包名,这样就能直接跳转到你的app下,否则只能跳转到列表。

@RequiresApi(api = Build.VERSION_CODES.O) private void startInstallPermissionSettingActivity() { //注意这个是8.0新API
        Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, 1); }

这里把 InstallPermissionActivity 设置为透明:

<activity android:name=".activity.InstallPermissionActivity" android:theme="@style/activity_translucent"/>


<style name="activity_translucent" parent="AppTheme">
       <item name="android:windowBackground">@color/translucent_background</item>
       <item name="android:windowIsTranslucent">true</item>
       <item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
</style>

<color name="translucent_background">#00000000</color>

 



做者:wuchao226
连接:https://www.jianshu.com/p/ea42040c7ace
来源:简书
简书著做权归做者全部,任何形式的转载都请联系做者得到受权并注明出处。


做者:wuchao226
连接:https://www.jianshu.com/p/ea42040c7ace
来源:简书
简书著做权归做者全部,任何形式的转载都请联系做者得到受权并注明出处。


做者:wuchao226
连接:https://www.jianshu.com/p/ea42040c7ace
来源:简书
简书著做权归做者全部,任何形式的转载都请联系做者得到受权并注明出处。
/** * @desciption: 应用更新组件入口,用来启动下载器并更新Notification */
public class UpdateService extends Service {
 
    public static final String APK_URL="apk_url";
    /** * 文件存放路径 */
    private String filePath;
    /** * 文件下载地址 */
    private String apkUrl;

    private NotificationUtils mNotificationUtils;

    @Override
    public void onCreate() {
        super.onCreate();
        filePath = Environment.getExternalStorageDirectory() + "/videobusiness/videobusiness.apk";
        mNotificationUtils = new NotificationUtils(this);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        apkUrl = intent.getStringExtra(APK_URL);
        notifyUser(getString(R.string.update_download_start), 0);
        startDownload();
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private void notifyUser(String content, int progress) {
        mNotificationUtils.sendNotificationProgress(getString(R.string.app_name), content, progress, progress >= 100 ? getContentIntent() : PendingIntent.getActivity(this, 0,
                new Intent(), PendingIntent.FLAG_UPDATE_CURRENT));
    }

    private void startDownload() {
        UpdateManager.getInstance().startDownload(apkUrl, filePath, new UpdateDownloadListener() {
            @Override
            public void onStart() {

            }

            @Override
            public void onPrepared(long contentLength, String downloadUrl) {

            }

            @Override
            public void onProgressChanged(int progress, String downloadUrl) {
                notifyUser(getString(R.string.update_download_processing), progress);
            }

            @Override
            public void onPaused(int progress, int completeSize, String downloadUrl) {
                notifyUser(getString(R.string.update_download_failed), progress);
                deleteApkFile();
                //中止服务自身
                stopSelf();
            }

            @Override
            public void onFinished(int completeSize, String downloadUrl) {
                notifyUser(getString(R.string.update_download_finish), 100);
                //中止服务自身
                stopSelf();
                startActivity(getInstallApkIntent());
            }

            @Override
            public void onFailure() {
                notifyUser(getString(R.string.update_download_failed), 0);
                deleteApkFile();
                //中止服务自身
                stopSelf();
            }
        });
    }

    private PendingIntent getContentIntent() {
        return PendingIntent.getActivity(this, 0,
                getInstallApkIntent(), PendingIntent.FLAG_UPDATE_CURRENT);
    }

    private Intent getInstallApkIntent() {
        File apkFile = new File(filePath);
        // 经过Intent安装APK文件
        final Intent installIntent = new Intent(Intent.ACTION_VIEW);
        installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        installIntent.addCategory(Intent.CATEGORY_DEFAULT);
        //兼容7.0
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            Uri apkUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", apkFile);
            installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");
            //兼容8.0
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                boolean hasInstallPermission = this.getPackageManager().canRequestPackageInstalls();
                if (!hasInstallPermission) {
                    InstallPermissionActivity.sListener = new UpdateManager.InstallPermissionListener() {
                        @Override
                        public void permissionSuccess() {
                            installApk();
                        }

                        @Override
                        public void permissionFail() {
                            Toast.makeText(UpdateService.this, "受权失败,没法安装应用", Toast.LENGTH_LONG).show();
                        }
                    };
                    Intent intent = new Intent(this, InstallPermissionActivity.class);
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    return intent;
                }
            }
        } else {
            installIntent.setDataAndType(Uri.parse("file://" + apkFile.getPath()),
                    "application/vnd.android.package-archive");
        }
        return installIntent;
    }

    /** * 8.0 权限获取后的安装 */
    private void installApk() {
        File apkFile = new File(filePath);
        // 经过Intent安装APK文件
        Intent installIntent = new Intent(Intent.ACTION_VIEW);
        installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        installIntent.addCategory(Intent.CATEGORY_DEFAULT);
        Uri apkUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", apkFile);
        installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        startActivity(installIntent);
    }

    /** * 删除无用apk文件 */
    private void deleteApkFile() {
        File apkFile = new File(filePath);
        if (apkFile.exists() && apkFile.isFile()) {
            apkFile.delete();
        }
    }
}
相关文章
相关标签/搜索