Files
assistant-android/app/src/main/java/com/gh/download/DownloadManager.java
chenjuntao e082b321eb 尝试解决 https://gitlab.ghzhushou.com/halo/assistant-android/issues/7
1. 当设备满足系统版本为 8.0+ 且应用在后台运行时,使用 startForegroundService() 启动 DownloadService ,否则使用原来的 startService() 启动 DownloadService

2. 当应用从后台切换回前台并且 DownloadService 是前台服务时,手动调用 Service.stopForeground() 来去掉前台服务标记
2018-12-17 16:18:05 +08:00

587 lines
21 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();
// }
}