带更新内容提示的自动更新的一种实现
思路如下:
1.实现一个只有程序安装后第一次打开时才会执行的方法,这样方便在配置文件PreferenceManager.getDefaultSharedPreferences(Context)中保存程序的一些信息,比如apk安装时间,这个值之后会和服务器上的apk修改时间做比较来判断是否要更新,如我这个例子里的showOnFirstLaunch(),程序入口活动JumpActivity?onCreate时获取程序包信息中的versionCode版本代码号,如果与配置中保存的版本号不一致,表示安装新版后第一次打开程序,则执行之后的代代码片段,如保存当前版本号,设置新版已安装的值为true,多设了这个值的目的是,我们自动更新时虽然下载了新版本,但是没有对其安装,则打开旧版本时提示下载的更新没有被安装,给用户一个安装的选择。
?
JumpActivity.class
private void showOnFirstLaunch() { setContentView(R.layout.splash); try { PackageInfo info = getPackageManager().getPackageInfo(PACKAGE_NAME, 0); int currentVersion = info.versionCode; prefs = PreferenceManager.getDefaultSharedPreferences(this); int lastVersion = prefs.getInt(PreferencesActivity.KEY_HELP_VERSION_SHOWN, 0); if (currentVersion != lastVersion) {//安装好后第一次启动 //设置版本号 prefs.edit().putInt(PreferencesActivity.KEY_HELP_VERSION_SHOWN, currentVersion).commit(); prefs.edit().putBoolean(PreferencesActivity.KEY_LATEST_VERSION_INSTALL, true).commit(); File apk = new File(apkFile);if(apk.exists())apk.delete();//显示关于对话框Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); intent.setClassName(this, HelpActivity.class.getName()); startActivity(intent); finish(); }else {//不是第一次启动 //是否完成上次下载的更新的安装 if(!prefs.getBoolean(PreferencesActivity.KEY_LATEST_VERSION_INSTALL, true)){ new AlertDialog.Builder(JumpActivity.this) .setTitle(R.string.app_update_title) .setCancelable(false) .setMessage(R.string.app_update_not_install) .setPositiveButton(R.string.button_update_ok,new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) {File apk = new File(apkFile); if(apk.exists())install();//下载的文件存在则安装 else {//不存在则提示重新下载 Intent update = new Intent(JumpActivity.this,UpdateService.class); startService(update); }} }) .setNegativeButton(R.string.button_update_no,new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) {//如果放弃本次更新,将不再进行提示prefs.edit().putBoolean(PreferencesActivity.KEY_LATEST_VERSION_INSTALL, true).commit();Log.v(TAG, "129");launchActivity();} }) .show(); return; } launchActivity(); } } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, e); } }?
?
?
private void install() { Intent i = new Intent(); i.setAction(Intent.ACTION_VIEW); i.setDataAndType(Uri.fromFile(new File(apkFile)), "application/vnd.android.package-archive"); startActivity(i); }
?这个例子做了一个简单的跳转功能,通过Integer.parseInt(Build.VERSION.SDK) >= 5来分别启动适合不同系统版本的活动,使程序可以兼容跟多的手机。
?
private void launchActivity(){ if (Integer.parseInt(Build.VERSION.SDK) >= CameraManager.VERSION_CODES_LEVEL) {intent = new Intent(this,BJFILE.class);}else {intent = new Intent(this,EarlyBJFILE.class);} startActivity(intent); finish(); }?
2.在BJFILE或EarlyBJFILE活动中重我们使用一个Service来检查首否需要更新应用,当服务器上的apk文件的最后修改时间与程序配置文件中的最后修改时间不同,则表示发现新更新。
?
UpdateService.class
?
@Overridepublic void onStart(Intent intent, int startId) {super.onStart(intent, startId);Log.v(TAG, "onStart");new Thread(){public void run(){if(checkUpdate()){//如果有更新,则显示更新界面UpdateActivity,类似一个对话框 Intent update = new Intent(UpdateService.this,UpdateActivity.class); update.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); update.putExtra(VERSITON, newVersion); startActivity(update); }else stopSelf();}}.start();} //可能是不存在该请求对象,返回的向应头中没有设置last-modified域,则取到的值为0private long getLastModified() {try {URL url = new URL(UpdateActivity.updateURL);URLConnection con = (URLConnection) url.openConnection();Log.v(TAG, "con.getLastModified()"+con.getLastModified());return con.getLastModified();} catch (MalformedURLException e) {e.printStackTrace();Log.v(TAG, "MalformedURLException");return 1;} catch (IOException e) {e.printStackTrace();Log.v(TAG, "IOException");return 2;}}//判断是否需要更新private boolean checkUpdate(){SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);long latest = prefs.getLong(PreferencesActivity.KEY_LATEST_VERSION, 0);newVersion = getLastModified();Log.v(TAG, "latest=" + latest);Log.v(TAG, "newVersion=" + newVersion);if(newVersion>latest)return true;else return false;}
?3.UpdateActivity的样式为
?
<style name="BackgroundOnly"><item name="android:windowBackground">@null</item><item name="android:windowContentOverlay">@null</item><item name="android:windowAnimationStyle">@null</item><item name="android:windowNoTitle">true</item><item name="android:windowNoDisplay">true</item><item name="android:windowIsFloating">true</item></style>
?源代码
import java.io.BufferedReader;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.client.ClientProtocolException;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import android.app.Activity;import android.app.AlertDialog;import android.app.ProgressDialog;import android.content.DialogInterface;import android.content.Intent;import android.content.SharedPreferences;import android.net.Uri;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.os.Message;import android.preference.PreferenceManager;import android.util.Log;public class UpdateActivity extends Activity{private static String TAG = "UpdateActivity";public static String updateURL = "http://www.bjnote.com/down4/bjnote.apk";//更新程序位置private static String updateContentURL="http://www.bjnote.com/down4/bjnote.txt";//更新内容private StringBuffer sb =null;private ProgressDialog download;private Handler mHandler;protected static final int GUI_STOP_NOTIFIER = 0x108;protected static final int GUI_THREADING_NOTIFIER = 0x109;protected static final int GUI_ERROR_NOTIFIER = 0x110;protected static final int GUI_IO_NOTIFIER = 0x111;protected static final int GUI_INTERRUPTED_NOTIFIER = 0x112;protected static final int GUI_PROGRESS_NOTIFIER = 0x113;//开始进度条的进度值显示protected static final int GUI_UPDATE_CONTENT_NOTIFIER = 0x114;//有更新文本private int count = 0;private long total;private DownThread downThread;private UpdateContent updateContent;private SharedPreferences prefs;private long newVersion;private String apkFile = Environment.getExternalStorageDirectory()+"/bjfile.apk"; class DownThread extends Thread{ private boolean cancel=false; private File file; public void run() { HttpClient client = new DefaultHttpClient(); HttpGet get = new HttpGet(updateURL); HttpResponse response = null; FileOutputStream fileOutputStream = null; InputStream is=null; try { response = client.execute(get); if(response.getStatusLine().getStatusCode()!=200)throw new IOException("StatusCode!=200"); HttpEntity entity = response.getEntity(); total = entity.getContentLength(); mHandler.sendEmptyMessage(GUI_PROGRESS_NOTIFIER); is = entity.getContent(); if (is != null) { file = new File(apkFile); fileOutputStream = new FileOutputStream(file); byte[] buf = new byte[1024]; int ch = -1; while ((ch = is.read(buf)) != -1) { if(cancel)throw new InterruptedException(); count+=ch; fileOutputStream.write(buf, 0, ch); mHandler.sendEmptyMessage(GUI_THREADING_NOTIFIER); } if(count==total)mHandler.sendEmptyMessage(GUI_STOP_NOTIFIER); fileOutputStream.flush(); if(fileOutputStream!=null)fileOutputStream.close(); } } catch (ClientProtocolException e) { e.printStackTrace(); mHandler.sendEmptyMessage(GUI_ERROR_NOTIFIER); if(file.exists())file.delete(); } catch (IOException e) { if(file.exists())file.delete(); e.printStackTrace(); mHandler.sendEmptyMessage(GUI_IO_NOTIFIER); } catch (InterruptedException e) {e.printStackTrace();if(file.exists())file.delete(); mHandler.sendEmptyMessage(GUI_INTERRUPTED_NOTIFIER);} finally{try {is.close();} catch (IOException e1) {e1.printStackTrace();}} } private void setCancel(boolean isCancel){ cancel = isCancel; } } //读取更新内容 class UpdateContent extends Thread{ public void run() { HttpClient client = new DefaultHttpClient(); HttpGet get = new HttpGet(updateContentURL); HttpResponse response = null; BufferedReader bis = null; try { response = client.execute(get); //添加更新提示 sb.append(UpdateActivity.this.getString(R.string.app_update_tip)); if(response.getStatusLine().getStatusCode()!=200){ throw new IOException("StatusCode!=200"); } HttpEntity entity = response.getEntity(); //XXX 可能需要改成其他的编码,Apple默认是gb2312 bis = new BufferedReader(new InputStreamReader(entity.getContent(),"gb2312")); String s=null; if (bis != null) { s=bis.readLine(); while (s!=null) { sb.append("\n"+s); s=bis.readLine(); } mHandler.sendEmptyMessage(GUI_UPDATE_CONTENT_NOTIFIER); bis.close(); } } catch (ClientProtocolException e) { e.printStackTrace(); stopService(); } catch (IOException e) { e.printStackTrace(); stopService(); } } } @Overridepublic void onCreate(Bundle bundle) {super.onCreate(bundle);Log.v(TAG, "onCreate");mHandler = new Handler() {public void handleMessage(Message msg) {switch(msg.what) {case GUI_THREADING_NOTIFIER:download.setProgress(count);break;case GUI_STOP_NOTIFIER: download.dismiss(); prefs.edit().putLong(PreferencesActivity.KEY_LATEST_VERSION, newVersion).commit(); //表示已下载但还未安装 prefs.edit().putBoolean(PreferencesActivity.KEY_LATEST_VERSION_INSTALL, false).commit();new AlertDialog.Builder(UpdateActivity.this) .setCancelable(false) .setTitle(R.string.app_update_title) .setMessage(R.string.button_update_finish) .setPositiveButton(R.string.button_ok,new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { install(); } }) .setNegativeButton(R.string.button_exit, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { stopService(); } }) .show();break;case GUI_IO_NOTIFIER:case GUI_ERROR_NOTIFIER:if(download!=null)download.dismiss();new AlertDialog.Builder(UpdateActivity.this) .setCancelable(false) .setMessage(R.string.app_update_error) .setPositiveButton(R.string.button_exit,new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { stopService(); } }) .show();break;case GUI_INTERRUPTED_NOTIFIER:download.dismiss();new AlertDialog.Builder(UpdateActivity.this) .setCancelable(false) .setMessage(R.string.app_update_cancel) .setPositiveButton(R.string.button_exit,new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { stopService(); //如果放弃本次更新,将不再进行提示 prefs.edit().putLong(PreferencesActivity.KEY_LATEST_VERSION, newVersion).commit(); prefs.edit().putBoolean(PreferencesActivity.KEY_LATEST_VERSION_INSTALL, true).commit(); } }) .show();break;case GUI_PROGRESS_NOTIFIER: download.setMax((int) total); download.setProgress(0);break;case GUI_UPDATE_CONTENT_NOTIFIER:download();break;}}};newVersion = getIntent().getLongExtra(UpdateService.VERSITON, 0);prefs = PreferenceManager.getDefaultSharedPreferences(this);sb = new StringBuffer();updateContent = new UpdateContent();updateContent.start();} private void install() { Intent i = new Intent(); i.setAction(Intent.ACTION_VIEW); i.setDataAndType(Uri.fromFile(new File(apkFile)), "application/vnd.android.package-archive"); startActivity(i); } private void download() { downThread = new DownThread(); new AlertDialog.Builder(this).setTitle(R.string.app_update_title).setCancelable(false).setMessage(sb.toString()).setPositiveButton(R.string.button_update_ok,new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { download = new ProgressDialog(UpdateActivity.this); download.setMessage(getString(R.string.app_update_warn)); download.setCancelable(false); download.setIndeterminate(false); download.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);download.setButton(getString(R.string.button_cancel), new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {downThread.setCancel(true);}});download.show();downThread.start();} }) .setNegativeButton(R.string.button_update_no, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {stopService();prefs.edit().putLong(PreferencesActivity.KEY_LATEST_VERSION, newVersion).commit();prefs.edit().putBoolean(PreferencesActivity.KEY_LATEST_VERSION_INSTALL, true).commit();} }).show();}/** * stopSelf,服务关闭自身 */private void stopService() {Intent stop = new Intent(this,UpdateService.class);stopService(stop);Log.v(TAG, "stopService");finish();Log.v(TAG, "finish activity");}@Overridepublic void onDestroy() {super.onDestroy();prefs=null;Log.v(TAG, "onDestroy");}}?
