Android文件下载及自定义通知显示下载进度

分享到:

主要实现了一下几个类:

(1)文件下载:设计自定义类,只需传入一个Handler、下载地址URLStr及保存路径及可实现下载的功能。handler主要用于线程间通信,跟新通知中的进度条。

     对于handler发送消息更新UI线程实现进度展示的时候一定注意不要太过频繁,过设置计数器隔一定时间才发送消息,不然容易引起系统奔溃

   (2) 通知(Notification):提供系统默认自带形式以及自定义通知栏布局两种形式。

  (3) 服务:后台服务,startService启动模式

 

package com.example.test;
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 android.annotation.SuppressLint;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.StrictMode;
import android.util.Log;
@SuppressLint("NewApi")
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)
{
Log.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;
}
@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(5000);
conn.setReadTimeout(20000);
iStream = conn.getInputStream();
} catch (MalformedURLException e) {
Log.i(TAG, "MalformedURLException");
e.printStackTrace();
} catch (Exception e) {
Log.i(TAG, "获得输入流失败");
e.printStackTrace();
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(apkFile);
} catch (FileNotFoundException e) {
Log.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 total = 0;
int times=0;//设置更新频率,频繁操作UI线程会导致系统奔溃
try {
Log.i("threadStatus", "开始下载");
while (false==interupted  && ((len = bis.read(buffer)) != -1)) {
fos.write(buffer, 0, len);
// 获取已经读取长度
total += len;
int p=(int)(total*rate);
Log.i("num", rate+","+total+","+p);
if(times>=512 || p==100)
{/*
这是防止频繁地更新通知,而导致系统变慢甚至崩溃。
非常重要。。。。。*/
Log.i("time", "time");
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);
Log.i(TAG, "下载完成结束");
}
Log.i(TAG, "强制中途结束");
//mhandler.sendEmptyMessage(4);
} catch (IOException e) {
Log.i(TAG, "异常中途结束");
mHandler.sendEmptyMessage(DOWNLOAD_FAIL);
e.printStackTrace();
}
}
else
{
Log.i(TAG, "外部存储卡不存在,下载失败!");
mHandler.sendEmptyMessage(DOWNLOAD_FAIL);
}
}
}  
package com.example.test;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.widget.RemoteViews;
/**
* Notification类,既可用系统默认的通知布局,也可以用自定义的布局
*
* @author lz
*
*/
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);
}
}  

package com.example.test;
import java.io.File;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
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.provider.Settings.Global;
import android.util.Log;
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 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");
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("文件下载失败!");
stopSelf();
break;
default:  //下载中
Log.i("service", "default"+msg.what);
//
myNotification.changeNotificationText(msg.what+"%");
myNotification.changeProgressStatus(msg.what);
}
}
};
public DownloadServices() {
// TODO Auto-generated constructor stub
//  mcontext=context;
Log.i("service","DownloadServices1");
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
Log.i("service","onCreate");
super.onCreate();
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
Log.i("service","onDestroy");
if(downFileThread!=null)
downFileThread.interuptThread();
stopSelf();
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
Log.i("service","onStartCommand");
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);
filePathString=Environment.getExternalStorageDirectory().getAbsolutePath() + "/family.apk";
//开启一个新的线程下载,如果使用Service同步下载,会导致ANR问题,Service本身也会阻塞
downFileThread=new  DownFileThread(updateHandler,"http://10.103.241.247:8013/update/download",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
Log.i("service","onStart");
super.onStart(intent, startId);
}
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
Log.i("service","onBind");
return null;
}
}  
昵    称:
验证码:

相关文档: