下面介绍的是apk如何进行版本的检测及下载更新!
最终效果:
更新前:
更新提示:
下载后提示安装:
安装更新后:
由于版本的更新及下载都是通过网络从服务端上获取的,所以需要一个服务端。
新建一个服务端 updateApkServer,在这里该服务端没有特殊用途,只用来提供版本信息而已。其项目结构图:
所有的版本信息记录在 version.xml
- <?xmlversion="1.0"encoding="UTF-8"?>
- <update>
- <version>2.0_20120530</version>
- <versionCode>2</versionCode>
- <updateTime>2012-05-30</updateTime>
- <apkName>安卓_02_20120530.apk</apkName>
- <downloadURL>http://localhost:8080/updateApkServer/sems.apk</downloadURL>
- <displayMessage>新版本发布,赶紧下载吧##1.新增A功能##2.新增B功能##3.新增C功能</displayMessage>
- </update>
服务端上有个updateApkDemo2.apk,其实就是把下面的客户端改了下内容和名称把它扔了进来,只是为了个demo下载演示而已。
OK,服务端就这样了。
下面是android端。
客户端项目结构图:
UpdateApkDemoActivity.java
- packagecom.royal.updateApk;
- importandroid.app.Activity;
- importandroid.os.Bundle;
- /**
- *更新视图界面类
- *
- *@authorRoyal
- *
- */
- publicclassUpdateApkDemoActivityextendsActivity{
- /**Calledwhentheactivityisfirstcreated.*/
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- //版本更新检查
- UpdateManagerum=newUpdateManager(UpdateApkDemoActivity.this);
- um.checkUpdate();
- }
- }
一个比较重要的版本更新核心服务类,靠他了。
UpdateManager.java
- packagecom.royal.updateApk;
- importjava.io.File;
- importjava.io.FileOutputStream;
- importjava.io.IOException;
- importjava.io.InputStream;
- importjava.net.HttpURLConnection;
- importjava.net.MalformedURLException;
- importjava.net.URL;
- importandroid.app.AlertDialog.Builder;
- importandroid.content.Context;
- importandroid.content.DialogInterface;
- importandroid.content.DialogInterface.OnClickListener;
- importandroid.content.Intent;
- importandroid.content.pm.PackageInfo;
- importandroid.content.pm.PackageManager;
- importandroid.content.pm.PackageManager.NameNotFoundException;
- importandroid.net.Uri;
- importandroid.os.Environment;
- importandroid.os.Handler;
- importandroid.os.Message;
- importandroid.view.LayoutInflater;
- importandroid.view.View;
- importandroid.widget.ProgressBar;
- importcom.royal.model.VersionInfo;
- importcom.royal.util.XMLParserUtil;
- /**
- *APK更新管理类
- *
- *@authorRoyal
- *
- */
- publicclassUpdateManager{
- //上下文对象
- privateContextmContext;
- //更新版本信息对象
- privateVersionInfoinfo=null;
- //下载进度条
- privateProgressBarprogressBar;
- //是否终止下载
- privatebooleanisInterceptDownload=false;
- //进度条显示数值
- privateintprogress=0;
- /**
- *参数为Context(上下文activity)的构造函数
- *
- *@paramcontext
- */
- publicUpdateManager(Contextcontext){
- this.mContext=context;
- }
- publicvoidcheckUpdate(){
- //从服务端获取版本信息
- info=getVersionInfoFromServer();
- if(info!=null){
- try{
- //获取当前软件包信息
- PackageInfopi=mContext.getPackageManager().getPackageInfo(mContext.getPackageName(),PackageManager.GET_CONFIGURATIONS);
- //当前软件版本号
- intversionCode=pi.versionCode;
- if(versionCode<info.getVersionCode()){
- //如果当前版本号小于服务端版本号,则弹出提示更新对话框
- showUpdateDialog();
- }
- }catch(NameNotFoundExceptione){
- e.printStackTrace();
- }
- }
- }
- /**
- *从服务端获取版本信息
- *
- *@return
- */
- privateVersionInfogetVersionInfoFromServer(){
- VersionInfoinfo=null;
- URLurl=null;
- try{
- //10.0.2.2相当于localhost
- url=newURL("http://10.0.2.2:8080/updateApkServer/version.xml");
- }catch(MalformedURLExceptione){
- e.printStackTrace();
- }
- if(url!=null){
- try{
- //使用HttpURLConnection打开连接
- HttpURLConnectionurlConn=(HttpURLConnection)url.openConnection();
- //读取服务端version.xml的内容(流)
- info=XMLParserUtil.getUpdateInfo(urlConn.getInputStream());
- urlConn.disconnect();
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- returninfo;
- }
- /**
- *提示更新对话框
- *
- *@paraminfo
- *版本信息对象
- */
- privatevoidshowUpdateDialog(){
- Builderbuilder=newBuilder(mContext);
- builder.setTitle("版本更新");
- builder.setMessage(info.getDisplayMessage());
- builder.setPositiveButton("下载",newOnClickListener(){
- @Override
- publicvoidonClick(DialogInterfacedialog,intwhich){
- dialog.dismiss();
- //弹出下载框
- showDownloadDialog();
- }
- });
- builder.setNegativeButton("以后再说",newOnClickListener(){
- @Override
- publicvoidonClick(DialogInterfacedialog,intwhich){
- dialog.dismiss();
- }
- });
- builder.create().show();
- }
- /**
- *弹出下载框
- */
- privatevoidshowDownloadDialog(){
- Builderbuilder=newBuilder(mContext);
- builder.setTitle("版本更新中...");
- finalLayoutInflaterinflater=LayoutInflater.from(mContext);
- Viewv=inflater.inflate(R.layout.update_progress,null);
- progressBar=(ProgressBar)v.findViewById(R.id.pb_update_progress);
- builder.setView(v);
- builder.setNegativeButton("取消",newOnClickListener(){
- publicvoidonClick(DialogInterfacedialog,intwhich){
- dialog.dismiss();
- //终止下载
- isInterceptDownload=true;
- }
- });
- builder.create().show();
- //下载apk
- downloadApk();
- }
- /**
- *下载apk
- */
- privatevoiddownloadApk(){
- //开启另一线程下载
- ThreaddownLoadThread=newThread(downApkRunnable);
- downLoadThread.start();
- }
- /**
- *从服务器下载新版apk的线程
- */
- privateRunnabledownApkRunnable=newRunnable(){
- @Override
- publicvoidrun(){
- if(!android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)){
- //如果没有SD卡
- Builderbuilder=newBuilder(mContext);
- builder.setTitle("提示");
- builder.setMessage("当前设备无SD卡,数据无法下载");
- builder.setPositiveButton("确定",newOnClickListener(){
- @Override
- publicvoidonClick(DialogInterfacedialog,intwhich){
- dialog.dismiss();
- }
- });
- builder.show();
- return;
- }else{
- try{
- //服务器上新版apk地址
- URLurl=newURL("http://10.0.2.2:8080/updateApkServer/updateApkDemo2.apk");
- HttpURLConnectionconn=(HttpURLConnection)url.openConnection();
- conn.connect();
- intlength=conn.getContentLength();
- InputStreamis=conn.getInputStream();
- Filefile=newFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/updateApkFile/");
- if(!file.exists()){
- //如果文件夹不存在,则创建
- file.mkdir();
- }
- //下载服务器中新版本软件(写文件)
- StringapkFile=Environment.getExternalStorageDirectory().getAbsolutePath()+"/updateApkFile/"+info.getApkName();
- FileApkFile=newFile(apkFile);
- FileOutputStreamfos=newFileOutputStream(ApkFile);
- intcount=0;
- bytebuf[]=newbyte[1024];
- do{
- intnumRead=is.read(buf);
- count+=numRead;
- //更新进度条
- progress=(int)(((float)count/length)*100);
- handler.sendEmptyMessage(1);
- if(numRead<=0){
- //下载完成通知安装
- handler.sendEmptyMessage(0);
- break;
- }
- fos.write(buf,0,numRead);
- //当点击取消时,则停止下载
- }while(!isInterceptDownload);
- }catch(MalformedURLExceptione){
- e.printStackTrace();
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- }
- };
- /**
- *声明一个handler来跟进进度条
- */
- privateHandlerhandler=newHandler(){
- publicvoidhandleMessage(Messagemsg){
- switch(msg.what){
- case1:
- //更新进度情况
- progressBar.setProgress(progress);
- break;
- case0:
- progressBar.setVisibility(View.INVISIBLE);
- //安装apk文件
- installApk();
- break;
- default:
- break;
- }
- };
- };
- /**
- *安装apk
- */
- privatevoidinstallApk(){
- //获取当前sdcard存储路径
- Fileapkfile=newFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/updateApkFile/"+info.getApkName());
- if(!apkfile.exists()){
- return;
- }
- Intenti=newIntent(Intent.ACTION_VIEW);
- //安装,如果签名不一致,可能出现程序未安装提示
- i.setDataAndType(Uri.fromFile(newFile(apkfile.getAbsolutePath())),"application/vnd.android.package-archive");
- mContext.startActivity(i);
- }
- }
update_prgress.xml 用于显示下载时的进度条
- <?xmlversion="1.0"encoding="utf-8"?>
- <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- <ProgressBar
- android:id="@+id/pb_update_progress"
- style="?android:attr/progressBarStyleHorizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"/>
- </LinearLayout>
另外附上2个类,pojo、util
VersionInfo.java
- packagecom.royal.model;
- /**
- *软件版本信息对象
- *
- *@authorRoyal
- *
- */
- publicclassVersionInfo{
- //版本描述字符串
- privateStringversion;
- //版本更新时间
- privateStringupdateTime;
- //新版本更新下载地址
- privateStringdownloadURL;
- //更新描述信息
- privateStringdisplayMessage;
- //版本号
- privateintversionCode;
- //apk名称
- privateStringapkName;
- publicStringgetVersion(){
- returnversion;
- }
- publicvoidsetVersion(Stringversion){
- this.version=version;
- }
- publicStringgetUpdateTime(){
- returnupdateTime;
- }
- publicvoidsetUpdateTime(StringupdateTime){
- this.updateTime=updateTime;
- }
- publicStringgetDownloadURL(){
- returndownloadURL;
- }
- publicvoidsetDownloadURL(StringdownloadURL){
- this.downloadURL=downloadURL;
- }
- publicStringgetDisplayMessage(){
- returndisplayMessage;
- }
- publicvoidsetDisplayMessage(StringdisplayMessage){
- this.displayMessage=displayMessage;
- }
- publicintgetVersionCode(){
- returnversionCode;
- }
- publicvoidsetVersionCode(intversionCode){
- this.versionCode=versionCode;
- }
- publicStringgetApkName(){
- returnapkName;
- }
- publicvoidsetApkName(StringapkName){
- this.apkName=apkName;
- }
- }
XMLParserUtil.java 就是用来解析服务端 version.xml 用的
- packagecom.royal.util;
- importjava.io.IOException;
- importjava.io.InputStream;
- importorg.xmlpull.v1.XmlPullParser;
- importorg.xmlpull.v1.XmlPullParserException;
- importorg.xmlpull.v1.XmlPullParserFactory;
- importcom.royal.model.VersionInfo;
- /**
- *XML文档解析工具类
- *
- *@authorRoyal
- *
- */
- publicclassXMLParserUtil{
- /**
- *获取版本更新信息
- *
- *@paramis
- *读取连接服务version.xml文档的输入流
- *@return
- */
- publicstaticVersionInfogetUpdateInfo(InputStreamis){
- VersionInfoinfo=newVersionInfo();
- try{
- XmlPullParserFactoryfactory=XmlPullParserFactory.newInstance();
- factory.setNamespaceAware(true);
- XmlPullParserparser=factory.newPullParser();
- parser.setInput(is,"UTF-8");
- inteventType=parser.getEventType();
- while(eventType!=XmlPullParser.END_DOCUMENT){
- switch(eventType){
- caseXmlPullParser.START_TAG:
- if("version".equals(parser.getName())){
- info.setVersion(parser.nextText());
- }elseif("updateTime".equals(parser.getName())){
- info.setUpdateTime(parser.nextText());
- }elseif("updateTime".equals(parser.getName())){
- info.setUpdateTime(parser.nextText());
- }elseif("downloadURL".equals(parser.getName())){
- info.setDownloadURL(parser.nextText());
- }elseif("displayMessage".equals(parser.getName())){
- info.setDisplayMessage(parseTxtFormat(parser.nextText(),"##"));
- }elseif("apkName".equals(parser.getName())){
- info.setApkName(parser.nextText());
- }elseif("versionCode".equals(parser.getName())){
- info.setVersionCode(Integer.parseInt(parser.nextText()));
- }
- break;
- caseXmlPullParser.END_TAG:
- break;
- }
- eventType=parser.next();
- }
- }catch(XmlPullParserExceptione){
- e.printStackTrace();
- }catch(IOExceptione){
- e.printStackTrace();
- }
- returninfo;
- }
- /**
- *根据指定字符格式化字符串(换行)
- *
- *@paramdata
- *需要格式化的字符串
- *@paramformatChar
- *指定格式化字符
- *@return
- */
- publicstaticStringparseTxtFormat(Stringdata,StringformatChar){
- StringBufferbackData=newStringBuffer();
- String[]txts=data.split(formatChar);
- for(inti=0;i<txts.length;i++){
- backData.append(txts[i]);
- backData.append("\n");
- }
- returnbackData.toString();
- }
- }
最后一个别忘了开启网络权限和SD卡读写权限。
AndroidManifest.xml
- <?xmlversion="1.0"encoding="utf-8"?>
- <manifestxmlns:android="http://schemas.android.com/apk/res/android"
- package="com.royal.updateApk"
- android:versionCode="1"
- android:versionName="1.0">
- <uses-sdkandroid:minSdkVersion="8"/>
- <application
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name">
- <activity
- android:name=".UpdateApkDemoActivity"
- android:label="@string/app_name">
- <intent-filter>
- <actionandroid:name="android.intent.action.MAIN"/>
- <categoryandroid:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- </application>
- <!--开启网络权限-->
- <uses-permissionandroid:name="android.permission.INTERNET"/>
- <!--在SDCard中创建与删除文件权限-->
- <uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
- <!--往SDCard写入数据权限-->
- <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- </manifest>