Android Service下载文件并自定义通知提示下载

时间:2023-01-27 09:57:36
最近要做一个更新sdk,里面用到了service后台下载,自定义通知提示下载进度,下面直接贴上代码.

下面是UpdateUtils.java ,告诉你如何使用

package com.cnziz.updatelib;

import com.cnziz.updatelib.download.DownloadServices;
import com.cnziz.updatelib.utils.Listener.onUpdateListener;

import android.content.Context;
import android.content.Intent;

public class UpdateUtils {

public static UpdateUtils mUpdateUtils;

public static UpdateUtils getInstanse(){
if (null == mUpdateUtils) {
mUpdateUtils = new UpdateUtils();
}
return mUpdateUtils;
}

public void update(Context context, String appInfo, onUpdateListener mUpdateListener){
String url = "https://qd.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk";
String filePath = "/sdcard/download";
download(context, url, filePath, mUpdateListener);
}
/**
* 下载更新
* @param context
* @param url 下载地址
* @param filePath 保存目录
* @param mUpdateListener
*/
public void download(Context context, String url, String filePath, onUpdateListener mUpdateListener){
Intent intent = new Intent(context, DownloadServices.class);
intent.putExtra("url", url);
intent.putExtra("file_path", filePath);
context.startService(intent);
}
}


DownloadService.java

package com.cnziz.updatelib.download;

import java.io.File;
import java.io.IOException;

import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;

import com.cnziz.updatelib.R;
import com.cnziz.updatelib.utils.Listener.onUpdateListener;
import com.cnziz.updatelib.utils.LogUtils;

public class DownloadServices extends Service {
private final static int DOWNLOAD_COMPLETE = -2;
private final static int DOWNLOAD_FAIL = -1;

// 自定义通知栏类
MyNotification myNotification;

String filePathString; // 下载文件绝对路径(包括文件名)

// 通知栏跳转Intent
private Intent updateIntent = null;

private PendingIntent updatePendingIntent = null;

DownFileThread downFileThread; // 自定义文件下载线程

private onUpdateListener mUpdateListener;

private Handler updateHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DOWNLOAD_COMPLETE:
// 点击安装PendingIntent
Uri uri = Uri.fromFile(downFileThread.getApkFile());
Intent installIntent = new Intent(Intent.ACTION_VIEW);
installIntent.setDataAndType(uri,
"application/vnd.android.package-archive");
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(installIntent);
// updatePendingIntent = PendingIntent.getActivity(
// DownloadServices.this, 0, installIntent, 0);
// myNotification.changeContentIntent(updatePendingIntent);
// myNotification.notification.defaults = Notification.DEFAULT_SOUND;// 铃声提醒
// myNotification.changeNotificationText("下载完成,请点击安装!");

// 停止服务
// myNotification.removeNotification();
stopSelf();
break;
case DOWNLOAD_FAIL:
// 下载失败
// myNotification.changeProgressStatus(DOWNLOAD_FAIL);
myNotification.changeNotificationText("文件下载失败!");
mUpdateListener.downloadFail();
stopSelf();
break;
default: // 下载中
LogUtils.e("service", "index" + msg.what);
// myNotification.changeNotificationText(msg.what+"%");
myNotification.changeProgressStatus(msg.what);
}
}
};

public DownloadServices() {
// TODO Auto-generated constructor stub
// mcontext=context;
LogUtils.e("service", "DownloadServices1");

}

@Override
public void onCreate() {
// TODO Auto-generated method stub
LogUtils.e("service", "onCreate");
super.onCreate();
}

@Override
public void onDestroy() {
// TODO Auto-generated method stub
LogUtils.e("service", "onDestroy");
if (downFileThread != null)
downFileThread.interuptThread();
// if (null != myNotification) {
// myNotification.removeNotification();
// }
stopSelf();
super.onDestroy();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
LogUtils.e("service", "onStartCommand");
String url = intent.getStringExtra("url");
String filePath = intent.getStringExtra("file_path");
mUpdateListener = (onUpdateListener) intent.getParcelableExtra("listener");
// updateIntent = new Intent(this, MainActivity.class);
// PendingIntent updatePendingIntent = PendingIntent.getActivity(this,
// 0,
// updateIntent, 0);
myNotification = new MyNotification(this, updatePendingIntent, 1);

// myNotification.showDefaultNotification(R.drawable.ic_launcher, "测试",
// "开始下载");
myNotification.showCustomizeNotification(R.drawable.ic_launcher,
"测试下载", R.layout.notification);
if (!filePath.endsWith("/")) {
filePath = filePath +"/";
}
File path = new File(filePath);
if (!path.exists()) {
path.mkdir();
}
String[] s = url.split("/");
filePathString = filePath + s[s.length-1];
File file = new File(filePathString);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 开启一个新的线程下载,如果使用Service同步下载,会导致ANR问题,Service本身也会阻塞
downFileThread = new DownFileThread(
updateHandler,
url,
filePathString);
new Thread(downFileThread).start();

return super.onStartCommand(intent, flags, startId);
}

@Override
@Deprecated
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
LogUtils.e("service", "onStart");
super.onStart(intent, startId);
}

@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
LogUtils.e("service", "onBind");
return null;
}

}

下载线程 DownFileThread.java

package com.cnziz.updatelib.download;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;

import com.cnziz.updatelib.utils.LogUtils;

import android.annotation.SuppressLint;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.StrictMode;
import android.util.Log;

public class DownFileThread implements Runnable {
public final static int DOWNLOAD_COMPLETE = -2;
public final static int DOWNLOAD_FAIL = -1;
public final static String TAG = "DownFileThread";
Handler mHandler; // 传入的Handler,用于像Activity或service通知下载进度
String urlStr; // 下载URL
File apkFile; // 文件保存路径
boolean isFinished; // 下载是否完成
boolean interupted = false; // 是否强制停止下载线程

public DownFileThread(Handler handler, String urlStr, String filePath) {
LogUtils.i(TAG, urlStr);
this.mHandler = handler;
this.urlStr = urlStr;
apkFile = new File(filePath);
isFinished = false;
}

public File getApkFile() {
if (isFinished)
return apkFile;
else
return null;
}

public boolean isFinished() {
return isFinished;
}

/**
* 强行终止文件下载
*/
public void interuptThread() {
interupted = true;
}

@SuppressLint("NewApi")
@Override
public void run() {
// TODO Auto-generated method stub
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
java.net.URL url = null;
HttpURLConnection conn = null;
InputStream iStream = null;
// if (DEVELOPER_MODE)
{
StrictMode
.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites()
.detectNetwork() // or .detectAll() for all
// detectable problems
.penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects().penaltyLog()
.penaltyDeath().build());
}
try {
url = new java.net.URL(urlStr);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5 * 1000);
conn.setReadTimeout(20 * 1000);
iStream = conn.getInputStream();
} catch (MalformedURLException e) {
LogUtils.e(TAG, "MalformedURLException");
e.printStackTrace();
} catch (Exception e) {
LogUtils.e(TAG, "获得输入流失败");
e.printStackTrace();
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(apkFile);
} catch (FileNotFoundException e) {
LogUtils.i(TAG, "获得输出流失败:new FileOutputStream(apkFile);");
e.printStackTrace();
}
BufferedInputStream bis = new BufferedInputStream(iStream);
byte[] buffer = new byte[1024];
int len;
// 获取文件总长度
int length = conn.getContentLength();
double rate = (double) 100 / length; // 最大进度转化为100
int timeLoad = length/100/1024;
int total = 0;
int times = 0;// 设置更新频率,频繁操作UI线程会导致系统奔溃
try {
LogUtils.e("threadStatus", "开始下载");
while (false == interupted && ((len = bis.read(buffer)) != -1)) {
fos.write(buffer, 0, len);
// 获取已经读取长度
total += len;
int p = (int) (total * rate);
// Log.e("num", rate + "," + total + "," + p);
if (times >= timeLoad || p == 100) {
/*
* 这是防止频繁地更新通知,而导致系统变慢甚至崩溃。 非常重要。。。。。
*/
LogUtils.e("time", String.valueOf(times));
times = 0;
Message msg = Message.obtain();
msg.what = p;
mHandler.sendMessage(msg);
}
times++;
}
fos.close();
bis.close();
iStream.close();
if (total == length) {
isFinished = true;
mHandler.sendEmptyMessage(DOWNLOAD_COMPLETE);
LogUtils.e(TAG, "下载完成结束");
return;
}
LogUtils.e(TAG, "强制中途结束");
// mhandler.sendEmptyMessage(4);
} catch (IOException e) {
LogUtils.e(TAG, "异常中途结束");
mHandler.sendEmptyMessage(DOWNLOAD_FAIL);
e.printStackTrace();
}
} else {
LogUtils.e(TAG, "外部存储卡不存在,下载失败!");
mHandler.sendEmptyMessage(DOWNLOAD_FAIL);
}
}
}

自定义通知 MyNotification.java

package com.cnziz.updatelib.download;

import com.cnziz.updatelib.R;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.widget.RemoteViews;

public class MyNotification {
public final static int DOWNLOAD_COMPLETE = -2;
public final static int DOWNLOAD_FAIL = -1;
Context mContext; // Activity或Service上下文
Notification notification; // notification
NotificationManager nm;
String titleStr; // 通知标题
String contentStr; // 通知内容
PendingIntent contentIntent; // 点击通知后的动作
int notificationID; // 通知的唯一标示ID
int iconID; // 通知栏图标
long when = System.currentTimeMillis();
RemoteViews remoteView = null; // 自定义的通知栏视图

/**
*
* @param context
* Activity或Service上下文
* @param contentIntent
* 点击通知后的动作
* @param id
* 通知的唯一标示ID
*/
public MyNotification(Context context, PendingIntent contentIntent, int id) {
// TODO Auto-generated constructor stub
mContext = context;
notificationID = id;
this.contentIntent = contentIntent;
this.nm = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);
}

/**
* 显示自定义通知
*
* @param icoId
* 自定义视图中的图片ID
* @param titleStr
* 通知栏标题
* @param layoutId
* 自定义布局文件ID
*/
public void showCustomizeNotification(int icoId, String titleStr,
int layoutId) {
this.titleStr = titleStr;
notification = new Notification(R.drawable.ic_launcher, titleStr, when);
notification.flags = Notification.FLAG_ONLY_ALERT_ONCE;
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.contentIntent = this.contentIntent;

// 1、创建一个自定义的消息布局 view.xml
// 2、在程序代码中使用RemoteViews的方法来定义image和text。然后把RemoteViews对象传到contentView字段
if (remoteView == null) {
remoteView = new RemoteViews(mContext.getPackageName(), layoutId);
remoteView.setImageViewResource(R.id.ivNotification, icoId);
remoteView.setTextViewText(R.id.tvTitle, titleStr);
remoteView.setTextViewText(R.id.tvTip, "开始下载");
remoteView.setProgressBar(R.id.pbNotification, 100, 0, false);
notification.contentView = remoteView;
}
nm.notify(notificationID, notification);
}

/**
* 更改自定义布局文件中的进度条的值
*
* @param p
* 进度值(0~100)
*/
public void changeProgressStatus(int p) {
if (notification.contentView != null) {
if (p == DOWNLOAD_FAIL)
notification.contentView.setTextViewText(R.id.tvTip, "下载失败! ");
else if (p == 100)
notification.contentView.setTextViewText(R.id.tvTip,
"下载完成,请点击安装");
else
notification.contentView.setTextViewText(R.id.tvTip, "进度(" + p
+ "%)");
notification.contentView.setProgressBar(R.id.pbNotification, 100,
p, false);
}
nm.notify(notificationID, notification);
}

public void changeContentIntent(PendingIntent intent) {
this.contentIntent = intent;
notification.contentIntent = intent;
}

/**
* 显示系统默认格式通知
*
* @param iconId
* 通知栏图标ID
* @param titleText
* 通知栏标题
* @param contentStr
* 通知栏内容
*/
public void showDefaultNotification(int iconId, String titleText,
String contentStr) {
this.titleStr = titleText;
this.contentStr = contentStr;
this.iconID = iconId;

notification = new Notification();
notification.tickerText = titleStr;
notification.icon = iconID;
notification.flags = Notification.FLAG_INSISTENT;
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.contentIntent = this.contentIntent;

// 添加声音效果
// notification.defaults |= Notification.DEFAULT_SOUND;

// 添加震动,后来得知需要添加震动权限 : Virbate Permission
// mNotification.defaults |= Notification.DEFAULT_VIBRATE ;

// 添加状态标志
// FLAG_AUTO_CANCEL 该通知能被状态栏的清除按钮给清除掉
// FLAG_NO_CLEAR 该通知能被状态栏的清除按钮给清除掉
// FLAG_ONGOING_EVENT 通知放置在正在运行
// FLAG_INSISTENT 通知的音乐效果一直播放
notification.flags = Notification.FLAG_ONLY_ALERT_ONCE;
changeNotificationText(contentStr);
}

/**
* 改变默认通知栏的通知内容
*
* @param content
*/
public void changeNotificationText(String content) {
notification.setLatestEventInfo(mContext, titleStr, content,
contentIntent);

// 设置setLatestEventInfo方法,如果不设置会App报错异常
// NotificationManager mNotificationManager = (NotificationManager)
// getSystemService(Context.NOTIFICATION_SERVICE);
// 注册此通知
// 如果该NOTIFICATION_ID的通知已存在,会显示最新通知的相关信息 ,比如tickerText 等
nm.notify(notificationID, notification);
}

/**
* 移除通知
*/
public void removeNotification() {
// 取消的只是当前Context的Notification
nm.cancel(notificationID);
}
}


主要代码都在这了,下面是完整的项目链接
https://github.com/yan1348/ServiceDownLoad