App在開發過程當中,隨着業務場景的不斷增多,功能的不斷完善,早期下載App的用戶便沒法體驗最新的功能,爲了能讓用戶更及時的體驗App最新版本,在App開發過程加入App自動更新功能便顯得尤其重要。更新apk主要分爲二類: (1)用戶點擊更新後,前臺進行下載,下載過程當中用戶沒法操做 (2)後臺進行下載,下載完成後,service回調進行apk的安裝,下載過程當中用戶可操做,原本講解第一種android
實例下載api
1. 運行前準備服務器
本文附帶實例: 實例中會下載一個應用程序(本公司的另外一個產品:優鮮派 無病毒,請安心測試), 若是用戶須要體驗自動下載其它的產品,請自行配置apk下載路徑: 將下圖中的url路徑換成本身的apk路徑便可, apk的路徑能夠到360或者應用寶的官方網站上獲取網絡
2. 本實例運行效果圖: app
2.1 啓動應用程序dom
2.2 點擊 更新 操做, 開始下載最新版本apk,進度條顯示下載進度ide
2.3 apk下載完成以後,自動安裝下載的apk包, 注意下圖的箭頭處的意思測試
3.代碼講解優化
3.1 程序啓動後, 調用後臺接口獲取最新apk版本信息(包括版本號與版本須要升級功能信息), 將最新版本號與當前apk版本號進行比較,若是版本號不一致,則彈出提示框,提示用戶新版本中包含哪些最新信息, 用戶根據自動須要選擇是否更新網站
得到當前apk版本號:
public static int getVersion(Context context) { try { PackageManager manager = context.getPackageManager(); PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0); String version = info.versionName; int versioncode = info.versionCode; return versioncode; } catch (Exception e) { e.printStackTrace(); } return 0; }
因爲沒法模擬與後臺的交互,這裏將網絡版本號寫死,比當前apk版本號要大,模擬更新的場景
彈出對話框,提示新版本新功能
private void getVersion(final int vision) { // {"data":{"content":"其餘bug修復。","id":"2","api_key":"android", // // "version":"2.1"},"msg":"獲取成功","status":1} String data = ""; //網絡請求獲取當前版本號和下載連接 //實際操做是從服務器獲取 //demo寫死了 String newversion = "2.1";//網絡版本號 String content = "\n" + "就不告訴你咱們更新了什麼-。-\n" + "\n" + "----------萬能的分割線-----------\n" + "\n" + "(ㄒoㄒ) 被老闆打了一頓,仍是來告訴你吧:\n" + "1.下架商品誤買了?恩。。。我搞了點小動做就不會出現了\n" + "2.側邊欄、彈框優化 —— 這個你本身去探索吧,總得留點懸念嘛-。-\n";//更新內容 double newversioncode = Double .parseDouble(newversion); int cc = (int) (newversioncode); System.out.println(newversion + "v" + vision + ",," + cc); if (cc != vision) { if (vision < cc) { System.out.println(newversion + "v" + vision); // 版本號不一樣 ShowDialog(vision, newversion, content, url); } } }
3.2 在彈出的對話框,用戶點擊"更新"與"取消"操做
更新時構建進度條頁面
private void ShowDialog(int vision, String newversion, String content, final String url) { new android.app.AlertDialog.Builder(this) .setTitle("版本更新") .setMessage(content) .setPositiveButton("更新", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); pBar = new CommonProgressDialog(MainActivity.this); pBar.setCanceledOnTouchOutside(false); pBar.setTitle("正在下載"); pBar.setCustomTitle(LayoutInflater.from( MainActivity.this).inflate( R.layout.title_dialog, null)); pBar.setMessage("正在下載"); pBar.setIndeterminate(true); pBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pBar.setCancelable(true); // downFile(URLData.DOWNLOAD_URL); final DownloadTask downloadTask = new DownloadTask( MainActivity.this); downloadTask.execute(url); pBar.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { downloadTask.cancel(true); } }); } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }) .show(); }
3.3 開啓線程類下載應用程序,將下載進度顯示到進度條上
線程啓動前調用: onPreExecute
線程下載apk過程: onPostExecute
進度條更新: onProgressUpdate
下載apk完成回調: onPostExecute
class DownloadTask extends AsyncTask<String, Integer, String> { private Context context; private PowerManager.WakeLock mWakeLock; public DownloadTask(Context context) { this.context = context; } @Override protected String doInBackground(String... sUrl) { InputStream input = null; OutputStream output = null; HttpURLConnection connection = null; File file = null; try { URL url = new URL(sUrl[0]); connection = (HttpURLConnection) url.openConnection(); connection.connect(); if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { return "Server returned HTTP " + connection.getResponseCode() + " " + connection.getResponseMessage(); } int fileLength = connection.getContentLength(); if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { file = Environment.getExternalStorageDirectory(); if (!file.exists()) { // 判斷父文件夾是否存在 if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } } file = new File(file, DOWNLOAD_NAME); } else { Toast.makeText(MainActivity.this, "sd卡未掛載", Toast.LENGTH_LONG).show(); } input = connection.getInputStream(); output = new FileOutputStream(file); byte data[] = new byte[4096]; long total = 0; int count; while ((count = input.read(data)) != -1) { // allow canceling with back button if (isCancelled()) { input.close(); return null; } total += count; // publishing the progress.... if (fileLength > 0) // only if total length is known publishProgress((int) (total * 100 / fileLength)); output.write(data, 0, count); } } catch (Exception e) { System.out.println(e.toString()); return e.toString(); } finally { try { if (output != null) output.close(); if (input != null) input.close(); } catch (IOException ignored) { ignored.toString(); } if (connection != null) connection.disconnect(); } return null; } @Override protected void onPreExecute() { super.onPreExecute(); // take CPU lock to prevent CPU from going off if the user // presses the power button during download PowerManager pm = (PowerManager) context .getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName()); mWakeLock.acquire(); pBar.show(); } @Override protected void onProgressUpdate(Integer... progress) { super.onProgressUpdate(progress); // if we get here, length is known, now set indeterminate to false pBar.setIndeterminate(false); pBar.setMax(100); pBar.setProgress(progress[0]); } @Override protected void onPostExecute(String result) { mWakeLock.release(); pBar.dismiss(); if (result != null) { // 申請多個權限。 AndPermission.with(MainActivity.this) .requestCode(REQUEST_CODE_PERMISSION_SD) .permission(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) // rationale做用是:用戶拒絕一次權限,再次申請時先徵求用戶贊成,再打開受權對話框,避免用戶勾選再也不提示。 .rationale(rationaleListener ).send(); Toast.makeText(context, "您未打開SD卡權限" + result, Toast.LENGTH_LONG).show(); } else { update(context); } } }
4. 安裝下載完的apk包
private void update(Context context) { //安裝應用 Log.i("lxl update->","update"); File apkFile = new File(Environment.getExternalStorageDirectory(), DOWNLOAD_NAME); Intent intent =new Intent(Intent.ACTION_VIEW); if (apkFile == null || context == null) { Log.i("lxl update->","null."); throw new NullPointerException(); } if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.N) { Uri contentUri = FileProvider.getUriForFile(context.getApplicationContext(), BuildConfig.APPLICATION_ID + ".fileProvider",apkFile); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setDataAndType(contentUri,"application/vnd.android.package-archive"); Log.i("lxl update->",">>>==Build.VERSION_CODES.N"); }else{ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setDataAndType(Uri.fromFile(apkFile),"application/vnd.android.package-archive"); Log.i("lxl update->","<<<<< Build.VERSION_CODES.N"); } startActivity(intent); }