文档章节

实现Android应用自动更新

紫韵
 紫韵
发布于 2016/03/02 09:58
字数 1469
阅读 430
收藏 13
点赞 1
评论 1

一、自动更新实现流程

  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错误(程序未响应),所以该示例中的下载操作放到一个异步任务中进行的,也可以采用工作线程的方式


© 著作权归作者所有

共有 人打赏支持
紫韵
粉丝 3
博文 19
码字总数 34323
作品 0
武汉
加载中

评论(1)

h
heart162
createSDDir(path);
file = createSDFile(path + fileName);
这样写不好,相当于直接抛异常了,应该先判文件是否存在,把本来应该自己检查的错误丢给异常处理是不好的习惯
Android P Beta 2 及终版 API 强势来袭!

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

谷歌开发者 ⋅ 06/07 ⋅ 0

Android8.0与Android studio3.0不得不说的几个变化

最近,有朋友在自己的App上遇到这样的问题:在android 8.0系统的手机上图标变成了机器人,而在8.0以下的系统上却完全正常。这个问题的根因就是8.0系统做了一些改进,我们在适配应用的时候也需...

充电实践 ⋅ 04/15 ⋅ 0

Google I/O 2018 : 这里是所有你不能错过的大新闻

作者:陈伊莉 张伟 来自雷锋网的报道 又到了一年一度的科技公司竭尽全力展示各自软硬件产品优越性、前瞻性的盛会。 当地时间5月7日,微软召开了 Bulid 开发者大会。就在后一天,谷歌开发者大...

陈伊莉 ⋅ 05/09 ⋅ 0

谷歌I/O大会8大看点:有Android系统全面更新

谷歌I/O大会8大看点:有Android系统全面更新 2018-05-08 10:27编辑: 枣泥布丁分类:业界动态来源:cnBate Android谷歌I/O大会 招聘信息: C++工程师 Cocos2d-x游戏客户端开发 iOS开发工程师...

枣泥布丁 ⋅ 05/08 ⋅ 0

Android app 在线更新那点事儿(适配Android6.0、7.0、8.0)

一、前言 app在线更新是一个比较常见需求,新版本发布时,用户进入我们的app,就会弹出更新提示框,第一时间更新新版本app。在线更新分为以下几个步骤: 在线更新就上面几个步骤,前2步比较简...

codeGoogle ⋅ 04/28 ⋅ 0

我的Android重构之旅:架构篇

EF A舞蹈服 去年10月底来到了新公司,刚开始接手 Android 项目时,发现该项目真的是一团遭,项目开发上没有任何架构可言,开发人员连简单的 MVC、MVP 都不了解,Activity 及其臃肿,业务边界...

codeGoogle ⋅ 05/31 ⋅ 0

AndroidManifest.xml详解

我们在进行APP开发的时候都会遇到一个文件:AndroidManifest.xml。从刚开始进行Android开发,到现在已经过去了几个月,还是对这个文件一知半解,只知道它是配置用的。但是这文件里的东西具体...

闪电的蓝熊猫 ⋅ 05/14 ⋅ 0

Android Studio 3.2新功能特性

android studio3.2预览版本已经发布了,下面这些功能在最新的版本已经提供,但可能尚未在测试版本中发布渠道中提供。 什么是新的助理 Android Studio 3.2有一个新的Assistant面板,可以通知您...

我就是马云飞 ⋅ 06/14 ⋅ 0

Android P Beta!您想要知道的所有更新内容都在这里

I/O 大会的第一天,我们公布了下一个版本的 Android,也就是 Android P 的 beta 版本。Android P 将 AI 定位为操作系统的核心,并侧重于提供智能且简洁的体验。让我们一起来了解下这个版本带...

谷歌开发者 ⋅ 05/10 ⋅ 0

Android清单文件详解(一) ---- 一切从开始

在AndroidManifest.xml文件中,首先看到是的<manifest>节点,它是整个应用程序的基本属性,涵盖了默认进程名字,应用程序标识,安装位置,对系统的要求以及应用程序的版本等。它是AndroidMa...

mickelfeng ⋅ 06/04 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Mahout推荐算法API详解

前言 用Mahout来构建推荐系统,是一件既简单又困难的事情。简单是因为Mahout完整地封装了“协同过滤”算法,并实现了并行化,提供非常简单的API接口;困难是因为我们不了解算法细节,很难去根...

xiaomin0322 ⋅ 26分钟前 ⋅ 0

WampServer默认web服务器根目录位置

安装WampServer之后的web服务器根目录默认位置在WampServer安装目录下的www:

临江仙卜算子 ⋅ 28分钟前 ⋅ 0

Redux的一些手法记录

Redux Redux的基本概念见另一篇文。 这里记录一下Redux在项目中的实际操作的手法。 actions 首先定义action.js,actions的type,可以另起一个action-type.js文件。 action-type.js用来存...

LinearLaw ⋅ 29分钟前 ⋅ 0

android 手势检测(左右滑动、上下滑动)

GestureDetector类可以让我们快速的处理手势事件,如点击,滑动等。 使用GestureDetector分三步: 1. 定义GestureDetector类 2. 初始化手势类,同时设置手势监听 3. 将touch事件交给gesture...

王先森oO ⋅ 43分钟前 ⋅ 0

java 方法的执行时间监控 设置超时(Future 接口)

java 方法的执行时间监控 设置超时(Future 接口) import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor......

青峰Jun19er ⋅ 48分钟前 ⋅ 0

一名开源小白的Apache成长自述

今天收到了来自Apache Vote我成为Serviceomb项目Committer的邮件,代表自己的贡献得到了充分的肯定;除了感谢团队的给力支持,我更希望将自己的成长经历——如何践行Apache Way的心得介绍给大...

微服务框架 ⋅ 50分钟前 ⋅ 0

vim介绍、颜色显示和移动光标、一般模式下复制、剪切和粘贴

1.vim 是 vi 的升级版 vim 是带有颜色显示的 mini安装的系统,一般都不带有vim [root@aminglinux-128 ~]# yum install -y vim-enhanced已加载插件:fastestmirror, langpacksLoading mir...

oschina130111 ⋅ 50分钟前 ⋅ 0

Deepin 操作系统四面楚歌

作为国内做的最好的 Linux 发行版,源自 Debian sid 的 Deepin 目前正面临重重困境,新版本不断延期,开发人员离职,bug 长期得不到修复,和 Debian/Ubuntu 的兼容性问题也面临越来越严重的挑...

六库科技 ⋅ 50分钟前 ⋅ 0

MyBatis之动态sql

我们需要知道的是,使用mybatis重点是对sql的灵活解析和处理。在原先的UserMappser.xml中,我们这样查询表中满足条件的记录 : 123 <select id="findUserList" parameterType="userQuery...

瑟青豆 ⋅ 51分钟前 ⋅ 0

这届俄罗斯世界杯的冷门那么多怎么办?

最纯粹的世界杯,最神奇的大冷门。 德国0比1被墨西哥摩擦了。 日本历史性的赢了哥伦比亚。 C罗也挑平了西班牙。 梅西被冰岛狮吼吼愣神了。 就连11次进世界杯4强的巴西也被瑞士逼平了。 天台已...

开源中国众包平台 ⋅ 51分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部