欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android实现通知栏下载更新app示例

程序员文章站 2023-10-28 20:13:58
1.设计思路,使用versioncode定义为版本升级参数。android为我们定义版本提供了2个属性:复制代码 代码如下:

1.设计思路,使用versioncode定义为版本升级参数。
android为我们定义版本提供了2个属性:

复制代码 代码如下:

<manifest package="com.cnblogs.tianxia.subway"
android:versioncode="1" <!--integer类型,系统不显示给用户-->
android:versionname="1.0"<!--string类型,系统显示用户-->
></manifest>

谷歌建议我们使用versioncode自增来表明版本升级,无论是大的改动还是小的改动,而versionname是显示用户看的软件版本,作为显示使用。所以我们选择了versioncode作为我们定义版本升级的参数。

2.工程目录
为了对真实项目或者企业运用有实战指导作用,我模拟一个独立的项目,工程目录设置的合理严谨一些,而不是仅仅一个demo。
假设我们以上海地铁为项目,命名为"subway",工程结构如下,

3.版本初始化和版本号的对比。
首先定义在全局文件global.java中定义变量localversion和serverversion分别存放本地版本号和服务器版本号。

复制代码 代码如下:

public class global {
//版本信息
public static int localversion = 0;
public static int serverversion = 0;
public static string downloaddir = "app/download/";
}

因为本文只是重点说明升级更新,为了防止其他太多无关代码冗余其中,我直接在subwayapplication中定义方法initglobal()方法。
复制代码 代码如下:

/**
* 初始化全局变量
* 实际工作中这个方法中serverversion从服务器端获取,最好在启动画面的activity中执行
*/
public void initglobal(){
try{
global.localversion = getpackagemanager().getpackageinfo(getpackagename(),0).versioncode; //设置本地版本号
global.serverversion = 1;//假定服务器版本为2,本地版本默认是1
}catch (exception ex){
ex.printstacktrace();
}
}

如果检测到新版本发布,提示用户是否更新,我在subwayactivity中定义了checkversion()方法:
复制代码 代码如下:

/**
* 检查更新版本
*/
public void checkversion(){

if(global.localversion < global.serverversion){
//发现新版本,提示用户更新
alertdialog.builder alert = new alertdialog.builder(this);
alert.settitle("软件升级")
.setmessage("发现新版本,建议立即更新使用.")
.setpositivebutton("更新", new dialoginterface.onclicklistener() {
public void onclick(dialoginterface dialog, int which) {
//开启更新服务updateservice
//这里为了把update更好模块化,可以传一些updateservice依赖的值
//如布局id,资源id,动态获取的标题,这里以app_name为例
intent updateintent =new intent(subwayactivity.this, updateservice.class);
updateintent.putextra("titleid",r.string.app_name);
startservice(updateintent);
}
})
.setnegativebutton("取消",new dialoginterface.onclicklistener(){
public void onclick(dialoginterface dialog, int which) {
dialog.dismiss();
}
});
alert.create().show();
}else{
//清理工作,略去
//cheanupdatefile(),文章后面我会附上代码
}
}

好,我们现在把这些东西串一下:
第一步在subwayapplication的oncreate()方法中执行initglobal()初始化版本变量。

复制代码 代码如下:

public void oncreate() {
super.oncreate();
initglobal();
}

第二步在subwayactivity的oncreate()方法中检测版本更新checkversion()。
复制代码 代码如下:

public void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.main);
checkversion();

现在入口已经打开,在checkversion方法的第18行代码中看出,当用户点击更新,我们开启更新服务,从服务器上下载最新版本。
4.使用service在后台从服务器端下载,完成后提示用户下载完成,并关闭服务。
定义一个服务updateservice.java,首先定义与下载和通知相关的变量:

复制代码 代码如下:

//标题
private int titleid = 0;

//文件存储
private file updatedir = null;  
private file updatefile = null;

//通知栏
private notificationmanager updatenotificationmanager = null;
private notification updatenotification = null;
//通知栏跳转intent
private intent updateintent = null;
private pendingintent updatependingintent = null;

在onstartcommand()方法中准备相关的下载工作:

复制代码 代码如下:

@override
public int onstartcommand(intent intent, int flags, int startid) {
//获取传值
titleid = intent.getintextra("titleid",0);
//创建文件
if(android.os.environment.media_mounted.equals(android.os.environment.getexternalstoragestate())){
updatedir = new file(environment.getexternalstoragedirectory(),global.downloaddir);
updatefile = new file(updatedir.getpath(),getresources().getstring(titleid)+".apk");
}

this.updatenotificationmanager = (notificationmanager)getsystemservice(notification_service);
this.updatenotification = new notification();

//设置下载过程中,点击通知栏,回到主界面
updateintent = new intent(this, subwayactivity.class);
updatependingintent = pendingintent.getactivity(this,0,updateintent,0);
//设置通知栏显示内容
updatenotification.icon = r.drawable.arrow_down_float;
updatenotification.tickertext = "开始下载";
updatenotification.setlatesteventinfo(this,"上海地铁","0%",updatependingintent);
//发出通知
updatenotificationmanager.notify(0,updatenotification);

//开启一个新的线程下载,如果使用service同步下载,会导致anr问题,service本身也会阻塞
new thread(new updaterunnable()).start();//这个是下载的重点,是下载的过程

return super.onstartcommand(intent, flags, startid);
}


上面都是准备工作

从代码中可以看出来,updaterunnable类才是真正下载的类,出于用户体验的考虑,这个类是我们单独一个线程后台去执行的。
下载的过程有两个工作:1.从服务器上下载数据;2.通知用户下载的进度。
线程通知,我们先定义一个空的updatehandler。
[/code]
private handler updatehandler = new handler(){
@override
public void handlemessage(message msg) {

}
};
[/code]
再来创建updaterunnable类的真正实现:

复制代码 代码如下:

class updaterunnable implements runnable {
message message = updatehandler.obtainmessage();
public void run() {
message.what = download_complete;
try{
//增加权限<uses-permission android:name="android.permission.write_external_storage">;
if(!updatedir.exists()){
updatedir.mkdirs();
}
if(!updatefile.exists()){
updatefile.createnewfile();
}
//下载函数,以qq为例子
//增加权限<uses-permission android:name="android.permission.internet">;
long downloadsize = downloadupdatefile("http://softfile.3g.qq.com:8080/msoft/179/1105/10753/mobileqq1.0(android)_build0198.apk",updatefile);
if(downloadsize>0){
//下载成功
updatehandler.sendmessage(message);
}
}catch(exception ex){
ex.printstacktrace();
message.what = download_fail;
//下载失败
updatehandler.sendmessage(message);
}
}
}
</uses-permission></uses-permission>

下载函数的实现有很多,我这里把代码贴出来,而且我们要在下载的时候通知用户下载进度:
复制代码 代码如下:

public long downloadupdatefile(string downloadurl, file savefile) throws exception {
//这样的下载代码很多,我就不做过多的说明
int downloadcount = 0;
int currentsize = 0;
long totalsize = 0;
int updatetotalsize = 0;

httpurlconnection httpconnection = null;
inputstream is = null;
fileoutputstream fos = null;

try {
url url = new url(downloadurl);
httpconnection = (httpurlconnection)url.openconnection();
httpconnection.setrequestproperty("user-agent", "pacifichttpclient");
if(currentsize > 0) {
httpconnection.setrequestproperty("range", "bytes=" + currentsize + "-");
}
httpconnection.setconnecttimeout(10000);
httpconnection.setreadtimeout(20000);
updatetotalsize = httpconnection.getcontentlength();
if (httpconnection.getresponsecode() == 404) {
throw new exception("fail!");
}
is = httpconnection.getinputstream();
fos = new fileoutputstream(savefile, false);
byte buffer[] = new byte[4096];
int readsize = 0;
while((readsize = is.read(buffer)) > 0){
fos.write(buffer, 0, readsize);
totalsize += readsize;
//为了防止频繁的通知导致应用吃紧,百分比增加10才通知一次
if((downloadcount == 0)||(int) (totalsize*100/updatetotalsize)-10>downloadcount){
downloadcount += 10;
updatenotification.setlatesteventinfo(updateservice.this, "正在下载", (int)totalsize*100/updatetotalsize+"%", updatependingintent);
updatenotificationmanager.notify(0, updatenotification);
}
}
} finally {
if(httpconnection != null) {
httpconnection.disconnect();
}
if(is != null) {
is.close();
}
if(fos != null) {
fos.close();
}
}
return totalsize;
}

下载完成后,我们提示用户下载完成,并且可以点击安装,那么我们来补全前面的handler吧。
先在updateservice.java定义2个常量来表示下载状态:

复制代码 代码如下:

//下载状态
private final static int download_complete = 0;
private final static int download_fail = 1;

根据下载状态处理主线程:
复制代码 代码如下:

private handler updatehandler = new handler(){
@override
public void handlemessage(message msg) {
switch(msg.what){
case download_complete:
//点击安装pendingintent
uri uri = uri.fromfile(updatefile);
intent installintent = new intent(intent.action_view);
installintent.setdataandtype(uri, "application/vnd.android.package-archive");
updatependingintent = pendingintent.getactivity(updateservice.this, 0, installintent, 0);

updatenotification.defaults = notification.default_sound;//铃声提醒
updatenotification.setlatesteventinfo(updateservice.this, "上海地铁", "下载完成,点击安装。", updatependingintent);
updatenotificationmanager.notify(0, updatenotification);

//停止服务
stopservice(updateintent);
case download_fail:
//下载失败
updatenotification.setlatesteventinfo(updateservice.this, "上海地铁", "下载完成,点击安装。", updatependingintent);
updatenotificationmanager.notify(0, updatenotification);
default:
stopservice(updateintent);
}
}
};



至此,文件下载并且在通知栏通知进度。
发现本人废话很多,其实几句话的事情,来来回回写了这么多,啰嗦了,后面博文我会朝着精简方面努力。
ps:前面说要附上cheanupdatefile()的代码
复制代码 代码如下:

file updatefile = new file(global.downloaddir,getresources().getstring(r.string.app_name)+".apk");
if(updatefile.exists()){
//当不需要的时候,清除之前的下载文件,避免浪费用户空间
updatefile.delete();
}