Android應(yīng)用更新之自動(dòng)檢測(cè)版本及自動(dòng)升級(jí)
步驟:
1.檢測(cè)當(dāng)前版本的信息AndroidManifest.xml–>manifest–>[Android]
2.從服務(wù)器獲取版本號(hào)(版本號(hào)存在于xml文件中)并與當(dāng)前檢測(cè)到的版本進(jìn)行匹配,如果不匹配,提示用戶進(jìn)行升級(jí),如果匹配則進(jìn)入程序主界面。(demo中假設(shè)需要更新)
3.當(dāng)提示用戶進(jìn)行版本升級(jí)時(shí),如果用戶點(diǎn)擊了“更新”,系統(tǒng)將自動(dòng)從服務(wù)器上下載安裝包并進(jìn)行自動(dòng)升級(jí),如果點(diǎn)擊取消將進(jìn)入程序主界面。
效果圖如下:



下面介紹一下代碼的實(shí)現(xiàn):
1.獲取應(yīng)用的當(dāng)前版本號(hào),我是封裝了一個(gè)工具類來(lái)獲取
// 獲取本版本號(hào),是否更新 int vision = Tools.getVersion(this);
獲取當(dāng)前版本號(hào)工具類:
public class Tools {
/**
* 檢查是否存在SDCard
*
* @return
*/
public static boolean hasSdcard() {
String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED)) {
return true;
} else {
return false;
}
}
/**
* 2 * 獲取版本號(hào) 3 * @return 當(dāng)前應(yīng)用的版本號(hào) 4
*/
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;
}
}
2.獲取服務(wù)器版本號(hào),是否要更新(此處就是簡(jiǎn)單的網(wǎng)絡(luò)請(qǐng)求拿到需要的數(shù)據(jù)即可,我是寫了固定值)
// 獲取更新版本號(hào)
private void getVersion(final int vision) {
// {"data":{"content":"其他bug修復(fù)。","id":"2","api_key":"android",
// // "version":"2.1"},"msg":"獲取成功","status":1}
String data = "";
//網(wǎng)絡(luò)請(qǐng)求獲取當(dāng)前版本號(hào)和下載鏈接
//實(shí)際操作是從服務(wù)器獲取
//demo寫死了
String newversion = "2.1";//更新新的版本號(hào)
String content = "\n" +
"就不告訴你我們更新了什么-。-\n" +
"\n" +
"----------萬(wàn)能的分割線-----------\n" +
"\n" +
"(ㄒoㄒ) 被老板打了一頓,還是來(lái)告訴你吧:\n" +
"1.下架商品誤買了?恩。。。我搞了點(diǎn)小動(dòng)作就不會(huì)出現(xiàn)了\n" +
"2.側(cè)邊欄、彈框優(yōu)化 —— 這個(gè)你自己去探索吧,總得留點(diǎn)懸念嘛-。-\n";//更新內(nèi)容
String url = "http://openbox.mobilem.#/index/d/sid/3429345";//安裝包下載地址
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);
// 版本號(hào)不同
ShowDialog(vision, newversion, content, url);
}
}
}
3.接下來(lái)就是下載文件了
(1) 顯示下載
此處用的是自定義按鈕:
/**
* 升級(jí)系統(tǒng)
*
* @param content
* @param url
*/
private void ShowDialog(int vision, String newversion, String content,
final String url) {
final MaterialDialog dialog = new MaterialDialog(this);
dialog.content(content).btnText("取消", "更新").title("版本更新 ")
.titleTextSize(15f).show();
dialog.setCanceledOnTouchOutside(false);
dialog.setOnBtnClickL(new OnBtnClickL() {// left btn click listener
@Override
public void onBtnClick() {
dialog.dismiss();
}
}, new OnBtnClickL() {// right btn click listener
@Override
public void onBtnClick() {
dialog.dismiss();
// pBar = new ProgressDialog(MainActivity.this,
// R.style.dialog);
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);
}
});
}
});
}
原生的按鈕:
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();
(2)通過(guò)異步任務(wù)實(shí)現(xiàn)進(jìn)度++
/**
* 下載應(yīng)用
*
* @author Administrator
*/
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();
// expect HTTP 200 OK, so we don't mistakenly save error
// report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return "Server returned HTTP "
+ connection.getResponseCode() + " "
+ connection.getResponseMessage();
}
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
file = new File(Environment.getExternalStorageDirectory(),
DOWNLOAD_NAME);
if (!file.exists()) {
// 判斷父文件夾是否存在
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
}
} 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) {
}
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) {
// // 申請(qǐng)多個(gè)權(quán)限。大神的界面
// AndPermission.with(MainActivity.this)
// .requestCode(REQUEST_CODE_PERMISSION_OTHER)
// .permission(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
// // rationale作用是:用戶拒絕一次權(quán)限,再次申請(qǐng)時(shí)先征求用戶同意,再打開授權(quán)對(duì)話框,避免用戶勾選不再提示。
// .rationale(new RationaleListener() {
// @Override
// public void showRequestPermissionRationale(int requestCode, Rationale rationale) {
// // 這里的對(duì)話框可以自定義,只要調(diào)用rationale.resume()就可以繼續(xù)申請(qǐng)。
// AndPermission.rationaleDialog(MainActivity.this, rationale).show();
// }
// }
// )
// .send();
// 申請(qǐng)多個(gè)權(quán)限。
AndPermission.with(MainActivity.this)
.requestCode(REQUEST_CODE_PERMISSION_SD)
.permission(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
// rationale作用是:用戶拒絕一次權(quán)限,再次申請(qǐng)時(shí)先征求用戶同意,再打開授權(quán)對(duì)話框,避免用戶勾選不再提示。
.rationale(rationaleListener
)
.send();
Toast.makeText(context, "您未打開SD卡權(quán)限" + result, Toast.LENGTH_LONG).show();
} else {
// Toast.makeText(context, "File downloaded",
// Toast.LENGTH_SHORT)
// .show();
update();
}
}
}
此處下載apk文件,需要獲取SD的讀寫權(quán)限(用的是嚴(yán)大的權(quán)限庫(kù))
權(quán)限庫(kù)GitHub
private static final int REQUEST_CODE_PERMISSION_SD = 101;
private static final int REQUEST_CODE_SETTING = 300;
private RationaleListener rationaleListener = new RationaleListener() {
@Override
public void showRequestPermissionRationale(int requestCode, final Rationale rationale) {
// 這里使用自定義對(duì)話框,如果不想自定義,用AndPermission默認(rèn)對(duì)話框:
// AndPermission.rationaleDialog(Context, Rationale).show();
// 自定義對(duì)話框。
AlertDialog.build(MainActivity.this)
.setTitle(R.string.title_dialog)
.setMessage(R.string.message_permission_rationale)
.setPositiveButton(R.string.btn_dialog_yes_permission, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
rationale.resume();
}
})
.setNegativeButton(R.string.btn_dialog_no_permission, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
rationale.cancel();
}
})
.show();
}
};
//----------------------------------SD權(quán)限----------------------------------//
@PermissionYes(REQUEST_CODE_PERMISSION_SD)
private void getMultiYes(List<String> grantedPermissions) {
Toast.makeText(this, R.string.message_post_succeed, Toast.LENGTH_SHORT).show();
}
@PermissionNo(REQUEST_CODE_PERMISSION_SD)
private void getMultiNo(List<String> deniedPermissions) {
Toast.makeText(this, R.string.message_post_failed, Toast.LENGTH_SHORT).show();
// 用戶否勾選了不再提示并且拒絕了權(quán)限,那么提示用戶到設(shè)置中授權(quán)。
if (AndPermission.hasAlwaysDeniedPermission(this, deniedPermissions)) {
AndPermission.defaultSettingDialog(this, REQUEST_CODE_SETTING)
.setTitle(R.string.title_dialog)
.setMessage(R.string.message_permission_failed)
.setPositiveButton(R.string.btn_dialog_yes_permission)
.setNegativeButton(R.string.btn_dialog_no_permission, null)
.show();
// 更多自定dialog,請(qǐng)看上面。
}
}
//----------------------------------權(quán)限回調(diào)處理----------------------------------//
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[]
grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
/**
* 轉(zhuǎn)給AndPermission分析結(jié)果。
*
* @param object 要接受結(jié)果的Activity、Fragment。
* @param requestCode 請(qǐng)求碼。
* @param permissions 權(quán)限數(shù)組,一個(gè)或者多個(gè)。
* @param grantResults 請(qǐng)求結(jié)果。
*/
AndPermission.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CODE_SETTING: {
Toast.makeText(this, R.string.message_setting_back, Toast.LENGTH_LONG).show();
//設(shè)置成功,再次請(qǐng)求更新
getVersion(Tools.getVersion(MainActivity.this));
break;
}
}
}
(3) 當(dāng)apk文件下載完畢時(shí),打開安裝
private void update() {
//安裝應(yīng)用
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(Environment
.getExternalStorageDirectory(), DOWNLOAD_NAME)),
"application/vnd.android.package-archive");
startActivity(intent);
}
源碼
此demo已經(jīng)上傳到GitHub,如有需要自行下載
GitHub: 鏈接地址
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android仿微信菜單(Menu)(使用C#和Java分別實(shí)現(xiàn))
這篇文章主要介紹了Android仿微信菜單(Menu)(使用C#和Java分別實(shí)現(xiàn)),本文分別給出C#和Java版的運(yùn)行效果及實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-06-06
Android Studio實(shí)現(xiàn)簡(jiǎn)單的QQ登錄界面的示例代碼
這篇文章主要介紹了Android Studio實(shí)現(xiàn)簡(jiǎn)單的QQ登錄界面的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
Android recyclerview實(shí)現(xiàn)拖拽排序和側(cè)滑刪除
這篇文章主要為大家詳細(xì)介紹了Android recyclerview實(shí)現(xiàn)拖拽排序和側(cè)滑刪除,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02
Android開發(fā)之基于DialogFragment創(chuàng)建對(duì)話框的方法示例
這篇文章主要介紹了Android開發(fā)之基于DialogFragment創(chuàng)建對(duì)話框的方法,結(jié)合實(shí)例形式分析了DialogFragment創(chuàng)建對(duì)話框的具體功能與布局相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-08-08
Android自定義ViewGroup實(shí)現(xiàn)標(biāo)簽流效果
這篇文章主要為大家詳細(xì)介紹了Android自定義ViewGroup實(shí)現(xiàn)標(biāo)簽流效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
分析Android多主題顏色的相關(guān)問(wèn)題
這篇文章總結(jié)了在Android開發(fā)多主題顏色的時(shí)候會(huì)遇到的一些問(wèn)題,然后給出解決方案,讓大家可以解決問(wèn)題,有需要的下面一起來(lái)看看吧。2016-08-08
在Android中通過(guò)Intent使用Bundle傳遞對(duì)象的使用方法
這篇文章主要介紹了在Android中通過(guò)Intent使用Bundle傳遞對(duì)象的使用方法,詳細(xì)介紹Intent使用Bundle傳遞對(duì)象的方法。有需要的可以了解一下。2016-11-11
Android編程實(shí)現(xiàn)獲取新浪天氣預(yù)報(bào)數(shù)據(jù)的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)獲取新浪天氣預(yù)報(bào)數(shù)據(jù)的方法,涉及Android基于新浪接口的調(diào)用及數(shù)據(jù)處理技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11

