Android App实现应用内部自动更新的最基本方法示例
这只是初步的实现,并没有加入自动编译等功能。需要手动更改更新的xml文件和最新的apk。
共涉及到四个文件!
一、客户端
androidupdatetestactivity:程序首页
main.xml:首页布局
update:更新类
softupdate_progress:更新等待界面
updage
package majier.test; 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 java.util.hashmap; import javax.xml.parsers.documentbuilder; import javax.xml.parsers.documentbuilderfactory; import org.w3c.dom.document; import org.w3c.dom.element; import org.w3c.dom.node; import org.w3c.dom.nodelist; import android.app.alertdialog; import android.app.dialog; import android.app.alertdialog.builder; import android.content.context; import android.content.dialoginterface; import android.content.intent; import android.content.dialoginterface.onclicklistener; 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 android.widget.toast; public class update { private static final int download = 1; private static final int download_finish = 2; private static final int connect_failed = 0; private static final int connect_success = 1; hashmap<string, string> mhashmap; private string msavepath; private int progress; private boolean cancelupdate = false; private context mcontext; private progressbar mprogress; private dialog mdownloaddialog; private string mxmlpath; // 服务器更新xml存放地址 public update(context context, string xmlpath, string savepath) { this.mcontext = context; this.mxmlpath = xmlpath; this.msavepath = savepath; } private handler mhandler = new handler() { public void handlemessage(message msg) { switch (msg.what) { case download: mprogress.setprogress(progress); break; case download_finish: installapk(); break; default: break; } }; }; /** * 检查更新 */ public void checkupdate() { new thread(new runnable() { @override public void run() { try { url url = new url(mxmlpath); httpurlconnection conn = (httpurlconnection) url .openconnection(); conn.setconnecttimeout(5000); inputstream instream = conn.getinputstream(); mhashmap = parsexml(instream); message msg = new message(); msg.what = connect_success; handler.sendmessage(msg); } catch (exception e) { message msg = new message(); msg.what = connect_failed; handler.sendmessage(msg); } } }).run(); } /** * 访问服务器更新xml */ handler handler = new handler() { @override public void handlemessage(message msg) { super.handlemessage(msg); switch (msg.what) { case connect_failed: toast.maketext(mcontext, "访问服务器失败!", toast.length_short).show(); break; case connect_success: if (null != mhashmap) { int servicecode = integer.valueof(mhashmap.get("version")); if (servicecode > getversioncode(mcontext)) { shownoticedialog(); } } break; } } }; /** * 获取程序版本号 */ private int getversioncode(context context) { int versioncode = 0; try { versioncode = context.getpackagemanager().getpackageinfo( mcontext.getpackagename(), 0).versioncode; } catch (namenotfoundexception e) { e.printstacktrace(); } return versioncode; } /** * 是否更新提示窗口 */ private void shownoticedialog() { alertdialog.builder builder = new builder(mcontext); builder.settitle("软件更新"); builder.setmessage("检测到新版本,是否更新?"); 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(); } }); dialog noticedialog = builder.create(); noticedialog.show(); } /** * 下载等待窗口 */ private void showdownloaddialog() { alertdialog.builder builder = new builder(mcontext); builder.settitle("正在更新"); final layoutinflater inflater = layoutinflater.from(mcontext); view v = inflater.inflate(r.layout.softupdate_progress, null); mprogress = (progressbar) v.findviewbyid(r.id.update_progress); builder.setview(v); builder.setnegativebutton("取消下载", new onclicklistener() { @override public void onclick(dialoginterface dialog, int which) { dialog.dismiss(); cancelupdate = true; } }); mdownloaddialog = builder.create(); mdownloaddialog.show(); downloadapk(); } /** * 涓嬭浇apk鏂囦欢 */ private void downloadapk() { new downloadapkthread().start(); } /** * 下载程序 */ private class downloadapkthread extends thread { @override public void run() { try { if (environment.getexternalstoragestate().equals( environment.media_mounted)) { url url = new url(mhashmap.get("url")); httpurlconnection conn = (httpurlconnection) url .openconnection(); conn.connect(); int length = conn.getcontentlength(); inputstream is = conn.getinputstream(); file file = new file(msavepath); if (!file.exists()) { file.mkdir(); } file apkfile = new file(msavepath, mhashmap.get("name")); 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); mhandler.sendemptymessage(download); if (numread <= 0) { mhandler.sendemptymessage(download_finish); break; } fos.write(buf, 0, numread); } while (!cancelupdate); fos.close(); is.close(); } } catch (malformedurlexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } mdownloaddialog.dismiss(); } }; /** * 安装apk */ private void installapk() { file apkfile = new file(msavepath, mhashmap.get("name")); if (!apkfile.exists()) { return; } intent i = new intent(intent.action_view); i.setdataandtype(uri.parse("file://" + apkfile.tostring()), "application/vnd.android.package-archive"); mcontext.startactivity(i); } private hashmap<string, string> parsexml(inputstream instream) throws exception { hashmap<string, string> hashmap = new hashmap<string, string>(); // 实例化一个文档构建器工厂 documentbuilderfactory factory = documentbuilderfactory.newinstance(); // 通过文档构建器工厂获取一个文档构建器 documentbuilder builder = factory.newdocumentbuilder(); // 通过文档通过文档构建器构建一个文档实例 document document = builder.parse(instream); // 获取xml文件根节点 element root = document.getdocumentelement(); // 获得所有子节点 nodelist childnodes = root.getchildnodes(); for (int j = 0; j < childnodes.getlength(); j++) { // 遍历子节点 node childnode = (node) childnodes.item(j); if (childnode.getnodetype() == node.element_node) { element childelement = (element) childnode; // 版本号 if ("version".equals(childelement.getnodename())) { hashmap.put("version", childelement.getfirstchild() .getnodevalue()); } // 软件名称 else if (("name".equals(childelement.getnodename()))) { hashmap.put("name", childelement.getfirstchild() .getnodevalue()); } // 下载地址 else if (("url".equals(childelement.getnodename()))) { hashmap.put("url", childelement.getfirstchild() .getnodevalue()); } } } return hashmap; } }
androidupdatetestactivity
package majier.test; import android.app.activity; import android.os.bundle; import android.os.environment; public class androidupdatetestactivity extends activity { /** called when the activity is first created. */ @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); update(); } private void update() { string sdpath = environment.getexternalstoragedirectory() + "/"; string msavepath = sdpath + "boiler/"; update updatemanager = new update(this, "http://localhost:8011/abcd.xml", msavepath); updatemanager.checkupdate(); } }
main.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="fill_parent" android:orientation="vertical" > <textview android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> </linearlayout>
softupdate_progress.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/update_progress" android:layout_width="fill_parent" android:layout_height="wrap_content" style="?android:attr/progressbarstylehorizontal" /> </linearlayout>
每次生成新的apk前,需要修改系统的版本号。
修改version code 和version name。上面的代码可以看出,系统是根据version code来判断是否需要更新的。version name作为一个版本名称。
这里我建议version code从10开始,这样方面名称修改(1.1、1.2)。
修改完成后,生成系统。然后将apk文件放在服务端的文件下。
二、服务端
服务端主要是建立一个网址供用户下载apk。在iis上新建网站
http://localhost:8011/。将更新文件和更新的xml放在目录下。
version.xml格式
<update> <version>12</version> <name>boilerandroid_1.1</name> <url>http://192.168.0.33:8011/boilerandroid.apk</url> </update>
version对应着新程序的version code;
name随便起名;
url对应apk的下载路径。
在这里有可能会遇见一个问题,访问url路径时iis报错。主要是因为iis并不认识apk,不知道如何处理。
这里我们在iis中新增安卓程序的mime类型,来使apk支持下载。
在“iis管理器”中查看所建立的网站——mime类型——添加。
文件扩展名:.apk
mime类型:application/vnd.android.package-archive
这样就可以下载了。
目前只是一个简单的自动更新程序。我们可以看出,其中版本号需要自己填写,而且要与xml中的对应,apk需要生成后放在更新网址下。
这么的人为操作,很容易造成失误。因此,接下来我们要研究下自动发布更新版本,并且版本号与svn对应,在提交svn后,自动改变程序的版本号。
上一篇: 深入理解MySQL中的事务机制
下一篇: aspx与ascx,ashx的用法总结