说到android的版本更新,要写一个出来还是需要耗费一点时间的,这次我们来写一个版本更新服务,用户进入app后有新版本需要更新则提示用户确认更新,更新在后台运行,不影响用户的正常操作,用户体验相对较好。
Android版本更新的步骤:
1、获取当前app版本号,代码如下
public String getAppVersion() {
try {
PackageInfo packinfo = pm.getPackageInfo(context.getPackageName(),0);
return packinfo.versionName;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return "1.0";
}
2、从服务器获取版本号,并与当前版本进行对比,如果相同则不更新,如果服务器版本号大于当前版本号,弹出更新对话框,提示用户进行更新升级。弹出对话框今天就不在这里写了,这部分代码相对还是比较简单的,当然如果我们需要一个比较好看的对话框,系统提供的原生对话框是不能满足我们的需求的,需要自定义对话框,读者可以自己去研究这方面的代码。
3、用户确认进行版本更新后,将从服务器下载更新的apk并进行自动升级。此处就用到了我们今天说到的版本更新服务,具体代码如下,部分代码已经做了注释,就不详细说明了。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
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 org.apache.http.params.CoreConnectionPNames;
import com.ygwaimai.manage.R;
import com.ygwaimai.manage.util.AppInfo;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.widget.RemoteViews;
import android.widget.Toast;
public class UpdateService extends Service {
private NotificationManager nm;
private Notification notification;
private File tempFile = null;
private boolean cancelUpdate = false;
private MyHandler myHandler;
private int download_precent = 0;
private RemoteViews views;
private int notificationId = 1234;
private int appicon;
public static final int TIME_OUT_MILL = 50000;// 超时毫秒数
private static final String DOWNLOAD_APK_PATH = "APK_UPDATE";
//更新进度标记
private static final int UPDATE_DOWNLOAD_PRECENCE = 3;
//下载完成标记
private static final int DOWNLOAD_FINISH = 2;
//下载APK失败标记
private static final int DOWNLOAD_FAIL = 4;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notification = new Notification();
notification.icon = android.R.drawable.stat_sys_download;
// notification.icon=android.R.drawable.stat_sys_download_done;
// notification.tickerText=getString(R.string.app_name)+"更新";
notification.tickerText = "版本更新";
notification.when = System.currentTimeMillis();
notification.defaults = Notification.DEFAULT_LIGHTS;
// 设置任务栏中下载进程显示的views
views = new RemoteViews(getPackageName(), R.layout.update);
notification.contentView = views;
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(), 0);
notification.setLatestEventInfo(this, "", "", contentIntent);
// 将下载任务添加到任务栏中
nm.notify(notificationId, notification);
myHandler = new MyHandler(Looper.myLooper(), this);
// 初始化下载任务内容views,初始化进度为 0
Message message = myHandler.obtainMessage(UPDATE_DOWNLOAD_PRECENCE, 0);
myHandler.sendMessage(message);
// 启动线程开始执行下载任务
String url = intent.getStringExtra("DOWNLOAD_URL");
downFile(url);
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
// 停止掉当前的服务
stopSelf();
super.onDestroy();
}
// 下载更新文件
private void downFile(final String url) {
new Thread() {
public void run() {
try {
HttpClient client = new DefaultHttpClient();
// params[0]代表连接的url
System.out.println("下载url======" + url);
HttpGet request = new HttpGet(url);
HttpResponse response = client.execute(request);
// 请求超时
client.getParams().setParameter(
CoreConnectionPNames.CONNECTION_TIMEOUT,
TIME_OUT_MILL);
// 读取超时
client.getParams().setParameter(
CoreConnectionPNames.SO_TIMEOUT, TIME_OUT_MILL);
client.getParams().setIntParameter("http.socket.timeout",
TIME_OUT_MILL);
HttpEntity entity = response.getEntity();
long length = entity.getContentLength();
InputStream is = entity.getContent();
if (is != null) {
File rootFile = new File(
Environment.getExternalStorageDirectory(),
"/" + DOWNLOAD_APK_PATH);
if (!rootFile.exists() && !rootFile.isDirectory())
rootFile.mkdir();
tempFile = new File(
Environment.getExternalStorageDirectory(),
"/"+DOWNLOAD_APK_PATH + "/"
+ url.substring(url.lastIndexOf("/") + 1));
if (tempFile.exists())
tempFile.delete();
tempFile.createNewFile();
// 已读出流作为参数创建一个带有缓冲的输出流
BufferedInputStream bis = new BufferedInputStream(is);
// 创建一个新的写入流,讲读取到的图像数据写入到文件中
FileOutputStream fos = new FileOutputStream(tempFile);
// 已写入流作为参数创建一个带有缓冲的写入流
BufferedOutputStream bos = new BufferedOutputStream(fos);
int read;
long count = 0;
int precent = 0;
byte[] buffer = new byte[1024];
while ((read = bis.read(buffer)) != -1 && !cancelUpdate) {
bos.write(buffer, 0, read);
count += read;
precent = (int) (((double) count / length) * 100);
// 每下载完成5%就通知任务栏进行修改下载进度
if (precent - download_precent >= 5) {
download_precent = precent;
Message message = myHandler.obtainMessage(UPDATE_DOWNLOAD_PRECENCE,precent);
myHandler.sendMessage(message);
}
}
bos.flush();
bos.close();
fos.flush();
fos.close();
is.close();
bis.close();
}
if (!cancelUpdate) {
Message message = myHandler.obtainMessage(DOWNLOAD_FINISH, tempFile);
myHandler.sendMessage(message);
} else {
tempFile.delete();
}
} catch (ClientProtocolException e) {
e.printStackTrace();
Message message = myHandler.obtainMessage(DOWNLOAD_FAIL, "下载更新文件失败");
myHandler.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
Message message = myHandler.obtainMessage(DOWNLOAD_FAIL, "下载更新文件失败");
myHandler.sendMessage(message);
} catch (Exception e) {
e.printStackTrace();
Message message = myHandler.obtainMessage(DOWNLOAD_FAIL, "下载更新文件失败");
myHandler.sendMessage(message);
}
}
}.start();
}
/**
* 安装下载后的apk文件
* @param file
* @param context
*/
private void Instanll(File file, Context context) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
context.startActivity(intent);
}
/* 事件处理类 */
class MyHandler extends Handler {
private Context context;
public MyHandler(Looper looper, Context c) {
super(looper);
this.context = c;
// 获取应用图标
AppInfo appinfo = new AppInfo(c);
appicon = appinfo.getAppIcon2();
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg != null) {
switch (msg.what) {
case DOWNLOAD_FINISH:
// 下载完成后清除所有下载信息,执行安装提示
download_precent = 0;
nm.cancel(notificationId);
Instanll((File) msg.obj, context);
// 停止掉当前的服务
stopSelf();
break;
case UPDATE_DOWNLOAD_PRECENCE:
// 更新状态栏上的下载进度信息
views.setImageViewResource(R.id.ivLogo, appicon);
views.setTextViewText(R.id.tvProcess, "已下载"
+ download_precent + "%");
views.setProgressBar(R.id.pbDownload, 100,
download_precent, false);
notification.contentView = views;
nm.notify(notificationId, notification);
break;
case DOWNLOAD_FAIL:
nm.cancel(notificationId);
break;
}
}
}
}
}
Notification使用到的布局文件update.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#f2f2f2" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/ivLogo"
android:layout_width="45dip"
android:layout_height="45dip"
android:layout_margin="10dip"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_toRightOf="@+id/ivLogo"
android:layout_marginRight="10dip"
android:layout_alignParentRight="true"
android:layout_centerVertical="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/textgray"
android:text="版本更新"
android:textSize="18dip"
android:textStyle="bold"/>
<ProgressBar
android:id="@+id/pbDownload"
android:layout_width="fill_parent"
android:layout_height="12dip"
android:progress="0"
android:max="100"
style="?android:attr/progressBarStyleHorizontal" />
<TextView
android:id="@+id/tvProcess"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#6a6a6a"
android:text="已下载0%"
android:textSize="12dip"
android:textStyle="bold" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
用到的工具类AppInfo.java
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
public class AppInfo {
private Context context;
private PackageManager pm;
public AppInfo(Context context) {
this.context = context;
pm = context.getPackageManager();
}
/*
* 获取程序 图标
*/
public Drawable getAppIcon() {
Drawable dr = null;
try {
ApplicationInfo info = pm.getApplicationInfo(
context.getPackageName(), 0);
dr = info.loadIcon(pm);
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return dr;
}
/*
* 获取程序 图标
*/
public int getAppIcon2() {
int icon = 0;
try {
ApplicationInfo info = pm.getApplicationInfo(
context.getPackageName(), 0);
icon = info.icon;
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return icon;
}
/*
* 获取程序的版本号
*/
public String getAppVersion() {
try {
PackageInfo packinfo = pm.getPackageInfo(context.getPackageName(),
0);
return packinfo.versionName;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return "1.0";
}
/*
* 获取程序的名字
*/
public String getAppName(String packname) {
try {
ApplicationInfo info = pm.getApplicationInfo(packname, 0);
return info.loadLabel(pm).toString();
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/*
* 获取程序的权限
*/
public String[] getAppPremission(String packname) {
try {
PackageInfo packinfo = pm.getPackageInfo(packname,
PackageManager.GET_PERMISSIONS);
// 获取到所有的权限
return packinfo.requestedPermissions;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return null;
}
/*
* 获取程序的签名
*/
public String getAppSignature(String packname) {
try {
PackageInfo packinfo = pm.getPackageInfo(packname,
PackageManager.GET_SIGNATURES);
// 获取到所有的权限
return packinfo.signatures[0].toCharsString();
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return null;
}
public String getAppPackageName() {
PackageInfo info;
try {
info = context.getPackageManager().getPackageInfo(
context.getPackageName(), 0);
// 当前应用的版本名称
String versionName = info.versionName;
// 当前版本的版本号
int versionCode = info.versionCode;
// 当前版本的包名
String packageNames = info.packageName;
return packageNames;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
在Activity中使用,我们只要传入版本更新的url就可以了,相关的下载和安装操作都在UpdateService 里面实现了,
用起来是不是很简单。
Intent intent = new Intent(ctx,UpdateService.class);
intent.putExtra("DOWNLOAD_URL", version_url);
startService(intent);
最后不要忘了在配置文件在AndroidManifest.mxl中中添加网络和SD卡读写权限,还有注册Service
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
<service android:name ="com.ygwaimai.manage.service.UpdateService" />
这样一个简单的版本更新服务就写好了,当然这只是比较简单的实现,具体做起来我们还要考虑很多问题,这只能看具体需求了,比如我们还可以加一些逻辑,判断sd卡里面有没有apk安装包,并和当前版本比较,看是否已经下载有新版本了,我们没有安装,这时就不必要再去下载一次了,浪费时间还浪费流量。还可以在下载的部分添加增量更新功能,这是现在比较流行的一种更新方法,这部分内容比较多,今天就不在这里介绍了,有兴趣的朋友可以自己去研究,一个版本更新的功能要做得比较好还是需要蛮多东西要考虑的,这篇文章就写到这里,夜了,晚安。