作为新手,对于这个问题,我直接采用的方法就是网上找源码;互联网是个神奇的东西,特别是像android这样开源的语言(不过话说开源开的有点儿过,平台多元化,苦煞了像我这样靠着它吃饭的程序猿),只要你想研究,总能找到有利用价值的解决方案。
废话不多说,版本更新一篇文章说清楚,思路是这样:
1、在android应用每次登录的时候,通过网络访问远程的配置文件(当然版本控制文件可以多种格式,xml、json、甚至txt……依据个人兴趣而定,知道怎么解析就行),确定要不要更新(若版本号改变,那么就更新;版本控制文件由开发者根据版本变化手动修改;我这里采用的json文件写配置)
2、通过解析版本配置文件,将所得版本号与现有应用的版本号进行比对,判断是否需要下载更新;
3、当然版本控制文件可以包含新的apk的URL,新版本特性……附加信息;
4、在提供版本控制文件的同时,还需要服务器安放新版本的apk文件,通过URI指定新版本的apk地址供远程访问下载到本地手机SD卡;
5、这里要非常注意的一个问题:版本更新需要appkey的统一,旧版本和新版本都需要是已签名的apk,否则安装更新无法通过。(前面几步按部就班就行,我在测试的过程中apk文件下载到本地,可就是安装不上,显示“安装为完成”,然后每次都提示安装更新,就是这个问题,甚是无语。由于是独自作战,这个问题竟然困扰了我好几天,加了那么多的android技术群,发了n遍的“拜求贴”,就是没人反馈我,悲痛欲绝,还是从一篇大拿的博客上找到了问题根源:app key)
那篇博文如下:http://blog.csdn.net/shimiso/article/details/6440282
android的客户端更新功能,相信只有做过的才知道其中的辛酸,一要做好断点续传,二要做好数据库的初始化工作,三要做好签名,四要做好版本校验的算法并且能显示动态进度条和百分比。断点续传好做,但是数据库初始化麻烦点,我们的做法是把sqlite库文件直接从raw下拷贝至sd卡中,并设置了sqlite的读取库路径指向它,感觉这样好一点。签名一开始不知道,每次覆盖都提示安装未完成,后来才明白为了保证应用的唯一性,它就像是身份证一样,其他没什么作用,和塞班的签名不是一回事,封装apk必须保证在同一签名文件下才可相互覆盖安装!
json解析方法:
String jsonversion = NetworkTool.getContent(VersionUri);
JSONArray array = new JSONArray(jsonversion);
// 解析Version网页,获取版本号
if (array.length() > 0) {
JSONObject obj = array.getJSONObject(0);
NowVersion = obj.getString("verCode");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
上代码:关键类——UpdateActivity.java
1 package com.android.Update;
2
3 import android.app.Activity;
4 import android.app.AlertDialog;
5 import android.app.Dialog;
6 import android.app.ProgressDialog;
7 import android.content.Context;
8 import android.content.DialogInterface;
9 import android.content.Intent;
10 import android.net.Uri;
11 import android.os.Bundle;
12 import android.util.Log;
13 import android.view.Menu;
14 import android.view.MenuItem;
15 import android.webkit.URLUtil;
16 import android.widget.Toast;
17 import java.io.File;
18 import java.io.FileInputStream;
19 import java.io.FileNotFoundException;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.net.URL;
24 import java.net.URLConnection;
25 import java.util.Properties;
26 import org.json.JSONArray;
27 import org.json.JSONObject;
28
29 public class UpdateActivity extends Activity {
30 private static final String TAG = "DOWNLOADAPK";
31 private String PastVersion;//旧版本号
32 private String NowVersion;//新版本号
33 public ProgressDialog pBar; //进度条
34 private String currentFilePath = ""; //文件
35 //预安装软件的扩展名
36 private String fileEx="";
37 private String fileNa="";//软件名称
38 //APK下载地址
39 //TODO 这里要自定义
40 private String strURL="http://open.sina.com.cn/update.apk";
41 //版本更新配置文件URi
42 //TODO 这里也是
43 private String VersionUri ="http://open.sina.com.cn/update.json";
44 @Override
45 public void onCreate(Bundle savedInstanceState) {
46 super.onCreate(savedInstanceState);
47 setContentView(R.layout.main);
48 }
49
50 @Override
51 public boolean onCreateOptionsMenu(Menu menu) {
52 menu.add(Menu.NONE, Menu.FIRST + 1, 5, "检测更新").setIcon(
53 android.R.drawable.ic_menu_upload);
54 menu.add(Menu.NONE,Menu.FIRST+2,4,"退出").setIcon(android.R.drawable.ic_menu_delete);
55 return true;
56 }
57
58 //处理下载URL文件自定义的函数
59 private void getFile(final String strPath)
60 {
61 pBar.show();
62 try
63 {
64 if (strPath.equals(currentFilePath) )
65 {
66 getDataSource(strPath);
67 }
68 currentFilePath = strPath;
69 Runnable r = new Runnable()
70 {
71 public void run()
72 {
73 try
74 {
75 getDataSource(strPath);
76 }
77 catch (Exception e)
78 {
79 Log.e(TAG, e.getMessage(), e);
80 }
81 }
82 };
83 new Thread(r).start();
84 }
85 catch(Exception e)
86 {
87 e.printStackTrace();
88 }
89 }
90
91 /*取得远程文件*/
92 private void getDataSource(String strPath) throws Exception
93 {
94 if (!URLUtil.isNetworkUrl(strPath))
95 {
96 Log.d("Tag","error");
97 }
98 else
99 {
100 /*取得URL*/
101 URL myURL = new URL(strPath);
102 /*建立联机*/
103 URLConnection conn = myURL.openConnection();
104 conn.connect();
105 /*InputStream 下载文件*/
106 InputStream is = conn.getInputStream();
107 if (is == null)
108 {
109 Log.d("tag","error");
110 throw new RuntimeException("stream is null");
111 }
112 /*建立临时文件*/
113 File myTempFile = File.createTempFile(fileNa, "."+fileEx);
114 myTempFile.getAbsolutePath();
115 /*将文件写入临时盘*/
116 FileOutputStream fos = new FileOutputStream(myTempFile);
117 byte buf[] = new byte[128];
118 do
119 {
120 int numread = is.read(buf);
121 if (numread <= 0)
122 {
123 break;
124 }
125 fos.write(buf, 0, numread);
126 }while (true);
127
128 /*打开文件进行安装*/
129 openFile(myTempFile);
130 //openFile(c);
131 try
132 {
133 is.close();
134 }
135 catch (Exception ex)
136 {
137 Log.d("Tag","error");
138 Log.e(TAG, "error: " + ex.getMessage(), ex);
139 }
140 }
141 }
142
143 /* 在手机上打开文件的method */
144 private void openFile(File f)
145 {
146 pBar.cancel();
147 Intent intent = new Intent();
148 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
149 intent.setAction(android.content.Intent.ACTION_VIEW);
150
151 /* 调用getMIMEType()来取得MimeType */
152 String type = getMIMEType(f);
153 /* 设定intent的file与MimeType */
154 intent.setDataAndType(Uri.fromFile(f),type);
155 startActivity(intent);
156 }
157
158 /* 判断文件MimeType的method */
159 private String getMIMEType(File f)
160 {
161 String type="";
162 String fName=f.getName();
163 /* 取得扩展名 */
164 String end=fName.substring(fName.lastIndexOf(".")+1,fName.length()).toLowerCase();
165
166 /* 按扩展名的类型决定MimeType */
167 if(end.equals("m4a")||end.equals("mp3")||end.equals("mid")||end.equals("xmf")||end.equals("ogg")||end.equals("wav"))
168 {
169 type = "audio";
170 }
171 else if(end.equals("3gp")||end.equals("mp4"))
172 {
173 type = "video";
174 }
175 else if(end.equals("jpg")||end.equals("gif")||end.equals("png")||end.equals("jpeg")||end.equals("bmp"))
176 {
177 type = "image";
178 }
179 else if(end.equals("apk"))
180 {
181 /* android.permission.INSTALL_PACKAGES */
182 type = "application/vnd.android.package-archive";
183 }
184 else
185 {
186 type="*";
187 }
188 /*如果无法直接打开,就跳出软件清单给使用者选择 */
189 if(end.equals("apk"))
190 {
191 }
192 else
193 {
194 type += "/*";
195 }
196 return type;
197 }
198
199
200
201 private void delFile(String strFileName)
202 {
203 File myFile = new File(strFileName);
204 if(myFile.exists())
205 {
206 myFile.delete();
207 }
208 }
209
210 @Override
211 public boolean onOptionsItemSelected(MenuItem item) {
212 try {
213 String jsonversion = NetworkTool.getContent(VersionUri);
214 Log.d(TAG, "verjson:"+jsonversion);
215 JSONArray array = new JSONArray(jsonversion);
216 // 解析Version网页,获取版本号
217 if (array.length() > 0) {
218 JSONObject obj = array.getJSONObject(0);
219 NowVersion = obj.getString("verCode");
220 }
221 } catch (Exception e) {
222 // TODO Auto-generated catch block
223 e.printStackTrace();
224 }
225
226 // 装载获取当前的版本号
227 load();
228
229 //当有最新版本的时候
230 Log.d("update", "pastVersion:"+PastVersion+"&&Nowversion:"+NowVersion);
231 if(PastVersion != null&&!(PastVersion.equals(NowVersion)))
232 {
233 save();
234 Dialog dialog = new AlertDialog.Builder(UpdateActivity.this).setTitle("系统更新")
235 .setMessage("发现新版本,请更新!")// 设置内容
236 .setPositiveButton("确定",// 设置确定按钮
237 new DialogInterface.OnClickListener() {
238 public void onClick(DialogInterface dialog,
239 int which) {
240 pBar = new ProgressDialog(UpdateActivity.this);
241 pBar.setTitle("正在下载");
242 pBar.setMessage("请稍候...");
243 pBar.setProgressStyle(ProgressDialog.STYLE_SPINNER);
244 fileEx = strURL.substring(strURL.lastIndexOf(".")+1,strURL.length()).toLowerCase();
245 fileNa = strURL.substring(strURL.lastIndexOf("/")+1,strURL.lastIndexOf("."));
246 getFile(strURL);
247 }
248 }).setNegativeButton("取消",
249 new DialogInterface.OnClickListener() {
250 public void onClick(DialogInterface dialog,
251 int whichButton) {
252 // 点击"取消"按钮之后退出程序
253 }
254 }).create();// 创建
255 // 显示对话框
256 dialog.show();
257 }
258 //
259 else{
260 save();
261 Toast.makeText(this, "当前为最新版本", Toast.LENGTH_LONG).show();
262 }
263 return false;
264
265 }
266
267 @Override
268 public void onOptionsMenuClosed(Menu menu) {
269 // Toast.makeText(this, "选项菜单关闭了", Toast.LENGTH_LONG).show();
270 }
271
272 @Override
273 public boolean onPrepareOptionsMenu(Menu menu) {
274 // Toast.makeText(this,
275 // "选项菜单显示之前onPrepareOptionsMenu方法会被调用,你可以用此方法来根据打当时的情况调整菜单",
276 // Toast.LENGTH_LONG).show();
277
278 // 如果返回false,此方法就把用户点击menu的动作给消费了,onCreateOptionsMenu方法将不会被调用
279
280 return true;
281
282 }
283
284 boolean load ()
285 {
286 Properties properties = new Properties();
287 try
288 {
289 FileInputStream stream = this.openFileInput("Versionfile.cfg");
290 //读取文件内容
291 properties.load(stream);
292 }
293 catch (FileNotFoundException e)
294 {
295 return false;
296 }
297 catch(IOException e)
298 {
299 return false;
300 }
301 PastVersion = String.valueOf(properties.get("Version").toString());
302 Log.d("UpdateActivity", "Pastversion:"+PastVersion);
303 return true;
304 }
305
306 boolean save()
307 {
308 Properties properties = new Properties();
309 properties.put("Version", NowVersion);
310
311 try
312 {
313 FileOutputStream stream = this.openFileOutput("Versionfile.cfg",Context.MODE_WORLD_WRITEABLE);//openFileOutput(第一个参数指定文件名称,指定操作模式)用于把数据输出到文件当中
314 properties.store(stream,"");
315 }
316 catch (FileNotFoundException e)
317 {
318 return false;
319 }
320 catch(IOException e)
321 {
322 return false;
323 }
324 return true;
325 }
326 }
关键代码:NetworkTool .java
1 package com.android.Update;
2
3 import java.io.BufferedReader;
4 import java.io.InputStream;
5 import java.io.InputStreamReader;
6
7 import org.apache.http.HttpEntity;
8 import org.apache.http.HttpResponse;
9 import org.apache.http.client.HttpClient;
10 import org.apache.http.client.methods.HttpGet;
11 import org.apache.http.impl.client.DefaultHttpClient;
12 import org.apache.http.params.HttpConnectionParams;
13 import org.apache.http.params.HttpParams;
14 import org.apache.http.util.EncodingUtils;
15
16 public class NetworkTool {
17
18 /**
19 * 获取网址内容
20 * @param url
21 * @return
22 * @throws Exception
23 */
24 public static String getContent(String url) throws Exception{
25 StringBuilder sb = new StringBuilder();
26
27 HttpClient client = new DefaultHttpClient();
28 HttpParams httpParams = client.getParams();
29 //设置网络超时参数
30 HttpConnectionParams.setConnectionTimeout(httpParams, 3000);
31 HttpConnectionParams.setSoTimeout(httpParams, 5000);
32 HttpResponse response = client.execute(new HttpGet(url));
33 HttpEntity entity = response.getEntity();
34 if (entity != null) {
35 InputStream is = entity.getContent();
36 BufferedReader reader = new BufferedReader(new InputStreamReader(is, "GB2312"), 8192);
37
38 String line = null;
39 while ((line = reader.readLine())!= null){
40 //line = EncodingUtils.getString(line.getBytes("GB2312"), "UTF-8");
41 sb.append(line + "\n");
42 }
43 reader.close();
44 }
45 return sb.toString();
46 }
47 }
json文件:update.json,放在远程服务器
[{"appname":"Update Client","apkname":"update.apk","apkUrl":"http://open.sina.com.cn/android/update.apk","verCode":"1","verName":"1.0"}]
有了新版本的应用apk,首先上传签名后的apk至指定的服务器地址(如本例:strURL="http://open.sina.com.cn/update.apk"),然后修改json文件的vercode、vername,注意apk和json的一致!
附源码:android在线版本更新源代码http://download.csdn.net/detail/dignity568/4168683