一.如何獲取應用程序自動更新
1.服務器中,存儲一份JSON文件,里面記錄著應用的版本號,和應用的下載地址,當我們啟動程序的時候,會訪問這個JSON文件,如果本應用程序的版本號小于服務器的版本號,則會提示用戶是否更新.會有以下幾種情況:
1.用戶點擊取消,界面則跳轉到主界面
2.用戶點擊更新
2.1跳轉到系統的安裝頁面
2.1.1用戶點擊安裝按鈕,安裝完成后回到桌面
2.1.2用戶點擊取消按鈕,這時候我們開啟跳轉安裝頁面的方法要用startActivityForResult(intent,請求碼(用于分辨是誰開啟了安裝 頁面,一旦用戶點擊了取消,則回到前一個activity頁面,觸發onActivityResult方法,方法內判斷請求碼是否一樣,在去跳轉主界面))
示例代碼:
public class SplashActivity extends Activity {
private static final String TAG = "SplashActivity";
private static final int REQUEST_CODE = 100;
@BindView(R.id.tv_version_name) //注解,就等同于findViewById(R.id.xxx)找的過程
TextView tvVersionName; //將findViewById(R.id.xxx)找到的控件賦值給tvVersionName
private int localVersionCode;
private String desc;
private String downloadUrl;
private ProgressDialog progressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
ButterKnife.bind(this);//這是第三方框架
//獲取版本名稱方法
String packageVersionName = getPackageVersionName();
tvVersionName.setText(packageVersionName);
//在應用程序上,有一個versioncode,將本地的versioncode和服務器上的versioncode做一個比對
//本地versionCode(localVersionCode)<服務器versionCode(remoteVersionCode),則需要下載更新.
//從sp中將存儲的是否需要更新的狀態,獲取出來 true 要檢測更新 false 不要檢測更新
boolean updateAuto = SharePreUtil.getBooleanValue(this, Constant.UPDATE_ATUO, true);
if(updateAuto){
checkVersion();
}else{
new Thread(){
@Override
public void run() {
try {
Thread.sleep(3000);
enterHome();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}
private void checkVersion() {
//1.獲取本地VersionCode
localVersionCode = getPackageVersionCode();
//2.發送網絡請求,獲取服務器的versionCode,okhttp加jar包 okhttp和okio這2個jar包
//通過分析,服務器必須提供,versionCode,downloadUrl,新版本的描述,新版本的版本名稱,json
//2.1 創建一個客戶端,此客戶端可以給服務器發請求
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(20, TimeUnit.SECONDS)
.connectTimeout(20,TimeUnit.SECONDS).
build();
//2.2 請求對象創建
String url = "http://10.0.2.2:8080/update.json";
Request request = new Request.Builder()
.get()
.url(url)
.build();
//2.3 通過客戶端把請求發出去
Call call = okHttpClient.newCall(request);
//2.4 處理請求的結果
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//請求失敗
enterHome();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//請求有結果,成功
//子線程中觸發onResponse方法,不能夠操作UI
//獲取服務器中的json里面的數據
ResponseBody body = response.body();
String json = body.string();
//手動解析,gson效率高
try {
JSONObject jsonObject = new JSONObject(json);
desc = jsonObject.getString("desc");
downloadUrl = jsonObject.getString("downloadUrl");
int remoteVersionCode = jsonObject.getInt("versionCode");
//服務器和本地的versionCode進行比對
if (remoteVersionCode>localVersionCode){
//服務器有新版本,可供下載
runOnUiThread(new Runnable() {
@Override
public void run() {
showDialog();
}
});
}else{
//服務器沒有更新的版本,可以直接進入應用程序的后一個界面
try {
Thread.sleep(3000);
enterHome();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (JSONException e) {
e.printStackTrace();
enterHome();
}
}
});
}
private void showDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("是否下載新版本?");
builder.setMessage(desc);//設置對話框除標題外的描述內容
builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
//下載apk,下載路徑downloadUrl,考慮在何處存儲 files cache() sd卡
downloadApk();
}
});
builder.setNegativeButton("稍后再說", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
//跳轉到應用程序的后一個界面
enterHome();
}
});
//如果用戶點擊回退按鈕,隱藏了對話框,則可以被如下方法監聽
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
enterHome();
}
});
builder.show();
}
private void downloadApk() {
//下載apk,并且指定放置下載文件的路徑,sd卡
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
//創建一個進度條對話框,用于顯示下載進度
progressDialog = new ProgressDialog(this);
//默認情況下對話框進度條圓形轉圈的
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.show();
//手機的sd卡可用
//sd卡存儲文件的路徑
final String path = Environment.getExternalStorageDirectory().getAbsolutePath()
+File.separator+"mobilesafe.apk";
//如何根據downloadUrl進行下載,okhttp下載
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.get()
.url(downloadUrl)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//請求失敗
enterHome();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//請求成功,從服務器的響應對象中獲取apk,流(服務器 輸入流(提供數據) 本地 輸出流(寫入文件))
ResponseBody body = response.body();
//告知progressDialog總進度,不變
progressDialog.setMax((int) body.contentLength());
//inputStream就是服務器把需要下載的apk以流的形式提供給客戶端
InputStream inputStream = body.byteStream();
File file = new File(path);
FileOutputStream fos = new FileOutputStream(file);
byte[] bytes = new byte[1024];
int len = 0;
int temp = 0;//用于記錄目前下載的到的位置
while((len = inputStream.read(bytes))!=-1){
//將讀過的數據寫入文件中
fos.write(bytes,0,len);
//告知progressDialog在下載過程中的當前進度
temp += len;
//將當前的下載位置,設置給progressDialog
progressDialog.setProgress(temp);
//沒下載一段數據,睡眠一段時間
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//一旦循環結束,則sd卡中就有一個從服務器下載下來的apk
//下載完成后,則隱藏對話框
progressDialog.dismiss();
//安裝apk,這個要裝的apk在哪里
installApk(file);
}
});
}
}
/**
* 安裝指定路徑下的apk
* @param file 需要安裝文件的路徑
*/
private void installApk(File file) {
//找到系統的安裝界面,把安裝過程中要用到的東西傳遞進去,讓系統幫助我們安裝.
/*<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" />
<data android:scheme="file" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>*/
Intent intent = new Intent("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");
//通過隱式意圖開啟系統的安裝apk界面
startActivityForResult(intent,REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//在系統的安裝界面結束后,則會調用此方法,在此方法中需要開啟后續的界面,確保用戶能夠使用低版本的apk
if (requestCode == REQUEST_CODE){
enterHome();
}
super.onActivityResult(requestCode, resultCode, data);
}
private void enterHome() {
Intent intent = new Intent(this, HomeActivity.class);
startActivity(intent);
finish();
}
private String getPackageVersionName() {
//1.PackageManager 包的管理者對象
PackageManager pm = getPackageManager();
//2.獲取應用的配置信息,在此處傳遞0獲取的是基本信息(包名,版本名稱,版本號)
try {
PackageInfo packageInfo = pm.getPackageInfo(getPackageName(),0);
String versionName = packageInfo.versionName;
return versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return "";
}
private int getPackageVersionCode() {
//1.PackageManager 包的管理者對象
PackageManager pm = getPackageManager();
//2.獲取應用的配置信息,在此處傳遞0獲取的是基本信息(包名,版本名稱,版本號)
try {
PackageInfo packageInfo = pm.getPackageInfo(getPackageName(),0);
int versionCode = packageInfo.versionCode;
return versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return 0;
}
//給tvVersionName注冊上了點擊事件
@OnClick(R.id.tv_version_name)
public void onViewClicked() {
//此方法中的代碼,就等同于onClick中的代碼
}
}