1. 程式人生 > >Android app更新功能

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上面也有對應專案,可以看看。

轉載請標明出處,謝謝!