http://blog.csdn.net/mu0206mu/article/details/7204746

Android应用程序的升级(自身升级)

一、引言:

很多的Android应用都具有版本检测和自动更新的功能,用户一键就可以完成软件的升级和更新。Android应用程序的升级本质上是利用了Linux系统的软件包管理和安装机制,而对于上层这一功能的开发来说很容易,只需要我们开发人员利用Android自带的API就可以实现。

二、功能说明:

1、本示例用来实现单个应用程序的自身升级

2、程序启动时,连接tomcat7 web服务器进行版本的检测,若有新版本则提示更新

3、将从web服务器下载的新版本的APK文件放到sdcard中

4、监听新版本的APK应用是否安装完成,如果是,则将下载的apk文件从sdcard中删除

三、程序框架流程:


四、环境说明:

1、 服务器端:Ubuntu下的tomcat7web服务器,安装后默认端口是8080,Android模拟器访问时要将apk文件放到 /var/lib/tomcat7/webapps/ROOT/目录下,Android模拟器的访问方式是http://10.0.2.2/NewAppSample.apk

2、 Android模拟器端的开发环境:

Ubuntu+eclipse+ADT

五、流程详解及关键点说明:

(一)新版本的应用程序(NewAppSample)准备:

a)新建一个android工程,编辑其版本代码为2,高于我们的旧版本用于更新测试,版本名称为1.0.1

b)编辑应用程序对应的版本信息文件version.json

说明:后缀为json的文件是一种轻量级的数据交换格式,比xml要快很多,适合于小型数据的网络交换,其实质类似键值对,键用字符串的形式表示与其值用冒号隔开,能存储多种数据类型。

(二) 旧版本的应用程序准备:

1、在其AndroidManifest.xml中定义版本代码为versionCode=”1”让其自动生成即可,我们主要利用程序的版本代码的高低来判断是否有新的版本,用于更新。

2、我们在应用程序启动时自动联网检测是否有新的版本,即在onCreate()函数中进行联网检测。

a)从服务器获得读取版本信息文件version.json,我们单独写了一个类来实现,用其GetUpdateInfo静态方法来返回读取的version.json,返回形式是字符串。代码如下

b)获得当前旧的应用程序版本信息,我们单独封装了一个类CurrentVersion,用其中的静态方法来获得当前应用的版本信息,包括程序的名称版本,代码版本,和应用程序名字。

代码如下:

c)将从服务器version.json获得的字符串解析出我们需要的版本信息

d)进行代码版本的比较,提示是否更新当前的应用。

(三) 显示更新提示框


(四) 下载新的APK文件


下载完成时要将进度条对话框取消并进行是否安装新应用的提示

(五) 安装新的应用:


Intent的setDataAndType的一个参数是应用程序的绝对路径(在sdcard中),第二个参数是文件对应的MIME类型,android系统中的APK文件默认为application/vnd.android.package-archive,该文件的MIME类型在tomcat服务器中的/var/lib/tomcat7/conf文件中有对应。

(六) 网络检测代码和sdcard中APK文件的删除

关键说明:若不用广播接收的方式,直接在安装后的代码中实现删除下载的APK文件的话,会出现还没安装完成就把APK文件删除了的情况。在进入安装新的APK文件时会进入系统的提示进行一步一步的安装操作,所以我们无法判断应用程序什么时候完全安装完成。我们用监听(应用程序安装或替换的)广播的方式来实现,当接受到应用程序有ADDED或则REPLACED的广播时我们再执行APK文件的删除操作。

六、 Demo效果图例:

1.提示更新

2.下载新版本的应用

3.提示是否安装

4.进入系统安装提示

5.正在安装

6.安装完成

7.打开新版本的应用

七、完成过程中出现的问题以及关键点说明:

1.Android模拟器连接tomcat7服务器下载时访问地址IP不能用localhost,因为android模拟器把localhost当成自己了,应该用10.0.2.2测试

2.下载的APK文件和版本信息的json文件应该放在/var/lib/tomcat7/webapps/ROOT/目录下不然无法访问到。

3.JSON文件的解析方式参考JSON附文理解。

4.示例中涉及到的权限:

a)与sdcard相关的权限:示例中我们需要在sdcard中创建和删除文件的权限和sdcard的读写权限。

b)与网络相关的权限:示例中我们需要访问网络的权限和获得网络状态的权限(测试网络是否可用),示例中我们只测试了网络是否可用,我们还可以添加网络是否已经连接的进一步判断。

5.监听应用程序是否安装完成

在工程的Manifest.xml文件中添加要接受的广播action,这里我们监听应用程序本身的替换和系统中应用程序的添加两个action,应用程序的替换监听好像只能监听自身被替换,这一点待考察。

源码下载地址:本篇源码下载

八、 JSON附文:

JSON的定义

一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性。业内主流技术为其提供了完整的解决方案(有点类似于正则表达式,获得了当今大部分语言的支持),从而可以在不同平台间进行数据交换。JSON采用兼容性很高的文本格式,同时也具备类似于C语言体系的行为。

为什么用JSON?

很简单,因为它比xml快十倍。

有哪些应用案例?

Twitter、豆瓣、facebook等公司的开放api,一般这些服务都会提供多种格式供开发人员选择(xml、json、atom等),而在手机终端上,我们自然希望给用户最佳体验,所以我选用最有效率的json格式。

JSON的结构:

Name/ValuePairs类似所熟知的Keyedlist、Hash table、Disctionary和Associative array。在Android平台中同时存在另外一个类“Bundle”,某种程度上具有相似的行为。

org.json.JSONObjectArray,一组有序的数据列表。

Android中JSON相关的类(4个)和Exceptions(1个):

lJSONArray

lJSONObject

lJSONStringer

lJSONTokener

lJSONException

JSONObject:

这是系统中有关JSON定义的基本单元,其包含一对儿(Key/Value)数值。它对外部(External:应用toString()方法输出的数值)调用的响应体现为一个标准的字符串(例如:{"JSON": "Hello, World"},最外被大括号包裹,其中的Key和Value被冒号":"分隔)。其对于内部(Internal)行为的操作格式略微,例如:初始化一个JSONObject实例,引用内部的put()方法添加数值:newJSONObject().put("JSON", "Hello, World!"),在Key和Value之间是以逗号","分隔。

Value的类型包括:Boolean、JSONArray、JSONObject、Number、String或者默认值JSONObject.NULL object。

有两个不同的取值方法:

get():在确定数值存在的条件下使用,否则当无法检索到相关Key时,将会抛出一个Exception信息。

opt():这个方法相对比较灵活,当无法获取所指定数值时,将会返回一个默认数值,并不会抛出异常。

JSONArray:

它代表一组有序的数值。将其转换为String输出(toString)所表现的形式是用方括号包裹,数值以逗号”,”分隔(例如:[value1,value2,value3],大家可以亲自利用简短的代码更加直观的了解其格式)。这个类的内部同样具有查询行为,get()和opt()两种方法都可以通过index索引返回指定的数值,put()方法用来添加或者替换数值。

同样这个类的value类型可以包括:Boolean、JSONArray、JSONObject、Number、String或者默认值JSONObject.NULL object。

JSONStringer:

根据官方的解释,这个类可以帮助快速和便捷的创建JSON text。其最大的优点在于可以减少由于格式的错误导致程序异常,引用这个类可以自动严格按照JSON语法规则(syntaxrules)创建JSON text。每个JSONStringer实体只能对应创建一个JSON text。

根据下边的实例来了解其它相关信息:

string myString=newJSONStringer().object()

.key("AR").value("www.Androidres.com!")

.endObject()

.toString();

结果是一组标准格式的JSON text:{”AR”:”www.Androidres.com!”}

其中的.object()和.endObject()必须同时使用,是为了按照Object标准给数值添加边界。同样,针对数组也有一组标准的方法来生成边界.array()和.endArray()。

JSONTokener:

这个是系统为JSONObject和JSONArray构造器解析JSON source string的类,它可以从source string中提取数值信息。

JSONException:

是JSON.org类抛出的异常信息。

如果某个app有内嵌的sqlite数据库,则可以在应用程序app前增加一个专门用于升级的应用update app。在升级时先使用update app,如果有新版本的话可以去服务端下载最新的app,如果没有新版本的话则直接调用本地的app。

Update app的大致思路是这样的:

Java代码
public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        mDB = new MapVersionTable(this);                if (checkNewVersion()) {                if (apkUrl == null)         return;                downloadAPK(apkUrl);        killProcess();        installAPK();        finish();        } else {        if (checkApp()) {        invokeAPK();                finish();        }else {        downloadAPK(apkUrl);        installAPK();            finish();        }        }    }


其中MapVersionTable是用于update app记录应用程序版本的。

checkNewVersion()用于检查是否有新版本的存在,并将服务端的版本号存入mapVersion变量,将服务端的应用地址存放在apkUrl变量。这段检查应用程序的方法,其实是利用rest方式进行访问,当然也可以用web service或者其他通讯方式。

Java代码
private boolean checkNewVersion() {try {URL url=new URL(AppConfig.REST_URL);SAXParserFactory factory=SAXParserFactory.newInstance();factory.setNamespaceAware(true);factory.setValidating(false);    SAXParser parser=factory.newSAXParser();    InputStream is = url.openStream();    parser.parse(is, new DefaultHandler(){private String cur="";private int step;@Overridepublic void startDocument() throws SAXException {step = 0;}@Overridepublic void startElement(String uri, String localName,String qName, Attributes attributes)throws SAXException {cur = localName;}@Overridepublic void characters(char[] ch, int start, int length)throws SAXException {String str = new String(ch, start, length).trim();if (str == null || str.equals(""))return;if (cur.equals("url")) {apkUrl = str;}if (cur.equals("map_version")) {mapVersion = str;}}@Overridepublic void endElement(String uri, String localName,String qName) throws SAXException {step = step + 1;}@Overridepublic void endDocument() throws SAXException {super.endDocument();}});} catch (MalformedURLException e) {e.printStackTrace();} catch (ParserConfigurationException e) {e.printStackTrace();} catch (SAXException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}if (diffVersion(mapVersion)) return true;elsereturn false;}



diffVersion()是将服务端版本号和本地版本号进行比较,如果存在新版本,则将最新的版本号存入数据库并调用downloadAPK();如果不存在新版本,则会调用本地的app。不过在调用本地app前需要先判断本地的app是否安装,这个时候使用checkApp()方法。

Java代码
private boolean diffVersion(String mapVersion) {String lastVersion = mDB.getLastMapVersion();if (lastVersion == null) {mDB.setMapVersion(mapVersion);return true;}if (!lastVersion.equals(mapVersion)) {mDB.setMapVersion(mapVersion);return true;}elsereturn false;}



checkApp()该方法用于检查本地是否安装该app

Java代码
private boolean checkApp() {Intent intent = new Intent(Intent.ACTION_VIEW);  intent.setClassName("com.android.settings",         "com.android.settings.InstalledAppDetails"); intent.putExtra("com.android.settings.ApplicationPkgName",  AppConfig.APKNAME);  List<ResolveInfo> acts = getPackageManager().queryIntentActivities(          intent, 0);  if (acts.size() > 0) {  return true;} elsereturn false;}




killProcess()是杀掉进程,防止升级时该应用还在使用。

Java代码
private void killProcess() {activityMan = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);process = activityMan.getRunningAppProcesses();int len = process.size();for(int i = 0;i<len;i++) {if (process.get(i).processName.equals(AppConfig.PKG)) {android.os.Process.killProcess(process.get(i).pid);}}}



installAPK()将下载的app进行安装

Java代码
private void installAPK() {String fileName = getSDPath() +"/download/"+AppConfig.APKNAME;Intent intent = new Intent(Intent.ACTION_VIEW);intent.setDataAndType(Uri.fromFile(new File(fileName)), "application/vnd.android.package-archive");startActivity(intent);}



invokeAPK()根据包名,调用本地的应用。

Java代码
private void invokeAPK() {Intent i=new Intent();i.setComponent(new ComponentName(AppConfig.PKG, AppConfig.CLS));startActivity(i);}



最后,不要忘记关闭数据库 呵呵

Java代码
protected void onDestroy() {super.onDestroy();try {    mDB.close(); // be sure to close} catch (Exception e) {}}

更多相关文章

  1. Ubuntu 14.04 LTS 安装 文泉驿微米黑 字体到android studio
  2. Android应用程序消息处理机制(Looper、Handler)分析
  3. TextView文字阴影效果以及styles文件的使用
  4. 用Lazarus编译Android工程
  5. [Android]混淆代码后生成带签名的apk
  6. Android(安卓)应用程序开发基础
  7. android:sharedUserId问题
  8. Android开发5――文件读写
  9. NPM 和webpack 的基础使用

随机推荐

  1. Android学习进阶和IoC
  2. Android中自定义控件和属性
  3. Android Launcher 分析
  4. Android之RelativeLayout属性
  5. Android 相对布局 RelativeLayout 属性
  6. 创建和使用Android(安卓)library工程
  7. android修改手机虚拟设备管理器路径
  8. android studio 使用adb命令传递文件到an
  9. Android与js交互实例
  10. Unity与Android的互相调用