前言:在软件开发的尾声应该都会遇到这个问题,还好网上资料很多,所以基本不费什么力气就搞定了,现记录于下。这里用的PHP服务器。

​ 效果图:(PHP服务器)​

​                   初始界面                      检测后,如果已是最新版​

​  Android提示版本更新_android​     Android提示版本更新_java_02                                 ​

​如果不是最新版,提示更新                  正在下载                             安装新程序    ​

 Android提示版本更新_json_03  Android提示版本更新_json_04  Android提示版本更新_android_05

 一、准备知识

 在AndroidManifest.xml里定义了每个Android apk的版本标识:

​[html] 

view plain

copy

  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2. package="com.example.try_downloadfile_progress"
  3. android:versionCode="1"
  4. android:versionName="1.0" >

其中,android:versionCode和android:versionName两个字段分别表示版本代码,版本名称。versionCode是整型数字,versionName是字符串。由于version是给用户看的,不太容易比较大小,升级检查时,可以以检查versionCode为主,方便比较出版本的前后大小。

那么,在应用中如何读取AndroidManifest.xml中的versionCode和versionName呢?可以使用PackageManager的API,参考以下代码:

​[java] 

view plain

copy

  1. /**
  2. * 获取软件版本号
  3. * @param context
  4. * @return
  5. */
  6. public static int getVerCode(Context context) {
  7. int verCode = -1;
  8. try {
  9. //注意:"com.example.try_downloadfile_progress"对应AndroidManifest.xml里的package="……"部分
  10. verCode = context.getPackageManager().getPackageInfo(
  11. "com.example.try_downloadfile_progress", 0).versionCode;
  12. } catch (NameNotFoundException e) {
  13. Log.e("msg",e.getMessage());
  14. }
  15. return verCode;
  16. }
  17. /**
  18. * 获取版本名称
  19. * @param context
  20. * @return
  21. */
  22. public static String getVerName(Context context) {
  23. String verName = "";
  24. try {
  25. verName = context.getPackageManager().getPackageInfo(
  26. "com.example.try_downloadfile_progress", 0).versionName;
  27. } catch (NameNotFoundException e) {
  28. Log.e("msg",e.getMessage());
  29. }
  30. return verName;
  31. }

这里要注意一个地方:代码里的“com.example.try_downloadfile_progress”对应AndroidManifest.xml里的package="……"部分

二、XML代码 

 XML代码非常简单,就是如初始化界面那样,在里面加一个BUTTON而已。代码如下:

​[html] 

view plain

copy

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. tools:context=".MainActivity" >
  6. <Button
  7. android:id="@+id/chek_newest_version"
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content"
  10. android:layout_margin="5dip"
  11. android:text="检测最新版本"/>
  12. </RelativeLayout>

三、辅助类构建(Common)

 这里为了开发方便,将一些公共的函数,单独放在Common类中实现,代码如下:

​[java] 

view plain

copy

  1. package com.example.try_downloadfile_progress;
  2. /**
  3. * @author harvic
  4. * @date 2014-5-7
  5. * @address http://blog.csdn.net/harvic880925
  6. */
  7. import java.io.BufferedReader;
  8. import java.io.InputStreamReader;
  9. import java.util.List;
  10. import org.apache.http.HttpResponse;
  11. import org.apache.http.NameValuePair;
  12. import org.apache.http.client.entity.UrlEncodedFormEntity;
  13. import org.apache.http.client.methods.HttpPost;
  14. import org.apache.http.impl.client.DefaultHttpClient;
  15. import org.apache.http.protocol.HTTP;
  16. import android.content.Context;
  17. import android.content.pm.PackageManager.NameNotFoundException;
  18. import android.util.Log;
  19. public class Common {
  20. public static final String SERVER_IP="http://192.168.1.105/";
  21. public static final String SERVER_ADDRESS=SERVER_IP+"try_downloadFile_progress_server/index.php";//软件更新包地址
  22. public static final String UPDATESOFTADDRESS=SERVER_IP+"try_downloadFile_progress_server/update_pakage/baidu.apk";//软件更新包地址
  23. /**
  24. * 向服务器发送查询请求,返回查到的StringBuilder类型数据
  25. *
  26. * @param ArrayList
  27. *            <NameValuePair> vps POST进来的参值对
  28. * @return StringBuilder builder 返回查到的结果
  29. * @throws Exception
  30. */
  31. public static StringBuilder post_to_server(List<NameValuePair> vps) {
  32. DefaultHttpClient httpclient = new DefaultHttpClient();
  33. try {
  34. HttpResponse response = null;
  35. // 创建httpost.访问本地服务器网址
  36. HttpPost httpost = new HttpPost(SERVER_ADDRESS);
  37. StringBuilder builder = new StringBuilder();
  38. httpost.setEntity(new UrlEncodedFormEntity(vps, HTTP.UTF_8));
  39. response = httpclient.execute(httpost); // 执行
  40. if (response.getEntity() != null) {
  41. // 如果服务器端JSON没写对,这句是会出异常,是执行不过去的
  42. BufferedReader reader = new BufferedReader(
  43. new InputStreamReader(response.getEntity().getContent()));
  44. String s = reader.readLine();
  45. for (; s != null; s = reader.readLine()) {
  46. builder.append(s);
  47. }
  48. }
  49. return builder;
  50. } catch (Exception e) {
  51. // TODO: handle exception
  52. Log.e("msg",e.getMessage());
  53. return null;
  54. } finally {
  55. try {
  56. httpclient.getConnectionManager().shutdown();// 关闭连接
  57. // 这两种释放连接的方法都可以
  58. } catch (Exception e) {
  59. // TODO Auto-generated catch block
  60. Log.e("msg",e.getMessage());
  61. }
  62. }
  63. }
  64. /**
  65. * 获取软件版本号
  66. * @param context
  67. * @return
  68. */
  69. public static int getVerCode(Context context) {
  70. int verCode = -1;
  71. try {
  72. //注意:"com.example.try_downloadfile_progress"对应AndroidManifest.xml里的package="……"部分
  73. verCode = context.getPackageManager().getPackageInfo(
  74. "com.example.try_downloadfile_progress", 0).versionCode;
  75. } catch (NameNotFoundException e) {
  76. Log.e("msg",e.getMessage());
  77. }
  78. return verCode;
  79. }
  80. /**
  81. * 获取版本名称
  82. * @param context
  83. * @return
  84. */
  85. public static String getVerName(Context context) {
  86. String verName = "";
  87. try {
  88. verName = context.getPackageManager().getPackageInfo(
  89. "com.example.try_downloadfile_progress", 0).versionName;
  90. } catch (NameNotFoundException e) {
  91. Log.e("msg",e.getMessage());
  92. }
  93. return verName;
  94. }
  95. }

这里除了上面我们提到的两个函数getVerCode和getVerName外,还有几个常量和一个函数定义,含义分别如下:

SERVER_IP:服务器IP地址(大家在拿到试验的时候,要改成自己服务器IP地址) 

SERVER_ADDRESS:android程序要将请求发送到的页面地址,无须更改。

UPDATESOFTADDRESS:更新安装包存放的位置,无须更改。

 函数StringBuilder post_to_server(List<NameValuePair> vps)是访问服务器并返回结果的功能封装。传进去名值对,返回StringBuilder对象

 四、主页面代码构建

 1、首先设置AndroidManifest.xml,使其能访问网络和SD卡

在</manifest>标签上面,加入:

​[html] 

view plain

copy

  1. <uses-permission android:name="android.permission.INTERNET" >
  2. </uses-permission>
  3. <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" >
  4. </uses-permission>
  5. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >
  6. </uses-permission>

2、主页代码:

先贴出全部代码,然后逐步讲解,全部代码如下:

​[java] 

view plain

copy

  1. package com.example.try_downloadfile_progress;
  2. /**
  3. * @author harvic
  4. * @date 2014-5-7
  5. * @address http://blog.csdn.net/harvic880925
  6. */
  7. import java.io.File;
  8. import java.io.FileOutputStream;
  9. import java.io.IOException;
  10. import java.io.InputStream;
  11. import java.util.ArrayList;
  12. import java.util.List;
  13. import org.apache.http.HttpEntity;
  14. import org.apache.http.HttpResponse;
  15. import org.apache.http.NameValuePair;
  16. import org.apache.http.client.ClientProtocolException;
  17. import org.apache.http.client.HttpClient;
  18. import org.apache.http.client.methods.HttpGet;
  19. import org.apache.http.impl.client.DefaultHttpClient;
  20. import org.apache.http.message.BasicNameValuePair;
  21. import org.json.JSONArray;
  22. import android.net.Uri;
  23. import android.os.AsyncTask;
  24. import android.os.Bundle;
  25. import android.os.Environment;
  26. import android.os.Handler;
  27. import android.app.Activity;
  28. import android.app.AlertDialog;
  29. import android.app.Dialog;
  30. import android.app.ProgressDialog;
  31. import android.content.DialogInterface;
  32. import android.content.Intent;
  33. import android.util.Log;
  34. import android.view.View;
  35. import android.view.View.OnClickListener;
  36. import android.widget.Button;
  37. public class MainActivity extends Activity {
  38. Button m_btnCheckNewestVersion;
  39. long m_newVerCode; //最新版的版本号
  40. String m_newVerName; //最新版的版本名
  41. String m_appNameStr; //下载到本地要给这个APP命的名字
  42. Handler m_mainHandler;
  43. ProgressDialog m_progressDlg;
  44. @Override
  45. protected void onCreate(Bundle savedInstanceState) {
  46. super.onCreate(savedInstanceState);
  47. setContentView(R.layout.activity_main);
  48. //初始化相关变量
  49. initVariable();
  50. m_btnCheckNewestVersion.setOnClickListener(btnClickListener);
  51. }
  52. private void initVariable()
  53. {
  54. m_btnCheckNewestVersion = (Button)findViewById(R.id.chek_newest_version);
  55. m_mainHandler = new Handler();
  56. m_progressDlg =  new ProgressDialog(this);
  57. m_progressDlg.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
  58. // 设置ProgressDialog 的进度条是否不明确 false 就是不设置为不明确
  59. m_progressDlg.setIndeterminate(false);
  60. m_appNameStr = "haha.apk";
  61. }
  62. OnClickListener btnClickListener = new View.OnClickListener() {
  63. @Override
  64. public void onClick(View v) {
  65. // TODO Auto-generated method stub
  66. new checkNewestVersionAsyncTask().execute();
  67. }
  68. };
  69. class checkNewestVersionAsyncTask extends AsyncTask<Void, Void, Boolean>
  70. {
  71. @Override
  72. protected Boolean doInBackground(Void... params) {
  73. // TODO Auto-generated method stub
  74. if(postCheckNewestVersionCommand2Server())
  75. {
  76. int vercode = Common.getVerCode(getApplicationContext()); // 用到前面第一节写的方法
  77. if (m_newVerCode > vercode) {
  78. return true;
  79. } else {
  80. return false;
  81. }
  82. }
  83. return false;
  84. }
  85. @Override
  86. protected void onPostExecute(Boolean result) {
  87. // TODO Auto-generated method stub
  88. if (result) {//如果有最新版本
  89. doNewVersionUpdate(); // 更新新版本
  90. }else {
  91. notNewVersionDlgShow(); // 提示当前为最新版本
  92. }
  93. super.onPostExecute(result);
  94. }
  95. @Override
  96. protected void onPreExecute() {
  97. // TODO Auto-generated method stub
  98. super.onPreExecute();
  99. }
  100. }
  101. /**
  102. * 从服务器获取当前最新版本号,如果成功返回TURE,如果失败,返回FALSE
  103. * @return
  104. */
  105. private Boolean postCheckNewestVersionCommand2Server()
  106. {
  107. StringBuilder builder = new StringBuilder();
  108. JSONArray jsonArray = null;
  109. try {
  110. // 构造POST方法的{name:value} 参数对
  111. List<NameValuePair> vps = new ArrayList<NameValuePair>();
  112. // 将参数传入post方法中
  113. vps.add(new BasicNameValuePair("action", "checkNewestVersion"));
  114. builder = Common.post_to_server(vps);
  115. jsonArray = new JSONArray(builder.toString());
  116. if (jsonArray.length()>0) {
  117. if (jsonArray.getJSONObject(0).getInt("id") == 1) {
  118. m_newVerName = jsonArray.getJSONObject(0).getString("verName");
  119. m_newVerCode = jsonArray.getJSONObject(0).getLong("verCode");
  120. return true;
  121. }
  122. }
  123. return false;
  124. } catch (Exception e) {
  125. Log.e("msg",e.getMessage());
  126. m_newVerName="";
  127. m_newVerCode=-1;
  128. return false;
  129. }
  130. }
  131. /**
  132. * 提示更新新版本
  133. */
  134. private void doNewVersionUpdate() {
  135. int verCode = Common.getVerCode(getApplicationContext());
  136. String verName = Common.getVerName(getApplicationContext());
  137. String str= "当前版本:"+verName+" Code:"+verCode+" ,发现新版本:"+m_newVerName+
  138. " Code:"+m_newVerCode+" ,是否更新?";
  139. Dialog dialog = new AlertDialog.Builder(this).setTitle("软件更新").setMessage(str)
  140. // 设置内容
  141. .setPositiveButton("更新",// 设置确定按钮
  142. new DialogInterface.OnClickListener() {
  143. @Override
  144. public void onClick(DialogInterface dialog,
  145. int which) {
  146. m_progressDlg.setTitle("正在下载");
  147. m_progressDlg.setMessage("请稍候...");
  148. downFile(Common.UPDATESOFTADDRESS);  //开始下载
  149. }
  150. })
  151. .setNegativeButton("暂不更新",
  152. new DialogInterface.OnClickListener() {
  153. public void onClick(DialogInterface dialog,
  154. int whichButton) {
  155. // 点击"取消"按钮之后退出程序
  156. finish();
  157. }
  158. }).create();// 创建
  159. // 显示对话框
  160. dialog.show();
  161. }
  162. /**
  163. *  提示当前为最新版本
  164. */
  165. private void notNewVersionDlgShow()
  166. {
  167. int verCode = Common.getVerCode(this);
  168. String verName = Common.getVerName(this);
  169. String str="当前版本:"+verName+" Code:"+verCode+",/n已是最新版,无需更新!";
  170. Dialog dialog = new AlertDialog.Builder(this).setTitle("软件更新")
  171. .setMessage(str)// 设置内容
  172. .setPositiveButton("确定",// 设置确定按钮
  173. new DialogInterface.OnClickListener() {
  174. @Override
  175. public void onClick(DialogInterface dialog,
  176. int which) {
  177. finish();
  178. }
  179. }).create();// 创建
  180. // 显示对话框
  181. dialog.show();
  182. }
  183. private void downFile(final String url)
  184. {
  185. m_progressDlg.show();
  186. new Thread() {
  187. public void run() {
  188. HttpClient client = new DefaultHttpClient();
  189. HttpGet get = new HttpGet(url);
  190. HttpResponse response;
  191. try {
  192. response = client.execute(get);
  193. HttpEntity entity = response.getEntity();
  194. long length = entity.getContentLength();
  195. m_progressDlg.setMax((int)length);//设置进度条的最大值
  196. InputStream is = entity.getContent();
  197. FileOutputStream fileOutputStream = null;
  198. if (is != null) {
  199. File file = new File(
  200. Environment.getExternalStorageDirectory(),
  201. m_appNameStr);
  202. fileOutputStream = new FileOutputStream(file);
  203. byte[] buf = new byte[1024];
  204. int ch = -1;
  205. int count = 0;
  206. while ((ch = is.read(buf)) != -1) {
  207. fileOutputStream.write(buf, 0, ch);
  208. count += ch;
  209. if (length > 0) {
  210. m_progressDlg.setProgress(count);
  211. }
  212. }
  213. }
  214. fileOutputStream.flush();
  215. if (fileOutputStream != null) {
  216. fileOutputStream.close();
  217. }
  218. down();
  219. } catch (ClientProtocolException e) {
  220. e.printStackTrace();
  221. } catch (IOException e) {
  222. e.printStackTrace();
  223. }
  224. }
  225. }.start();
  226. }
  227. private void down() {
  228. m_mainHandler.post(new Runnable() {
  229. public void run() {
  230. m_progressDlg.cancel();
  231. update();
  232. }
  233. });
  234. }
  235. void update() {
  236. Intent intent = new Intent(Intent.ACTION_VIEW);
  237. intent.setDataAndType(Uri.fromFile(new File(Environment
  238. .getExternalStorageDirectory(), m_appNameStr)),
  239. "application/vnd.android.package-archive");
  240. startActivity(intent);
  241. }
  242. }

逐步讲解:

​1、OnCreate函数:​

先从主函数开始讲,OnCreate函数中只有两部分,一个是initVariable()初始化变量,这个不多说,难度不大;第二个是为版本检测按钮设置监听函数——btnClickListener,而在btnClickListener函数中可以明显的看到,其中也只有一个类checkNewestVersionAsyncTask,这里采用异步方式处理更新问题。下面看checkNewestVersionAsyncTask函数

​2、checkNewestVersionAsyncTask函数​

​[java] 

view plain

copy

  1. class checkNewestVersionAsyncTask extends AsyncTask<Void, Void, Boolean>
  2. {
  3. @Override
  4. protected Boolean doInBackground(Void... params) {
  5. // TODO Auto-generated method stub
  6. if(postCheckNewestVersionCommand2Server())
  7. {
  8. int vercode = Common.getVerCode(getApplicationContext()); // 用到前面第一节写的方法
  9. if (m_newVerCode > vercode) {
  10. return true;
  11. } else {
  12. return false;
  13. }
  14. }
  15. return false;
  16. }
  17. @Override
  18. protected void onPostExecute(Boolean result) {
  19. // TODO Auto-generated method stub
  20. if (result) {//如果有最新版本
  21. doNewVersionUpdate(); // 更新新版本
  22. }else {
  23. notNewVersionDlgShow(); // 提示当前为最新版本
  24. }
  25. super.onPostExecute(result);
  26. }
  27. @Override
  28. protected void onPreExecute() {
  29. // TODO Auto-generated method stub
  30. super.onPreExecute();
  31. }
  32. }

​(1)首先看后台操作doInBackground​

首先利用postCheckNewestVersionCommand2Server()函数将请求发送到服务器,该函数根据是否请求成功返回TRUE或FALSE,然后将从服务器上获取的版本代码与本地的版本代码进行比较,如果要更新返回TRUE,如果不要更新返回FASLE。

下面看看postCheckNewestVersionCommand2Server()的代码:

​[java] 

view plain

copy

  1. private Boolean postCheckNewestVersionCommand2Server()
  2. {
  3. StringBuilder builder = new StringBuilder();
  4. JSONArray jsonArray = null;
  5. try {
  6. // 构造POST方法的{name:value} 参数对
  7. List<NameValuePair> vps = new ArrayList<NameValuePair>();
  8. // 将参数传入post方法中
  9. vps.add(new BasicNameValuePair("action", "checkNewestVersion"));
  10. builder = Common.post_to_server(vps);
  11. jsonArray = new JSONArray(builder.toString());
  12. if (jsonArray.length()>0) {
  13. if (jsonArray.getJSONObject(0).getInt("id") == 1) {
  14. m_newVerName = jsonArray.getJSONObject(0).getString("verName");
  15. m_newVerCode = jsonArray.getJSONObject(0).getLong("verCode");
  16. return true;
  17. }
  18. }
  19. return false;
  20. } catch (Exception e) {
  21. Log.e("msg",e.getMessage());
  22. m_newVerName="";
  23. m_newVerCode=-1;
  24. return false;
  25. }
  26. }

这里就是构建名值对,然后发向服务器,如果获取到了值就返回TURE,如果没获取到值,就返回FALSE。服务器返回的JSON值为:

​[html] 

view plain

copy

  1. [{"id":"1","verName":"1.0.1","verCode":"2"}]

对于服务器代码,由于是用PHP写的,这里就不再列出了,在源码里有。

​(2)onPostExecute()继续第一部分,在doInBackground操作完成后,如果需要更新doInBackground返回TRUE,否则返回FASLE,所以在onPostExecute中根据result不同调用不同的函数,利用doNewVersionUpdate(); 提示用户更新最新版本。利用notNewVersionDlgShow(); /提示用户当前即为最新版本,无需更新。

对于notNewVersionDlgShow()函数仅仅是创建了个对话框,没什么实体内容,就不再具体讲解。下面具体讲述doNewVersionUpdate()下载,更新与安装程序的过程。

​3、doNewVersionUpdate()提示版本更新具体代码如下:

​[java] 

view plain

copy

  1. private void doNewVersionUpdate() {
  2. int verCode = Common.getVerCode(getApplicationContext());
  3. String verName = Common.getVerName(getApplicationContext());
  4. String str= "当前版本:"+verName+" Code:"+verCode+" ,发现新版本:"+m_newVerName+
  5. " Code:"+m_newVerCode+" ,是否更新?";
  6. Dialog dialog = new AlertDialog.Builder(this).setTitle("软件更新").setMessage(str)
  7. // 设置内容
  8. .setPositiveButton("更新",// 设置确定按钮
  9. new DialogInterface.OnClickListener() {
  10. @Override
  11. public void onClick(DialogInterface dialog,
  12. int which) {
  13. m_progressDlg.setTitle("正在下载");
  14. m_progressDlg.setMessage("请稍候...");
  15. downFile(Common.UPDATESOFTADDRESS);  //开始下载
  16. }
  17. })
  18. .setNegativeButton("暂不更新",
  19. new DialogInterface.OnClickListener() {
  20. public void onClick(DialogInterface dialog,
  21. int whichButton) {
  22. // 点击"取消"按钮之后退出程序
  23. finish();
  24. }
  25. }).create();// 创建
  26. // 显示对话框
  27. dialog.show();
  28. }

这里创建一个具有确定按钮和取消按钮功能的对话框,如果用户点击取消按钮,会利用finish()结束掉程序运行;如果点击确定按钮,则利用 downFile(Common.UPDATESOFTADDRESS); 函数开始程序下载,其中downFile()函数传进去的参数是APP所在的服务器地址。注意这里的地址要具体到下载文件,比如这里是http://192.168.1.105/server/XXX.apk

​4、downFile(final String url)下载并显示进度

具体代码如下:

​[java] 

view plain

copy

  1. private void downFile(final String url)
  2. {
  3. m_progressDlg.show();
  4. new Thread() {
  5. public void run() {
  6. HttpClient client = new DefaultHttpClient();
  7. HttpGet get = new HttpGet(url);
  8. HttpResponse response;
  9. try {
  10. response = client.execute(get);
  11. HttpEntity entity = response.getEntity();
  12. long length = entity.getContentLength();
  13. m_progressDlg.setMax((int)length);//设置进度条的最大值
  14. InputStream is = entity.getContent();
  15. FileOutputStream fileOutputStream = null;
  16. if (is != null) {
  17. File file = new File(
  18. Environment.getExternalStorageDirectory(),
  19. m_appNameStr);
  20. fileOutputStream = new FileOutputStream(file);
  21. byte[] buf = new byte[1024];
  22. int ch = -1;
  23. int count = 0;
  24. while ((ch = is.read(buf)) != -1) {
  25. fileOutputStream.write(buf, 0, ch);
  26. count += ch;
  27. if (length > 0) {
  28. m_progressDlg.setProgress(count);//设置当前进度
  29. }
  30. }
  31. }
  32. fileOutputStream.flush();
  33. if (fileOutputStream != null) {
  34. fileOutputStream.close();
  35. }
  36. down();  //告诉HANDER已经下载完成了,可以安装了
  37. } catch (ClientProtocolException e) {
  38. e.printStackTrace();
  39. } catch (IOException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. }.start();
  44. }

通过利用httpClient的get方法,获取指定URL的内容,然后写到本地SD卡中,对于进度条,首先利用m_progressDlg.setMax((int)length);设置进度条的最大值,然后在读取返回结果并写到本地时,利用 m_progressDlg.setProgress(count);设置当前进度。在全部写完以后,调用down()函数,通知HANDER安装程序。

​5、安装程序​

安装程序主要利用下面两个函数:

​[java] 

view plain

copy

  1. /**
  2. * 告诉HANDER已经下载完成了,可以安装了
  3. */
  4. private void down() {
  5. m_mainHandler.post(new Runnable() {
  6. public void run() {
  7. m_progressDlg.cancel();
  8. update();
  9. }
  10. });
  11. }
  12. /**
  13. * 安装程序
  14. */
  15. void update() {
  16. Intent intent = new Intent(Intent.ACTION_VIEW);
  17. intent.setDataAndType(Uri.fromFile(new File(Environment
  18. .getExternalStorageDirectory(), m_appNameStr)),
  19. "application/vnd.android.package-archive");
  20. startActivity(intent);
  21. }

由于在android程序中必须依循单线程操作UI,所以在非主线程中不能操作UI,否则程序会崩掉,具体参见:《AsnyncTask与handler(一)——AsyncTask异步处理​》与《AsnyncTask与handler(二)——handler消息机制》。所以这里作用handler的方式更新UI。

好了,到这就全部讲完了,下面给出客户端与服务器端源码,懂PHP的童鞋赚到了有木有……