思路如下:
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
@Override
public 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域,则取到的值为0
private 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();
}
}
}
@Override
public 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() {
@Override
public 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");
}
@Override
public void onDestroy() {
super.onDestroy();
prefs=null;
Log.v(TAG, "onDestroy");
}
}