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

jopen 10年前

主要实现了一下几个类:

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