app版本更新和下载,通知栏实时进度(使用服务和广播)
每个app都需要有版本更新的功能,下面简单介绍一下最近在项目中使用的app更新功能。
1、首先需要使用服务和广播实现后台更新,使用到了xUtils,其他的网络请求框架代码类似。
服务代码如下,注释写的很详细
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import org.xutils.common.Callback;
import org.xutils.ex.HttpException;
import org.xutils.http.RequestParams;
import org.xutils.x;
import java.io.File;
import java.text.NumberFormat;
/**
* 下载的services,配合xutils的httputils使用,完成notification的下载功能
*/
public class UpdateService extends Service {
//是否已经开始下载
private boolean isBegin = false;
Intent intent;
private NumberFormat numberFormat;
public static Callback.Cancelable downLoadHandler;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
intent = new Intent();
numberFormat = NumberFormat.getInstance();
numberFormat.setMaximumFractionDigits(2);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String downUrl = intent.getStringExtra("downUrl");
String filePath = intent.getStringExtra("filePath");
//如果下载地址为空,则什么都不干
if (TextUtils.isEmpty(downUrl)) {
stopSelf();
// throw new IllegalArgumentException("the download url is empty!!!!");
return START_NOT_STICKY;
}
if (isBegin) {
//此时已经开始了
return START_NOT_STICKY;
} else {
isBegin = true;
}
downLoad(downUrl,filePath);
return super.onStartCommand(intent, flags, startId);
}
int progress = -1;
private void downLoad(final String downUrl,String filePath) {
RequestParams requestParams = new RequestParams(downUrl);
requestParams.setSaveFilePath(filePath);
downLoadHandler = x.http().get(requestParams, new Callback.ProgressCallback<File>() {
@Override
public void onSuccess(File result) {
intent.setAction("com.ycb.www.complete");
intent.putExtra("filepath", result.getAbsolutePath());
Log.i("tag", "onSuccess");
Log.i("tag", result.getAbsolutePath());
sendBroadcast(intent);
stopSelf();
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
intent.setAction("com.ycb.www.failed");
intent.putExtra("downUrl", downUrl);
Log.i("tag", "onFailure!!!"+ex.getMessage());
// sendBroadcast(intent);
if (ex instanceof HttpException) {
HttpException httpEx = (HttpException) ex;
Log.i("tag","onError:"+httpEx.getCode()+httpEx.getMessage());
}
}
@Override
public void onCancelled(CancelledException cex) {
Log.i("tag", "onCancelled");
stopSelf();
}
@Override
public void onFinished() {
Log.i("tag", "onFinished");
// stopSelf();
}
@Override
public void onWaiting() {
Log.i("tag", "onWaiting");
}
@Override
public void onStarted() {
Log.i("tag","Started");
intent.putExtra("rate",0);
intent.setAction("com.ycb.www.updating");
sendBroadcast(intent);
}
@Override
public void onLoading(long total, long current, boolean isDownloading) {
Double rate= (double)current / (double)total;
String format = numberFormat.format(rate);
int r= (int) (Double.valueOf(format)*100);
Log.i("tag", ""+r);
intent.putExtra("rate", r);
intent.setAction("com.ycb.www.updating");
sendBroadcast(intent);
}
});
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
2、然后添加一个广播,用于实时处理下载进度,并在通知栏实时显示
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.widget.RemoteViews;
import java.io.File;
/**
* notification更新的广播接收者,根据action不同,做出的结果不同,
* 其中intent因为是同一个intent的,所以并没有new 新的
*/
public class UpdateReceiver extends BroadcastReceiver {
private NotificationManager manager;
private RemoteViews views;
private Notification notification;
@Override
public void onReceive(Context context, Intent intent) {
if (notification == null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
initNotification(context);
else {
initNotificationForLowVersion(context);
}
}
String action = intent.getAction();
switch (action) {
case "com.ycb.www.cancel":
manager.cancel(0);
UpdateService.downLoadHandler.cancel();
break;
case "com.ycb.www.failed":
intent.setAction("com.ycb.www.restart");
PendingIntent failedpendingIntent = PendingIntent.getBroadcast(context, 200, intent, PendingIntent.FLAG_CANCEL_CURRENT);
views.setOnClickPendingIntent(R.id.ll_content, failedpendingIntent);
views.setTextViewText(R.id.tv_info, "下载失败,点击重试");
manager.notify(0, notification);
break;
case "com.ycb.www.restart":
manager.cancel(0);
intent.setClass(context, UpdateService.class);
context.startService(intent);
break;
case "com.ycb.www.install":
manager.cancel(0);
Intent startInstall = new Intent();
startInstall.setAction(Intent.ACTION_VIEW);
String filepath = intent.getStringExtra("filepath");
startInstall.setDataAndType(Uri.fromFile(new File(filepath)), "application/vnd.android.package-archive");
startInstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(startInstall);
break;
case "com.ycb.www.complete":
intent.setAction("com.ycb.www.install");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 200, intent, PendingIntent.FLAG_CANCEL_CURRENT);
views.setOnClickPendingIntent(R.id.ll_content, pendingIntent);
views.setTextViewText(R.id.tv_info, "下载完成,点击安装");
views.setProgressBar(R.id.progressBar, 100, 100, false);
manager.notify(0, notification);
break;
case "com.ycb.www.updating":
int rate = intent.getIntExtra("rate", 0);
views.setTextViewText(R.id.tv_info, "正在下载...." + rate + "%");
views.setProgressBar(R.id.progressBar, 100, rate, false);
manager.notify(0, notification);
}
}
private void initNotificationForLowVersion(Context context) {
//设置notifiction布局
views = new RemoteViews(context.getPackageName(), R.layout.notification_update);
notification = new Notification();
notification.when = System.currentTimeMillis();
notification.tickerText = "xxxx新版正在下载";
//设置view
notification.contentView = views;
//设置小图标
notification.icon = R.mipmap.icon;
//设置布局文件中的textView的内容
views.setTextViewText(R.id.tv_info, "下载中....0%");
//设置布局文件中的ProgressBar进度
views.setProgressBar(R.id.progressBar, 100, 0, false);
//退出的intent
Intent intent = new Intent("com.ycb.www.cancel");
//退出的延迟意图
PendingIntent mPendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), 200, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//点击之后退出
views.setOnClickPendingIntent(R.id.ib_close, mPendingIntent);
}
/**
* 初始化notification
*
* @param context
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void initNotification(Context context) {
manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
views = new RemoteViews(context.getPackageName(), R.layout.notification_update);
Notification.Builder builder = new Notification.Builder(context.getApplicationContext());
notification = builder.setAutoCancel(false).setSmallIcon(R.mipmap.icon).setContentText("下载中").setContentTitle("下载").
setWhen(System.currentTimeMillis()).setTicker("xxxxx新版正在下载")
.setContent(views).build();
views.setTextViewText(R.id.tv_info, "下载中....0%");
views.setProgressBar(R.id.progressBar, 100, 0, false);
Intent intent = new Intent("com.ycb.www.cancel");
PendingIntent mPendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), 200, intent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.ib_close, mPendingIntent);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
3、在MainActivity或者LoginActivity页面检测app版本是否有更新,若有更新,开启服务,注册广播,开始下载更新操作
在onCreate方法中添加如下代码:
//检查版本更新
private void checkAppVersion() {
try {
PackageManager manager = this.getPackageManager();
PackageInfo info = manager.getPackageInfo(this.getPackageName(), 0);
versionCode = info.versionName;
System.out.println("versionCode:" + versionCode);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
OkHttpUtils.get()
.url(Constant.DB_URL + Constant.PORT + Constant.XHB_DIR + Constant.GET_APP_VERSION_DATA)
.addParams("type", "0")
.build()
.execute(new StringCallback() {
@Override
public void onError(Call call, Exception e) {
Toast.makeText(LoginActivity.this, "网络异常", Toast.LENGTH_SHORT).show();
}
@Override
public void onResponse(String response) {
System.out.println("downApp:" + response);
//版本号对比,若不同下载app
downLoadApp(response);
}
});
}
}).start();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
倘若有更新时,开启服务执行下载操作,执行下面方法:
//比对版本号,更新app
private void downLoadApp(String response) {
final AppVersionResponse appVersionResponse = new Gson().fromJson(response, AppVersionResponse.class);
AppVersionResponse.DataBean dataBean = appVersionResponse.getData();
if (appVersionResponse.isIsSuccess() && dataBean != null) {
if (!versionCode.equals(appVersionResponse.getData().getCode())) {
//强制更新
if(dataBean.getIsForced() == 1){
btn_land.setEnabled(false);
//弹出窗口是否强制更新版本
AlertDialog alertDialog = new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_info).setTitle("版本升级")
.setMessage(dataBean.getContent())
.setCancelable(false)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//下载操作
downLoad(appVersionResponse.getData().getCode());
}
}).create();
alertDialog.show();
}else if(dataBean.getIsForced() == 0){
//非强制更新
// btn_land.setEnabled(false);
//弹出窗口是否强制更新版本
AlertDialog alertDialog = new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_info).setTitle("版本升级提示")
.setMessage(dataBean.getContent())
.setCancelable(false)
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
})
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//下载操作
downLoad(appVersionResponse.getData().getCode());
}
}).create();
alertDialog.show();
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
//下载app
private void downLoad(final String version) {
updateReceiver = new UpdateReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("com.ycb.www.cancel");
filter.addAction("com.ycb.www.failed");
filter.addAction("com.ycb.www.restart");
filter.addAction("com.ycb.www.install");
filter.addAction("com.ycb.www.complete");
filter.addAction("com.ycb.www.updating");
registerReceiver(updateReceiver,filter);
Intent intent = new Intent(this, UpdateService.class);
String url = Constant.DB_URL + Constant.PORT + Constant.XHB_DIR + Constant.DOWNLOAD_APP_URL;
intent.putExtra("downUrl", url);
intent.putExtra("filePath",Environment.getExternalStorageDirectory().getPath()+"/Downloads/xiahubao_"+version+".apk");
startService(intent);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
另外需要在配置文件中添加下面的服务和广播注册
<receiver android:name=".appUpdateUtils.UpdateReceiver">
<intent-filter>
<action android:name="com.ycb.www.complete" />
<action android:name="com.ycb.www.install" />
<action android:name="com.ycb.www.cancel" />
<action android:name="com.ycb.www.updating" />
<action android:name="com.ycb.www.failed" />
<action android:name="com.ycb.www.restart" />
</intent-filter>
</receiver>
<service android:name=".appUpdateUtils.UpdateService" />
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
最后,在通知栏显示的自定义进度条代码如下,紧供参考
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/img_menu"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@mipmap/icon" />
<LinearLayout
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginTop="6dp"
android:orientation="vertical">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progressDrawable="@drawable/seek_bar_progress_bg" />
<TextView
android:id="@+id/tv_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:textColor="@color/textBlackColor"
/>
</LinearLayout>
<ImageButton
android:id="@+id/ib_close"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="3dp"
android:background="@null"
android:src="@android:drawable/ic_menu_close_clear_cancel" />
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
progressDrawable的配置如下,显示的更美观
<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<solid android:color="#c6c6c6" />
<corners android:radius="3dp"/>
</shape>
</item>
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<solid android:color="#c6c6c6" />
<corners android:radius="3dp"/>
</shape>
</clip>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<solid android:color="#06a7fa" />
<corners android:radius="3dp"/>
</shape>
</clip>
</item>
</layer-list>