文档章节

实现Android应用自动更新

紫韵
 紫韵
发布于 2016/03/02 09:58
字数 1469
阅读 462
收藏 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错误(程序未响应),所以该示例中的下载操作放到一个异步任务中进行的,也可以采用工作线程的方式


© 著作权归作者所有

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

评论(1)

h
heart162
createSDDir(path);
file = createSDFile(path + fileName);
这样写不好,相当于直接抛异常了,应该先判文件是否存在,把本来应该自己检查的错误丢给异常处理是不好的习惯
android应用开发简单理解

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

blackylin
2013/06/18
0
2
Android消息推送完美方案

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

Yujan
2014/04/10
0
0
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...

谷歌开发者
06/07
0
0
[安卓教学] 创建你的第一个安卓应用 - 第四课 - 启动另外一个activity

在完成上节课后,你有了可以显示包含一个文字输入框和一个按钮的activity(一个屏幕)的应用。在这节课中,你将在“MainActivity”类中添加一些代码,使得在用户点击“Send”按钮时,启动另外...

夜游长空
2014/03/22
0
2

没有更多内容

加载失败,请刷新页面

加载更多

Vavr User Guide中英对照版

Vavr User Guide(Vavr用户指南) Daniel Dietrich, Robert Winkler - Version 0.9.2,2018-10-01 0. Vavr Vavr是Java 8 的对象函数式扩展,目标是减少代码行数,提高代码质量,提供了持久化集合...

白石
25分钟前
1
0
Spark2.40 Streaming编程指南

Spark Streaming编程指南 概观 一个快速的例子 基本概念 链接 初始化StreamingContext 离散流(DStreams) 输入DStreams和Receivers DStreams的转换 DStreams的输出操作 DataFrame和SQL操作 ...

天天爬网
26分钟前
0
0
oozie 调度pyspark

http://www.learn4master.com/big-data/pyspark/run-pyspark-on-oozie

MPRO
30分钟前
1
0
转:文件系统read,write缓存,有点意思

当我们在linux系统下写程序的时候,经常会读文件或者写文件,既然要操作文件,就会用文件操作函数,其中涉及读写的不乏有read/write或者fread/fwrite,但是我们应该选择哪一种呢,少安毋躁,...

臧教授
35分钟前
1
0
通过示例学习Javascript闭包

译者按: 在上一篇博客,我们通过实现一个计数器,了解了如何使用闭包(Closure),这篇博客将提供一些代码示例,帮助大家理解闭包。 原文: JavaScript Closures for Dummies 译者: Fundebug 为...

Fundebug
35分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部