文档章节

实现Android应用自动更新

紫韵
 紫韵
发布于 2016/03/02 09:58
字数 1469
阅读 467
收藏 13

一、自动更新实现流程

  1. 从服务器端获取最新应用的版本信息

  2. 从本地配置文件中读取本地应用的版本信息

  3. 比较服务端版本信息和本地版本信息,若服务器端的版本号大于本地的版本号,则转4,否则结束

  4. 弹框提示用户应用程序有更新的版本,询问是否需要下载,若是则转5,否则弹框消失,结束

  5. 从服务器端获取最新版应用程序,存于本地,并进行安装

二、自动更新实现过程

  1. 新建一个Android工程,如我的工程文件目录为:                                              

  2. 在工程文件中新建一个activity,用于呈现自动更新提示对话框。如上图工程中的AutoUpdateActivity,该activity代码如下:

  3. //一个用于呈现提示用于进行更新的页面
    public class AutoUpdateActivity extends Activity {
    	private static final String TAG = "AutoUpdateActivity";    //日志打印标志
    	private static int localVersionCode, serverVersionCode = 0;   //本地和服务器端应用版本号
    	private String fileName, filePath;    //应用程序保存文件名和文件路径
    	private Context context;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		//设置当前activity的布局文件,任意布局文件皆可
    		setContentView(R.layout.activity_auto_update);    
    		context = this;
    		filePath = "Star";
    	}
    
    	@Override
    	protected void onResume() {
    		super.onResume();
    		// 检查是否需要进行版本更新,该函数必须在onResume或者onStart中调用,否则可能报错
    		checkUpdate();
    	}
    
    	// 检查是否有新的版本
    	private void checkUpdate() {
    
    		// 获取本地应用程序版本号
    		try {
    			localVersionCode = context.getPackageManager().getPackageInfo(
    					context.getPackageName(), 0).versionCode;
    		} catch (NameNotFoundException e) {
    			e.printStackTrace();
    		}
    
    		// 比较本地版本号与服务器端版本号,判断是否需要更新
    		if (getServerVersionCode() > localVersionCode) {
    			fileName = "Star.apk";
    			showUpdateDialog();
    		}
    	}
    
    	// 获取服务器端应用程序版本号
    	private int getServerVersionCode() {
    		RequestParams params = new RequestParams();
    		StringBuilder versionCode = new StringBuilder();
    		versionCode.append("{\"getVersionCode\"}");
    		params.put("param", versionCode.toString());
    
    		AsyncHttpClient client = new AsyncHttpClient();
    		//向服务器请求最新版本号的URL地址
    		String urlString = utils.Constants.SERVER_BASE_URL
    				+ utils.Constants.GET_VERSION_CODE;
    		client.post(urlString, params, new AsyncHttpResponseHandler() {
    			@Override
    			public void onSuccess(String response) {
    				serverVersionCode = Integer.parseInt(JsonUtil
    						.getResult(response));
    			}
    
    			@Override
    			public void onFailure(Throwable error, String content) {
    				serverVersionCode = 0;
    				Log.d(TAG, content + " ");
    			}
    		});
    		return serverVersionCode;
    	}
    
    	// 显示应用程序更新对话框
    	private void showUpdateDialog() {
    	        //创建一个对话框
    		AlertDialog.Builder builder = new AlertDialog.Builder(context);
    		//设置对话框标题、显示信息等
    		builder.setTitle("版本更新").setMessage("Star有新版本啦,快下载体验吧~")
    				.setIcon(com.star.R.drawable.ic_update_dialog);
    		//设置对话框的响应按钮
    		builder.setPositiveButton("下载", new OnClickListener() {
    			@Override
    			public void onClick(DialogInterface dialog, int which) {
    				dialog.dismiss();
    				//DownTask是一个我自己写的AsyncTask类,用于下载最新版的应用程序
    				DownTask downTask = new DownTask(context, filePath, fileName);
    				downTask.execute(Constants.SERVER_BASE_URL
    						+ Constants.GET_NEW_VERSION);
    			}
    		});
    		builder.setNegativeButton("以后再说", new OnClickListener() {
    
    			@Override
    			public void onClick(DialogInterface dialog, int which) {
    				dialog.dismiss();    //对话框消失
    			}
    		});
    		builder.show();    //必须调用该函数,否则创建的对话框将不会显示
    	}
    }

        上面代码中的checkUpdate()函数包含了上述原理一中的1、2、3步,当if条件判断为真时,显示应用更新提示对话框,即调用showUpdateDilaog()方法,用户选择“下载”,则下载该应用程序,并进行更新(这部分为调用DownTask进行处理);

  4. DownTask类代码如下:

  5. //DownTask是通过一部任务进行应用下载的,也可以开启一个新的工作线程进行下载
    public class DownTask extends AsyncTask<String, Integer, String> {
    	private static final String TAG = "DownTask";
    	private Context mContext;
    	private AlertDialog aDialog;
    	private String filePath;
    	private String fileName;
    
    	/**
    	 * 构造函数
    	 * 
    	 * @param ctx
    	 * @param savePath
    	 *            :下载资源保存路径
    	 @param fileName
    	 *            :下载资源保存文件名
    	 */
    	public DownTask(Context ctx, String savePath, String fileName) {
    		mContext = ctx;
    		filePath = savePath;
    		this.fileName = fileName;
    	}
    
    	@SuppressWarnings("unused")
    	@Override
    	protected String doInBackground(String... params) {
    		//FileUtils是自己写的一个文件工具
    		FileUtils fileUtil = new FileUtils();
    		String result = null;
    		File file;
    		try {
    			file = new File(fileUtil.getSDPATH() + filePath + "/" + fileName);
    			if (file.exists()) {
    				result = "File exist!";
    				Log.i(TAG, result);
    			} else {
    				fileUtil.createSDDir(filePath);
    				file.createNewFile();
    				URL url = new URL(params[0]);
    				HttpURLConnection urlConnection = (HttpURLConnection) url
    						.openConnection();
    				fileUtil.write2SDFromInput(filePath, fileName,
    						urlConnection.getInputStream());
    				if (file != null) {
    					result = "success";
    				} else {
    					result = "Fail";
    				}
    			}
    		} catch (IOException e1) {
    			e1.printStackTrace();
    		}
    		Log.i(TAG, result);
    		return result;
    	}
    
    	@Override
    	protected void onPostExecute(String result) {
    		Toast.makeText(mContext, "最新版Star已经下载好啦,赶快试用吧!", 
    		                Toast.LENGTH_LONG);
    		Log.i(TAG, "Finish download");
    		aDialog.dismiss();
    		installApk();
    	}
    
    	// 安装apk文件
    	private void installApk() {
    		File apkfile = new File(filePath + "/" + fileName);
    		if (!apkfile.exists()) {
    			return;
    		}
    		
    		//调用安装应用程序的Intent
    		Intent i = new Intent(Intent.ACTION_VIEW);
    		i.setDataAndType(Uri.parse("file://" + apkfile.toString()),
    				"application/vnd.android.package-archive");
    		mContext.startActivity(i);
    	}
    
    	@Override
    	protected void onPreExecute() {
    		AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
    		builder.setTitle("下载Star").setMessage("新版本下载中...")
    				.setIcon(R.drawable.ic_update_dialog).show();
    		aDialog = builder.create();
    	}
    }
  6. FileUtils代码:

  7. public class FileUtils {
    	private String SDPATH;
    
    	private int FILESIZE = 4 * 1024;
    
    	public String getSDPATH() {
    		return SDPATH;
    	}
    	
    	public FileUtils() {
    		// 得到当前外部存储设备的目录( /SDCARD )
    		SDPATH = Environment.getExternalStorageDirectory() + "/";
    	}
    
    	/**
    	 * 在SD卡上创建文件
    	 * 
    	 * @param fileName
    	 * @return
    	 * @throws IOException
    	 */
    	public File createSDFile(String fileName) throws IOException {
    		File file = new File(SDPATH + fileName);
    		file.createNewFile();
    		return file;
    	}
    
    	/**
    	 * 在SD卡上创建目录
    	 * 
    	 * @param dirName
    	 * @return
    	 */
    	public File createSDDir(String dirName) {
    		File dir = new File(SDPATH + dirName);
    		dir.mkdir();
    		return dir;
    	}
    
    	/**
    	 * 判断SD卡上的文件夹是否存在
    	 * 
    	 * @param fileName
    	 * @return
    	 */
    	public boolean isFileExist(String fileName) {
    		File file = new File(SDPATH + fileName);
    		return file.exists();
    	}
    
    	/**
    	 * 将一个InputStream里面的数据写入到SD卡中
    	 * 
    	 * @param path
    	 * @param fileName
    	 * @param input
    	 * @return
    	 */
    	public File write2SDFromInput(String path, String fileName,
    			InputStream input) {
    		File file = null;
    		OutputStream output = null;
    		try {
    			createSDDir(path);
    			file = createSDFile(path + fileName);
    			output = new FileOutputStream(file);
    			byte[] buffer = new byte[FILESIZE];
    
    			int length;
    			while ((length = (input.read(buffer))) > 0) {
    				output.write(buffer, 0, length);
    			}
    			output.flush();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			try {
    				output.close();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    		return file;
    	}
    }
  8. 最后别忘了在manifest文件中添加相应的权限以及该activity

  9.     <!-- 写SD卡的权限 -->
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <!--  网络权限 -->
        <uses-permission android:name="android.permission.INTERNET"/>
    
        <application
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity
                android:name="activity.AutoUpdateActivity"
                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>
  10. 注意:Android不能在UI线程中进行比较耗时的操作,否则可能导致页面阻塞,甚至引发ANR错误(程序未响应),所以该示例中的下载操作放到一个异步任务中进行的,也可以采用工作线程的方式


© 著作权归作者所有

共有 人打赏支持
紫韵
粉丝 4
博文 21
码字总数 34323
作品 0
武汉
私信 提问
加载中

评论(1)

h
heart162
createSDDir(path);
file = createSDFile(path + fileName);
这样写不好,相当于直接抛异常了,应该先判文件是否存在,把本来应该自己检查的错误丢给异常处理是不好的习惯
Android 应用的自动升级、更新模块的实现

我们看到很多Android应用都具有自动更新功能,用户一键就可以完成软件的升级更新。得益于Android系统的软件包管理和安装机制,这一功能实现起来相当简单,下面我们就来实践一下。首先给出界面...

无鸯
2011/09/27
29K
5
android应用开发简单理解

做了个应用,总结下对于android 应用的简单理解 从上图可以简单看出,主线程启动,这里把各种操作称为action,主要分为3部分: ui(视图绘制)、event(事件处理)和other(数据或网络等处理) 1.当...

blackylin
2013/06/18
0
2
GoogleServices之GooglePlayService官方文档翻译

GoogleServices之GooglePlayService官方文档翻译 在更广泛的设备上给你的应用程序更多的特性去吸引用户,使用GooglePlayService,你的应用程序能利用最新的优势,谷歌驱动特性比如地图,Googl...

CTS_Peter
2015/01/09
0
0
Android P Beta 2 及终版 API 强势来袭!

在四周前的 Google I/O 开发者大会上,我们发布了Android P 的首个 Beta 版,将人工智能 (AI) 定位为操作系统的核心,并侧重于提供智能且简洁的体验。 今天,我们隆重推出 Android P Beta 2...

谷歌开发者
2018/06/07
0
0
Android消息推送完美方案

推送功能在手机应用开发中越来越重要,已经成为手机开发的必须。在Android应用开发中,由于众所周知的原因,Android消息推送我们不得不大费周折。本文就是用来和大家共同探讨一种Android消息...

Yujan
2014/04/10
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周一乱弹 —— 白掌柜说了卖货不卖身

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @爱漫爱 :这是一场修行分享羽肿的单曲《Moony》 手机党少年们想听歌,请使劲儿戳(这里) @clouddyy :开不开心? 开心呀, 我又不爱睡懒觉…...

小小编辑
今天
8
0
大数据教程(11.7)hadoop2.9.1平台上仓库工具hive1.2.2搭建

上一篇文章介绍了hive2.3.4的搭建,然而这个版本已经不能稳定的支持mapreduce程序。本篇博主将分享hive1.2.2工具搭建全过程。先说明:本节就直接在上一节的hadoop环境中搭建了! 一、下载apa...

em_aaron
今天
3
0
开始看《JSP&Servlet学习笔记》

1:WEB应用简介。其中1.2.1对Web容器的工作流程写得不错 2:编写Servlet。搞清楚了Java的Web目录结构,以及Web.xml的一些配置作用。特别是讲了@WebServlet标签 3:请求与响应。更细致的讲了从...

max佩恩
今天
4
0
mysql分区功能详细介绍,以及实例

一,什么是数据库分区 前段时间写过一篇关于mysql分表的的文章,下面来说一下什么是数据库分区,以mysql为例。mysql数据库中的数据是以文件的形势存在磁盘上的,默认放在/mysql/data下面(可...

吴伟祥
今天
3
0
SQL语句查询

1.1 排序 通过order by语句,可以将查询出的结果进行排序。放置在select语句的最后。 格式: SELECT * FROM 表名 ORDER BY 排序字段ASC|DESC; ASC 升序 (默认) DESC 降序 1.查询所有商品信息,...

stars永恒
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部