Android应用检查更新并下载安装

现在很多的安卓APP都有检查更新的功能,最近也在做这个,在网上查阅了下相关的信息。

更新的原理就是检查客户端的版本号和服务端的版本号进行比对,如果服务端的版本号大于客户端,就提示更新,否则无,

一般会在服务端放一个APP和一个xml文件,xml文件中有版本号,下载地址等,当客户端请求服务端时,会得到xml文件,

并解析获得xml文件中的内容来判断是否需要更新等,实现的思路大致就是这样。

我在做这个时,参考了一个大神写的文章,地址:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1201/2088.html

感谢分享!

先来看一下我的结构图:一个客户端一个服务端,客户端eclipse不介绍了,服务端也是eclipse和tomact,服务端可以有两个方式,一种我是

用了一个servlet来封装一个json数组,当然也需要用到Gson了,往list集合中添加数据,并转换为json数组;另一中直接给个.json文件,里面放置

需要用到的版本号地址等信息;在浏览器中请求Servlet可以得到这样的一个信息:

<pre name="code" class="html"><pre name="code" class="plain">[{"updateMessage":"1,增加一些功能;2,修复了一些BUG","url":"http://10.8.2.81:8080/WebTest/CZSoft.apk","versionCode":"2"}]
<span style="font-family: Arial, Helvetica, sans-serif;">也就是json数组,里面是版本号地址等,还有就是json 文件了,里面也是放置的json数组,同上;</span>

 
 



上面是结构图,下面贴代码:

首先是MainActivity里面的,比较简单,就是一个按钮:主要就是在检查更新的地方加上:

UpdateChecker updateChecker = new UpdateChecker(MainActivity.this);
updateChecker.setCheckUrl("http://10.8.2.81:8080/WebTest/AppServlet");
//updateChecker.setCheckUrl("http://10.8.2.81:8080/WebTest/version.json");
updateChecker.checkForUpdates();
加上这两句代码即可。


MainActivity 一个按钮的点击事件,

public class MainActivity extends Activity {
	
	private Button updateBtn;
	
	//UpdateChecker updateChecker;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {		
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.main);
		
		/**
		 * 可以是手动检测更新,也可以是打开app后自己检测,根据需要
		 */
		updateBtn = (Button) findViewById(R.id.updateBtn);
		updateBtn.setOnClickListener(new OnClickListener() {			
			@Override
			public void onClick(View v) {
				/**
				 * 有两种方式,一种是直接请求得到json,另一种是给一个.json的文件,同样的功能
				 * 很多都是在服务端放置.xml文件,放json文件也可
				 */
				UpdateChecker updateChecker = new UpdateChecker(MainActivity.this);
				updateChecker.setCheckUrl("http://10.8.2.81:8080/WebTest/AppServlet");
				//updateChecker.setCheckUrl("http://10.8.2.81:8080/WebTest/version.json");
				updateChecker.checkForUpdates();
				
			}
		});
		
	}

}
这里的请求地址得到的就是上面说的json数组:
[{"updateMessage":"1,增加一些功能;2,修复了一些BUG","url":"http://10.8.2.81:8080/WebTest/CZSoft.apk","versionCode":"2"}]
当然也可以使用下面的地址,直接请求json文件,得到同样的结构;

点击按钮检查更新时:如果从请求地址得到的版本号大于客户端的版本号就会提示更新,点击下载就会自动下载,这么并没有做后台更新,



下载完成后自动提示安装:


点击安装就自动安装了,


AppVersion  代码:版本号,地址等,实体类

package com.android.update;

public class AppVersion {

	 private String updateMessage;
	    private String apkUrl;
	    private int apkCode;
	    public static final String APK_DOWNLOAD_URL = "url";
	    public static final String APK_UPDATE_CONTENT = "updateMessage";
	    public static final String APK_VERSION_CODE = "versionCode";
	                                                                                                                                           
	    public void setUpdateMessage(String updateMessage) {
	        this.updateMessage = updateMessage;
	    }
	                                                                                                                                           
	    public String getUpdateMessage() {
	        return updateMessage;
	    }
	                                                                                                                                           
	    public void setApkUrl(String apkUrl) {
	        this.apkUrl = apkUrl;
	    }
	                                                                                                                                           
	    public String getApkUrl() {
	        return apkUrl;
	    }
	                                                                                                                                           
	    public void setApkCode(int apkCode) {
	        this.apkCode = apkCode;
	    }
	                                                                                                                                           
	    public int getApkCode() {
	        return apkCode;
	    }
	}

UpdateChecker  代码:

package com.android.update;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.zip.GZIPInputStream;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.ResultReceiver;
import android.util.Log;
import android.widget.Toast;

public class UpdateChecker {

	public static final String TAG = "UpdateChecker";

	private Context mContext;
	// 检查版本信息的线程
	private Thread mThread;
	// 版本对比地址
	private String mCheckUrl;
	private AppVersion mAppVersion;
	// 下载apk的对话框
	private ProgressDialog mProgressDialog;

	private File apkFile;

	public void setCheckUrl(String url) {
		mCheckUrl = url;

		Log.e(TAG, "---url===" + mCheckUrl);
	}

	public UpdateChecker(Context context) {
		mContext = context;
		// instantiate it within the onCreate method
		mProgressDialog = new ProgressDialog(context);
		mProgressDialog.setMessage("正在下载");
		mProgressDialog.setIndeterminate(false);
		mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		mProgressDialog.setCancelable(true);
		mProgressDialog
				.setOnCancelListener(new DialogInterface.OnCancelListener() {
					@Override
					public void onCancel(DialogInterface dialog) {

					}
				});
		mProgressDialog
				.setOnDismissListener(new DialogInterface.OnDismissListener() {
					@Override
					public void onDismiss(DialogInterface dialog) {
						// TODO Auto-generated method stub

					}
				});
	}

	public void checkForUpdates() {
		if (mCheckUrl == null) {
			// throw new Exception("checkUrl can not be null");
			return;
		}
		final Handler handler = new Handler() {
			public void handleMessage(Message msg) {
				if (msg.what == 1) {
					mAppVersion = (AppVersion) msg.obj;
					try {
						int versionCode = mContext.getPackageManager()
								.getPackageInfo(mContext.getPackageName(), 0).versionCode;
						if (mAppVersion.getApkCode() > versionCode) {
							showUpdateDialog();
						} else {
							Toast.makeText(mContext, "已经是最新版本",
									Toast.LENGTH_SHORT).show();
						}
					} catch (PackageManager.NameNotFoundException ignored) {
						//
					}
				}
			}
		};
		mThread = new Thread() {
			@Override
			public void run() {
				// if (isNetworkAvailable(mContext)) {
				Message msg = new Message();
				String json = sendPost();
				Log.i("jianghejie", "json = " + json);
				if (json != null) {
					AppVersion appVersion = parseJson(json);
					msg.what = 1;
					msg.obj = appVersion;
					handler.sendMessage(msg);
				} else {
					Log.e(TAG, "can't get app update json");
				}
			}
		};
		mThread.start();
	}

	/**
	 * dialog提示信息
	 */
	public void showUpdateDialog() {
		AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
		// builder.setIcon(R.drawable.icon);
		builder.setTitle("有新版本");
		builder.setMessage(mAppVersion.getUpdateMessage());
		builder.setPositiveButton("下载", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int whichButton) {
				downLoadApk();
			}
		});
		builder.setNegativeButton("忽略", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int whichButton) {

			}
		});
		builder.show();

	}

	/**
	 * 下载apk
	 */
	public void downLoadApk() {
		String apkUrl = mAppVersion.getApkUrl();
		String dir = mContext.getExternalFilesDir("apk").getAbsolutePath();
		File folder = Environment.getExternalStoragePublicDirectory(dir);
		if (folder.exists() && folder.isDirectory()) {
			// do nothing
		} else {
			folder.mkdirs();
		}
		String filename = apkUrl.substring(apkUrl.lastIndexOf("/"),
				apkUrl.length());
		String destinationFilePath = dir + "/" + filename;
		// Log.e(TAG, "---filename==="+filename);
		// Log.e(TAG, "---destinationFilePath==="+destinationFilePath);
		apkFile = new File(destinationFilePath);
		mProgressDialog.show();
		Intent intent = new Intent(mContext, DownloadService.class);
		intent.putExtra("url", apkUrl);
		intent.putExtra("dest", destinationFilePath);
		intent.putExtra("receiver", new DownloadReceiver(new Handler()));
		mContext.startService(intent);

	}

	/**
	 * 请求网络
	 * 
	 * @return
	 */
	protected String sendPost() {
		HttpURLConnection uRLConnection = null;
		InputStream is = null;
		BufferedReader buffer = null;
		String result = null;
		try {
			URL url = new URL(mCheckUrl);
			uRLConnection = (HttpURLConnection) url.openConnection();
			uRLConnection.setDoInput(true);
			uRLConnection.setDoOutput(true);
			uRLConnection.setRequestMethod("POST");
			uRLConnection.setUseCaches(false);
			uRLConnection.setConnectTimeout(10 * 1000);
			uRLConnection.setReadTimeout(10 * 1000);
			uRLConnection.setInstanceFollowRedirects(false);
			uRLConnection.setRequestProperty("Connection", "Keep-Alive");
			uRLConnection.setRequestProperty("Charset", "UTF-8");
			uRLConnection
					.setRequestProperty("Accept-Encoding", "gzip, deflate");
			uRLConnection
					.setRequestProperty("Content-Type", "application/json");
			uRLConnection.connect();
			is = uRLConnection.getInputStream();
			String content_encode = uRLConnection.getContentEncoding();
			if (null != content_encode && !"".equals(content_encode)
					&& content_encode.equals("gzip")) {
				is = new GZIPInputStream(is);
			}
			buffer = new BufferedReader(new InputStreamReader(is));
			StringBuilder strBuilder = new StringBuilder();
			String line;
			while ((line = buffer.readLine()) != null) {
				strBuilder.append(line);
			}
			result = strBuilder.toString();
		} catch (Exception e) {
			Log.e(TAG, "http post error", e);
		} finally {
			if (buffer != null) {
				try {
					buffer.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (uRLConnection != null) {
				uRLConnection.disconnect();
			}
		}
		return result;
	}

	/**
	 * 解析json信息
	 * 
	 * @param json
	 * @return
	 */
	private AppVersion parseJson(String json) {
		AppVersion appVersion = new AppVersion();
		try {
			/**
			 * 这里根据请求返回的是json数组还是对象,选择相应的解析方式
			 */
			JSONArray jsonArray = new JSONArray(json);
			// JSONObject obj = new JSONObject(json);
			for (int i = 0; i < jsonArray.length(); i++) {
				JSONObject obj = jsonArray.getJSONObject(0);
				Log.e(TAG, "---obj==" + obj);
				String updateMessage = obj
						.getString(AppVersion.APK_UPDATE_CONTENT);
				String apkUrl = obj.getString(AppVersion.APK_DOWNLOAD_URL);

				// Log.e(TAG, "---apkUrl=="+apkUrl);

				int apkCode = obj.getInt(AppVersion.APK_VERSION_CODE);
				appVersion.setApkCode(apkCode);
				appVersion.setApkUrl(apkUrl);
				appVersion.setUpdateMessage(updateMessage);
			}

		} catch (JSONException e) {
			Log.e(TAG, "parse json error", e);
		}
		return appVersion;
	}

	private class DownloadReceiver extends ResultReceiver {
		public DownloadReceiver(Handler handler) {
			super(handler);
		}

		@Override
		protected void onReceiveResult(int resultCode, Bundle resultData) {
			super.onReceiveResult(resultCode, resultData);
			if (resultCode == DownloadService.UPDATE_PROGRESS) {
				int progress = resultData.getInt("progress");
				mProgressDialog.setProgress(progress);
				if (progress == 100) {
					mProgressDialog.dismiss();
					// 如果没有设置SDCard写权限,或者没有sdcard,apk文件保存在内存中,需要授予权限才能安装
					String[] command = { "chmod", "777", apkFile.toString() };
					try {
						ProcessBuilder builder = new ProcessBuilder(command);
						builder.start();
						Intent intent = new Intent(Intent.ACTION_VIEW);
						intent.setDataAndType(Uri.fromFile(apkFile),
								"application/vnd.android.package-archive");
						mContext.startActivity(intent);
					} catch (Exception e) {

					}
				}
			}
		}
	}

}

DownloadService  代码:

package com.android.update;

import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import android.app.IntentService;
import android.content.Intent;
import android.os.Bundle;
import android.os.ResultReceiver;

public class DownloadService extends IntentService {

	public static final int UPDATE_PROGRESS = 8344;
	private static final String TAG = "DownloadService";

	public DownloadService() {
		super("DownloadService");

	}

	@Override
	protected void onHandleIntent(Intent intent) {
		String urlToDownload = intent.getStringExtra("url");

		// Log.e(TAG, "---------urlToDownload"+urlToDownload);
		String fileDestination = intent.getStringExtra("dest");
		ResultReceiver receiver = (ResultReceiver) intent
				.getParcelableExtra("receiver");
		try {
			URL url = new URL(urlToDownload);
			URLConnection connection = url.openConnection();
			connection.connect();
			// this will be useful so that you can show a typical 0-100%
			// progress bar
			int fileLength = connection.getContentLength();
			// download the file
			InputStream input = new BufferedInputStream(
					connection.getInputStream());
			OutputStream output = new FileOutputStream(fileDestination);
			byte data[] = new byte[100];
			long total = 0;
			int count;
			while ((count = input.read(data)) != -1) {
				total += count;
				// publishing the progress....
				Bundle resultData = new Bundle();
				resultData.putInt("progress", (int) (total * 100 / fileLength));
				receiver.send(UPDATE_PROGRESS, resultData);
				output.write(data, 0, count);
			}
			output.flush();
			output.close();
			input.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		Bundle resultData = new Bundle();
		resultData.putInt("progress", 100);
		receiver.send(UPDATE_PROGRESS, resultData);
	}

}
布局文件就一个main.xml里面就是一个按钮,这个就不贴了。

服务端的代码:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		response.setContentType("text/html");
		response.setCharacterEncoding("UTF-8");		
		AppMessage apps = new AppMessage();		
		apps.setUpdateMessage("1,增加一些功能;2,修复了一些BUG");
		apps.setUrl("http://10.8.2.81:8080/WebTest/CZSoft.apk");
		apps.setVersionCode("2");
		List<AppMessage> list = new ArrayList<AppMessage>();
		list.add(apps);
		/**
		 * 用gson将list集合转换为json数组
		 */
		Gson gson = new Gson();		
		String jsons = gson.toJson(list);		
		System.out.print(jsons);
		PrintWriter out = response.getWriter(); 
		out.print(jsons);
		
	}

就是一个servlet里面封装了一个json数组,当然,服务端里面还会放置apk,versison.json文件,最后不要忘了在AndroidManifest.xml

文件中配置service,还有相关的权限:

<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
       <activity android:name="com.android.update.MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name="com.android.update.DownloadService"/>
    </application>
<uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.WAKE_LOCK" />


以上就是这部分的代码,最参考用,

最后还是感谢http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1201/2088.html这位大神的博客,

感谢分享!
源码下载



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值