文档章节

实现Android应用自动更新

紫韵
 紫韵
发布于 2016/03/02 09:58
字数 1469
阅读 440
收藏 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
GoogleServices之GooglePlayService官方文档翻译

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

CTS_Peter
2015/01/09
0
0
Android消息推送完美方案

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

Yujan
2014/04/10
0
0
Android P Beta 2 及终版 API 强势来袭!

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

谷歌开发者
06/07
0
0
Android应用自动更新功能实现使用AsyncTask!

我所开发应用不是面向大众的应用,所以无法放到应用市场去让大家下载,然后通过应用市场更新.所以我必要做一个应用自动更新功能.但是不难,Thanks to下面这篇博客: Android应用自动更新功能的实...

李海珍
2012/05/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

精选Spring Boot三十五道必知必会知识点!

Spring Boot、Spring MVC 和 Spring 有什么区别? 1、Spring Spring最重要的特征是依赖注入。所有 SpringModules 不是依赖注入就是 IOC 控制反转。 当我们恰当的使用 DI 或者是 IOC 的时候,...

java知识分子
16分钟前
1
0
docker多容器部署lnmp环境

环境:RHEL7.5 ip:192.168.10.102,主机名:lb02 一、创建web、数据库目录 web网站目录为:/wwwroot,属主属组:www [root@lb02 ~]# mkdir /wwwroot[root@lb02 ~]# useradd -s /sbin/nolo...

人在艹木中
45分钟前
1
0
eclipse运行springboot项目报错‘找不到或无法加载主类’

这是一个很烦躁的问题~,往往困住大家好长时间,然后各种百度。借此,咱将这个问题有可能产生的原因进行一下总结。若有不完善之处欢迎大家在下面留言指出~~ Duang!问题出现 然后开始尝试解决...

Code辉
今天
1
0
springboot oauth2 跨域设置

@Overridepublic void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/security/**") .authentica......

昆虫大侠
今天
1
0
08-利用思维导图梳理JavaSE-泛型

08-利用思维导图梳理JavaSE-泛型 主要内容 1.泛型的基本概念 1.1.定义 1.2.使用前提 1.3.使用泛型的好处 2.泛型的使用 2.1.泛型类定义 2.2.泛型对象定义 2.3.泛型中的构造方法 2.4.泛型方法的...

飞鱼说编程
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部