`
萧_瑟
  • 浏览: 160614 次
社区版块
存档分类
最新评论

android 从服务器下载更新新版本软件 demo

阅读更多

下面介绍的是apk如何进行版本的检测及下载更新!

 

最终效果:

 

更新前:


 

 

更新提示:



 
 下载后提示安装:


 

安装更新后:


 

 

由于版本的更新及下载都是通过网络从服务端上获取的,所以需要一个服务端。

 

新建一个服务端 updateApkServer,在这里该服务端没有特殊用途,只用来提供版本信息而已。其项目结构图:


 

所有的版本信息记录在 version.xml

 

 

<?xml version="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

package com.royal.updateApk;

import android.app.Activity;
import android.os.Bundle;

/**
 * 更新视图界面类
 * 
 * @author Royal
 * 
 */
public class UpdateApkDemoActivity extends Activity {
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// 版本更新检查
		UpdateManager um = new UpdateManager(UpdateApkDemoActivity.this);
		um.checkUpdate();
	}
}

 

 

一个比较重要的版本更新核心服务类,靠他了。

 

UpdateManager.java

package com.royal.updateApk;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ProgressBar;

import com.royal.model.VersionInfo;
import com.royal.util.XMLParserUtil;

/**
 * APK更新管理类
 * 
 * @author Royal
 * 
 */
public class UpdateManager {

	// 上下文对象
	private Context mContext;
	//更新版本信息对象
	private VersionInfo info = null;
	// 下载进度条
	private ProgressBar progressBar;
	// 是否终止下载
	private boolean isInterceptDownload = false;
	//进度条显示数值
	private int progress = 0;

	/**
	 * 参数为Context(上下文activity)的构造函数
	 * 
	 * @param context
	 */
	public UpdateManager(Context context) {
		this.mContext = context;
	}

	public void checkUpdate() {
		// 从服务端获取版本信息
		info = getVersionInfoFromServer();
		if (info != null) {
			try {
				// 获取当前软件包信息
				PackageInfo pi = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), PackageManager.GET_CONFIGURATIONS);
				// 当前软件版本号
				int versionCode = pi.versionCode;
				if (versionCode < info.getVersionCode()) {
					// 如果当前版本号小于服务端版本号,则弹出提示更新对话框
					showUpdateDialog();
				}
			} catch (NameNotFoundException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 从服务端获取版本信息
	 * 
	 * @return
	 */
	private VersionInfo getVersionInfoFromServer() {
		VersionInfo info = null;
		URL url = null;
		try {
			// 10.0.2.2相当于localhost
			url = new URL("http://10.0.2.2:8080/updateApkServer/version.xml");
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		if (url != null) {
			try {
				// 使用HttpURLConnection打开连接
				HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
				// 读取服务端version.xml的内容(流)
				info = XMLParserUtil.getUpdateInfo(urlConn.getInputStream());
				urlConn.disconnect();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return info;
	}

	/**
	 * 提示更新对话框
	 * 
	 * @param info
	 *            版本信息对象
	 */
	private void showUpdateDialog() {
		Builder builder = new Builder(mContext);
		builder.setTitle("版本更新");
		builder.setMessage(info.getDisplayMessage());
		builder.setPositiveButton("下载", new OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				dialog.dismiss();
				// 弹出下载框
				showDownloadDialog();
			}
		});
		builder.setNegativeButton("以后再说", new OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				dialog.dismiss();
			}
		});
		builder.create().show();
	}

	/**
	 * 弹出下载框
	 */
	private void showDownloadDialog() {
		Builder builder = new Builder(mContext);
		builder.setTitle("版本更新中...");
		final LayoutInflater inflater = LayoutInflater.from(mContext);
		View v = inflater.inflate(R.layout.update_progress, null);
		progressBar = (ProgressBar) v.findViewById(R.id.pb_update_progress);
		builder.setView(v);
		builder.setNegativeButton("取消", new OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				dialog.dismiss();
				//终止下载
				isInterceptDownload = true;
			}
		});
		builder.create().show();
		//下载apk
		downloadApk();
	}
	
	/**
	 * 下载apk
	 */
	private void downloadApk(){
		//开启另一线程下载
		Thread downLoadThread = new Thread(downApkRunnable);
		downLoadThread.start();
	}
	
	/**
	 * 从服务器下载新版apk的线程
	 */
	private Runnable downApkRunnable = new Runnable(){
		@Override
		public void run() {
			if (!android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
				//如果没有SD卡
				Builder builder = new Builder(mContext);
				builder.setTitle("提示");
				builder.setMessage("当前设备无SD卡,数据无法下载");
				builder.setPositiveButton("确定", new OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						dialog.dismiss();
					}
				});
				builder.show();
				return;
			}else{
				try {
					//服务器上新版apk地址
					URL url = new URL("http://10.0.2.2:8080/updateApkServer/updateApkDemo2.apk");
					HttpURLConnection conn = (HttpURLConnection)url.openConnection();
					conn.connect();
					int length = conn.getContentLength();
					InputStream is = conn.getInputStream();
					File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/updateApkFile/");
					if(!file.exists()){
						//如果文件夹不存在,则创建
						file.mkdir();
					}
					//下载服务器中新版本软件(写文件)
					String apkFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/updateApkFile/" + info.getApkName();
					File ApkFile = new File(apkFile);
					FileOutputStream fos = new FileOutputStream(ApkFile);
					int count = 0;
					byte buf[] = new byte[1024];
					do{
						int numRead = 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 (MalformedURLException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	};
	
	/**
	 * 声明一个handler来跟进进度条
	 */
	private Handler handler = new Handler() {
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case 1:
				// 更新进度情况
				progressBar.setProgress(progress);
				break;
			case 0:
				progressBar.setVisibility(View.INVISIBLE);
				// 安装apk文件
				installApk();
				break;
			default:
				break;
			}
		};
	};
	
	/**
	 * 安装apk
	 */
	private void installApk() {
		// 获取当前sdcard存储路径
		File apkfile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/updateApkFile/" + info.getApkName());
		if (!apkfile.exists()) {
			return;
		}
		Intent i = new Intent(Intent.ACTION_VIEW);
		// 安装,如果签名不一致,可能出现程序未安装提示
		i.setDataAndType(Uri.fromFile(new File(apkfile.getAbsolutePath())), "application/vnd.android.package-archive"); 
		mContext.startActivity(i);
	}
}

 

update_prgress.xml  用于显示下载时的进度条

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns: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

package com.royal.model;

/**
 * 软件版本信息对象
 * 
 * @author Royal
 * 
 */
public class VersionInfo {

	// 版本描述字符串
	private String version;
	// 版本更新时间
	private String updateTime;
	// 新版本更新下载地址
	private String downloadURL;
	// 更新描述信息
	private String displayMessage;
	// 版本号
	private int versionCode;
	// apk名称
	private String apkName;

	public String getVersion() {
		return version;
	}

	public void setVersion(String version) {
		this.version = version;
	}

	public String getUpdateTime() {
		return updateTime;
	}

	public void setUpdateTime(String updateTime) {
		this.updateTime = updateTime;
	}

	public String getDownloadURL() {
		return downloadURL;
	}

	public void setDownloadURL(String downloadURL) {
		this.downloadURL = downloadURL;
	}

	public String getDisplayMessage() {
		return displayMessage;
	}

	public void setDisplayMessage(String displayMessage) {
		this.displayMessage = displayMessage;
	}

	public int getVersionCode() {
		return versionCode;
	}

	public void setVersionCode(int versionCode) {
		this.versionCode = versionCode;
	}

	public String getApkName() {
		return apkName;
	}

	public void setApkName(String apkName) {
		this.apkName = apkName;
	}

}
 

XMLParserUtil.java  就是用来解析服务端 version.xml 用的

 

package com.royal.util;

import java.io.IOException;
import java.io.InputStream;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import com.royal.model.VersionInfo;

/**
 * XML文档解析工具类
 * 
 * @author Royal
 * 
 */
public class XMLParserUtil {

	/**
	 * 获取版本更新信息
	 * 
	 * @param is
	 *            读取连接服务version.xml文档的输入流
	 * @return
	 */
	public static VersionInfo getUpdateInfo(InputStream is) {
		VersionInfo info = new VersionInfo();
		try {
			XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
			factory.setNamespaceAware(true);
			XmlPullParser parser = factory.newPullParser();
			parser.setInput(is, "UTF-8");
			int eventType = parser.getEventType();
			while (eventType != XmlPullParser.END_DOCUMENT) {
				switch (eventType) {
				case XmlPullParser.START_TAG:
					if ("version".equals(parser.getName())) {
						info.setVersion(parser.nextText());
					} else if ("updateTime".equals(parser.getName())) {
						info.setUpdateTime(parser.nextText());
					} else if ("updateTime".equals(parser.getName())) {
						info.setUpdateTime(parser.nextText());
					} else if ("downloadURL".equals(parser.getName())) {
						info.setDownloadURL(parser.nextText());
					} else if ("displayMessage".equals(parser.getName())) {
						info.setDisplayMessage(parseTxtFormat(parser.nextText(), "##"));
					} else if ("apkName".equals(parser.getName())) {
						info.setApkName(parser.nextText());
					} else if ("versionCode".equals(parser.getName())) {
						info.setVersionCode(Integer.parseInt(parser.nextText()));
					}
					break;
				case XmlPullParser.END_TAG:
					break;
				}
				eventType = parser.next();
			}
		} catch (XmlPullParserException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return info;
	}

	/**
	 * 根据指定字符格式化字符串(换行)
	 * 
	 * @param data
	 *            需要格式化的字符串
	 * @param formatChar
	 *            指定格式化字符
	 * @return
	 */
	public static String parseTxtFormat(String data, String formatChar) {
		StringBuffer backData = new StringBuffer();
		String[] txts = data.split(formatChar);
		for (int i = 0; i < txts.length; i++) {
			backData.append(txts[i]);
			backData.append("\n");
		}
		return backData.toString();
	}

}
 

最后一个别忘了开启网络权限和SD卡读写权限。

 

AndroidManifest.xml

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.royal.updateApk"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".UpdateApkDemoActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    
    <!-- 开启网络权限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 在SDCard中创建与删除文件权限 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <!-- 往SDCard写入数据权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

</manifest>
 

 

 

 

 

 

 

 

 

  • 大小: 14.4 KB
  • 大小: 16.6 KB
  • 大小: 6.8 KB
  • 大小: 19.2 KB
  • 大小: 22.5 KB
  • 大小: 6.3 KB
分享到:
评论
5 楼 abc天残 2015-05-29  
http://www.iteye.com/images/smiles/icon_redface   .gif
4 楼 萧_瑟 2013-02-25  
donnell 写道
有个BUG,XML地址无效时会出错,为什么?

地址无效的话,那么你的apk就下载不下了吧?那么也就无法后续的安装啦,这应该不算BUG吧,我可能没做完美处理;如果地址有效还出错的话,那才是demo的问题。
3 楼 donnell 2013-02-17  
有个BUG,XML地址无效时会出错,为什么?
2 楼 萧_瑟 2012-11-19  
jiajiaguo 写道
在updateAPKServer这个工程里面缺三个包,这三个包应该也是楼主写的,我把这俩工程导入eclipse,结果没法运行,是否能共享下更全面的信息呢?

updateAPKServer只是作为一个服务端,启动服务就好了,不需要什么包,你说的缺三个包是哪三个包?还有就是做demo的时候最好自己新建,然后拷贝代码到自己的项目中,可能有些会因为环境问题跑不通的。我是用maven建的项目
1 楼 jiajiaguo 2012-11-15  
在updateAPKServer这个工程里面缺三个包,这三个包应该也是楼主写的,我把这俩工程导入eclipse,结果没法运行,是否能共享下更全面的信息呢?

相关推荐

    android版本更新 demo

    这个“android版本更新 demo”项目旨在展示如何实现一个有效的版本更新系统,包括检查新版本、下载更新包以及自动升级等功能。以下是对这些关键知识点的详细解释: 1. **版本控制**:在Android应用开发中,版本控制...

    android 版本更新DEMO

    这个"android 版本更新DEMO"旨在为开发者提供一个实用的示例,指导他们如何在自己的应用中实现自动检查和下载新版本的功能。 首先,我们来看看`JavaApk源码说明.txt`。这个文档可能包含了关于DEMO源码的详细解释,...

    服务器下载更新的Demo

    【标题】"服务器下载更新的Demo"涉及到的知识点主要集中在服务器管理和软件更新方面,特别是针对Tomcat这款广泛应用的Java Servlet容器。Tomcat是Apache软件基金会的Jakarta项目下的一个开源项目,它实现了Java ...

    android应用下载安装apk升级版本实现demo适配Android10

    在Android应用开发中,版本升级是一项重要的功能,它能让用户无缝地从旧版本过渡到新版本,提升用户体验。本示例“android应用下载安装apk升级版本实现demo适配Android10”聚焦于如何在Android 10(API级别29)及更...

    版本更新Demo

    3. **更新检测机制**:应用需要定期检查服务器上是否存在新版本。这通常通过HTTP请求或者使用推送通知实现。服务器端则维护一个最新版本信息,供客户端查询。 4. **下载流程**:当检测到新版本时,用户会收到更新...

    android软件更新Demo

    总之,"android软件更新Demo"是一个实用的教程,涵盖了从检查更新到下载安装的整个过程,是理解Android应用自动更新机制的好起点。开发者可以在此基础上根据具体需求进行定制,比如添加断点续传功能、优化网络请求...

    android版本更新Demo案例

    总结起来,Android版本更新Demo案例的核心包括检测新版本、下载更新包、显示下载进度以及自动安装新版本。这个过程中涉及到网络请求、UI交互、文件操作和权限管理等多个方面的知识。通过实践这样的案例,开发者可以...

    android_版本更新_demo

    在Android应用开发中,版本更新是一项重要的功能,...综上所述,"android_版本更新_demo"项目涵盖了从获取版本信息到下载安装的整个更新流程,提供了丰富的学习资源,帮助开发者理解和实现Android应用的自动更新功能。

    android实现软件更新的小demo

    2. **JSON解析**:服务器返回的数据通常是JSON格式,包含新版本号、更新日志、下载链接等。使用如Gson或Jackson库解析JSON,提取所需信息。 3. **权限管理**:在Android 6.0(API 23)及以上版本,需要在运行时动态...

    android提示更新下载安装demo

    本示例"android提示更新下载安装demo"聚焦于如何实现这一过程,通过一个简洁的示例来演示Android应用如何检测新版本,并引导用户进行下载安装。以下是关于这个主题的详细知识点: 1. 版本检查: - `...

    android版本更新 demo.rar

    5. **安装更新**: 下载完成后,应用需要在用户同意的情况下安装新版本。在Android 7.0(API级别24)及更高版本,应用可以使用`ACTION_VIEW` Intent并设置`FLAG_GRANT_WRITE_URI_PERMISSION`来安装外部存储上的APK。...

    Android自动下载更新安装demo

    在Android平台上,自动下载更新安装是一项重要的功能,它允许应用在后台下载最新的版本,并在适当的时候自动或引导用户进行安装,以确保应用始终处于最新状态,提供更好的用户体验和安全性。这个"Android自动下载...

    Android版本更新demo

    2. 设计一个API接口,返回JSON或XML格式的更新信息,包括新版本号、更新内容和下载链接。 3. 解析接收到的更新信息,判断是否需要更新。 4. 如果需要更新,显示更新提示对话框,让用户选择是否立即更新、稍后更新或...

    安卓版本更新Demo

    10. **测试与兼容性**:在发布新版本前,必须进行全面的测试,确保新版本在不同设备和Android版本上都能正常工作,避免因更新导致的问题。 11. **回滚机制**:如果新版本出现严重问题,开发者应准备回滚计划,让...

    android 应用更新功能 检测更新 自动下载安装 demo updatedemo

    - **比较版本号**:应用启动时,通过调用服务器接口获取最新版本信息,与本地的`versionCode`进行比较,如果新版本大于本地,表示有更新。 - **异步处理**:检测更新的过程应在后台线程进行,避免阻塞UI。 4. **...

    android app自动升级demo

    - 应用启动时或者在合适的时间点(如后台任务、用户触发检查更新),向服务器发送请求获取新版本信息。然后与本地应用的版本进行比较,如果服务器的`versionCode`大于本地的,说明有新版本可用。 4. **弹出更新...

    android软件更新demo

    总的来说,这个"android软件更新demo"是一个实践性的教程,它展示了如何在Android应用中集成自动检查和下载更新的功能,利用Jsoup解析服务器数据,为开发者提供了实现这一功能的基础。通过深入理解和实践这个demo,...

    android 版本升级demo

    - 当检测到新版本时,应用会显示一个对话框告知用户有可用更新,并提供下载选项。这个Demo中的对话框用于展示下载进度,这通常通过AsyncTask或ProgressDialog来实现。 - 文件下载通常使用HttpURLConnection或...

    发现新版本自动更新demo

    在Android应用开发中,"发现新版本自动更新demo"是一个常见的功能,旨在提供无缝的用户体验,确保用户始终能够运行应用的最新版本。这个demo通常包括一个启动时的闪屏界面,用于检查是否有可用的新版本,如果检测到...

Global site tag (gtag.js) - Google Analytics