目錄
一、 前提準(zhǔn)備
1、獲取服務(wù)器
2、使用工具操作云服務(wù)器
?二、Json格式網(wǎng)頁
?三、創(chuàng)建file_paths.xml及修改AndroidManifest.xml
四、在java代碼加入更新檢測(cè)代碼
效果如圖:
??????? 可以強(qiáng)制更新和非強(qiáng)制更新,和瀏覽器下載安裝包。
一、 前提準(zhǔn)備
1、獲取服務(wù)器
????????首先去獲取云服務(wù)器,如:阿里云服務(wù)器(學(xué)生可免費(fèi)領(lǐng)取六個(gè)月)、騰訊云服務(wù)器、華為云服務(wù)器等。具體操作參考:從零開始用阿里云服務(wù)器搭建網(wǎng)頁_阿里云怎么裝修網(wǎng)頁打開_zstar-_的博客-CSDN博客
??????? 云服務(wù)器用于存放json格式網(wǎng)頁和安裝包,json可以對(duì)app進(jìn)行版本控制更新、顯示版本、顯示更新內(nèi)容、提供安裝包下載位置等信息。
2、使用工具操作云服務(wù)器
??????? 可以使用指令控制,也可以使用工具控制服務(wù)器,選用putty軟件進(jìn)行遠(yuǎn)程控制,winSCP軟件進(jìn)行文件傳輸。下載鏈接
?????? 以下是winSCP界面的服務(wù)器文件:可按下面文件找到存放安裝包和網(wǎng)頁的文件夾。
如下就是對(duì)APP進(jìn)行版本更新的網(wǎng)頁和安裝包存放的文件夾:
?二、Json格式網(wǎng)頁
以下是html網(wǎng)頁代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>UFCFans</title>
</head>
<body>
<pre><code id="json"></code></pre>
</body>
<script type="text/javascript">
let btn = document.querySelector('#json');
let data = {"hasUpdate": true,"NoIgnorable": true,
"versionCode": 2,
"versionName": "2.0",
"updateLog": "\n1、新增本地緩存。\n2、解決部分BUG。\n3、優(yōu)化使用體驗(yàn)。",
"apkUrl": "http://8.130.127.118:8080/ufcfans.apk",
"webUrl": "http://8.130.127.118:8080/ufcfans.apk",
"apkSize": "29.5MB"};
btn.textContent = JSON.stringify(data, null, 2);
</script>
</html>
下面是相關(guān)變量說明:
{
hasUpdate: true, //是否有更新 默認(rèn)true
NoIgnorable: true, //不 可忽略更新 強(qiáng)制:true 非強(qiáng)制:false
versionCode: 51, //服務(wù)端的版本號(hào)
versionName: "2.4.1", //服務(wù)端的版本名
updateLog: "\n1、更改保存圖片的存儲(chǔ)路徑。\n2、更改軟件更新的提示模式。\n3、調(diào)整非強(qiáng)制更新控制方式。\n4、新增存儲(chǔ)權(quán)限的申請(qǐng)授權(quán)位置。", //更新提示內(nèi)容
apkUrl: "https://www.yuming.com/assets/a某o130.apk",//新版本APK直鏈下載地址
webUrl: "https://yirj.gitee.io/update111",//瀏覽器更新鏈接,隨意放(直鏈、藍(lán)奏、官網(wǎng)均可)
apkSize: "29.5MB" //新版本的大小 隨意寫就好
}
?三、創(chuàng)建file_paths.xml及修改AndroidManifest.xml
?創(chuàng)建file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
?修改AndroidManifest.xml
?文章來源地址http://www.zghlxwxcb.cn/news/detail-654344.html
<!-- 擁有完全的網(wǎng)絡(luò)訪問權(quán)限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 修改或刪除您的USB存儲(chǔ)設(shè)備中的內(nèi)容 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 查看網(wǎng)絡(luò)連接 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
.....
android:label="@string/app_name"
android:requestLegacyExternalStorage="true">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
.........
</application>
四、在java代碼加入更新檢測(cè)代碼
????????在軟件的檢測(cè)更新界面的Oncreate
方法下,加入檢測(cè)更新的代碼。
??????? NoIgnorable變量為true則強(qiáng)制更新,無取消按鈕,為false有取消按鈕;
versionName, updateLog, apkSize, apkUrl, webUrl和upl變量為你的網(wǎng)頁網(wǎng)址,如:UFCFanshttp://8.130.127.118:8080
public static JSONObject jSONObject = null;
private static boolean hasUpdate = true;
private static boolean NoIgnorable; //是否有更新。 不可忽略的更新
private static int versionCode = 0;
private static String versionName, updateLog, apkSize, apkUrl, webUrl;
private static String[] upl;
/**
* 獲取當(dāng)前使用的軟件包的版本號(hào)
*/
public int getVersionCode() {
try {
//獲取packagemanager的實(shí)例
PackageManager packageManager = getPackageManager();
//getPackageName()是你當(dāng)前類的包名,0代表是獲取版本信息
PackageInfo packInfo = packageManager.getPackageInfo(getPackageName(), 0);
Log.e("TAG", "版本號(hào)" + packInfo.versionCode); //更新軟件用的是版本號(hào)
return packInfo.versionCode;
} catch (Exception e) {
e.printStackTrace();
}
return 1;
}
/**
* 提示版本更新的對(duì)話框
*/
public void showDialogUpdate() {
//hasUpdate為true且程序版本號(hào)<服務(wù)端版本號(hào),提示用戶更新
if (NoIgnorable) { //NoIgnorable為true 就是強(qiáng)制更新,無“取消”按鈕
// 這里的屬性可以一直設(shè)置,因?yàn)槊看卧O(shè)置后返回的是一個(gè)builder對(duì)象
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setCancelable(false); //開啟強(qiáng)制更新,無法觸摸外部關(guān)閉
builder.setTitle("是否升級(jí)到" + versionName + "版本?").
// 設(shè)置提示框的圖標(biāo)
// setIcon(R.drawable.ic_launcher).
// 設(shè)置要顯示的信息
setMessage("新版本大小:" + apkSize + "\n" + updateLog).
// 設(shè)置確定按鈕
setPositiveButton("更新", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
loadNewVersionProgress();//程序內(nèi)直接下載最新的版本程序
}
}).
setNeutralButton("瀏覽器下載", new DialogInterface.OnClickListener() {//中性按鈕 應(yīng)用內(nèi)下載失敗可用它更新
@Override
public void onClick(DialogInterface dialog, int which) {
Uri uri = Uri.parse(webUrl);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
finish(); //強(qiáng)制更新,點(diǎn)擊后銷毀應(yīng)用
}
});
// 顯示對(duì)話框
builder.create().show();
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
// builder.setCancelable(false); //非強(qiáng)制更新,屏蔽此行,觸摸外部或退出鍵可關(guān)閉
builder.setTitle("是否升級(jí)到" + versionName + "版本?").
setMessage("新版本大小:" + apkSize + "\n" + updateLog).
setPositiveButton("更新", new DialogInterface.OnClickListener() {//正按鈕
@Override
public void onClick(DialogInterface dialog, int which) {
loadNewVersionProgress();//下載最新的版本程序
}
}).setNegativeButton("取消", new DialogInterface.OnClickListener() {//負(fù)按鈕
@Override
public void onClick(DialogInterface dialog, int which) {
// finish(); //屏蔽銷毀,不做任何處理
}
}).setNeutralButton("瀏覽器下載", new DialogInterface.OnClickListener() {//中性按鈕
@Override
public void onClick(DialogInterface dialog, int which) {
Uri uri = Uri.parse(webUrl);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
//finish(); //屏蔽銷毀,訪問瀏覽器,程序不會(huì)退出
}
});
// 顯示對(duì)話框
builder.create().show();
}
}
//輪詢驗(yàn)證兩個(gè)更新鏈接,返回有效鏈接
public static String checkUrl(String[] ltl) {
String resultUrl = null;
for (String url : ltl) {
resultUrl = url;
try {
//調(diào)用檢查鏈接是否有效的方法
String result = get(url);
if (result != null && result.length() != 0) {
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return resultUrl;
}
//檢查更新鏈接是否有效的方法
public static String get(String url) {
URL infoUrl = null;
InputStream inStream = null;
String line = "";
try {
infoUrl = new URL(url);
URLConnection connection = infoUrl.openConnection();
HttpURLConnection httpConnection = (HttpURLConnection) connection;
int responseCode = httpConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
inStream = httpConnection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, "utf-8"));
StringBuilder strber = new StringBuilder();
while ((line = reader.readLine()) != null)
strber.append(line + "\n");
inStream.close();
int start = strber.indexOf("{");
int end = strber.indexOf("}");
String json = strber.substring(start, end + 1);
return json;
}
} catch (MalformedURLException e) {
} catch (IOException e) {
}
return "";
}
/**
* 使用檢查過的有效鏈接,獲取服務(wù)端json數(shù)據(jù)
*/
public static JSONObject GetServerJson() {
URL infoUrl = null;
InputStream inStream = null;
String line = "";
try {
String uurl = checkUrl(upl);
infoUrl = new URL(uurl); //json格式信息的API,使用案例。
URLConnection connection = infoUrl.openConnection();
HttpURLConnection httpConnection = (HttpURLConnection) connection;
int responseCode = httpConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
inStream = httpConnection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, "utf-8"));
StringBuilder strber = new StringBuilder();
while ((line = reader.readLine()) != null)
strber.append(line + "\n");
inStream.close();
int start = strber.indexOf("{");
int end = strber.indexOf("}");
String json = strber.substring(start, end + 1);
if (json != null) {
try {
jSONObject = new JSONObject(json);
hasUpdate = jSONObject.getBoolean("hasUpdate");
NoIgnorable = jSONObject.getBoolean("NoIgnorable");
versionCode = jSONObject.getInt("versionCode");
versionName = jSONObject.getString("versionName");
updateLog = jSONObject.getString("updateLog");
apkSize = jSONObject.getString("apkSize");
apkUrl = jSONObject.getString("apkUrl");
webUrl = jSONObject.getString("webUrl");
return jSONObject;
} catch (JSONException e) {
e.printStackTrace();
}
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 應(yīng)用內(nèi)直鏈升級(jí)方法,下載新版本程序
*/
private void loadNewVersionProgress() {
ProgressDialog pd = new ProgressDialog(this);
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
//獲取手機(jī)的根目錄存儲(chǔ)位置,以及直鏈鏈接最后一個(gè)“/”后文字作為文件名展示在界面
pd.setMessage("下載最新版本安裝包到:" + Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + apkUrl.substring(apkUrl.lastIndexOf("/") + 1));
pd.setCancelable(false); //開啟強(qiáng)制更新,觸摸屏幕其他位置無法關(guān)閉
pd.show();
//啟動(dòng)子線程下載任務(wù)
new Thread() {
@Override
public void run() {
Looper.prepare();
try {
File file = getFileFromServer(apkUrl, pd); //調(diào)用下載服務(wù)方法動(dòng)態(tài)顯示進(jìn)度
//sleep(2000);//設(shè)置休眠兩秒之后再啟動(dòng)安裝
installApk(file); //下載完成直接安裝
// pd.dismiss(); //屏蔽,結(jié)束掉進(jìn)度條對(duì)話框,防止強(qiáng)制更新出Bug
} catch (Exception e) {
//直鏈下載apk異常失敗提示容錯(cuò)。
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setCancelable(false);//開啟強(qiáng)制更新,觸摸屏幕其他位置無法關(guān)閉
builder.setTitle("下載失敗:").setMessage("1.請(qǐng)檢查存儲(chǔ)權(quán)限是否開啟。\n2.請(qǐng)檢查網(wǎng)絡(luò)連接是否正常。\n3.使用瀏覽器下載新版本。\nother:"+e.getMessage());
builder.setPositiveButton("退出", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
finish();
}
}).setNeutralButton("瀏覽器下載", new DialogInterface.OnClickListener() {//中性按鈕 應(yīng)用內(nèi)下載失敗可用它更新
@Override
public void onClick(DialogInterface dialog, int which) {
Uri uri = Uri.parse(webUrl);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
if (NoIgnorable) {
finish(); //強(qiáng)制更新,點(diǎn)擊后銷毀應(yīng)用
}else{
pd.dismiss(); //不強(qiáng)制更新,跳轉(zhuǎn)到瀏覽器并銷毀應(yīng)用內(nèi)下載失敗的進(jìn)度條
}
}
});
builder.create().show();
}
Looper.loop();
}
}.start();
}
/**
* 安裝apk
*/
protected void installApk(File file) {
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= 24) {
Uri apkUri = FileProvider.getUriForFile(this, "com.example.ufcfans.fileprovider", file); //這里要寫你程序的包名,已實(shí)驗(yàn)不可使用${applicationId}
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
}
this.startActivity(intent);
}
/**
* 從服務(wù)器獲取apk文件的代碼
* 傳入網(wǎng)址uri,進(jìn)度條對(duì)象即可獲得一個(gè)File文件
* (要在子線程中執(zhí)行哦)
*/
public static File getFileFromServer(String uri, ProgressDialog pd) throws Exception {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
URL url = new URL(uri);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
//獲取到文件的大小
pd.setMax(conn.getContentLength()); //字節(jié)的方式顯示下載進(jìn)度
InputStream is = conn.getInputStream();
//獲取直鏈鏈接最后一個(gè)“/”后文字作為文件名,下載存儲(chǔ)到手機(jī)
File file = new File(Environment.getExternalStorageDirectory(), apkUrl.substring(apkUrl.lastIndexOf("/", apkUrl.lastIndexOf("")) + 1));
FileOutputStream fos = new FileOutputStream(file);
BufferedInputStream bis = new BufferedInputStream(is);
byte[] buffer = new byte[1024];
int len;
int total = 0;
while ((len = bis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
total += len;
//獲取當(dāng)前下載量
pd.setProgress(total);//字節(jié)方式顯示下載量
}
fos.close();
bis.close();
is.close();
return file;
} else {
return null;
}
}
/**
* 權(quán)限的驗(yàn)證及處理,相關(guān)方法
*/
private void getReadPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE}, 10001);
} else {//沒有則請(qǐng)求獲取權(quán)限,示例權(quán)限是:存儲(chǔ)權(quán)限,需要其他權(quán)限請(qǐng)更改或者替換
ActivityCompat.requestPermissions(this,
new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE}, 10001);
}
} else {//如果已經(jīng)獲取到了權(quán)限則直接進(jìn)行下一步操作
Log.e(TAG, "全部權(quán)限已經(jīng)授權(quán)成功");
}
}
}
/**
* 一個(gè)或多個(gè)權(quán)限請(qǐng)求結(jié)果回調(diào)
* 循環(huán)回調(diào)獲取權(quán)限,除非勾選禁止后不再詢問,之后提示用戶引導(dǎo)用戶去設(shè)置
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 10001:
for (int i = 0; i < grantResults.length; i++) {
// 如果拒絕獲取權(quán)限
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
//判斷是否勾選禁止后不再詢問
boolean flag = ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i]);
if (flag) {
getReadPermissions();
return;//用戶權(quán)限是一個(gè)一個(gè)的請(qǐng)求的,只要有拒絕,剩下的請(qǐng)求就可以停止,再次請(qǐng)求打開權(quán)限了
} else { // 勾選不再詢問,并拒絕
Toast.makeText(this, "請(qǐng)到設(shè)置中打開權(quán)限", Toast.LENGTH_LONG).show();
return;
}
}
}
//Toast.makeText(this, "權(quán)限開啟完成", Toast.LENGTH_LONG).show();
break;
default:
break;
}
}
/**
* 忽略https的證書校驗(yàn) 的相關(guān)方法
*/
public static void handleSSLHandshake() {
try {
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}};
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
} catch (Exception ignored) {
}
}
至此,已完成對(duì)應(yīng)用的版本控制更新,通過
更改html文件的versionVode控制更新,如:默認(rèn)為1,改為2時(shí),用戶APP會(huì)彈出更新框。
文章來源:http://www.zghlxwxcb.cn/news/detail-654344.html
?
到了這里,關(guān)于【Android】-- 如何對(duì)APP版本控制/更新?的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!