Android APP升级(兼容Android7.0报错,Android8.0不能自动安装)

之前有写过利用腾讯Bugly实现APP的热更新以及版本升级Android 热更新框架Bugly-9步完成热更新/自动更新/异常上报分析,今天来讲一下不借助第三方的应用升级。

演示效果:

更新1更新2

原理:

1.将新版本上传到自己的服务器,有服务器将最新版本信息记录

2.当用户打开app或者手动触发版本检查时向服务器请求版本信息以及最新版本apk的下载地址

3.判断当前版本是不是最新版本,如果不是则通过下载地址下载apk

4.下载完成后吊起安装程序进行安装覆盖

5.实现了版本更新

本次例子用到框架:

1.easypermissions  权限控制

2.gson  json对象解析

3.xUtils-2.6.14.jar 网络请求

所用权限(注意最后一个权限):

 <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"/>

    <!--以下这个权限如果不加,在高版本的android手机上如果没有开启运行未知来源的安装,将会在下载完成后无法调起安装程序-->
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

注意点:

 1."android.permission.REQUEST_INSTALL_PACKAGES"这个权限必须加上,否则可能会在apk下载完成后不能吊起安装程序(一闪而逝)

2.Android8以上如果要在通知栏显示下载进度,需要进行notification的适配Android8.0 notification channel

3.在Android6.0以上需要进行权限的申请

4.Android7.0以上文件读取需要通过FileProvider进行操作关于 Android 7.0 适配中 FileProvider 部分的总结

下面看主要代码:

MainActivity

package cn.humanetplan.updateappdemo;

import android.Manifest;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.view.View;
import android.widget.TextView;

import com.google.gson.Gson;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.RequestParams;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
import com.lidroid.xutils.http.client.HttpRequest;

import java.util.List;

import pub.devrel.easypermissions.EasyPermissions;

;

public class MainActivity extends BaseActivity implements EasyPermissions.PermissionCallbacks{
    TextView tv_check, tv_versionName;

    @Override
    public void DoSthBeforeInflate() {

    }

    @Override
    protected int setContentLayout() {
        return R.layout.activity_main;
    }

    @Override
    protected void init() {
        tv_check = findViewById(R.id.tv_check);
        tv_versionName = findViewById(R.id.tv_versionName);
        tv_versionName.setText("当前版本V"+BuildConfig.VERSION_NAME);
        tv_check.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                CheckVersion();
            }
        });
    }
    VersionBean versionBean;
    private void CheckVersion() {
        tipDialog.show();
        HttpUtils httpUtils = new HttpUtils();
        RequestParams params = new RequestParams();
        httpUtils.send(HttpRequest.HttpMethod.GET, "http://47.107.173.197/Android/GetVersionInfo.php", new RequestCallBack<String>() {
            @Override
            public void onSuccess(ResponseInfo<String> responseInfo) {
                tipDialog.dismiss();
                try {
                    Gson gson = new Gson();

                    versionBean = gson.fromJson(responseInfo.result, VersionBean.class);
                    if (versionBean != null && versionBean.isSuccess()) {
                        DialogUtils.ShowTipsDialog(MainActivity.this, "发现新版本,是否立即更新?", "当前版本V"+versionBean.getVersionName()+"\n"+versionBean.getRemark(), new DialogReturnListner() {
                            @Override
                            public void onResultReturn(String... params) {

                            }

                            @Override
                            public void onResultReturn(int p1, String... params) {

                            }

                            @Override
                            public void onResultReturn(boolean p1, String... params) {
                                if (p1) {
                                    if (EasyPermissions.hasPermissions(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE)){
                                        StartUpdate(versionBean);
                                    }else {
                                        EasyPermissions.requestPermissions(MainActivity.this,"升级程序将App下载到手的过程中需要用到手机文件操作权限,请同意后才能进行正常升级",1234,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE);
                                    }


                                }
                            }
                        });
                    }
                } catch (Exception e) {

                }

            }

            @Override
            public void onFailure(HttpException e, String s) {
                tipDialog.dismiss();
            }
        });

    }
    private void StartUpdate(VersionBean versionBean) {
        if (versionBean==null){
            return;
        }
        ToastUtils.showToast(MainActivity.this, "正在更新...");
        Intent intent=new Intent(MainActivity.this,ServiceLoadNewVersion.class);
        intent.putExtra("path",versionBean.getApkPath());
        startService(intent);
    }

    @Override
    public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
        if (requestCode==1234){
            if (perms.contains(Manifest.permission.READ_EXTERNAL_STORAGE) && perms.contains(Manifest.permission.WRITE_EXTERNAL_STORAGE)){
                StartUpdate(versionBean);
            }
        }
    }

    @Override
    public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
        ToastUtils.showToast(this,"没有获取相关的权限,无法正常操作!");
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        // Forward results to EasyPermissions
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }
}

 ServiceLoadNewVersion

package cn.humanetplan.updateappdemo;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.FileProvider;
import android.util.Log;
import android.widget.RemoteViews;

import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.HttpHandler;
import com.lidroid.xutils.http.RequestParams;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;

import java.io.File;

/**
 * 下载 新 版本 的 服务
 */
public class ServiceLoadNewVersion extends Service {


    private RemoteViews remoteViews = null;
    private Notification notification = null;
    private NotificationManager notificationManager = null;
    private PendingIntent pReDownLoadIntent = null;

    private Handler myHandler;

    // notification id
    private final int NOTIFICATION_ID = 1000;
    private final int START = 1001;
    private final int LOADING = 1002;
    private final int FINISHED = 1003;
    private final int LOAD_ERROR = 1004;
    private String url;
    private String filePath;

    String apkName = "updateTest.apk";
    String loagPath = "";

    public ServiceLoadNewVersion() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        try {
            String path = intent.getStringExtra("path");
            url = path;
            myHandler = new MyHandler();
            initUrls();

            initNotification();

            // 开始 下载
            new DownLoadThread(url, filePath).start();
        } catch (Exception e) {

        }
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 开始 下载
     */
    private void initUrls() {
        File file = new File(
                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
                , apkName);
        if (file.exists()) {
            file.delete();
        }
        filePath = file.getAbsolutePath();
    }

    NotificationCompat.Builder builder = null;

    /**
     * 配置 通知栏显示 样式
     */
    private void initNotification() {
        String id = "chanel_update";
        String name = "水务集团";
        notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //如果是8以上的系统。需要传一个channelId.
            builder = new NotificationCompat.Builder(this, id);
        } else {
            builder = new NotificationCompat.Builder(this);
        }

        builder.setContentTitle(getResources().getString(R.string.app_name) + "新版本下载").
                setContentText("下载进行中...")
                .setVibrate(new long[]{0})
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.applogo))
                .setSmallIcon(R.mipmap.applogo)
                .setProgress(100, 0, false);


        // 下载 失败 重新 下载 的 PendingIntent
        Intent reDownLoadIntent = new Intent(this, this.getClass());
        reDownLoadIntent.putExtra("path", loagPath);
        pReDownLoadIntent = PendingIntent.getService(this, 200, reDownLoadIntent, PendingIntent.FLAG_CANCEL_CURRENT);


        remoteViews = new RemoteViews(getPackageName(), R.layout.view_download_notification);
        remoteViews.setTextViewText(R.id.textViewTitle, "正在下载");
        remoteViews.setTextViewText(R.id.textViewProgress, "进度0%");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//android8.0以上通知的适配
            NotificationChannel mChannel = new NotificationChannel(id, name, NotificationManager.IMPORTANCE_LOW);
            mChannel.enableVibration(false);
            mChannel.setVibrationPattern(new long[]{0});

            notificationManager.createNotificationChannel(mChannel);
            notification = builder.build();
        } else {

            notification = builder.build();
        }
    }


    class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case START:
                    notificationManager.notify(NOTIFICATION_ID, notification);

                    break;
                case LOADING:
                    builder.setProgress(100, msg.arg1, false);
                    builder.setContentText("下载进行中" + msg.arg1 + "/100");
                    notification = builder.build();
                    notificationManager.notify(NOTIFICATION_ID, notification);
                    //关键部分,如果你不重新更新通知,进度条是不会更新的
                    break;
                case FINISHED:

                    notification.flags |= Notification.FLAG_AUTO_CANCEL;
//                    //关键部分,如果你不重新更新通知,进度条是不会更新的
                    notificationManager.notify(NOTIFICATION_ID, notification);
                    notificationManager.cancel(NOTIFICATION_ID);
                    installApkNew(null);

                    break;
                case LOAD_ERROR:
                    builder.setContentTitle("新版本下载失败");
                    builder.setContentText("下载失败,点击重新下载!");
                    notification.contentIntent = pReDownLoadIntent;
//                    notification.flags = Notification.FLAG_NO_CLEAR; // 点击通知 不消失
                    //关键部分,如果你不重新更新通知,进度条是不会更新的
                    notificationManager.notify(NOTIFICATION_ID, notification);
                    break;
            }
        }
    }

    //安装apk

    protected void installApkNew(Uri uri) {

        try {
            File file = new File(filePath);
            Intent intent = new Intent(Intent.ACTION_VIEW);
            // 由于没有在Activity环境下启动Activity,设置下面的标签
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            if (Build.VERSION.SDK_INT >= 24) { //判读版本是否在7.0以上
                //参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致   参数3  共享的文件
                Uri apkUri =
                        FileProvider.
                                getUriForFile(getApplicationContext(),
                                        BuildConfig.APPLICATION_ID + ".provider",
                                        file);

                //添加这一句表示对目标应用临时授权该Uri所代表的文件
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
            } else {
                Uri mUri = Uri.fromFile(file);
                mUri = Uri.parse(mUri.toString().replace("content", "file"));
                intent.setDataAndType(mUri,
                        "application/vnd.android.package-archive");
            }


            startActivity(intent);
        } catch (Exception e) {

            e.printStackTrace();
            Log.i("ServiceLoadNewVersion", "exc  " + e.toString());
        }
    }


    class DownLoadThread extends Thread {
        private String url;
        private String filePath;

        public DownLoadThread(String url, String filePath) {
            this.url = url;
            this.filePath = filePath;
        }

        @Override
        public void run() {

            HttpUtils http = new HttpUtils();
            RequestParams params = new RequestParams();
            HttpHandler handler = http.download(
                    url,//url
                    filePath, // 文件保存路径,
                    params,
                    true, // 如果目标文件存在,接着未完成的部分继续下载。服务器不支持RANGE时将从新下载。
                    false, // 如果从请求返回信息中获取到文件名,下载完成后自动重命名。
                    new RequestCallBack<File>() {
                        @Override
                        public void onStart() {
                            myHandler.sendEmptyMessage(START);
                        }

                        @Override
                        public void onLoading(long total, long current, boolean isUploading) {
                            Message message = myHandler.obtainMessage();
                            message.what = LOADING;
                            message.arg1 = (int) ((float) current / (float) total * 100);
                            myHandler.sendMessage(message);


                        }

                        @Override
                        public void onSuccess(ResponseInfo<File> responseInfo) {
                            myHandler.sendEmptyMessage(FINISHED);
                        }


                        @Override
                        public void onFailure(HttpException error, String msg) {
                            myHandler.sendEmptyMessage(LOAD_ERROR);
                        }
                    });
        }
    }
}

Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cn.humanetplan.updateappdemo">
    <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"/>

    <!--以下这个权限如果不加,在高版本的android手机上如果没有开启运行未知来源的安装,将会在下载完成后无法调起安装程序-->
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AppCompat.Light.NoActionBar">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!--android 7.0以上文件访问需要通过FileProvider进行,否则会报错-->
        <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/filepaths"/>
        </provider>
        <service android:name=".ServiceLoadNewVersion"
            android:enabled="true"
            android:exported="true"/>
    </application>

</manifest>

代码比较简单,没有太多注释。

完整代码: UpdateAppDemo.zip

Github:https://github.com/shouPol/UpdateDemo

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值