okhttpUtils是okhttp的一個工具包,首先在app的build.gradle中添加以下內容:
implementation 'com.squareup.okhttp3:okhttp:4.3.0'
implementation 'com.squareup.okio:okio:2.4.3'
implementation 'com.zhy:okhttputils:2.6.2'
//實例中使用了Toast和Dialog的第三方框架,如需使用請添加以下內容
implementation 'com.github.GrenderG:Toasty:1.3.0'
implementation 'com.afollestad.material-dialogs:core:0.9.6.0'
首先實例流程爲:①獲取當前APP版本號 ②請求檢查更新接口 ③若有新版本則提示下載 ④若點擊下載則通過異步請求將服務器上的APK文件下載完成後自動跳轉安裝界面
注意由於谷歌要求針對高版本安卓開發,低版本不允許上架, 所以當前實例並未考慮低版本安卓,測試機爲安卓7-10
首先在安卓7以上存在一個問題,就是普通file格式的uri無法直接使用,需通過共享目錄生成content格式的uri,所以我們需要在androidmanifest.xml的application中添加以下內容:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.packagename.fileProvider" //改爲你的包名.fileProvider即可
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
然後在res目錄中新建一個xml目錄,如果已有則不需要新建,在xml中新建一個files_paths.xml並寫入以下內容:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!-- 物理路徑爲Context.getFilesDir() + /files/... -->
<files-path path="files" name="files" />
<!-- 物理路徑爲Context.getCacheDir() + /files/... -->
<cache-path path="files" name="cache" />
<!-- 物理路徑爲Environment.getExternalStorageDirectory() + /files/... -->
<external-path path="files" name="external" />
<!-- 物理路徑爲Context.getExternalFilesDir(String) + /files/... -->
<external-files-path path="files" name="externalfiles"/>
<!-- 物理路徑爲Context.getExternalCacheDir() + /files/... -->
<external-cache-path path="files" name="externalcache"/>
<!-- 物理路徑爲`Context.getExternalMediaDirs() + /files/..., 要求API21+ -->
<external-media-path name="externalmedia" path="files" />
<root-path name="root_path" path="."/>
</paths>
這樣我們的共享目錄就配置好了,在代碼中直接使用即可,接下來我將直接貼出檢查更新的完整點擊事件供大家參考(重要部分在代碼中註釋標出):
check_view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
long versionCode = 1000;
//獲取當前APP的版本號
try {
PackageInfo packageInfo = getContext().getApplicationContext()
.getPackageManager()
.getPackageInfo(getContext().getPackageName(), 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
versionCode = packageInfo.getLongVersionCode();
} else {
versionCode = packageInfo.versionCode;
}
} catch (PackageManager.NameNotFoundException e) {
Log.e("", e.getMessage());
}
//請求地址+等待提示框
String host = new Defines().SERVER_HOST+"checkUpdate";
final MaterialDialog waitForDialog = new MaterialDialog.Builder(getActivity())
.content("請稍等...")
.progress(true,-1)//等待圖標 true=圓形icon false=進度條
.cancelable(false)//不會被取消 (包括返回鍵和外部點擊都無法取消)
.build();
waitForDialog.show();
//調用檢查更新接口,傳入當前版本號
OkHttpUtils
.post()
.url(host)
.addParams("version_code",""+versionCode)
.build()
.execute(new StringCallback() {
@Override
public void onError(Call call, Exception e, int id) {
waitForDialog.dismiss();
Toasty.error(getActivity(),"服務器請求失敗", Toast.LENGTH_SHORT).show();
}
@Override
public void onResponse(String response, int id) {
waitForDialog.dismiss();
try {
JSONObject jsonObject = new JSONObject(response);
int code = jsonObject.getInt("code");
//code=200爲最新版本無需更新 code=250爲有新版本可供下載
if (code==200) {
Toasty.success(getActivity(),jsonObject.getString("msg"),Toast.LENGTH_SHORT).show();
} else if(code==250) {
final JSONObject data = jsonObject.getJSONObject("data");
//將後臺編寫的更新日誌的<br>打散重組爲\n以在安卓可以換行提示
String log = "";
String[] chrstr = data.getString("update_log").split("<br>");
for(int i=0;i<chrstr.length;i++)
log = log + chrstr[i]+"\n";
MaterialDialog dialog = new MaterialDialog.Builder(getContext())
.title(jsonObject.getString("msg"))
.content(log)
.positiveText("下載更新")
.negativeText("以後再說")
.cancelable(true)
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
//獲取APP內部存儲的chche文件夾路徑
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
cachePath = getActivity().getExternalCacheDir().getPath();
} else {
cachePath = getActivity().getFilesDir().getAbsolutePath();
}
//新建一個下載進度條並允許點擊其他區域關閉
final ProgressDialog progressDialog = new ProgressDialog(getContext());
progressDialog.setTitle("正在下載,請耐心等待...");
progressDialog.setMessage("點擊其他區域或返回以轉入後臺下載");
progressDialog.setCancelable(true);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.show();
try {
//通過okhttpUtils下載文件
OkHttpUtils.get()
.url(data.getString("position"))
.build()
.execute(new FileCallBack(cachePath,"fingerdorm.apk") {
@Override
public void onError(Call call, Exception e, int id) {
Toasty.error(getActivity(),"下載失敗",Toast.LENGTH_SHORT).show();
}
//inpogress回調爲更新進度條
@Override
public void inProgress(float progress, long total, int id) {
super.inProgress(progress, total, id);
progressDialog.setProgress((int) (100 * progress));
}
//下載完成,處理打開安裝
@Override
public void onResponse(File downloadFile, int id) {
Toasty.success(getActivity(),"下載完成,即將啓動安裝",Toast.LENGTH_LONG).show();
progressDialog.hide();
progressDialog.dismiss();
//開啓子線程,延遲三秒後執行安裝
new Thread(new Runnable()
{
@Override
public void run()
{
try {
sleep(3000);
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
cachePath = getContext().getExternalCacheDir().getPath();
} else {
cachePath = getContext().getFilesDir().getAbsolutePath();
}
File uriFile = new File(cachePath +"/fingerdorm.apk");
//這裏獲取uri的第二個參數一定要和androidmanifest中配置的的對應
Uri uri = getUriForFile(getContext(), "com.packaname.fileProvider", uriFile);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//打開APK執行的TYPE爲"application/vnd.android.package-archive"
intent.setDataAndType(uri,"application/vnd.android.package-archive");
getContext().startActivity(intent);
} catch (Exception e) {
Looper.prepare();
Toasty.error(getActivity(), "自動安裝失敗,請手動安裝", Toast.LENGTH_SHORT).show();
Looper.loop();
}
}
}).start();
}
});
} catch (JSONException e) {
Toasty.error(getActivity(), "服務器數據解析失敗", Toast.LENGTH_SHORT).show();
}
}
})
.onNegative(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
}
})
.build();
dialog.show();
}else{
Toasty.error(getActivity(),jsonObject.getString("msg"), Toast.LENGTH_SHORT).show();
}
} catch (JSONException e) {
e.printStackTrace();
Toasty.warning(getActivity(),"檢查更新失敗",Toast.LENGTH_LONG).show();
}
}
});
}
});