爲程序添加版本自動更新功能

OverView: 
程序 通過後臺每天檢查是否有最新版本,如果需要更新當前版本,將彈出對話框讓用戶選擇是否在當前通過Market來更新軟件 。
Knowledge Points:

  • SharedPreferences: 一個輕量級的存儲方法,類似於經常使用的.ini文件,它也是通過檢索關鍵字來取得相應的數值。之所以是成爲輕量級,是因爲它所能應用 的數值類型有限,對於存儲較大數值,效率相對較低。官方參考
  • System.currentTimeMillis:將當前時間以毫秒作爲單位來表示,用於比較兩個時間的先後順序。(其數值表示從1970-01-01 00:00:00直到當前時間的總毫秒數)官方參考
  • 通過網絡 來讀取信息:在checkUpdate()方法中包含了通過制定的URL來讀取網絡資源。具體操作步驟,請參考源代碼 。
  • Runnable: 在其內部的Run()方法中實現所要執行的任何代碼,當這個runnable interface被調用後可以視作爲新的線程。

Source Code:

public class hello extends Activity  {         /** Called when the activity is first created. */         private Handler mHandler;               @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.main);                  mHandler = new Handler();         /* Get Last Update Time from Preferences */         SharedPreferences prefs = getPreferences(0);         long lastUpdateTime =  prefs.getLong("lastUpdateTime", System.currentTimeMillis());         int curVersion = 0;                 try {                         curVersion = getPackageManager().getPackageInfo("linhai.com.hello", 0).versionCode;                 } catch (NameNotFoundException e) {                         // TODO Auto-generated catch block                         e.printStackTrace();                 }         Log.i("DEMO",String.valueOf(curVersion));         /* Should Activity Check for Updates Now? */         if ((lastUpdateTime + (24 * 60 * 60 * 1000)) < System.currentTimeMillis()) {             /* Save current timestamp for next Check*/            lastUpdateTime = System.currentTimeMillis();             SharedPreferences.Editor editor = getPreferences(0).edit();             editor.putLong("lastUpdateTime", lastUpdateTime);             editor.commit();                    /* Start Update */          //   checkUpdate.start();         }     }     /* This Thread checks for Updates in the Background */     private Thread checkUpdate = new Thread()      {         public void run() {             try {                 URL updateURL = new URL("http://my.company.com/update");                 URLConnection conn = updateURL.openConnection();                 InputStream is = conn.getInputStream();                 BufferedInputStream bis = new BufferedInputStream(is);                 ByteArrayBuffer baf = new ByteArrayBuffer(50);                 int current = 0;                 while((current = bis.read()) != -1){                      baf.app end((byte)current);                 }                 /* Convert the Bytes read to a String. */                 final String s = new String(baf.toByteArray());                         /* Get current Version Number */                 int curVersion = getPackageManager().getPackageInfo("your.app.id", 0).versionCode;                 int newVersion = Integer.valueOf(s);                 /* Is a higher version than the current already out? */                 if (newVersion > curVersion) {                     /* Post a Handler for the UI to pick up and open the Dialog */                     mHandler.post(showUpdate);                 }             } catch (Exception e) {             }         }     };     /* This Runnable creates a Dialog and asks the user to open the Market */     private Runnable showUpdate = new Runnable(){            public void run(){             new AlertDialog.Builder(hello.this)             .setIcon(R.drawable.ok)             .setTitle("Update Available")             .setMessage ("An update for is available!/n/nOpen Android Market and see the details?")             .setPositiveButton("Yes", new DialogInterface.OnClickListener() {                     public void onClick(DialogInterface dialog, int whichButton) {                             /* User clicked OK so do some stuff */                             Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market ://search?q=pname:your.app.id"));                             startActivity(intent);                     }             })             .setNegativeButton("No", new DialogInterface.OnClickListener() {                     public void onClick(DialogInterface dialog, int whichButton) {                             /* User clicked Cancel */                     }             })             .show();            }     };    }
複製代碼



分爲三個部分:

  • 置於onCreate()方法中的程序用於判斷當前時間是否需要檢查更新(如果距離上次更新時間大於1天,將啓動檢查更新)
  • 當以上條件滿足時,啓動checkUpdate來檢查當前程序是否爲最新版本。
  • 如果確定版本已過期,那麼將登錄market,並直接指向當前程序頁面。

*******************************************************************************************
向上言:
     本人在論壇 曾經發過一關於此問題的求助 帖,雖然大至的思路和上文差不多,關鍵點是在於程序如何更新,現在看到它這裏指出的更新方法居然是登錄market。不過以後發佈的程序都是在market中,問題就不存在。

  1.                             Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:your.app.id"));
  2.                             startActivity(intent);
複製代碼

大家都是在eclipse上開發 吧,在每次更新代碼,運行 模擬器 時,大家是否有注意到console的提示信息:

  1. [2009-06-06 19:53:50 - Hello] Android Launch!
  2. [2009-06-06 19:53:50 - Hello] adb is running normally.
  3. [2009-06-06 19:53:50 - Hello] Performing linhai.com.hello.hello activity launch
  4. [2009-06-06 19:53:50 - Hello] Automatic Target Mode: using existing emulator 'emulator-5554' running compatible AVD 'avd'
  5. [2009-06-06 19:53:50 - Hello] WARNING: Application does not specify an API level requirement!
  6. [2009-06-06 19:53:50 - Hello] Device API version is 3 (Android 1.5)
  7. [2009-06-06 19:53:50 - Hello] Uploading Hello.apk onto device 'emulator-5554'
  8. [2009-06-06 19:53:50 - Hello] Installing Hello.apk...
  9. [2009-06-06 19:54:05 - Hello] Application already exists. Attempting to re-install instead...
  10. [2009-06-06 19:54:31 - Hello] Success!
複製代碼

分析:
1。android 正常運行
2。通過配置文件AndroidManifest.xml中運行我們的程序
3。Uploading Hello.apk onto device 'emulator-5554' 這句是關鍵,更新我們的程序
4。Installing Hello.apk... 
5。Application already exists. Attempting to re-install instead... //程序已經存在,嘗試重新安裝

所以如果我們的程序要自動更新,本人初步猜想是和上面的步驟是一樣的。
詳看logcat中的log

  1. 06-06 11:54:02.567: DEBUG/PackageParser(582): Scanning package: /data/app/vmdl12464.tmp
  2. 06-06 11:54:08.048: INFO/PackageManager(582): Removing non-system package:linhai.com.hello
  3. 06-06 11:54:08.187: DEBUG/PackageManager(582): Removing package linhai.com.hello
  4. 06-06 11:54:08.286: DEBUG/PackageManager(582):   Activities: linhai.com.hello.hello
  5. 06-06 11:54:11.136: DEBUG/PackageManager(582): Scanning package linhai.com.hello
  6. 06-06 11:54:11.301: INFO/PackageManager(582): /data/app/vmdl12464.tmp changed; unpacking
  7. 06-06 11:54:11.626: DEBUG/installd(555): DexInv: --- BEGIN '/data/app/vmdl12464.tmp' ---
  8. 06-06 11:54:12.987: DEBUG/dalvikvm(7756): DexOpt: load 224ms, verify 265ms, opt 1ms
  9. 06-06 11:54:13.047: DEBUG/installd(555): DexInv: --- END '/data/app/vmdl12464.tmp' (success) ---
  10. 06-06 11:54:13.057: DEBUG/PackageManager(582):   Activities: linhai.com.hello.hello
  11. 06-06 11:54:15.608: INFO/installd(555): move /data/dalvik-cache/data@[email protected]@classes.dex -> /data/dalvik-cache/data@[email protected]@classes.dex
  12. 06-06 11:54:15.737: DEBUG/PackageManager(582): New package installed in /data/app/linhai.com.hello.apk
複製代碼

關於此類的自動更新的第三方管理 軟件已經有了叫aTrackDog ,其原理就是使用上面的方式。
關於得到版本號,使用:

  1. int curVersion = getPackageManager().getPackageInfo("your.app.id", 0).versionCode;
複製代碼

程序版本號的是放在AndroidManifest.xml文件中:

  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2.         package="linhai.com.hello" android:versionCode="2" android:versionName="2.0.1">
複製代碼

主點是關於:getPackageManager()在這個下面有很多方法,你可以通過它得,得到當前終端安裝的程序等。關於安裝包的函數是:getPackageManager().installPackage(packageURI)

動手試驗:
在dos狀態下運行:
1.JPG


查看logcat下的信息,大致和剛纔相同,分析流程:

  1. 06-06 12:18:58.827: INFO/jdwp(8368): received file descriptor 20 from ADB
  2. 06-06 12:19:02.546: DEBUG/PackageParser(582): Scanning package: /data/app/vmdl12465.tmp
  3. 06-06 12:19:07.738: INFO/PackageManager(582): /data/app/vmdl12465.tmp changed; unpacking
  4. 06-06 12:19:07.978: DEBUG/installd(555): DexInv: --- BEGIN '/data/app/vmdl12465.tmp' ---
  5. 06-06 12:19:09.617: DEBUG/dalvikvm(8378): DexOpt: load 254ms, verify 564ms, opt 3ms
  6. 06-06 12:19:09.697: DEBUG/installd(555): DexInv: --- END '/data/app/vmdl12465.tmp' (success) ---
  7. 06-06 12:19:11.907: INFO/installd(555): move /data/dalvik-cache/data@[email protected]@classes.dex -> /data/dalvik-cache/data@[email protected]@classes.dex
  8. 06-06 12:19:11.956: DEBUG/PackageManager(582): New package installed in /data/app/com.example.android.snake.apk
  9. 06-06 12:19:14.746: DEBUG/dalvikvm(8368): VM cleaning up
  10. 06-06 12:19:14.857: DEBUG/dalvikvm(8368): LinearAlloc 0x0 used 628420 of 4194304 (14%)
  11. 06-06 12:19:15.897: DEBUG/dalvikvm(582): GC freed 17704 objects / 903984 bytes in 615ms
  12. 06-06 12:19:15.936: DEBUG/HomeLoaders(625): application intent received: android.intent.action.PACKAGE_ADDED, replacing=false
  13. 06-06 12:19:15.936: DEBUG/HomeLoaders(625):   --> package:com.example.android.snake
  14. 06-06 12:19:15.936: DEBUG/HomeLoaders(625):   --> add package
複製代碼

1。接收數據,保存到臨時文件中/data/app/vmdl12465.tmp
2。解壓此文件,注意路徑/data/dalvik-cache/data@[email protected] @classes.dex
它是在data下的dalvik-cache下
3.安裝文件[這個步驟還包括查找程序是否已經安裝等]
4.使用GC清理內存

查看DDMS中的結構
2.JPG


看到此文件結構,應該可以想起linux下的文件系統 和它的權限管理,也就可以理解,爲什麼我們的程序無法在data下創建文件之類的問題了。

關於aTrackDog程序相關信息:http://atrackdog.a0soft.com/intro.php

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章