【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()的方法

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