您的位置:首页 > 移动开发 > Android开发

Android 检查更新 下载 安装

2013-10-24 22:38 405 查看
android 自动更新什么的网上一堆,废话不说,直接上代码

首先,用全局数据类保存更新URL(获取服务端版本、apk文件下载),以及文件路径等

public class Global {

/*
* =================== 版本信息 ================
*/

/**
* 本地安装版本
*/
public static int localVersion = 0;

/**
* 服务器版本
*/
public static int serverVersion = 0;

/*
* =================== 常量数据 =================
*/

/** 服务器url */
public static final String REQUEST_HOST = "http://xxxxxxxx";

/*
* 其他数据涉及公司项目,就不写出来了
*/

/** 数据存储目录 */
public static final String DATA_PATH = "xxxxx";

/** 升级存储目录 */
public static final String DOWNLOAD_DIR = DATA_PATH + "/download";

/** 更新应用url */
public static final String UPDATE_URL = REQUEST_HOST + "xxxxxx.apk";

/** 检查更新(服务器xml文件链接url) */
public static final String SERVER_VERSION_URL = REQUEST_HOST + "xxxxxx"; // 这里我们用xml保存版本信息

}

然后呢,既然想要检测版本,那么就要有本地安装版本code和服务器端的版本code

在AndroidManifest.xml文件中,versionCode用来识别应用版本code(用来比较大小的,integer),versionName则是给用户看的(string)

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="xxx.xxx.xxx"
android:versionCode="1"
android:versionName="1.0" >

那么在MyApplication中提供两个方法,getLocalVersion(获取本地版本code)和 startUpdateService(启动升级服务入口方法)

public class MApplication extends Application {

@Override
public void onCreate() {
super.onCreate();
// ......

getLocalVersion(); // 读取版本code
}

/**
* 获取应用版本信息
*/
public void getLocalVersion() {
try {
PackageInfo packageInfo = getApplicationContext().getPackageManager().getPackageInfo(getPackageName(), 0);
Global.localVersion = packageInfo.versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
}

}

现在就是重点的UpdateService了

public class UpdateService extends Service {

private static final String TAG = "UpdateService";
private static final int TIMEOUT = 5 * 1000;    // 超时
private static final int DOWN_OK = 1;
private static final int DOWN_ERROR = 0;

private String appName;

private NotificationManager notificationManager; // 通知管理器
private Notification notification; // 通知

private Intent updateIntent;
private PendingIntent pendingIntent;

private int notification_id = 1;

RemoteViews contentView;

@Override
public IBinder onBind(Intent arg0) {
return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
appName = intent.getStringExtra("appName");
// 创建文件
FileUtil.createFile(getString(R.string.app_name)); // 这里就是根据应用名在存储目录创建一个appname.apk文件
createNotification();
startUpdate();

return super.onStartCommand(intent, flags, startId);
}

/***
* 创建通知栏
*/
public void createNotification() {
/*
* 自定义Notification视图
*/
contentView = new RemoteViews(getPackageName(), R.layout.notification_item);
contentView.setTextViewText(R.id.notificationTitle, appName + "—" +getString(R.string.soft_update_progress));
contentView.setTextViewText(R.id.notificationPercent, "0%");
contentView.setProgressBar(R.id.notificationProgress, 100, 0, false);

updateIntent = new Intent(this, MainActivity.class);
updateIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
pendingIntent = PendingIntent.getActivity(this, 0, updateIntent, 0);

// 初始化通知
notification = new Notification(R.drawable.ic_logo,
getString(R.string.soft_down_start) + " " + appName, System.currentTimeMillis());
notification.flags |= Notification.FLAG_NO_CLEAR;
notification.defaults |= Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE; // 设置通知铃声和振动提醒
notification.contentView = contentView;
notification.contentIntent = pendingIntent;
// 发送通知
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(notification_id, notification);
// 清除通知铃声
notification.defaults = 0;
}

/***
* 开启线程下载更新
*/
public void startUpdate() {
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 添加通知声音
notification.defaults |= Notification.DEFAULT_SOUND;
switch (msg.what) {
case DOWN_OK:
// 下载完成,点击安装
Uri uri = Uri.fromFile(FileUtil.updateFile);
// 安装应用意图
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "application/vnd.android.package-archive");

pendingIntent = PendingIntent.getActivity(UpdateService.this, 0, intent, 0);

notification.tickerText = appName + " " + getString(R.string.soft_down_complete);
notification.setLatestEventInfo(UpdateService.this,
appName, getString(R.string.soft_down_ok), pendingIntent);

break;
case DOWN_ERROR:
notification.tickerText = appName + " " + getString(R.string.soft_down_error);
notification.setLatestEventInfo(UpdateService.this,
appName, getString(R.string.soft_down_error), pendingIntent);
}
notification.flags = Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(notification_id, notification);
stopService(updateIntent);
stopSelf();
}
};
// 启动线程下载更新
new Thread(new Runnable(){
@Override
public void run() {
try {
downloadUpdateFile(Global.UPDATE_URL, FileUtil.updateFile.toString(), handler);
} catch (Exception e) {
e.printStackTrace();
handler.sendMessage(handler.obtainMessage(DOWN_ERROR));
}
}
}).start();
}

/***
* 下载文件
*
* @return
* @throws MalformedURLException
*/
public void downloadUpdateFile(String down_url, String file, Handler handler)
throws Exception {
int totalSize;  // 文件总大小
InputStream inputStream;
FileOutputStream outputStream;

URL url = new URL(down_url);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(TIMEOUT);
httpURLConnection.setReadTimeout(TIMEOUT);
// 获取下载文件的size
totalSize = httpURLConnection.getContentLength();
if (httpURLConnection.getResponseCode() == 404) {
throw new Exception("fail!");
}
inputStream = httpURLConnection.getInputStream();
outputStream = new FileOutputStream(file, false);

// 异步任务开始下载
new UpdateAsyncTask(inputStream, outputStream, handler).execute(totalSize);
}

private class UpdateAsyncTask extends AsyncTask<Integer, Integer, Boolean> {

private InputStream in;

private FileOutputStream fos;

private Handler handler;

public UpdateAsyncTask(InputStream inputStream, FileOutputStream outputStream, Handler handler) {
super();
in = inputStream;
fos = outputStream;
this.handler = handler;
}

@Override
protected Boolean doInBackground(Integer... params) {
int totalSize = params[0];  // 下载总大小
int downloadCount = 0;  // 已下载大小
int updateProgress = 0; // 更新进度
int updateStep = 5; // 更新进度步进

byte buffer[] = new byte[1024];
int readsize = 0;
try {
while ((readsize = in.read(buffer)) != -1) {
fos.write(buffer, 0, readsize);
// 计算已下载到的大小
downloadCount += readsize;
// 先计算已下载的百分比,然后跟上次比较是否有增加,有则更新通知进度
int now = downloadCount * 100 / totalSize;
if (updateProgress < now) {
updateProgress = now;
Log.d(TAG, "update: " + updateProgress + "%");
publishProgress(updateProgress);
}
}
} catch (Exception e) {
Log.d(TAG, "download err===>\n");
e.printStackTrace();
return false;
} finally {
try {
fos.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}

@Override
protected void onProgressUpdate(Integer... values) {
int progress = values[0];
// 改变通知栏
contentView.setTextViewText(R.id.notificationPercent, progress + "%");
contentView.setProgressBar(R.id.notificationProgress, 100, progress, false);
// show_view
notificationManager.notify(notification_id, notification);
}

@Override
protected void onPostExecute(Boolean result) {
if (result) {
handler.sendMessage(handler.obtainMessage(DOWN_OK)); // 通知handler已经下载完成
} else {
handler.sendMessage(handler.obtainMessage(DOWN_ERROR)); // 通知handler下载出错
}
super.onPostExecute(result);
}

}

/**
* 检查版本信息
*/
public static void checkVersion(final Context context, final Handler msgHandler) {

final Handler promptHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
new AlertDialog.Builder(context).setTitle("应用升级")
.setMessage("发现新版本,要立即更新吗?")
.setPositiveButton("更新", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startUpdateService(context);
}
})
.setNegativeButton("以后再说", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
).show();
}
};
new Thread() {
@Override
public void run() {
getServiceVersion();
if (msgHandler != null)
msgHandler.sendMessage(msgHandler.obtainMessage(1));
if (Global.localVersion < Global.serverVersion) {
// 提示服务器有新版本,需要更新
promptHandler.sendMessage(promptHandler.obtainMessage(1));
}
}
}.start();
}

/**
* 启动升级服务下载升级
*/
public static void startUpdateService(Context context) {
Intent intent = new Intent(context, UpdateService.class);
intent.putExtra("appName", context.getString(R.string.app_name));
context.startService(intent);
}

/***
* 获取服务器端的serverVersion.
*/
public static void getServiceVersion() {
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(Global.UPDATE_VERSION_URL);
HttpResponse response = null;
try {
response = client.execute(get);
} catch (Exception e) {
Log.e(TAG, "get Service Version err==>\n");
e.printStackTrace();
client.getConnectionManager().closeExpiredConnections();
}
if (response != null) {
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
if (entity.isStreaming()) {
try {
InputStream in = entity.getContent();
Map<String, String> map = parseXml(in);
if (map != null) {
Global.serverVersion = Integer.parseInt((String) map.get("version"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}

/**
* 解析xml数据
* @param in
*/
public static Map<String, String> parseXml(InputStream in) {
// 这里是通过流读取xml文件,然后解析出一个Map出来,当然,这时候我们需要的是 version
}

}

以上用了4个static方法来完成更新下载功能的启动,或者抽出来写在别的地方也可以啦

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="5dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="5dp" >

<ImageView
android:id="@+id/notificationImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:contentDescription="@null"
android:src="@android:drawable/stat_sys_download" />

<TextView
android:id="@+id/notificationTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginLeft="3dp"
android:layout_toRightOf="@id/notificationImage"
android:gravity="left"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:textColor="#FFFFFF" />

<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/notificationTitle"
android:layout_alignParentRight="true"
android:layout_below="@id/notificationTitle"
android:layout_marginTop="3dp"
android:paddingLeft="8dp"
android:paddingRight="8dp" >

<ProgressBar
android:id="@+id/notificationProgress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="3dp"
android:paddingRight="3dp"
android:paddingTop="2dp" />

<TextView
android:id="@+id/notificationPercent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="#FFFFFF" />
</FrameLayout>

</RelativeLayout>

这个就是通知的布局了

对了,自定义Service一定要在清单文件里的Application节点下注册进去

<service android:name=".service.UpdateService"></service>

启动检查的例子:点击检查更新的组件

public class PreferencesActivity extends Activity {

//..............

/*
* 检查更新
*/
public void checkUpdate(View v) {
progressUpdate.setVisibility(0); // 显示进度圈圈
UpdateService.checkVersion(this, new Handler() {
@Override
public void handleMessage(Message msg) {
progressUpdate.setVisibility(View.GONE); // 获取到serverCode后,隐藏进度
}
});
}

}


效果如下









内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android java