1. 当设备满足系统版本为 8.0+ 且应用在后台运行时,使用 startForegroundService() 启动 DownloadService ,否则使用原来的 startService() 启动 DownloadService 2. 当应用从后台切换回前台并且 DownloadService 是前台服务时,手动调用 Service.stopForeground() 来去掉前台服务标记
587 lines
21 KiB
Java
587 lines
21 KiB
Java
package com.gh.download;
|
||
|
||
import android.content.Context;
|
||
import android.content.Intent;
|
||
import android.content.SharedPreferences;
|
||
import android.os.Build;
|
||
import android.os.Handler;
|
||
import android.os.Looper;
|
||
import android.os.Message;
|
||
import android.preference.PreferenceManager;
|
||
import android.support.annotation.Nullable;
|
||
import android.support.v4.util.ArrayMap;
|
||
import android.text.TextUtils;
|
||
|
||
import com.gh.common.exposure.ExposureEvent;
|
||
import com.gh.common.util.AppDebugConfig;
|
||
import com.gh.common.util.DataCollectionUtils;
|
||
import com.gh.common.util.DialogUtils;
|
||
import com.gh.common.util.GdtHelper;
|
||
import com.gh.common.util.MD5Utils;
|
||
import com.gh.common.util.PackageUtils;
|
||
import com.gh.gamecenter.entity.ApkEntity;
|
||
import com.gh.gamecenter.entity.GameEntity;
|
||
import com.gh.gamecenter.eventbus.EBDownloadStatus;
|
||
import com.gh.gamecenter.manager.PackagesManager;
|
||
import com.google.gson.Gson;
|
||
import com.halo.assistant.HaloApp;
|
||
import com.lightgame.config.CommonDebug;
|
||
import com.lightgame.download.ConnectionUtils;
|
||
import com.lightgame.download.DataChanger;
|
||
import com.lightgame.download.DataWatcher;
|
||
import com.lightgame.download.DownloadConfig;
|
||
import com.lightgame.download.DownloadDao;
|
||
import com.lightgame.download.DownloadEntity;
|
||
import com.lightgame.download.DownloadService;
|
||
import com.lightgame.download.DownloadStatus;
|
||
import com.lightgame.download.DownloadStatusListener;
|
||
import com.lightgame.download.DownloadStatusManager;
|
||
import com.lightgame.download.FileUtils;
|
||
import com.lightgame.utils.Utils;
|
||
import com.qq.gdt.action.ActionType;
|
||
|
||
import org.greenrobot.eventbus.EventBus;
|
||
|
||
import java.io.File;
|
||
import java.util.ArrayList;
|
||
import java.util.List;
|
||
import java.util.concurrent.LinkedBlockingQueue;
|
||
|
||
import static android.os.Build.MANUFACTURER;
|
||
|
||
public class DownloadManager implements DownloadStatusListener {
|
||
|
||
private static DownloadManager mInstance;
|
||
private static Gson gson = new Gson();
|
||
|
||
private Context mContext;
|
||
private Handler mHandler;
|
||
|
||
private ArrayMap<String, Long> lastTimeMap;
|
||
private ArrayMap<String, LinkedBlockingQueue<String>> platformMap;
|
||
private ArrayMap<String, ArrayMap<String, DownloadEntity>> gameMap;
|
||
|
||
private ArrayMap<String, DownloadStatus> statusMap;
|
||
private ArrayMap<String, DownloadEntity> downloadingMap;
|
||
|
||
private DownloadDao mDownloadDao;
|
||
|
||
@Override
|
||
public void onTaskCancelled(DownloadEntity entity) {
|
||
EBDownloadStatus status = new EBDownloadStatus("delete", entity.getName(),
|
||
entity.getPlatform(), entity.getUrl(), entity.getPackageName(), entity.getGameId());
|
||
status.setPluggable(entity.isPluggable());
|
||
EventBus.getDefault().post(status);
|
||
|
||
DownloadNotification.showDownloadingNotification(mContext);
|
||
|
||
DownloadManager.getInstance(mContext).putStatus(entity.getUrl(), DownloadStatus.delete);
|
||
|
||
downloadingMap.remove(entity.getUrl());
|
||
}
|
||
|
||
@Override
|
||
public void onTaskAdded(DownloadEntity entity) {
|
||
EventBus.getDefault().post(new EBDownloadStatus("download"));
|
||
|
||
DownloadNotification.showDownloadingNotification(mContext);
|
||
|
||
downloadingMap.put(entity.getUrl(), entity);
|
||
|
||
}
|
||
|
||
@Override
|
||
public void onTaskError(DownloadEntity entity) {
|
||
DownloadNotification.showDownloadingNotification(mContext);
|
||
downloadingMap.remove(entity.getUrl());
|
||
}
|
||
|
||
@Override
|
||
public void onTaskStatusChanged(DownloadEntity entity) {
|
||
|
||
}
|
||
|
||
@Override
|
||
public void onTaskDone(DownloadEntity entity) {
|
||
DownloadNotification.showDownloadingNotification(mContext);
|
||
DownloadNotification.showDownloadDoneNotification(mContext, entity);
|
||
downloadingMap.remove(entity.getUrl());
|
||
}
|
||
|
||
@Override
|
||
public void onTaskPaused(DownloadEntity entity) {
|
||
|
||
}
|
||
|
||
private DownloadManager(Context context) {
|
||
mContext = context.getApplicationContext();
|
||
mDownloadDao = DownloadDao.getInstance(mContext);
|
||
|
||
//TODO unregister this
|
||
DownloadStatusManager.getInstance().registerTaskStatusListener(this);
|
||
|
||
// 只有下载模块需要这坨东西,因此移动到这里初始化
|
||
ConnectionUtils.initHttpsUrlConnection(context);
|
||
|
||
// DownloadNotification.showDownloadingNotification(mContext);
|
||
|
||
lastTimeMap = new ArrayMap<>();
|
||
platformMap = new ArrayMap<>();
|
||
gameMap = new ArrayMap<>();
|
||
statusMap = new ArrayMap<>();
|
||
downloadingMap = new ArrayMap<>();
|
||
|
||
mHandler = new Handler(Looper.getMainLooper()) {
|
||
@Override
|
||
public void handleMessage(Message msg) {
|
||
String url = (String) msg.obj;
|
||
switch (msg.what) {
|
||
case DownloadConfig.CONTINUE_DOWNLOAD_TASK:
|
||
if (lastTimeMap.get(url) != null && System.currentTimeMillis() - lastTimeMap.get(url) >= 1000) {
|
||
DownloadEntity downloadEntity = getDownloadEntityByUrl(url);
|
||
if (downloadEntity != null) add(downloadEntity);
|
||
}
|
||
break;
|
||
case DownloadConfig.PAUSE_DOWNLOAD_TASK:
|
||
if (lastTimeMap.get(url) != null && System.currentTimeMillis() - lastTimeMap.get(url) >= 1000) {
|
||
pause(url);
|
||
}
|
||
break;
|
||
case DownloadConfig.DOWNLOAD_ROLL:
|
||
LinkedBlockingQueue<String> queue = platformMap.get(url);
|
||
if (queue.size() > 1) {
|
||
queue.offer(queue.poll());
|
||
Message message = Message.obtain();
|
||
message.obj = url;
|
||
message.what = DownloadConfig.DOWNLOAD_ROLL;
|
||
sendMessageDelayed(message, 3000);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
};
|
||
|
||
List<DownloadEntity> list = getAll();
|
||
for (DownloadEntity downloadEntity : list) {
|
||
statusMap.put(downloadEntity.getUrl(), downloadEntity.getStatus());
|
||
if (!DownloadStatus.done.equals(downloadEntity.getStatus())) {
|
||
downloadingMap.put(downloadEntity.getUrl(), downloadEntity);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
public ArrayMap<String, DownloadEntity> getDownloadingMap() {
|
||
return downloadingMap;
|
||
}
|
||
|
||
public static DownloadManager getInstance(Context context) {
|
||
if (mInstance == null) {
|
||
synchronized (DownloadManager.class) {
|
||
if (mInstance == null) {
|
||
mInstance = new DownloadManager(context.getApplicationContext());
|
||
}
|
||
}
|
||
}
|
||
return mInstance;
|
||
}
|
||
|
||
public static void createDownload(Context context,
|
||
GameEntity gameEntity,
|
||
String method,
|
||
String entrance,
|
||
String location,
|
||
boolean isSubscribe,
|
||
@Nullable ExposureEvent traceEvent) {
|
||
createDownload(context, gameEntity.getApk().get(0), gameEntity, method, entrance, location, isSubscribe, traceEvent);
|
||
}
|
||
|
||
public static void createDownload(final Context context,
|
||
ApkEntity apkEntity,
|
||
GameEntity gameEntity,
|
||
String method,
|
||
String entrance,
|
||
String location,
|
||
boolean isSubscribe,
|
||
@Nullable ExposureEvent traceEvent) {
|
||
|
||
// 安装指引
|
||
if ("Huawei".equalsIgnoreCase(MANUFACTURER) || "Oppo".equalsIgnoreCase(MANUFACTURER)) {
|
||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||
final SharedPreferences.Editor edit = sp.edit();
|
||
if (sp.getBoolean("InstallHint" + PackageUtils.getVersionName(), true)) {
|
||
try {
|
||
DialogUtils.showInstallHintDialog(context,
|
||
() -> edit.putBoolean("InstallHint" + PackageUtils.getVersionName(), false).apply());
|
||
} catch (Exception exception) {
|
||
exception.printStackTrace();
|
||
edit.putBoolean("InstallHint" + PackageUtils.getVersionName(), false).apply();
|
||
}
|
||
}
|
||
}
|
||
|
||
DownloadEntity downloadEntity = new DownloadEntity();
|
||
downloadEntity.setUrl(apkEntity.getUrl());
|
||
downloadEntity.setName(gameEntity.getName());
|
||
downloadEntity.setPath(FileUtils.getDownloadPath(context,
|
||
MD5Utils.getContentMD5(gameEntity.getName() + "_" + System.currentTimeMillis()) + ".apk"));
|
||
downloadEntity.setETag(apkEntity.getEtag());
|
||
downloadEntity.setIcon(gameEntity.getIcon());
|
||
downloadEntity.setPlatform(apkEntity.getPlatform());
|
||
downloadEntity.setPackageName(apkEntity.getPackageName());
|
||
downloadEntity.setGameId(gameEntity.getId());
|
||
downloadEntity.setEntrance(entrance);
|
||
downloadEntity.setExposureTrace(gson.toJson(traceEvent));
|
||
downloadEntity.setLocation(location);
|
||
downloadEntity.setVersionName(apkEntity.getVersion());
|
||
int installed = 0;
|
||
for (ApkEntity apk : gameEntity.getApk()) {
|
||
if (PackagesManager.INSTANCE.isInstalled(apk.getPackageName())) {
|
||
installed++;
|
||
}
|
||
}
|
||
downloadEntity.setInstalled(installed);
|
||
if ("更新".equals(method)) {
|
||
downloadEntity.setUpdate(true);
|
||
} else if ("插件化".equals(method)) {
|
||
downloadEntity.setPluggable(true);
|
||
}
|
||
downloadEntity.setPlugin(gameEntity.getTag() != null && gameEntity.getTag().size() != 0);
|
||
|
||
if (isSubscribe) {
|
||
DownloadManager.getInstance(context).subscribe(downloadEntity);
|
||
} else {
|
||
DownloadManager.getInstance(context).add(downloadEntity);
|
||
}
|
||
|
||
if (AppDebugConfig.IS_DEBUG) {
|
||
AppDebugConfig.logMethodWithParams(DownloadManager.class, apkEntity.getUrl(), downloadEntity.getUrl(), method, entrance, location);
|
||
}
|
||
|
||
//TODO remove
|
||
DownloadManager.getInstance(context).putStatus(downloadEntity.getUrl(), DownloadStatus.downloading);
|
||
|
||
// 收集下载数据
|
||
DataCollectionUtils.uploadDownload(context, downloadEntity, "开始");
|
||
GdtHelper.INSTANCE.logAction(ActionType.DOWNLOAD_APP,
|
||
GdtHelper.GAME_ID, downloadEntity.getGameId(),
|
||
GdtHelper.PLATFORM, downloadEntity.getPlatform());
|
||
}
|
||
|
||
/**
|
||
* 添加一个下载任务
|
||
*
|
||
* @param downloadEntity
|
||
*/
|
||
public void add(DownloadEntity downloadEntity) {
|
||
if (downloadEntity != null) {
|
||
String url = downloadEntity.getUrl();
|
||
checkDownloadEntryRecordValidate(url);
|
||
if (isFileCompleted(url)) {
|
||
downloadEntity.setStatus(DownloadStatus.done);
|
||
DataChanger.INSTANCE.notifyDataChanged(downloadEntity);
|
||
} else if (!isTaskDownloading(url)) {
|
||
startDownloadService(downloadEntity, DownloadStatus.add);
|
||
}
|
||
put(url, System.currentTimeMillis());
|
||
putStatus(url, DownloadStatus.downloading);
|
||
}
|
||
Utils.log(DownloadManager.class.getSimpleName(), "add");
|
||
}
|
||
|
||
/**
|
||
* 添加一个下载任务(WiFi时自动下载)
|
||
*
|
||
* @param downloadEntity
|
||
*/
|
||
public void subscribe(DownloadEntity downloadEntity) {
|
||
if (downloadEntity != null) {
|
||
String url = downloadEntity.getUrl();
|
||
checkDownloadEntryRecordValidate(url);
|
||
if (isFileCompleted(url)) {
|
||
downloadEntity.setStatus(DownloadStatus.done);
|
||
DataChanger.INSTANCE.notifyDataChanged(downloadEntity);
|
||
} else if (!isTaskDownloading(url)) {
|
||
startDownloadService(downloadEntity, DownloadStatus.subscribe);
|
||
}
|
||
put(url, System.currentTimeMillis());
|
||
putStatus(url, DownloadStatus.subscribe);
|
||
}
|
||
Utils.log(DownloadManager.class.getSimpleName(), "subscribe");
|
||
}
|
||
|
||
/**
|
||
* 根据url到本地sqlite数据库中查找并获取该文件的保存路径, 并检查改路径下文件是否存在,不存在则删除该条无效记录
|
||
*
|
||
* @param url 下载任务的标识url
|
||
*/
|
||
private boolean checkDownloadEntryRecordValidate(String url) {
|
||
DownloadEntity entry = getDownloadEntityByUrl(url);
|
||
if (entry != null && ((int) entry.getPercent()) != 0) {
|
||
File file = new File(entry.getPath());
|
||
if (!file.exists()) {
|
||
mDownloadDao.delete(url);
|
||
Utils.log(DownloadManager.class.getSimpleName(), "文件不存在,删除该条无效数据库记录!");
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 检查任务是否已经下载完成
|
||
*
|
||
* @param url
|
||
* @return
|
||
*/
|
||
public boolean isFileCompleted(String url) {
|
||
DownloadEntity entry = getDownloadEntityByUrl(url);
|
||
|
||
if (entry != null && entry.getPercent() == 100) {
|
||
Utils.log(DownloadManager.class.getSimpleName(), "文件已经下载完成!");
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public boolean isTaskDownloading(String url) {
|
||
if (DataChanger.INSTANCE.getDownloadingTasks().get(url) != null) {
|
||
Utils.log(DownloadManager.class.getSimpleName(), url + "正在下载!");
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
private Intent getIntent(DownloadEntity entry, DownloadStatus status) {
|
||
Intent service = new Intent(mContext, DownloadService.class);
|
||
service.putExtra(DownloadConfig.KEY_DOWNLOAD_ENTRY, entry);
|
||
service.putExtra(DownloadConfig.KEY_DOWNLOAD_ACTION, status.name());
|
||
return service;
|
||
}
|
||
|
||
/**
|
||
* 根据url获取某一个下载任务
|
||
*
|
||
* @param url 下载链接
|
||
* @return null表示下载列表中不存在该任务,否则返回下载任务
|
||
*/
|
||
public DownloadEntity getDownloadEntityByUrl(String url) {
|
||
if (TextUtils.isEmpty(url)) return null;
|
||
return mDownloadDao.get(url);
|
||
}
|
||
|
||
/**
|
||
* 根据包名获取某一个下载任务
|
||
*
|
||
* @param packageName 包名
|
||
* @return null表示下载列表中不存在该任务,否则返回下载任务
|
||
*/
|
||
public DownloadEntity getDownloadEntityByPackageName(String packageName) {
|
||
for (DownloadEntity downloadEntity : mDownloadDao.getAll()) {
|
||
if (packageName.equals(downloadEntity.getPackageName())) {
|
||
return downloadEntity;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
public void put(String url, long time) {
|
||
lastTimeMap.put(url, time);
|
||
}
|
||
|
||
public void putQueue(String name, LinkedBlockingQueue<String> queue) {
|
||
platformMap.put(name, queue);
|
||
}
|
||
|
||
public LinkedBlockingQueue<String> getQueue(String name) {
|
||
return platformMap.get(name);
|
||
}
|
||
|
||
public void removePlatform(String name, String platform) {
|
||
LinkedBlockingQueue<String> queue = platformMap.get(name);
|
||
if (queue != null) {
|
||
queue.remove(platform);
|
||
platformMap.put(name, queue);
|
||
}
|
||
}
|
||
|
||
public void initGameMap() {
|
||
gameMap.clear();
|
||
List<DownloadEntity> list = getAll();
|
||
if (list != null && list.size() != 0) {
|
||
String name;
|
||
for (DownloadEntity downloadEntity : list) {
|
||
name = downloadEntity.getName();
|
||
ArrayMap<String, DownloadEntity> map = gameMap.get(name);
|
||
if (map == null) {
|
||
map = new ArrayMap<>();
|
||
gameMap.put(name, map);
|
||
}
|
||
map.put(downloadEntity.getPlatform(), downloadEntity);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取所有下载列表中的任务
|
||
*
|
||
* @return null表示没有下载任务
|
||
*/
|
||
public List<DownloadEntity> getAll() {
|
||
if (CommonDebug.IS_DEBUG) {
|
||
CommonDebug.logMethodName(this);
|
||
}
|
||
return mDownloadDao.getAll();
|
||
}
|
||
|
||
public ArrayMap<String, DownloadEntity> getEntryMap(String name) {
|
||
return gameMap.get(name);
|
||
}
|
||
|
||
private void putStatus(String url, DownloadStatus status) {
|
||
statusMap.put(url, status);
|
||
onTaskStatusChanged(getDownloadEntityByUrl(url));
|
||
}
|
||
|
||
public DownloadStatus getStatus(String url) {
|
||
return statusMap.get(url);
|
||
}
|
||
|
||
public ArrayMap<String, DownloadStatus> getStatusMap() {
|
||
return statusMap;
|
||
}
|
||
|
||
public void sendMessageDelayed(Message msg, long delayMillis) {
|
||
mHandler.sendMessageDelayed(msg, delayMillis);
|
||
}
|
||
|
||
/**
|
||
* 根据url取消下载,并删除已下载的文件
|
||
*
|
||
* @param url
|
||
*/
|
||
public void cancel(String url) {
|
||
cancel(url, true);
|
||
}
|
||
|
||
public void cancel(String url, boolean isDeleteFile) {
|
||
DownloadEntity entry = mDownloadDao.get(url);
|
||
if (entry != null) {
|
||
if (isDeleteFile) {
|
||
FileUtils.deleteFile(entry.getPath());
|
||
}
|
||
mDownloadDao.delete(url);
|
||
Utils.log(DownloadManager.class.getSimpleName(), "cancel==>file and record were deleted!");
|
||
}
|
||
if (entry != null) {
|
||
entry.setStatus(DownloadStatus.cancel);
|
||
startDownloadService(entry, DownloadStatus.cancel);
|
||
Utils.log(DownloadManager.class.getSimpleName(), "cancel");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 取消并删除所有下载任务(包括下载中、等待、暂停状态的任务)
|
||
*/
|
||
public void cancelAll() {
|
||
for (DownloadEntity entry : DataChanger.INSTANCE.getDownloadEntries().values()) {
|
||
cancel(entry.getUrl(), true);
|
||
}
|
||
Utils.log(DownloadManager.class.getSimpleName(), "cancel all");
|
||
}
|
||
|
||
/**
|
||
* 开始所有下载任务
|
||
*/
|
||
public void startAll() {
|
||
for (DownloadEntity entry : downloadingMap.values()) {
|
||
add(entry);
|
||
}
|
||
Utils.log(DownloadManager.class.getSimpleName(), "start all");
|
||
}
|
||
|
||
/**
|
||
* 暂停所有正在下载的任务
|
||
*/
|
||
public void pauseAll() {
|
||
for (DownloadEntity entity : DataChanger.INSTANCE.getDownloadEntries().values()) {
|
||
pause(entity.getUrl());
|
||
}
|
||
Utils.log(DownloadManager.class.getSimpleName(), "pause all");
|
||
}
|
||
|
||
/**
|
||
* 根据任务url暂停下载
|
||
*
|
||
* @param url
|
||
*/
|
||
public void pause(String url) {
|
||
checkDownloadEntryRecordValidate(url);
|
||
DownloadEntity entry = DataChanger.INSTANCE.getDownloadEntries().get(url);
|
||
if (entry != null) {
|
||
startDownloadService(entry, DownloadStatus.pause);
|
||
put(url, System.currentTimeMillis());
|
||
statusMap.put(url, DownloadStatus.pause);
|
||
}
|
||
Utils.log(DownloadManager.class.getSimpleName(), "pause");
|
||
}
|
||
|
||
public void pause(DownloadEntity entity) {
|
||
if (entity != null) {
|
||
pause(entity.getUrl());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查数据库中当前是下载状态,但并未在下载进程中的下载数据
|
||
*/
|
||
public void checkAll() {
|
||
final List<String> urlList = new ArrayList<>(DataChanger.INSTANCE.getDownloadingTasks().keySet());
|
||
for (DownloadEntity downloadEntity : getAll()) {
|
||
if (!urlList.contains(downloadEntity.getUrl())
|
||
&& downloadEntity.getStatus().equals(DownloadStatus.downloading)) {
|
||
downloadEntity.setStatus(DownloadStatus.pause);
|
||
mDownloadDao.newOrUpdate(downloadEntity);
|
||
DataChanger.INSTANCE.notifyDataChanged(downloadEntity);
|
||
}
|
||
}
|
||
|
||
// 开启下载服务
|
||
startDownloadServiceInBackground();
|
||
DownloadNotification.showDownloadingNotification(mContext);
|
||
}
|
||
|
||
public void addObserver(DataWatcher dataWatcher) {
|
||
Utils.log(DownloadManager.class.getSimpleName(), "addObserver");
|
||
DataChanger.INSTANCE.addObserver(dataWatcher);
|
||
}
|
||
|
||
public void removeObserver(DataWatcher dataWatcher) {
|
||
Utils.log(DownloadManager.class.getSimpleName(), "removeObserver");
|
||
DataChanger.INSTANCE.deleteObserver(dataWatcher);
|
||
}
|
||
|
||
public void startDownloadServiceInBackground() {
|
||
mContext.startService(new Intent(mContext, DownloadService.class));
|
||
}
|
||
|
||
public void startDownloadService(DownloadEntity downloadEntity, DownloadStatus status) {
|
||
Intent serviceIntent = getIntent(downloadEntity, status);
|
||
// 当满足系统版本大于 8.0 、应用在后台运行时以前台服务开启
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||
&& !HaloApp.getInstance().isRunningForeground) {
|
||
serviceIntent.putExtra(DownloadService.KEY_SERVICE_ACTION, DownloadService.START_FOREGROUND);
|
||
mContext.startForegroundService(serviceIntent);
|
||
} else {
|
||
mContext.startService(serviceIntent);
|
||
}
|
||
}
|
||
//
|
||
// public void removeObservers() {
|
||
// Utils.log(DownloadManager.class.getSimpleName(), "removeObserver");
|
||
// DataChanger.INSTANCE.deleteObservers();
|
||
// }
|
||
|
||
}
|