Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 442|回复: 0

开发交流:为程序添加版本自动更新功能(转+详细分析)

[复制链接]

该用户从未签到

发表于 2011-10-24 09:51:11 | 显示全部楼层 |阅读模式
为程序添加版本自动更新功能(转+详细分析)
http://www.eoeandroid.com/thread-627-1-3.html


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.append((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中,问题就不存在。

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

大家都是在eclipse上开发吧,在每次更新代码,运行模拟器时,大家是否有注意到console的提示信息:

[2009-06-06 19:53:50 - Hello] Android Launch!
[2009-06-06 19:53:50 - Hello] adb is running normally.
[2009-06-06 19:53:50 - Hello] Performing linhai.com.hello.hello activity launch
[2009-06-06 19:53:50 - Hello] Automatic Target Mode: using existing emulator 'emulator-5554' running compatible AVD 'avd'
[2009-06-06 19:53:50 - Hello] WARNING: Application does not specify an API level requirement!
[2009-06-06 19:53:50 - Hello] Device API version is 3 (Android 1.5)
[2009-06-06 19:53:50 - Hello] Uploading Hello.apk onto device 'emulator-5554'
[2009-06-06 19:53:50 - Hello] Installing Hello.apk...
[2009-06-06 19:54:05 - Hello] Application already exists. Attempting to re-install instead...
[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


06-06 11:54:02.567: DEBUG/PackageParser(582): Scanning package: /data/app/vmdl12464.tmp
06-06 11:54:08.048: INFO/PackageManager(582): Removing non-system package:linhai.com.hello
06-06 11:54:08.187: DEBUG/PackageManager(582): Removing package linhai.com.hello
06-06 11:54:08.286: DEBUG/PackageManager(582):   Activities: linhai.com.hello.hello
06-06 11:54:11.136: DEBUG/PackageManager(582): Scanning package linhai.com.hello
06-06 11:54:11.301: INFO/PackageManager(582): /data/app/vmdl12464.tmp changed; unpacking
06-06 11:54:11.626: DEBUG/installd(555): DexInv: --- BEGIN '/data/app/vmdl12464.tmp' ---
06-06 11:54:12.987: DEBUG/dalvikvm(7756): DexOpt: load 224ms, verify 265ms, opt 1ms
06-06 11:54:13.047: DEBUG/installd(555): DexInv: --- END '/data/app/vmdl12464.tmp' (success) ---
06-06 11:54:13.057: DEBUG/PackageManager(582):   Activities: linhai.com.hello.hello
06-06 11:54:15.608: INFO/installd(555): move /data/dalvik-cache/data@app@vmdl12464.tmp@classes.dex -> /data/dalvik-cache/data@app@linhai.com.hello.apk@classes.dex
06-06 11:54:15.737: DEBUG/PackageManager(582): New package installed in /data/app/linhai.com.hello.apk


关于此类的自动更新的第三方管理软件已经有了叫aTrackDog,其原理就是使用上面的方式。
关于得到版本号,使用:

int curVersion = getPackageManager().getPackageInfo("your.app.id", 0).versionCode;


程序版本号的是放在AndroidManifest.xml文件中:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="linhai.com.hello" android:versionCode="2" android:versionName="2.0.1">

主点是关于:getPackageManager()在这个下面有很多方法,你可以通过它得,得到当前终端安装的程序等。关于安装包的函数是:getPackageManager().installPackage(packageURI)

动手试验:
在dos状态下运行:




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


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


接收数据,保存到临时文件中/data/app/vmdl12465.tmp
2。解压此文件,注意路径/data/dalvik-cache/data@app@vmdl12465.tmp@classes.dex
它是在data下的dalvik-cache下
3.安装文件[这个步骤还包括查找程序是否已经安装等]
4.使用GC清理内存

查看DDMS中的结构




看到此文件结构,应该可以想起linux下的文件系统和它的权限管理,也就可以理解,为什么我们的程序无法在data下创建文件之类的问题了。
转载:http://www.androidres.com/?p=349



关于aTrackDog程序相关信息:http://atrackdog.a0soft.com/intro.php



才发现这篇文章,仔细看了几遍,很棒的文章,大赞!
但是其中有个问题我觉得可以拿出来讨论下,就是获取指定软件的新版本的方法,我看到代码里使用的是:
URL updateURL = new URL("http://my.company.com/update");
这个应该是连到自己的服务器接口获取该应用的最新版本,再和本地已经安装版本号做对比,从而得到是否有新版本!

如此的话,存在一个很大的缺陷,依赖与您的服务器接口信息!

而如果能直接从market上获取该应用的版本就可以很好的解决了,但是你代码里也提到了:
Uri.parse("market://search?q=pname:your.app.id"));

market原来支持按照app的id直接获取应用的,但是现在只能通过search来搜索到结果。这样的话就行不通了。

而aTrackDog的模式相当于自己维护了一套应用的版本库,其有指定的接口可以获取对应的“理论上较新”的版本。
回复

使用道具 举报

该用户从未签到

发表于 2011-10-24 09:51:13 | 显示全部楼层

Re:开发交

学习学习一下~
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

GMT+8, 2025-1-10 22:49 , Processed in 0.452252 second(s), 47 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表