- 浏览: 160614 次
最新评论
-
bihongliang:
并且服务端一直报错:javax.bluetooth.Bluet ...
android 和 PC端 进行蓝牙通信 demo -
bihongliang:
你好,博主,我是windows10 64 位系统,加载了 64 ...
android 和 PC端 进行蓝牙通信 demo -
白云飘飘2016:
fcylf 写道win7 64位,pc端测试,报错:Nativ ...
android 和 PC端 进行蓝牙通信 demo -
fcylf:
win7 64位,pc端测试,报错:Native Librar ...
android 和 PC端 进行蓝牙通信 demo -
abc天残:
http://www.iteye.com/images/smi ...
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>
- updateApkServer.rar (56.7 KB)
- 下载次数: 444
- updateApkDemo.rar (165.1 KB)
- 下载次数: 472
评论
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,结果没法运行,是否能共享下更全面的信息呢?
发表评论
-
基于ZBar条形码、二维码扫描Demo
2015-07-29 15:08 2035Demo样式: -
左滑删除Demo
2015-07-29 14:48 717参考摘录:http://blog.csdn.net ... -
Fragment + ViewPager 底部菜单 demo
2015-07-17 17:33 634如附件 -
android googleMap key 的申请
2012-06-28 17:25 1203关于应用于android上 ... -
android phoneGap 静态页面中简单的数据传递
2012-06-27 17:14 2486最终效果: 主要采用方式: wi ... -
android PhoneGap JQuery Mobile Demo
2012-06-26 13:49 2950最终效果: 项目目录结构 ... -
android PhoneGap 自定义插件
2012-06-08 17:16 2263以"发送短信"功能 自定义插件 ... -
android PhoneGap 入门
2012-06-08 09:57 976最终效果: 项目结构图: 需要用到Ph ... -
android Animation图片渐变动画 Demo
2012-05-11 15:47 3557最终实现效果: 项目目录结构: main.x ... -
android Preference Demo
2012-05-11 09:05 2116最终实现效果 ... -
android 可编辑的下拉框 Demo
2012-04-27 10:34 4244最终实现效果: 项目目录结构: EditDropdownT ... -
Android解析json数组对象
2012-04-13 16:07 5365android有自带的JSON解析 ... -
Android使用KSOAP2调用WebService出现java.lang.NoClassDefFoundError.的解决办法
2012-04-13 14:51 13231.把项目bin目录下的原先生成的apk先删除 2.最 ... -
基于CXF的webService本地数据交互----PC端与Android端(三)
2012-03-31 09:49 2446本篇基于(二)的基础上续写 主要是JSON的数据交 ... -
android所见即所得界面设计工具---droiddraw
2012-03-08 16:06 1507android所见即所得界面设计工具---droiddraw ... -
基于CXF的webService本地数据交互----PC端与Android端(一)
2012-02-27 17:02 37741.下载CXF(我下载的是2.4.6) http://cxf ... -
蓝牙移动体感
2012-02-24 09:36 1059那些年,那些你不知道也不需要知道的事。 -
android menu自定义菜单 Demo
2012-02-24 09:36 1861package com.ruibin.menu; imp ... -
andoid点击按钮(ImageButton)时改变按钮的背景图片 SelectorDemo
2012-02-24 09:36 5259主要是用到selector这个属性! 1. a ... -
android 和 PC端 进行蓝牙通信 demo
2012-02-24 09:37 18140前提: 1. 使用真机测试 2. ...
相关推荐
这个“android版本更新 demo”项目旨在展示如何实现一个有效的版本更新系统,包括检查新版本、下载更新包以及自动升级等功能。以下是对这些关键知识点的详细解释: 1. **版本控制**:在Android应用开发中,版本控制...
这个"android 版本更新DEMO"旨在为开发者提供一个实用的示例,指导他们如何在自己的应用中实现自动检查和下载新版本的功能。 首先,我们来看看`JavaApk源码说明.txt`。这个文档可能包含了关于DEMO源码的详细解释,...
【标题】"服务器下载更新的Demo"涉及到的知识点主要集中在服务器管理和软件更新方面,特别是针对Tomcat这款广泛应用的Java Servlet容器。Tomcat是Apache软件基金会的Jakarta项目下的一个开源项目,它实现了Java ...
在Android应用开发中,版本升级是一项重要的功能,它能让用户无缝地从旧版本过渡到新版本,提升用户体验。本示例“android应用下载安装apk升级版本实现demo适配Android10”聚焦于如何在Android 10(API级别29)及更...
3. **更新检测机制**:应用需要定期检查服务器上是否存在新版本。这通常通过HTTP请求或者使用推送通知实现。服务器端则维护一个最新版本信息,供客户端查询。 4. **下载流程**:当检测到新版本时,用户会收到更新...
总之,"android软件更新Demo"是一个实用的教程,涵盖了从检查更新到下载安装的整个过程,是理解Android应用自动更新机制的好起点。开发者可以在此基础上根据具体需求进行定制,比如添加断点续传功能、优化网络请求...
总结起来,Android版本更新Demo案例的核心包括检测新版本、下载更新包、显示下载进度以及自动安装新版本。这个过程中涉及到网络请求、UI交互、文件操作和权限管理等多个方面的知识。通过实践这样的案例,开发者可以...
在Android应用开发中,版本更新是一项重要的功能,...综上所述,"android_版本更新_demo"项目涵盖了从获取版本信息到下载安装的整个更新流程,提供了丰富的学习资源,帮助开发者理解和实现Android应用的自动更新功能。
2. **JSON解析**:服务器返回的数据通常是JSON格式,包含新版本号、更新日志、下载链接等。使用如Gson或Jackson库解析JSON,提取所需信息。 3. **权限管理**:在Android 6.0(API 23)及以上版本,需要在运行时动态...
本示例"android提示更新下载安装demo"聚焦于如何实现这一过程,通过一个简洁的示例来演示Android应用如何检测新版本,并引导用户进行下载安装。以下是关于这个主题的详细知识点: 1. 版本检查: - `...
5. **安装更新**: 下载完成后,应用需要在用户同意的情况下安装新版本。在Android 7.0(API级别24)及更高版本,应用可以使用`ACTION_VIEW` Intent并设置`FLAG_GRANT_WRITE_URI_PERMISSION`来安装外部存储上的APK。...
在Android平台上,自动下载更新安装是一项重要的功能,它允许应用在后台下载最新的版本,并在适当的时候自动或引导用户进行安装,以确保应用始终处于最新状态,提供更好的用户体验和安全性。这个"Android自动下载...
2. 设计一个API接口,返回JSON或XML格式的更新信息,包括新版本号、更新内容和下载链接。 3. 解析接收到的更新信息,判断是否需要更新。 4. 如果需要更新,显示更新提示对话框,让用户选择是否立即更新、稍后更新或...
10. **测试与兼容性**:在发布新版本前,必须进行全面的测试,确保新版本在不同设备和Android版本上都能正常工作,避免因更新导致的问题。 11. **回滚机制**:如果新版本出现严重问题,开发者应准备回滚计划,让...
- **比较版本号**:应用启动时,通过调用服务器接口获取最新版本信息,与本地的`versionCode`进行比较,如果新版本大于本地,表示有更新。 - **异步处理**:检测更新的过程应在后台线程进行,避免阻塞UI。 4. **...
- 应用启动时或者在合适的时间点(如后台任务、用户触发检查更新),向服务器发送请求获取新版本信息。然后与本地应用的版本进行比较,如果服务器的`versionCode`大于本地的,说明有新版本可用。 4. **弹出更新...
总的来说,这个"android软件更新demo"是一个实践性的教程,它展示了如何在Android应用中集成自动检查和下载更新的功能,利用Jsoup解析服务器数据,为开发者提供了实现这一功能的基础。通过深入理解和实践这个demo,...
- 当检测到新版本时,应用会显示一个对话框告知用户有可用更新,并提供下载选项。这个Demo中的对话框用于展示下载进度,这通常通过AsyncTask或ProgressDialog来实现。 - 文件下载通常使用HttpURLConnection或...
在Android应用开发中,"发现新版本自动更新demo"是一个常见的功能,旨在提供无缝的用户体验,确保用户始终能够运行应用的最新版本。这个demo通常包括一个启动时的闪屏界面,用于检查是否有可用的新版本,如果检测到...