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

标签: android 文件 下载 | 发表时间:2014-04-24 16:35 | 作者:mybaby525
出处:http://blog.csdn.net

这几天在实现一个APK版本更新的功能,发现涉及的东西比较繁杂。本着一劳永逸的想法将相关的内容写成了相对比较独立的类供以后参考同时也与大家共享,欢迎大家批评指正

主要实现了一下几个类:

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

}


作者:mybaby525 发表于2014-4-24 8:35:56 原文链接
阅读:2 评论:0 查看评论

相关 [android 文件 下载] 推荐:

android 4.4 下载文件

- - CSDN博客推荐文章
在android4.0以后,下载程序如果在主线程中出现的话,会报android.os.NetworkOnMainThreadException 错误. 这可能是因为,在android的4.0以后使编码更加规范. 在主线程中下载可能会导致线程的假死状态. 这里我用android4.4编写了一个下载的demo.

Android下载并打开pdf文件

- - ITeye博客
下载并打开pdf文件,前提是手机上有可打开pdf文件的应用. System.out.println("我点击了按钮");. System.out.println("下载完成");. System.out.println("打开");. System.out.println("打开失败");. 已有 0 人发表留言,猛击->> 这里<<-参与讨论.

教你用电脑从 Google Play 下载 Android 程序 apk 文件

- - 小众软件 - Appinn
APK Downloader 是一款帮助你用电脑从 Google Play (原 Android Market ) 下载 Android 应用程序 apk 文件的 Chrome 扩展. Ivan 同学在 Group 讨论组 里推荐了一个用电脑从 Google Play 里下载 Android 程序的方法,可以直接下载到 apk 文件.

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

- - CSDN博客推荐文章
这几天在实现一个APK版本更新的功能,发现涉及的东西比较繁杂. 本着一劳永逸的想法将相关的内容写成了相对比较独立的类供以后参考同时也与大家共享,欢迎大家批评指正. (1)文件下载:设计自定义类,只需传入一个Handler、下载地址URLStr及保存路径及可实现下载的功能. handler主要用于线程间通信,跟新通知中的进度条.

Android 4.0 SDK 已可下载

- Elic - cnBeta.COM
Google今日在香港发布了Android 4.0系统,并面向程序员发布了开发工具包,现已可以在Android开发中心下载. 新的SDK支持移动数据控制、面部识别、高分辨率图像、增强共享等功能,详细信息请参看开发者中心页面:.

python 下载文件

- Eric - python相关的python 教程和python 下载你可以在老王python里寻觅
之前给大家分享的python 多线程抓取网页,我觉的大家看了以后,应该会对python 抓取网页有个很好的认识,不过这个只能用python 来抓取到网页的源代码,如果你想用做python 下载文件的话,上面的可能就不适合你了,最近我在用python 做文件下载的时候就遇到这个问题了,不过最终得以解决,为了让大家以后碰过这个问题有更好的解决办法,我把代码发出来:.

springmvc文件上传下载

- - ITeye博客
在网上搜索的代码 参考整理了一份. commons-fileupload.jar与commons-io-1.4.jar二个文件. 1、表单属性为: enctype="multipart/form-data". 2、springmvc配置.

android开发书籍emule下载链接

- jing77 - biAji HeRe
本来放在Verycd的,出于避免某些难以预料的问题的考虑(就像Verycd的诸多电影资源一样),我不得不觉得应该将Verycd作为一个备选方案. ed2k: [android.开发书籍].Beginning.Android.2.(Apress,.2010,.1430226293).pdf. ed2k: [android.开发书籍].Hello.Android.3rd.Edition.pdf.

CyanogenMod 7.0(Android 2.3.3)开放下载啰!

- allengaller - Engadget 中国版
客制化 Android OS 玩家又有新玩具了. Cyanogen 日前正式丢出 CyanogenMod 第七版,也就是以 Gingerbread Android 2.3.3 为基础的版本,这次除了一票手机外,还多支持了 B&N 家的电子阅读器 Nook Color 以及 Viewsonic G 等平板产品.

Android多任务多线程下载

- - 移动开发 - ITeye博客
关注微信号:javalearns   随时随地学Java. 打算实现一个下载功能,当然理想的功能要支持多任务下载、多线程下载、断点续传的功能,我想一步一步来,首先困难摆在了多任务这里. 开始的思路是在一个Service中启动下载的流操作,然后通过Service中声明一个Activity中的Handler更新UI(比如进度条.