Android app更新功能
最近公司客戶端做了升級功能,感覺學到了不少東西,因此做下記錄,以備後續檢視。
檢查版本更新可以放在splash頁面,也可以放在主頁面。
上圖是更新app的思維導圖,畫的不是很好,大家能看懂就好哈。 我在網上找了一個更加清楚的流程圖,大家可以看下這個,說的更詳細。
檢查更新的步驟大體分為以下三步:
1.首先判斷網路是否連通,如果網路連通則走更新app執行緒,否則跳過更新app執行緒。
2.更新執行緒首先獲取伺服器app版本號,如果版本號大於本地版本號時,提示使用者更新本地app版本。
3.更新版本時,首先從伺服器下載app最新版本到手機,接著安裝替換舊版本。
(1)判斷網路是否連通,且不在wifi狀態下時提示使用者是否用流量下載更新
public static int getNetworkState(Context context) { //得到連線管理器物件 ConnectivityManager connectivityManager = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); if (networkInfo != null && networkInfo.isConnected()) { if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) { return NETWORK_WIFI; } else if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) { return NETWORK_MOBILE; } } else { return NETWORK_NONE; } return NETWORK_NONE; }
(2)網路請求伺服器獲取伺服器版本號,和本地版本號做對比
請求網路工具類:
public class RequestServer { private static String TAG = "url"; public static String RequestServer(String urlString) { BufferedReader br = null; HttpURLConnection conn = null; StringBuilder sb = new StringBuilder(); try { URL reqURL = new URL(urlString); conn = (HttpURLConnection) reqURL.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(10000); //開啟網路通訊輸入流 int code = conn.getResponseCode(); LogUtil.e("code","code:"+code); if (code != 200){ return null; } //Log.e("chen", "code:" + code); InputStream is = conn.getInputStream(); //通過InputStream is 建立InputStreamReader物件 InputStreamReader isr = new InputStreamReader(is, "utf-8"); //通過InputStreamReader isr 建立BufferReader物件 br = new BufferedReader(isr); String line; while ((line = br.readLine()) != null) { sb.append(line); } } catch (Exception e) { e.printStackTrace(); } finally { if (conn != null) { conn.disconnect(); } if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } //return "{"msg":"繫結成功","return":true} return sb.toString(); } }
拿到伺服器資料並解析出伺服器軟體版本號,如果大於本地版本號,則提示使用者下載更新app。
try {
LogUtil.d(TAG, "json=" + json);
JSONObject jsonObject = new JSONObject(json);
apkPath = jsonObject.optString("installUrl");
LogUtil.d(TAG, "apkPath=" + apkPath);
must = jsonObject.optBoolean("must");
updateContent = jsonObject.optString("changelog");
versionCode = jsonObject.optInt("version");
LogUtil.d(TAG, "versionCode=" + versionCode);
if (versionCode > localVersionCode) {
myHander.sendEmptyMessage(UPDATE_VERSION);
}
} catch (JSONException e) {
e.printStackTrace();
}
(3)下載安裝最新app
/**
* 版本下載執行緒
* 實現Runnable介面
*/
private class downloadApkSdk implements Runnable {
private ProgressDialog dialog;
public downloadApkSdk(ProgressDialog dialog) {
this.dialog = dialog;
}
@Override
public void run() {
Logger.e(TAG, "DownloadApk is running");
String sdPath;
try {
// sdPath = Environment.getRootDirectory() + "/"; //目錄在/system 或者 /storage/sdcard0下
// sdPath = getFilesDir() + "/"; //目錄在/data/data/files下面
// sdPath = getCacheDir() + "/"; //目錄在/data/data/cache下面
sdPath = Environment.getExternalStorageDirectory() + "/"; //目錄在/storage/sdcard0下 或 /storage/emulated/0/
LogUtil.d("file", "path" + sdPath);
File file = new File(sdPath);
if (!file.exists() || file == null) {
file.mkdir();
}
mSavePath = sdPath + "yuntuiClient";
File dir = new File(mSavePath);
if (!dir.exists()) {
LogUtil.d("dir", "path" + dir.getPath());
dir.mkdir();
}
LogUtil.d("downloadApk", apkPath);
URL url = new URL(apkPath);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.connect();
int length = conn.getContentLength();
InputStream is = conn.getInputStream();
LogUtil.d(TAG, "mSavePath = " + mSavePath);
File apkFile = new File(mSavePath, versionName);
LogUtil.d("path", "path =" + apkFile.getPath());
FileOutputStream fos = new FileOutputStream(apkFile);
int count = 0;
byte[] buffer = new byte[1024];
while (!mIsCancel) {
int numread = is.read(buffer);
count += numread; // 計算進度條的當前位置
int mProgress = (int) (((float) count / length) * 100); // 更新進度條
dialog.setProgress(mProgress);
if (numread < 0) {
dialog.dismiss();
installAPK(apkFile);
break;
}
fos.write(buffer, 0, numread);
}
fos.close();
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
*下載完成,提示使用者安裝
*@param apkFile
*/
private void installAPK(File apkFile) {
//更改許可權
try {
String command = "chmod 777 " + apkFile.getAbsolutePath();
Log.i("lgx", "apkPath = " + apkFile.getAbsolutePath());
Runtime runtime = Runtime.getRuntime();
Process proc = runtime.exec(command);
} catch (IOException e) {
Log.i("lgx", "chmod fail!!!!");
e.printStackTrace();
}
if (Build.VERSION.SDK_INT >= 24) {//判斷版本是否在7.0以上
Uri apkUri = FileProvider.getUriForFile(context, "com.nxyuntui.yuntuiclient.fileprovider", apkFile);
Intent install = new Intent(Intent.ACTION_VIEW);
install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//新增這一句表示對目標應用臨時授權該Uri所代表的檔案
install.setDataAndType(apkUri, "application/vnd.android.package-archive");
startActivity(install);
} else {
Uri uri = Uri.parse("file://" + apkFile);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true); //表明不是未知來源
intent.setDataAndType(uri, "application/vnd.android.package-archive");
startActivity(intent);
}
}
(4)這種更新涉及到檔案儲存,裝置的儲存卡許可權,即WRITE_EXTERNAL_STORAGE,注意Android 6.0以上需要動態申請此許可權。
/**
*申請sd卡的讀寫許可權
*/
public void requestSDcard() {
if (Build.VERSION.SDK_INT >= 23) {
int REQUEST_CODE_CONTACT = 101;
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE}; //驗證是否許可許可權
for (String str : permissions) {
if (this.checkSelfPermission(str) != PackageManager.PERMISSION_GRANTED) { //申請許可權
this.requestPermissions(permissions, REQUEST_CODE_CONTACT);
return;
}
}
}
/**向用戶申請對應許可權
*@param requestCode
*@param permissions
*@param grantResults
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUESTCODE) {
if (permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE) && grantResults[0]
== PackageManager.PERMISSION_GRANTED) { //使用者同意
downloadApkSdk();
} else {
//使用者不同意
Toast.makeText(context, "儲存許可權不允許,將無法更新客戶端!", Toast.LENGTH_LONG).show();
}
}
}
(5)相容7.0以上版本,上面安裝方法裡面已經做了7.0以上版本和以下版本不通的相容,還有Manifest檔案部分需要相容的如下:
AndroidManifest.xml中加入:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.nxyuntui.yuntuiclient.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
注意: android:authorities=“com.nxyuntui.yuntuiclientTest.fileprovider” 裡面的com.nxyuntui.yuntuiclientTest是你自己的包名。
file_paths檔案如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<external-path path="yuntuiClient/" name="download"/> <!--目錄在/storage/sdcard0或者/storage/emulated/0下-->
<files-path path="yuntuiClient/" name="download"/> <!--目錄在/data/data/files下-->
<cache-path path="yuntuiClient/" name="download"/> <!--目錄在/data/data/cache下-->
</paths>
</resources>
注意: path和name都可以自己定義,即 path=“yuntuiClient/” name=“download”,上面三個路徑選擇符合條件的一個作為下載路徑即可。
需要注意的地方,後續如果還有會繼續更新。其實網上也有封裝好的通過瀏覽器更新的工具,這個很簡單,一句話就好了,github上面也有對應專案,可以看看。
轉載請標明出處,謝謝!