每个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);
}
});
}
}
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);
}
}
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();
}
倘若有更新时,开启服务执行下载操作,执行下面方法:
//比对版本号,更新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();
}
}
}
}
//下载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);
}
另外需要在配置文件中添加下面的服务和广播注册
<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" />
最后,在通知栏显示的自定义进度条代码如下,紧供参考
<?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>
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>