【Android】APP检测版本升级更新、apk安装

在Android项目中,经常需要检测新版本,然后询问用户是否需要更新

为了方便代码复用,我把大部分代码放到一个类里,大概思路是,先从服务器获取版本信息,再与当前版本信息进行比较,若有新版本,弹出对话框询问是否更新,若更新就进行下载,下完后启动安装

主要用到HttpURLConnection,jsonObject,PackageManager,File,FileProvider等内容

1.MainActivity.java

这里主要调用封装好的类,但是一定要完成权限检测,读写权限需要用户授权。关于运行时权限的请求可以参考之前的文章

【Android】Android 6.0 获取危险权限、运行时权限、一次申请多个权限

public class MainActivity extends AppCompatActivity {
    private boolean isCanWrite=false;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        requestPermission_single();//请求授权
        //检查更新
        CheckUpdate checkUpdate = new CheckUpdate(this,isCanWrite);
        checkUpdate.checkUpdate();

    private void requestPermission_single(){
        if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
        }else{
            Log.e("请求权限", "requestPermission_single: 已经获得读写权限" );
            isCanWrite = true;
        }
    }

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

        if (grantResults.length >0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
            Toast.makeText(getApplicationContext(),"已获取读写权限!!",Toast.LENGTH_SHORT).show();
            isCanWrite = true;
            CheckUpdate checkUpdate = new CheckUpdate(this,isCanWrite);
            checkUpdate.checkUpdate();
        }else {
            Toast.makeText(getApplicationContext(),"您拒绝了读写权限!!",Toast.LENGTH_SHORT).show();
        }

    }
}

2.CheckUpdate.java

public class CheckUpdate extends AppCompatActivity {
    private Context context;//上下文对象
    private String version,whatisnew,downloadurl;//从服务器获取的版本、说明、下载链接等信息
    private ProgressBar mProgressBar;//下载进度条
    private int mProgress;//下载的进度
    private boolean isCancel = false;//取消下载
    private boolean isCanWrite; //是否拥有读写权限
    private String savePath;//存储路径
    private Dialog downloadDialog;//显示下载的对话框

    public CheckUpdate(Context context,boolean isCanWrite){
        this.context = context;
        this.isCanWrite = isCanWrite;
    }

    public void checkUpdate(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null;
                BufferedReader reader = null;
                try{
                    URL url = new URL("http://XXXXXXX.php");
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    connection.setConnectTimeout(5000);
                    InputStream in = connection.getInputStream();
                    reader = new BufferedReader(new InputStreamReader(in));
                    StringBuilder response = new StringBuilder();
                    String line;
                    while ((line=reader.readLine()) != null){
                        response.append(line);
                    }
                    dealUpdateInfo(response.toString());
                }catch (Exception e){e.printStackTrace();}
                finally {
                    if(reader != null){
                        try{reader.close();}catch (Exception e){e.printStackTrace();}
                    }
                    if(connection != null){
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }

    private void dealUpdateInfo(final String response){
        //获取当前版本信息
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
               String version_json = response;
               try{
                   //解析从服务器获取到的json数据,根据自己服务器返回数据的实际情况进行解析
                   JSONObject jsonObject = new JSONObject(version_json);
                   version = jsonObject.getString("version");
                   whatisnew = jsonObject.getString("whatisnew");
                   downloadurl = jsonObject.getString("downloadurl");

                   String currentVersion = packageName(context);
                   //比较版本,看是否有新版本
                   if(Float.parseFloat(version)>Float.parseFloat(currentVersion)){
                       AlertDialog.Builder dialog = new AlertDialog.Builder(context);
                       dialog.setTitle("检测到新版本");
                       dialog.setMessage(whatisnew);
                       dialog.setCancelable(true);
                       dialog.setPositiveButton("下载", new DialogInterface.OnClickListener() {
                           @Override
                           public void onClick(DialogInterface dialogInterface, int i) {
                               if(!isCanWrite){
                                   Toast.makeText(context,"请先赋予存储权限!!",Toast.LENGTH_SHORT).show();
                               }else{
                                   showDownloadDialog();
                                   Toast.makeText(context,"已经获得权限!!",Toast.LENGTH_SHORT).show();
                               }

                           }
                       });
                       dialog.show();
                   }else {
                       Toast.makeText(context,"当前是最新版本",Toast.LENGTH_SHORT).show();
                   }

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

    //获取包名(版本名)
    public static String packageName(Context context) {
        PackageManager manager = context.getPackageManager();
        String name = null;
        try {
            PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
            name = info.versionName;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return name;
    }

    //显示下载进度条
    private void showDownloadDialog(){
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle("下载中");
        View view = LayoutInflater.from(context).inflate(R.layout.dialog_progress,null);
        mProgressBar = view.findViewById(R.id.id_progress);
        builder.setView(view);
        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                dialogInterface.dismiss();
                isCancel = true;
            }
        });
        downloadDialog = builder.create();
        downloadDialog.show();

        downloadAPK();
    }

    //下载文件
    private void downloadAPK() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                        String sdPath = Environment.getExternalStorageDirectory() + "/";
                        //文件保存路径
                        savePath = sdPath + "ice_aFewWord";

                        File dir = new File(savePath);
                        if (!dir.exists()){
                            dir.mkdir();
                        }
                        // 开始下载文件
                        HttpURLConnection conn = (HttpURLConnection) new URL(downloadurl).openConnection();
                        conn.connect();
                        InputStream is = conn.getInputStream();
                        int length = conn.getContentLength();

                        File apkFile = new File(savePath, version);
                        FileOutputStream fos = new FileOutputStream(apkFile);

                        int count = 0;
                        byte[] buffer = new byte[1024];
                        while (!isCancel){
                            int readData = is.read(buffer);
                            count += readData;
                            // 计算当前的下载进度
                            mProgress = (int) (((float)count/length) * 100);
                            // 更新进度条
                            updateProgressHandler.sendEmptyMessage(1);

                            // 下载完成
                            if (readData < 0){
                                updateProgressHandler.sendEmptyMessage(2);
                                break;
                            }
                            fos.write(buffer, 0, readData);
                        }
                        fos.close();
                        is.close();
                        conn.disconnect();
                    }
                }catch(Exception e){
                    Log.e("下载错误", "run: "+e.toString() );
                }
            }
        }).start();
    }

    private Handler updateProgressHandler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what){
                case 1:
                    mProgressBar.setProgress(mProgress);
                    break;
                case 2:
                    downloadDialog.dismiss();
                    installAPK();
            }
        }
    };


    //安装下载好的apk文件
    private void installAPK(){
        File apkFile = new File(savePath,version);
        if(apkFile.exists()){
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

            Uri uri;
            //若SDK版本大于等24,需要FileProvider才行,否则报错
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
                intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                //记得修改com.xxx.fileprovider与androidmanifest相同
                uri = FileProvider.getUriForFile(context,"com.ice.ice_aFewWord.fileprovider",apkFile);
                intent.setDataAndType(uri,"application/vnd.android.package-archive");
            }else{
                uri = Uri.parse("file://" + apkFile.toString());
            }
            intent.setDataAndType(uri, "application/vnd.android.package-archive");
            context.startActivity(intent);
        }
    }

}

注意:上面的代码使用到了FileProvider,需要在res文件夹新建xml,以及在AndroidManifest中进行配置,详情可参考之前的文章

Android】解决Android 7.0及以上版本文件暴露异常exposed beyond app through Intent.getData()的方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章