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

演示效果:

原理:

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

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

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

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

5.实现了版本更新

本次例子用到框架:

1.easypermissions  权限控制

2.gson  json对象解析

3.xUtils-2.6.14.jar 网络请求

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

                 

注意点:

 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() {            @Override            public void onSuccess(ResponseInfo 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 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 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() {                        @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 responseInfo) {                            myHandler.sendEmptyMessage(FINISHED);                        }                        @Override                        public void onFailure(HttpException error, String msg) {                            myHandler.sendEmptyMessage(LOAD_ERROR);                        }                    });        }    }}

Manifest

<?xml version="1.0" encoding="utf-8"?>                                                                                                                                                

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

完整代码: UpdateAppDemo.zip

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

 

 

 

更多相关文章

  1. Android新手入门2016(4)--Android(安卓)SDK下载代理设置
  2. [置顶] 【通知】▁▂▃ Himi 著作《Android游戏编程之从零开始》
  3. BitmapFactory类的decodeStream方法在网络超时或较慢的时候无法
  4. 让最新的 Android(安卓)Q Beta 3 强制重启的 Project Mainline,到
  5. Android源代码下载过程中无法下载repo的解决方法
  6. 安卓 android studio 报错 The specified Android(安卓)SDK Buil
  7. Android学习系列(34)--App应用之发布各广告平台版本
  8. 基於 Android(安卓)2.3.7 的 CyanogenMod 7.1 正式版推出
  9. android 自动检测版本升级

随机推荐

  1. Android(安卓)studio登录界面
  2. Android(安卓)使用Visualizer获取播放音
  3. TextView 实现原理 实践
  4. PhoneGap开发Android
  5. Android之EditText 属性汇总 +限定输入某
  6. Android(安卓)touch事件的派发流程
  7. Android(安卓)Studio 打包发布apk
  8. mysql错误:Access denied for user 'root'
  9. Android(安卓)Design Support Library
  10. Android中快速为Recyclerview添加头部