应用更新有两种:
1、 检测到更新,App实现下载功能,下载安装。
2、检查到更新,跳转应用市场下载。两种方法优缺点:
第一种:优点:是,下载统一,便于版本统一升级,只要用户点击更新,可以保证其实官方最新的包。
-
缺点:实现起来稍微优点麻烦,如果写不好,下载的包可能无法解析,有些机型导致下载了无法安装的问题。
第二种:
- 优点:实现简单,几乎不用写几行代码。
- 缺点:每个手机安装的应用市场不同,不同应用市场的审核速度不一样,导致用户用的版本参差不齐,提示更新后,用户去应用市场下载安装,发现还提示更新,是因为应用市场上不是最新包。
下面先给出第二种实习方法,再给第一种方法(实践后结论下载稳定)
//检查到需要更新,调用
ToolUtil.gotoMarket(mActivity, mActivity.getPackageName());
/**
* 打开市场
*/
public static void gotoMarket(Context context, String pck) {
if (!isHaveMarket(context)) {
Toast.makeText(context, "您手机中没有安装应用市场!", Toast.LENGTH_SHORT).show();
return;
}
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://details?id=" + pck));
if (intent.resolveActivity(context.getPackageManager()) != null) {
context.startActivity(intent);
}
}
/**
* 是否安装市场
*/
private static boolean isHaveMarket(Context context) {
Intent intent = new Intent();
intent.setAction("android.intent.action.MAIN");
intent.addCategory("android.intent.category.APP_MARKET");
PackageManager pm = context.getPackageManager();
List<ResolveInfo> infos = pm.queryIntentActivities(intent, 0);
return infos.size() > 0;
}
第二种到此就完了。可以验证下,前题是,你的app要在应用商场上线。没有上线的话,是找不到对应的APP的。
那么下来重点说下第一种:
先看效果图:
这里先说,图中的dialog,用的是github上 material-dialogs 。(这了忽略对话框的代码)
从左到右,检查更新-->是否需要下载-->点击下载-->状态栏和对话框同时走下载进度条-->最后下载成功。
- 提出下载的主要代码。
// 启动Service 开始下载
AppUpdateService.startUpdate(mContext, url, fileName, new AppUpdateService.OnProgressListener() {
@Override
public void onProgress(int progress) {
//更新对话框进度条
mDialog.setProgress(progress);
}
@Override
public void onSuccess(boolean isSuccess) {
mDialog.dismiss();
//失败提示
if (!isSuccess) {
ToastTools.toastLong("更新不成功");
}
}
});
这里用到的InternetService类,IntentService是Service的子类,
IntentService实现了work非阻塞线程,适合做耗时操作,可以自行Google or 百度 他们的区别和用法。
package com.hundun.yanxishe.service;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.widget.RemoteViews;
import com.hundun.yanxishe.R;
import com.hundun.yanxishe.tools.LogTool;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* An {@link IntentService} subclass for handling asynchronous task requests in
* a service on a separate handler thread.
* <p>
* helper methods.
*/
public class AppUpdateService extends IntentService {
private static final String ACTION_UPDATE = "com.hundun.yanxishe.service.action.update";
private static final String EXTRA_URL = "com.hundun.yanxishe.service.extra.url";
private static final String EXTRA_FILE_NAME = "com.hundun.yanxishe.service.extra.file.name";
private boolean isRunning = false;
private NotificationManager updateNotificationManager;
private Notification updateNotification;
private PendingIntent updatePendingIntent;
private static OnProgressListener mProgressListener;
public interface OnProgressListener {
void onProgress(int progress);
void onSuccess(boolean isSuccess);
}
public AppUpdateService() {
super("AppUpdateService");
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_UPDATE.equals(action)) {
final String param1 = intent.getStringExtra(EXTRA_URL);
final String param2 = intent.getStringExtra(EXTRA_FILE_NAME);
startDownloade(param1, param2);
}
}
}
@Override
public void onDestroy() {
mProgressListener = null;
super.onDestroy();
}
/**
* Starts this service to perform action Baz with the given parameters. If
* the service is already performing a task this action will be queued.
*
* @see IntentService
*/
public static void startUpdate(Context context, String param1, String param2, OnProgressListener pregressListener) {
mProgressListener = pregressListener;
Intent intent = new Intent(context, AppUpdateService.class);
intent.setAction(ACTION_UPDATE);
intent.putExtra(EXTRA_URL, param1);
intent.putExtra(EXTRA_FILE_NAME, param2);
context.startService(intent);
}
private void startDownloade(String url, String fileName) {
LogTool.debug("开始升级----" + url + "---" + fileName);
if (isRunning) {
return;
}
isRunning = true;
initRemoteView();
try {
boolean isSuccess = downloadUpdateFile(url, fileName);
if (mProgressListener != null) {
mProgressListener.onSuccess(isSuccess);
}
if (isSuccess) {
Uri uri = Uri.fromFile(new File(fileName));
Intent installIntent = new Intent(Intent.ACTION_VIEW);
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
installIntent.setDataAndType(uri, "application/vnd.android.package-archive");
startActivity(installIntent);
try {
updateNotificationManager.cancel(0);
} catch (Exception ex) {
LogTool.error(ex.getMessage(), ex);
}
} else {
Notification notification = new Notification.Builder(AppUpdateService.this)
.setContentTitle(getString(R.string.app_name))
.setContentText("下载失败")
.setSmallIcon(R.drawable.download)
.build();
updateNotificationManager.notify(0, notification);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 初始化状态栏进度条
*/
private void initRemoteView() {
try {
updateNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//状态栏提醒内容
updateNotification = new Notification.Builder(this)
.setTicker("版本更新下载")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.download).build();
updatePendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, RemoteViews.class), 0);
updateNotification.contentIntent = updatePendingIntent;
//状态栏提醒内容
updateNotification.contentView = new RemoteViews(getApplication().getPackageName(), R.layout.progress);
updateNotification.contentView.setProgressBar(R.id.progressBar1, 100, 0, false);
updateNotification.contentView.setTextViewText(R.id.textView1, "0%");
// 发出通知
updateNotificationManager.notify(0, updateNotification);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 下载文件
*
* @param downloadUrl
* @param filepath
* @return
* @throws Exception
*/
private boolean downloadUpdateFile(String downloadUrl, String filepath) {
try {
int downloadCount = 0;
int currentSize = 0;
long totalSize = 0;
int updateTotalSize = 0;
boolean result = false;
HttpURLConnection httpConnection = null;
InputStream is = null;
FileOutputStream fos = null;
File temp = new File(filepath + ".tmp");
if (temp.getParentFile().isDirectory()) {
temp.getParentFile().mkdirs();
}
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(20000);
httpConnection.setReadTimeout(120000);
updateTotalSize = httpConnection.getContentLength();
if (httpConnection.getResponseCode() == 404) {
throw new Exception("fail!");
}
is = httpConnection.getInputStream();
fos = new FileOutputStream(temp, 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) - 1 > downloadCount) {
downloadCount += 1;
try {
updateNotification.contentView.setProgressBar(R.id.progressBar1, 100, (int) totalSize * 100 / updateTotalSize,
false);
updateNotification.contentView.setTextViewText(R.id.textView1, (int) totalSize * 100 / updateTotalSize + "%");
updateNotification.contentIntent = updatePendingIntent;
updateNotificationManager.notify(0, updateNotification);
if (mProgressListener != null) {
mProgressListener.onProgress((int) totalSize * 100 / updateTotalSize);
System.out.println("AppUpdateService.downloadUpdateFile"+(int) totalSize * 100 / updateTotalSize);
}
} catch (Exception ex) {
LogTool.error(ex.getMessage(), ex);
}
}
}
temp.renameTo(new File(filepath));
temp.delete();
} finally {
if (httpConnection != null) {
httpConnection.disconnect();
}
if (is != null) {
is.close();
}
if (fos != null) {
fos.close();
}
result = updateTotalSize > 0 && updateTotalSize == totalSize;
if (!result) { //下载失败或者为下载完成
new File(filepath).delete();
}
}
return result;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
贴出状态栏的布局文件:
<?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:gravity="center_vertical"
android:orientation="horizontal"
>
<ImageView
android:id="@+id/imageView1"
android:layout_width="30dp"
android:layout_height="30dp"
android:paddingLeft="5dp"
android:scaleType="centerInside"
android:src="@drawable/download"
/>
<ProgressBar
android:id="@+id/progressBar1"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="4dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="5dp"
android:layout_weight="1"
android:progressDrawable="@drawable/progressbar_style"
/>
<TextView
android:id="@+id/textView1"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:text="100%"
android:textColor="#888888"
android:textSize="14sp"/>
</LinearLayout>
这里贴代码比较多,没什么讲解,但是如果你对照着每行代码写下来,应该不需要我讲解就会明白了。
核心代码已经全部给出,如有疑问,可以“简书”找 “小追兵”留言。
注意:
状态栏的图片setSmallIcon(R.drawable.download)
,这里的图片不要太大(我的6464 px),还有注意放在drawable文件夹下,否则可能会异报常。改了,如果还报异常,clean项目应该就好了。*
文件从网络下载,写流的部分,不是我写,搬砖搬来的。
最后:有需要Shadowsock翻墙账号可以私聊。