今天,簡單講講android里如何在app內(nèi)部直接使用服務(wù)器進行版本更新。
昨天,我講了如何使用應(yīng)用市場進行版本更新。但是使用應(yīng)用市場進行版本更新存在一個問題,就是app無法獲取應(yīng)用市場里app的版本信息,所以使用應(yīng)用市場進行版本更新時,需要搭一個服務(wù)器給app可以獲取到應(yīng)用市場最新的app的版本信息。
這里大家其實想到了吧,既然可以通過服務(wù)器獲取app的版本信息,那么也可以直接通過服務(wù)器下載最新的app進行版本更新,目前我看到大部分app是這樣做的,比如應(yīng)用寶,支付寶這些app。我之前查找了版本更新的資料也是這些內(nèi)容,這里記錄一下。
一.版本的基礎(chǔ)知識
????????版本控制的屬性包括versionCode和versionName。
(一)versionCode
????????版本號(versionCode)是相對比較重要的一個屬性。versionCode是一個Integer類型的值。所以大家在設(shè)置的時候,不要將versionCode設(shè)置的太大,最好不要超過Integer的取值范圍(當(dāng)然一般也是不會超過的),一般大家在發(fā)布自己的第一個應(yīng)用到市場的時候,版本取值為1(versionCode=1),這也是目前典型和普遍的做法。然后,每次發(fā)布更新版本時可以遞增versionCode的值。
(二)versionName
????????版本名(versionName)一個值為String類型的屬性,一般和VersionCode成對出現(xiàn)。VersionCode是方便程序開發(fā)者運行和維護Application而設(shè)置的一個有效的值。versionName是一個版本的描述,給用戶看的,也是用戶放在各個第3方平臺上提供給使用者看的一個版本名,可以說是對VersionCode的解釋和描述。一般格式可以為:1.1.2。(major.minor.point)的形式。
(三)版本控制小結(jié)
????????版本號(versionCode)是用于判斷是否升級的,一般每次版本更新,版本號加一。如果獲取服務(wù)器上的版本號比檢測到本程序的版本號高,那么提示升級。 版本名(versionName)用于顯示版本改變的幅度大小,比如從2.0.1改變?yōu)?.0.2可能只是修改了一個很小的debug,如果改變?yōu)?.1.0可能是新增了一些功能,如果改變?yōu)?.0.0可能是有很大幅度的修改,比如很多UI界面或功能的添加! ????????也就是版本號用于判斷是否可以升級,而版本名用于顯示給用戶看!
(四)版本控制的文件位置
????????這個要區(qū)分你是用Eclipse開發(fā)還是Studio開發(fā)。 ????????在Eclipse中本控制的屬性的位置是Manifest.xml中,如:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"android:versionCode="3"android:versionName="1.2.1"package="com.example.updateDemo"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme">。。。</application></manifest>
上面表示android的第三個版本程序,版本名:1.2.1
????????在Android Studio呢?也是可以在Manifest.xml中定義versionCode和versionName,但是這里設(shè)置是無效的! 需要在程序的build.grade文件中設(shè)置,圖解: 上面表示android的第三個版本程序,版本名:3.0.1
(五)版本信息的獲取,代碼
這里指的是獲取運行中的程序的版本號,代碼如下:
/** 獲取當(dāng)前程序的版本名*/
private String getVersionName() throws Exception{//獲取packagemanager的實例PackageManager packageManager = getPackageManager();//getPackageName()是你當(dāng)前類的包名,0代表是獲取版本信息PackageInfo packInfo = packageManager.getPackageInfo(getPackageName(), 0);Log.e("TAG","版本號"+packInfo.versionCode);Log.e("TAG","版本名"+packInfo.versionName);return packInfo.versionName;
}
/** 獲取當(dāng)前程序的版本號*/
private int getVersionCode() throws Exception{//獲取packagemanager的實例PackageManager packageManager = getPackageManager();//getPackageName()是你當(dāng)前類的包名,0代表是獲取版本信息PackageInfo packInfo = packageManager.getPackageInfo(getPackageName(), 0);Log.e("TAG","版本號"+packInfo.versionCode);Log.e("TAG","版本名"+packInfo.versionName);return packInfo.versionCode;
}
簡單講講,其實版本更新就是首先獲取到服務(wù)器的版本號與版本名稱,然后獲取app自己的版本號和版本名稱,進行比較。如果服務(wù)器的版本號大于app自己的版本號,那么就進行http請求獲取服務(wù)器的apk,進行下載。下載完成后就直接覆蓋安裝。
下面舉一個使用服務(wù)器進行版本更新的例子:
該小程序的特點是,當(dāng)有更新時,會彈出一個提示框,點擊確定,則在通知來創(chuàng)建一個進度條進行下載,點擊取消,則取消更新。
以下是詳細代碼:
1.創(chuàng)建布局文件notification_item.xml,用于在通知欄生成一個進度條和下載圖標(biāo)。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="fill_parent"android:padding="3dp" ><ImageViewandroid:id="@+id/notificationImage"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@android:drawable/stat_sys_download" /><TextViewandroid:id="@+id/notificationTitle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_toRightOf="@id/notificationImage"android:paddingLeft="6dp"android:textColor="#FF000000" /><TextViewandroid:id="@+id/notificationPercent"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/notificationImage"android:paddingTop="2dp"android:textColor="#FF000000" /><ProgressBarandroid:id="@+id/notificationProgress"style="@style/ProgressBarHorizontal_color"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignLeft="@id/notificationTitle"android:layout_alignParentRight="true"android:layout_alignTop="@id/notificationPercent"android:layout_below="@id/notificationTitle"android:paddingLeft="6dp"android:paddingRight="3dp"android:paddingTop="2dp" /></RelativeLayout>
2.創(chuàng)建AppContext類,該類繼承自Application。
package com.test.application;import android.app.Application;
import android.content.Context;import com.test.update.config.Config;public class AppContext extends Application {private static AppContext appInstance;private Context context;public static AppContext getInstance() {return appInstance;}@Overridepublic void onCreate() {// TODO Auto-generated method stubsuper.onCreate();appInstance = this;context = this.getBaseContext();
// // 獲取當(dāng)前版本號
// try {
// PackageInfo packageInfo = getApplicationContext()
// .getPackageManager().getPackageInfo(getPackageName(), 0);
// Config.localVersion = packageInfo.versionCode;
// Config.serverVersion = 1;// 假定服務(wù)器版本為2,本地版本默認是1
// } catch (NameNotFoundException e) {
// e.printStackTrace();
// }initGlobal();}public void initGlobal() {try {Config.localVersion = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode; // 設(shè)置本地版本號Config.serverVersion = 2;// 假定服務(wù)器版本為2,本地版本默認是1--實際開發(fā)中是從服務(wù)器獲取最新版本號,android具體與后端的交互見我另///外的博文} catch (Exception ex) {ex.printStackTrace();}}
}
3.創(chuàng)建配置文件類Config.java,在這個類里面定義一些與版本相關(guān)的常量
package com.test.update.config;public class Config {//版本信息public static int localVersion = 0;public static int serverVersion = 0;/* 下載包安裝路徑 */ public static final String savePath = "/sdcard/test/"; public static final String saveFileName = savePath + "test.apk";
}
4.編寫更新服務(wù)類UpdateServcie.java
package com.test.update;import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.widget.RemoteViews;import com.test.update.config.Config;public class UpdateService extends Service {// 標(biāo)題private int titleId = 0;// 文件存儲private File updateDir = null;private File updateFile = null;// 下載狀態(tài)private final static int DOWNLOAD_COMPLETE = 0;private final static int DOWNLOAD_FAIL = 1;// 通知欄private NotificationManager updateNotificationManager = null;private Notification updateNotification = null;// 通知欄跳轉(zhuǎn)Intentprivate Intent updateIntent = null;private PendingIntent updatePendingIntent = null;/**** 創(chuàng)建通知欄*/RemoteViews contentView;// 這樣的下載代碼很多,我就不做過多的說明int downloadCount = 0;int currentSize = 0;long totalSize = 0;int updateTotalSize = 0;// 在onStartCommand()方法中準(zhǔn)備相關(guān)的下載工作:@SuppressWarnings("deprecation")@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// 獲取傳值titleId = intent.getIntExtra("titleId", 0);// 創(chuàng)建文件if (android.os.Environment.MEDIA_MOUNTED.equals(android.os.Environment.getExternalStorageState())) {updateDir = new File(Environment.getExternalStorageDirectory(),Config.saveFileName);updateFile = new File(updateDir.getPath(), getResources().getString(titleId) + ".apk");}this.updateNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);this.updateNotification = new Notification();// 設(shè)置下載過程中,點擊通知欄,回到主界面updateIntent = new Intent(this, UpdateActivity.class);updatePendingIntent = PendingIntent.getActivity(this, 0, updateIntent,0);// 設(shè)置通知欄顯示內(nèi)容updateNotification.icon = R.drawable.ic_launcher;updateNotification.tickerText = "開始下載";updateNotification.setLatestEventInfo(this, "QQ", "0%",updatePendingIntent);// 發(fā)出通知updateNotificationManager.notify(0, updateNotification);// 開啟一個新的線程下載,如果使用Service同步下載,會導(dǎo)致ANR問題,Service本身也會阻塞new Thread(new updateRunnable()).start();// 這個是下載的重點,是下載的過程return super.onStartCommand(intent, flags, startId);}@Overridepublic IBinder onBind(Intent arg0) {// TODO Auto-generated method stubreturn null;}@SuppressLint("HandlerLeak")private Handler updateHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case DOWNLOAD_COMPLETE:// 點擊安裝PendingIntentUri uri = Uri.fromFile(updateFile);Intent installIntent = new Intent(Intent.ACTION_VIEW);installIntent.setDataAndType(uri,"application/vnd.android.package-archive");updatePendingIntent = PendingIntent.getActivity(UpdateService.this, 0, installIntent, 0);updateNotification.defaults = Notification.DEFAULT_SOUND;// 鈴聲提醒updateNotification.setLatestEventInfo(UpdateService.this,"QQ", "下載完成,點擊安裝。", updatePendingIntent);updateNotificationManager.notify(0, updateNotification);// 停止服務(wù)stopService(updateIntent);case DOWNLOAD_FAIL:// 下載失敗updateNotification.setLatestEventInfo(UpdateService.this,"QQ", "下載完成,點擊安裝。", updatePendingIntent);updateNotificationManager.notify(0, updateNotification);default:stopService(updateIntent);}}};public long downloadUpdateFile(String downloadUrl, File saveFile)throws Exception {HttpURLConnection httpConnection = null;InputStream is = null;FileOutputStream fos = null;try {URL url = new URL(downloadUrl);httpConnection = (HttpURLConnection) url.openConnection();httpConnection.setRequestProperty("User-Agent", "PacificHttpClient");if (currentSize > 0) {httpConnection.setRequestProperty("RANGE", "bytes="+ currentSize + "-");}httpConnection.setConnectTimeout(10000);httpConnection.setReadTimeout(20000);updateTotalSize = httpConnection.getContentLength();if (httpConnection.getResponseCode() == 404) {throw new Exception("fail!");}is = httpConnection.getInputStream();fos = new FileOutputStream(saveFile, false);byte buffer[] = new byte[4096];int readsize = 0;while ((readsize = is.read(buffer)) > 0) {fos.write(buffer, 0, readsize);totalSize += readsize;// 為了防止頻繁的通知導(dǎo)致應(yīng)用吃緊,百分比增加10才通知一次if ((downloadCount == 0)|| (int) (totalSize * 100 / updateTotalSize) - 10 > downloadCount) {downloadCount += 10;updateNotification.setLatestEventInfo(UpdateService.this,"正在下載", (int) totalSize * 100 / updateTotalSize+ "%", updatePendingIntent);/**** 在這里我們用自定的view來顯示Notification*/updateNotification.contentView = new RemoteViews(getPackageName(), R.layout.notification_item);updateNotification.contentView.setTextViewText(R.id.notificationTitle, "正在下載");updateNotification.contentView.setProgressBar(R.id.notificationProgress, 100, downloadCount, false);updateNotificationManager.notify(0, updateNotification);}}} finally {if (httpConnection != null) {httpConnection.disconnect();}if (is != null) {is.close();}if (fos != null) {fos.close();}}return totalSize;}class updateRunnable implements Runnable {Message message = updateHandler.obtainMessage();public void run() {message.what = DOWNLOAD_COMPLETE;try {// 增加權(quán)限<USES-PERMISSION// android:name="android.permission.WRITE_EXTERNAL_STORAGE">;if (!updateDir.exists()) {updateDir.mkdirs();}if (!updateFile.exists()) {updateFile.createNewFile();}// 下載函數(shù),以QQ為例子// 增加權(quán)限<USES-PERMISSION// android:name="android.permission.INTERNET">;long downloadSize = downloadUpdateFile("http://softfile.3g.qq.com:8080/msoft/179/1105/10753/MobileQQ1.0(Android)_Build0198.apk",updateFile);if (downloadSize > 0) {// 下載成功updateHandler.sendMessage(message);} } catch (Exception ex) {ex.printStackTrace();message.what = DOWNLOAD_FAIL;// 下載失敗updateHandler.sendMessage(message);}}}
}
5.編寫活動類UpdateActivity
package com.test.update;import com.test.update.config.Config;import android.support.v4.app.Fragment;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;public class UpdateActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);checkVersion();}/*** 檢查更新版本*/public void checkVersion() {if (Config.localVersion < Config.serverVersion) {Log.i("hgncxzy", "==============================");// 發(fā)現(xiàn)新版本,提示用戶更新AlertDialog.Builder alert = new AlertDialog.Builder(this);alert.setTitle("軟件升級").setMessage("發(fā)現(xiàn)新版本,建議立即更新使用.").setPositiveButton("更新",new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog,int which) {// 開啟更新服務(wù)UpdateService// 這里為了把update更好模塊化,可以傳一些updateService依賴的值// 如布局ID,資源ID,動態(tài)獲取的標(biāo)題,這里以app_name為例Intent updateIntent = new Intent(UpdateActivity.this,UpdateService.class);updateIntent.putExtra("titleId",R.string.app_name);startService(updateIntent);}}).setNegativeButton("取消",new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog,int which) {dialog.dismiss();}});alert.create().show();} else {// 清理工作,略去// cheanUpdateFile()}}
}
6.添加權(quán)限以及將服務(wù)靜態(tài)加載(在配置文件中加載)。
<uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
注冊服務(wù)
<service android:name="com.test.update.UpdateService" ></service>
完整的AndroidManifest.xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.test.update"android:versionCode="1"android:versionName="1.0" ><uses-sdkandroid:minSdkVersion="8"android:targetSdkVersion="8" /><applicationandroid:name="com.test.application.AppContext"android:icon="@drawable/ic_launcher"android:label="@string/app_name" ><activityandroid:name="com.test.update.UpdateActivity"android:label="@string/app_name" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><service android:name="com.test.update.UpdateService" ></service></application><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /></manifest>
簡單講講,這個例子首先在啟動app時彈出版本更新的提示框,點擊更新后,會發(fā)送http請求下載apk文件,同時生成一個通知,每隔%10進度時更新通知,下載完成后,點擊通知欄就可以進行版本更新。具體的內(nèi)容比較簡單,關(guān)于通知的內(nèi)容我會另寫一篇博客進行講解。
源碼下載:https://download.csdn.net/download/bzlj2912009596/10463834
android 如何使用服務(wù)器進行版本更新就講完了。
就這么簡單。
與50位技術(shù)專家面對面 20年技術(shù)見證,附贈技術(shù)全景圖
總結(jié)
以上是生活随笔 為你收集整理的android 如何使用服务器进行版本更新 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。