Service应用 IntentService文件下载和Notification前台通知

service做为四大组件之一,一般用来做长时间的耗时操作,像文件下载,音乐播放等。一般应用都会涉及到文件的下载,那怎么做到下载文件的同时,又不影响app使用,这时候就需要用到service组件了。而service比较消耗性能,启动后如果不手动停止,service会一直在后台运行,即使app退出,android为我们提供了一个IntentService服务类,很好的帮我们解决了这个问题,当任务完成后,IntentService会自动销毁,不需要我们手动stopService。
业务逻辑大概是这样:启动IntentService,在onStartCommand回调方法中初始化一个Notification,然后再onHandleIntent(子线程)中开始下载文件,并不断更新Notification的进度,当下载完成时,调用系统安装程序安装。代码如下:

service类:

public class DownloadService extends IntentService {

private NotificationManager notificationManger;
private Notification notification;
private NotificationCompat.Builder mBuilder;
private String title;
private boolean isRunning = false;
private static final int PUSH_NOTIFICATION_ID = (0x001);
private static final String PUSH_CHANNEL_ID = "PUSH_NOTIFY_ID";
private static final String PUSH_CHANNEL_NAME = "PUSH_NOTIFY_NAME";

public AppDownloadService() {
    super(AppDownloadService.class.getSimpleName());
}

public AppDownloadService(String name) {
    super(name);
}

@Override
protected void onHandleIntent(Intent intent) {
    downloadTask(intent);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if ( isRunning) {
        return super.onStartCommand(intent, flags, startId);
    }
    notificationManger = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel channel = new NotificationChannel(PUSH_CHANNEL_ID, PUSH_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
        if (notificationManger != null) {
            notificationManger.createNotificationChannel(channel);
        }
    }

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

/**
 * 方法: downloadTask 

* 描述: 开启一个下载线程

*/ protected void downloadTask(Intent intent) { if (intent == null && isRunning) { return; } isRunning = true; title = intent.getStringExtra(KeyContacts.KEY_TITLE);// 下载链接 displayNotificationMessage(title); url = intent.getStringExtra(KeyContacts.KEY_URL);// 下载链接 LogUtil.e("downloadTask download url:" + url); if (TextUtils.isEmpty(url)) { downloadCallBack.onError(DownloadUtil.ERROR); return; } try { String filePath = DownloadUtil.getTargetFile(AppDownloadService.this, url); DownloadUtil.download(AppDownloadService.this, downloadCallBack, url, filePath); } catch (Exception e) { downloadCallBack.onError(DownloadUtil.ERROR); } } @Override public void success() { updateNotification(DownloadUtil.FINISH, 0); DownloadUtil.installApk(AppDownloadService.this, DownloadUtil.getTargetFile(AppDownloadService.this, url)); } public void error(int errorType) { if (errorType == DownloadUtil.ERROR) { updateNotification(DownloadUtil.ERROR, 0); } else if (errorType == DownloadUtil.SDCARDNOUSE) { notificationManger.cancel(DownloadUtil.NOTIFY_ID_DOWNLOAD); notificationManger.cancel(DownloadUtil.NOTIFY_ID_FINISHED); ToastUtil.showCustomToast("SD卡无法使用"); } } public void downloading(int process) { updateNotification(DownloadUtil.DOWNLOADING, process); } /** * 方法: displayNotificationMessage

* 描述: 下载时候显示一个通知栏

*/ private void displayNotificationMessage(String title) { Intent notificationIntent = new Intent(); notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); mBuilder = new NotificationCompat.Builder(this); mBuilder.setContentTitle(title) .setWhen(System.currentTimeMillis()) .setContentText(url) .setSmallIcon(R.drawable.ic_launcher) .setContentIntent(contentIntent) .setChannelId(PUSH_CHANNEL_ID) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher)) .setTicker(title); notification = mBuilder.build(); notification.flags |= Notification.FLAG_NO_CLEAR; notificationManger.notify(DownloadUtil.NOTIFY_ID_DOWNLOAD, notification); } /** * 方法: displayCancelNotification

* 描述: 显示一个提示完成 或者出错的通知栏

*/ private void displayCancelNotification(String title, String content, int id) { Intent notificationIntent = new Intent(); if (id == DownloadUtil.NOTIFY_ID_FINISHED) { notificationIntent = DownloadUtil.getInstallIntent(this, DownloadUtil.getTargetFile(AppDownloadService.this, url)); } notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); mBuilder = new NotificationCompat.Builder(this); mBuilder.setContentTitle(title) .setWhen(System.currentTimeMillis()) .setContentText(content) .setSmallIcon(R.drawable.ic_launcher) .setContentIntent(contentIntent) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher)) .setTicker(content); notification = mBuilder.build(); notification.defaults |= Notification.DEFAULT_SOUND; notification.flags |= Notification.FLAG_AUTO_CANCEL; notificationManger.notify(id, notification); } /** * 方法: updateNotification

* 描述: 更新通知栏

*/ protected void updateNotification(int type, int progress) { LogUtil.d("type=" + type + "---progress=" + progress); switch (type) { case DownloadUtil.DOWNLOADING: notification.flags = Notification.FLAG_NO_CLEAR; mBuilder.setProgress(100, progress, false) .setContentInfo(progress + "%"); notificationManger.notify(DownloadUtil.NOTIFY_ID_DOWNLOAD, mBuilder.build()); break; case DownloadUtil.FINISH: notificationManger.cancel(DownloadUtil.NOTIFY_ID_DOWNLOAD); displayCancelNotification(title, "下载完成,点击安装", DownloadUtil.NOTIFY_ID_FINISHED); //notificationManger.cancel(NOTIFY_ID_FINISHED); return; case DownloadUtil.ERROR: displayCancelNotification(title, "下载失败", DownloadUtil.NOTIFY_ID_ERROR); notificationManger.cancel(DownloadUtil.NOTIFY_ID_DOWNLOAD); notificationManger.cancel(DownloadUtil.NOTIFY_ID_FINISHED); break; default: break; } } }

service中用到的一些方法:

//获取下载路径:
public static String getTargetFile(Context mContext, String url) {

    String fileName = getFileName(url);
    if (TextUtils.isEmpty(fileName)) {
        fileName = UUID.randomUUID().toString();
    }
    String filePath = getDownLoadFilePath(mContext);
    if (TextUtils.isEmpty(filePath)) {
        return "";
    } else {
        return filePath + File.separator + fileName;
    }
}

public static String getDownLoadFilePath(Context context) {
    if (FileUtil.isExternalStorageCanUse()) {
        File tempPath = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator +AppConfig.SDCARD_DIR_PATH);
        if (!tempPath.exists()) {
            tempPath.mkdirs();
        }
        return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + AppConfig.SDCARD_DIR_PATH;
    } else if (FileUtil.isRootStorageCanUse()) {
        return context.getCacheDir().getAbsolutePath();
    } else {
        //磁盘空间不足
        return "";
    }
}

//通过url生产文件名
public static String getFileName(String url) {
    String filename = "";
    // 从路径中获取
    if (TextUtils.isEmpty(filename)) {
        filename = url.substring(url.lastIndexOf("/") + 1);
    }
    return filename;
}

下载工具类:

public class DownloadUtil {


public static final int NOTIFY_ID_DOWNLOAD = 10001;
public static final int NOTIFY_ID_FINISHED = 10002;
public static final int NOTIFY_ID_ERROR = 10003;
public static final int FINISH = 1;
public static final int ERROR = 2;
public static final int DOWNLOADING = 3;
//SD卡无法使用
public static final int SDCARDNOUSE = 4;

/**
 * 方法: getTargetFile 

* 描述: 得到下载的目标文件 如果为空则为UUID

*/ public static String getTargetFile(Context mContext, String url) { String fileName = getFileName(url); fileName = MD5Util.md5(fileName); if(!fileName.endsWith(".apk")){ fileName = fileName + ".apk"; } String filePath = getDownLoadFilePath(mContext); if (TextUtils.isEmpty(filePath)) { return ""; } else { return filePath + File.separator + fileName; } } /** * 方法: installApk

* 描述: 发送安装APK指令

*/ public static void installApk(Context mContext, String fileName) { mContext.startActivity(getInstallIntent(mContext, fileName)); } /** * 方法: getInstallIntent

* 描述: 得到安装apk的intent

*/ public static Intent getInstallIntent(Context mContext, String fileName) { File apkFile = new File(fileName); if (apkFile.exists()) { Intent intent = new Intent(Intent.ACTION_VIEW); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri contentUri = FileProvider.getUriForFile(mContext, mContext.getPackageName() + ".fileProvider", apkFile); intent.setDataAndType(contentUri, "application/vnd.android.package-archive"); } else { intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } return intent; } return new Intent(); } public static void download(Context mContext, DownloadCallBack downloadCallBack, String url, String filePath) { int errorNumber = 1; //下载失败尝试重新下载(只尝试2次) while (!downloadInternal(mContext, downloadCallBack, url, filePath) && errorNumber < 2) { errorNumber++; } } /** * 方法: download

* 描述: 下载方法

*/ protected static boolean downloadInternal(Context mContext, DownloadCallBack downloadCallBack, String url, String filePath) { int downLoadFileSize; int fileSize = 0; FileOutputStream fos = null; InputStream is = null; boolean success = true; try { if(url.startsWith("https://")){兼容https下载链接 SSLContext sslContext = SSLContext.getInstance("SSL");//第一个参数为 返回实现指定安全套接字协议的SSLContext对象。第二个为提供者 TrustManager[] tm = {new MyX509TrustManager()}; sslContext.init(null, tm, new SecureRandom()); HttpsURLConnection localURLConnection = (HttpsURLConnection) new URL(url).openConnection(); localURLConnection.setSSLSocketFactory(sslContext.getSocketFactory()); localURLConnection.setReadTimeout(100000); localURLConnection.setConnectTimeout(100000); localURLConnection.setRequestMethod("GET"); localURLConnection.setRequestProperty("Accept-Language", "zh-CN"); localURLConnection.setRequestProperty("Charset", "UTF-8"); localURLConnection.setRequestProperty("Connection", "Keep-Alive"); localURLConnection.setRequestProperty("Accept-Encoding", "identity"); localURLConnection.connect(); is = localURLConnection.getInputStream(); fileSize = localURLConnection.getContentLength(); }else { HttpURLConnection localURLConnection = (HttpURLConnection) new URL(url).openConnection(); localURLConnection.setReadTimeout(100000); localURLConnection.setConnectTimeout(100000); localURLConnection.setRequestMethod("GET"); localURLConnection.setRequestProperty("Accept-Language", "zh-CN"); localURLConnection.setRequestProperty("Charset", "UTF-8"); localURLConnection.setRequestProperty("Connection", "Keep-Alive"); localURLConnection.setRequestProperty("Accept-Encoding", "identity"); localURLConnection.connect(); is = localURLConnection.getInputStream(); fileSize = localURLConnection.getContentLength(); } if (is == null) { downloadCallBack.onError(ERROR); return false; } //filePath = DownloadUtil.getTargetFile(mContext,url); if (TextUtils.isEmpty(filePath)) { LogUtil.e("downloadTask STORE ERROR URL:" + url); downloadCallBack.onError(SDCARDNOUSE); return false; } File apkFile = new File(filePath); File parentFile=apkFile.getParentFile(); if (!apkFile.exists()) { if (!parentFile.exists()){ parentFile.mkdirs(); } apkFile.createNewFile(); } fos = new FileOutputStream(filePath, false); // 把数据存入路径+文件名 byte buf[] = new byte[1024 * 4]; downLoadFileSize = 0; downloadCallBack.onDownloading(0); int readCount = 0; int count =0; long start = System.currentTimeMillis(); do { // 循环读取 int numread = 0; numread = is.read(buf); if (numread == -1) { break; } fos.write(buf, 0, numread); downLoadFileSize += numread; if(fileSize < 0){//做假效果 long now = System.currentTimeMillis(); if((now - start) % 1000 == 0){ count += 5 + (now - start) / 1000; if(count > 98) count = 98; downloadCallBack.onDownloading(count); } }else{ if (readCount % 10 == 0 ) { downloadCallBack.onDownloading(downLoadFileSize * 100 / fileSize); } } readCount++; } while (true); downloadCallBack.onSuccess(); } catch (Exception e) { downloadCallBack.onError(ERROR); success = false; } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } return success; } } }

回调接口:

public interface DownloadCallBack {
	public void onError(int errorType);
	public void onDownloading(int process);
	public void onSuccess();

}

好了一个完整的前台下载service就完成了,记得需要在清单中注册service,并申请文件的读写权限,检查未知应用安装权限。检查未知应用安装权限比较特殊,不能像动态权限一样去申请,需要跳转到设置页面手动设置,所以我没想好在哪里检查合适,有好的解决方案的小伙伴可以私信告诉我。

你可能感兴趣的:(Service应用 IntentService文件下载和Notification前台通知)