Android 实现service后台多任务下载notification进度条更新

来源:互联网 发布:python运维脚本交换机 编辑:程序博客网 时间:2024/04/30 13:28

只需要一串 URL 地址,就可以实现 后台多任务下载的demo,notification进度条更新信息封装好直接提取,更新频率等问题都已优化。

demo源码下载地址在CSDN上和eoe android里都有,这边就给个链接吧。

由于是很久之前的在eoe android 社区里发帖讨论回复过相关问题了,这里就发个原帖地址链接吧。

service后台多任务下载notification进度条断点续传。



首先是service服务文件,内部包含相关注解。主要是支持后台下载,并且更新下载进度到状态栏。



package com.ljp.download.service;import java.io.File;import java.io.FileOutputStream;import java.io.InputStream;import java.util.HashMap;import java.util.Map;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import android.app.Notification;import android.app.NotificationManager;import android.app.PendingIntent;import android.app.Service;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.net.ConnectivityManager;import android.net.NetworkInfo;import android.net.Uri;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.os.IBinder;import android.os.Message;import android.util.Log;import android.widget.RemoteViews;import android.widget.Toast;import com.ljp.download.tempActivity;import com.ljp.version.R;public class DownloadService extends Service {private NotificationManager notificationMrg;public static Map<String, MyThread> threadcache = new HashMap<String, MyThread>();private Holder holder;int notifyflag = 1;private ConnectivityManager connectivityManager;private NetworkInfo info;public void onCreate() {super.onCreate();notificationMrg = (NotificationManager) this.getSystemService(android.content.Context.NOTIFICATION_SERVICE);// 注册网络监听接收器IntentFilter mFilter = new IntentFilter();mFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);mFilter.addAction("android.basic.notification.click.pause");mFilter.addAction("android.basic.notification.click.cancel");mFilter.addAction("android.basic.notification.click.restart");registerReceiver(stateReceiver, mFilter);// 注册任务广播接收器}@SuppressWarnings("null")@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {super.onStartCommand(intent, flags, startId);System.out.println("Get intent:" + intent);final String apkurl = intent.getStringExtra("url");System.out.println("Get url from intent:" + apkurl);Intent notificationIntent = new Intent(getApplicationContext(),tempActivity.class);notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);notificationIntent.putExtra("url", apkurl);Bundle bundle = new Bundle();bundle.putInt("flag", notifyflag);Log.i("avi", "loadfile中的flag     " + notifyflag);notificationIntent.putExtras(bundle);// addflag设置跳转类型PendingIntent contentIntent1 = PendingIntent.getActivity(getApplicationContext(), notifyflag, notificationIntent,PendingIntent.FLAG_UPDATE_CURRENT);// 创建Notifcation对象,设置图标,提示文字Notification notification = new Notification(R.drawable.ic_launcher,"下载进度", System.currentTimeMillis());// 设定Notification出现时的声音,一般不建议自定义// System.currentTimeMillis()notification.flags = Notification.FLAG_ONGOING_EVENT| Notification.FLAG_NO_CLEAR;// 出现在 “正在运行的”栏目下面// notification.flags |= Notification.FLAG_NO_CLEAR;// 标题栏默认清除按钮失效RemoteViews contentView1 = new RemoteViews(getPackageName(),R.layout.notification_version);contentView1.setTextViewText(R.id.n_title, "准备下载");contentView1.setTextViewText(R.id.n_text, "当前进度:" + 0 + "% ");contentView1.setProgressBar(R.id.n_progress, 100, 0, false);notification.contentView = contentView1;notification.contentIntent = contentIntent1;final MyThread thread = new MyThread(notification, apkurl, notifyflag++);thread.setchanger(true);threadcache.put(apkurl, thread);thread.start();return START_REDELIVER_INTENT;}@Overridepublic void onDestroy() {super.onDestroy();unregisterReceiver(stateReceiver);}// 状态栏视图更新private Notification displayNotificationMessage(Notification notification,int count, int flag, String url, String filename) {notification.contentIntent = receiveMsmIntent(flag, url);RemoteViews contentView1 = notification.contentView;Log.i("TAG", "updata   flag==  " + flag);Log.i("TAG", "updata   count==  " + count);Log.i("TAG", "updata   filename==  " + filename);contentView1.setTextViewText(R.id.n_title, filename);contentView1.setTextViewText(R.id.n_text, "当前进度:" + count + "% ");contentView1.setProgressBar(R.id.n_progress, 100, count, false);Log.i("temp", "diaplaynotification   " + flag);notification.contentView = contentView1;// 提交一个通知在状态栏中显示。如果拥有相同标签和相同id的通知已经被提交而且没有被移除,该方法会用更新的信息来替换之前的通知。notificationMrg.notify(flag, notification);return notification;}@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn null;}private void sendMsg(int what, int c, String url,Notification notification, int flag, Uri uri, String filename,boolean changer) {Message msg = new Message();msg.what = what;// 用来识别发送消息的类型msg.arg1 = 0;holder = new Holder();holder.count = c;holder.url = url;holder.flag = flag;holder.notify = notification;holder.Uri = uri;holder.filename = filename;holder.changer = changer;msg.obj = holder;// 消息传递的自定义对象信息handler.sendMessage(msg);}// 定义一个Handler,用于处理下载线程与主线程间通讯private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {final Holder data = (Holder) msg.obj;if (!Thread.currentThread().isInterrupted()) {// 判断下载线程是否中断switch (msg.what) {case 1:Log.i("TAG", "handlemessage中的 case 1: data.count     "+ data.count);Log.i("TAG", "handlemessage中的 case 1: flag          "+ data.flag);if (data.count >= 99) {notificationMrg.cancel(data.flag);break;}if (threadcache.containsKey(data.url)) {// 每次更新时,先以key,扫描hashmap,存在则读取出来。Notification notification;MyThread thread;thread = threadcache.get(data.url);notification = thread.notification;notification = displayNotificationMessage(notification,data.count, data.flag, data.url, data.filename);thread.notification = notification;threadcache.put(data.url, thread);}break;case 2:Log.i("avi", "case 2中filename :  " + data.filename);Log.i("avi", "case 2中filename :  " + data.changer);if (data.changer) {openfile(data.Uri);} else {Log.i("avi", "取消下载");Toast.makeText(getApplicationContext(),data.filename + "下载取消", 1).show();}threadcache.remove(data.url);Intent cancel =new Intent().setAction("android.basic.button.click.cancel");cancel.putExtra("url", data.url);sendBroadcast(cancel);break;case -1:String error = msg.getData().getString("error");Toast.makeText(getApplication(), data.filename + "下载异常终止",1).show();Intent cancel2 =new Intent().setAction("android.basic.button.click.cancel");cancel2.putExtra("url", data.url);sendBroadcast(cancel2);break;// 否则输出错误提示}}super.handleMessage(msg);}};public void openfile(Uri url) {Intent intent = new Intent(Intent.ACTION_VIEW);// 区别于默认优先启动在activity栈中已经存在的activity(如果之前启动过,并还没有被destroy的话)而是无论是否存在,都重新启动新的activityintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.setDataAndType(url, "application/vnd.android.package-archive");startActivity(intent);}public class Holder {Notification notify;String url;int count;int flag;Uri Uri;String filename;boolean changer;}private BroadcastReceiver stateReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {Log.d("mark", "网络状态已经改变");connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);info = connectivityManager.getActiveNetworkInfo();if (info != null && info.isAvailable()) {String name = info.getTypeName();Log.d("mark", "当前网络名称:" + name);} else {Log.d("mark", "没有可用网络,下载服务停止");while (notifyflag > 0) {notificationMrg.cancel(notifyflag);notifyflag -= 1;}Toast.makeText(getApplicationContext(), "网络异常,下载服务停止!", 1).show();onDestroy();}}if (action.equals("android.basic.notification.click.cancel")) {Bundle bundle = intent.getExtras();int m = bundle.getInt("flag");String url = intent.getStringExtra("url");Log.i("avi", "cancel   clickbroadcast中         接收到的 flag: " + m);Log.i("avi", "cancel   clickbroadcast中         接收到的 url: "+ url);notificationMrg.cancel(m);if (threadcache.containsKey(url)) {MyThread onethread = threadcache.get(url);Log.i("avi", "cancel  clickbroadcast中 thread 线程      url: "+ onethread.geturl());onethread.setchanger(false);Log.i("avi","cancel   clickbroadcast中 thread 线程      url: "+ onethread.getchanger());}}if (action.equals("android.basic.notification.click.pause")) {Bundle bundle = intent.getExtras();int m = bundle.getInt("flag");String url = intent.getStringExtra("url");Log.i("avi", "pause  clickbroadcast中         接收到的 flag: " + m);Log.i("avi", "pause  clickbroadcast中         接收到的 url: " + url);// notificationMrg.cancel(m);if (threadcache.containsKey(url)) {MyThread onethread = threadcache.get(url);Log.i("avi", "pause   clickbroadcast中 thread 线程      url: "+ onethread.geturl());onethread.setSuspend(true);// 当前线程暂停/ 等待}}if (action.equals("android.basic.notification.click.restart")) {Bundle bundle = intent.getExtras();int m = bundle.getInt("flag");String url = intent.getStringExtra("url");Log.i("avi", "restart  clickbroadcast中         接收到的 flag: " + m);Log.i("avi", "restart  clickbroadcast中         接收到的 url: "+ url);// notificationMrg.cancel(m);if (threadcache.containsKey(url)) {MyThread onethread = threadcache.get(url);Log.i("avi","restart   clickbroadcast中 thread 线程      url: "+ onethread.geturl());onethread.setSuspend(false);// 调用setSuspend(false)让当前线程恢复/唤醒}}}};private PendingIntent receiveMsmIntent(int flag, String url) {Intent intent = new Intent(getApplicationContext(), tempActivity.class);intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);intent.putExtra("url", url);Bundle bundle = new Bundle();bundle.putInt("flag", flag);intent.putExtras(bundle);PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), flag, intent,PendingIntent.FLAG_UPDATE_CURRENT);return contentIntent;}<pre name="code" class="java">//接下来时写了一个自定义线程,专门用来处理下载逻辑public class MyThread extends Thread {private boolean changer;private String url;private int flag;private boolean suspend = false;private String control = ""; // 只是需要一个对象而已,这个对象没有实际意义private Notification notification;public MyThread(Notification notification, String apkurl, int notifyflag) {super();this.notification = notification;this.url = apkurl;this.flag = notifyflag;}public void run() {System.out.println("父类中的方法");String url = geturl();int flag = getflag();String filename;int first = url.lastIndexOf("/") + 1;filename = url.substring(first, url.length()).toLowerCase();Log.i("filename", "从url中截取的filename  " + filename);double m = 0.0;HttpClient client = new DefaultHttpClient();HttpGet get = new HttpGet(url);HttpResponse response;try {response = client.execute(get);HttpEntity entity = response.getEntity();double length = entity.getContentLength();InputStream is = entity.getContent();// 使用InputStream对文件进行读取,就是字节流的输入FileOutputStream fileOutputStream = null;File file = null;Log.i("avi", "开关状态      " + getchanger());if (is != null && getchanger()) {file = new File(Environment.getExternalStorageDirectory(),filename);fileOutputStream = new FileOutputStream(file);byte[] buf = new byte[1024];int ch = -1;float count = 0;// ch中存放从buf字节数组中读取到的字节个数while ((ch = is.read(buf)) != -1 && getchanger()) {synchronized (control) {if (getSuspend()) {try {Log.i("avi", "暂停下载任务                "+ getSuspend());Intent pause =new Intent().setAction("android.basic.button.click.pause");pause.putExtra("url", url);sendBroadcast(pause);control.wait();} catch (InterruptedException e) {e.printStackTrace();}}}fileOutputStream.write(buf, 0, ch);count += ch;// 从字节数组读取数据read(buf)后,返回,读取的个数,count中保存,已下载的数据字节数double temp = count / length;if (temp >= m) {Log.i("TAG", "读取字节循环中的count" + temp);m += 0.1;int load = (int) (count * 100 / length);sendMsg(1, load, url, notification, flag, null,filename, true);}// 函数调用handler发送信息}}// 文件输出流为空,则表示下载完成,安装apk文件Uri Url = Uri.fromFile(file);Log.i("TAG", "下载完成,传递文件位置Url  " + Url);sendMsg(2, 0, url, notification, 0, Url, filename, getchanger());fileOutputStream.flush();if (fileOutputStream != null) {fileOutputStream.close();}} catch (Exception e) {sendMsg(-1, 0, url, notification, 0, null, filename, true);}}public void setSuspend(boolean suspend) {this.suspend = suspend;if (!suspend) {synchronized (control) {String filename=geturlfilename(url);Log.i("avi", filename+"继续下载   :   " + getSuspend());Toast.makeText(getApplicationContext(), filename+"继续下载---", 1).show();Intent restart=new Intent();restart.setAction("android.basic.button.click.restart");restart.putExtra("url", url);sendBroadcast(restart);control.notify();}}}public boolean getSuspend() {return this.suspend;}// 设置开关状态public void setchanger(boolean changer) {this.changer = changer;}public boolean getchanger() {return this.changer;}public int getflag() {return this.flag;}public String geturl() {return this.url;}public String geturlfilename(String url){String filename;int first = url.lastIndexOf("/") + 1;filename = url.substring(first, url.length()).toLowerCase();return filename;}}}

接下来时主界面测试代码:

package com.ljp.download;import java.io.File;import java.io.FileOutputStream;import java.io.InputStream;import java.util.HashMap;import java.util.Map;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import android.app.ActivityGroup;import android.app.AlertDialog;import android.app.AlertDialog.Builder;import android.app.Dialog;import android.content.BroadcastReceiver;import android.content.ComponentName;import android.content.Context;import android.content.DialogInterface;import android.content.Intent;import android.content.IntentFilter;import android.net.ConnectivityManager;import android.net.NetworkInfo;import android.net.Uri;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.os.Message;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.widget.Button;import android.widget.LinearLayout;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.Toast;import com.ljp.download.service.DownloadService;import com.ljp.download.service.DownloadService.MyThread;import com.ljp.version.R;public class ServiceDownLoadActivity extends ActivityGroup {private String apkurl1 = "http://c.uzzf.com//ddl/anzhuozhinenguanjia.apk";private String apkurl2 = "http://file.liqucn.com/upload/2011/liaotian/Youni_3.4.1.3.apk";private String apkurl3 = "http://gdown.baidu.com/data/wisegame/77dae776f870e572/PPTVjuli_61.apk";private String apkurl4 = "http://11.gddx1.crsky.com/sj/201302/TaoYouhuiSlide-v3.0.1.apk";private String apkurl5 = "http://apka.mumayi.com/2013/07/26/36/366358/zhonghuawannianli_V4.2.0_mumayi_c2b92.apk";    private Map<String,Button> buttoncache =new HashMap<String,Button>();@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);setContentView(R.layout.layout_main);final Button button1=(Button)findViewById(R.id.startdown01);final Button button2=(Button)findViewById(R.id.startdown02);final Button button3=(Button)findViewById(R.id.startdown03);final Button button4=(Button)findViewById(R.id.startdown04);final Button button5=(Button)findViewById(R.id.startdown05);      button1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {int state=0;boolean netflag =isNetworkAvailable();Log.i("TAG", "网络状况netflag:   "+netflag);Map<String, MyThread> threadcache ;threadcache =DownloadService.threadcache;String filename=geturlfilename(apkurl1);if(threadcache.containsKey(apkurl1)| !netflag){if(!netflag)Toast.makeText(getApplicationContext(), "网络异常,请检查你的网络", 1).show();else Toast.makeText(getApplication(), filename+"下载运行中---", 1).show();}else{Intent intent=new Intent(ServiceDownLoadActivity.this, DownloadService.class);  //由intent启动service,后台运行下载进程,在服务中调用notifycation状态栏显示进度条intent.putExtra("url", apkurl1);startService(intent);button1.setText("正在下载中");buttoncache.put(apkurl1, button1);}}});             button2.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {boolean state=true;boolean netflag =isNetworkAvailable();Log.i("TAG", "网络状况netflag:   "+netflag);Map<String, MyThread> threadcache ;threadcache =DownloadService.threadcache;String filename=geturlfilename(apkurl2);if(threadcache.containsKey(apkurl2)| !netflag){if(!netflag)Toast.makeText(getApplicationContext(), "网络异常,请检查你的网络", 1).show();else Toast.makeText(getApplication(), filename+"下载运行中---", 1).show();}else{Intent intent=new Intent(ServiceDownLoadActivity.this, DownloadService.class);  //由intent启动service,后台运行下载进程,在服务中调用notifycation状态栏显示进度条intent.putExtra("url", apkurl2);startService(intent);button2.setText("正在下载中");buttoncache.put(apkurl2, button2);}}});       button3.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {boolean state=true;boolean netflag =isNetworkAvailable();Log.i("TAG", "网络状况netflag:   "+netflag);Map<String, MyThread> threadcache ;threadcache =DownloadService.threadcache;String filename=geturlfilename(apkurl3);if(threadcache.containsKey(apkurl3)| !netflag){if(!netflag)Toast.makeText(getApplicationContext(), "网络异常,请检查你的网络", 1).show();else Toast.makeText(getApplication(), filename+"下载运行中---", 1).show();}else{Intent intent=new Intent(ServiceDownLoadActivity.this, DownloadService.class);  //由intent启动service,后台运行下载进程,在服务中调用notifycation状态栏显示进度条intent.putExtra("url", apkurl3);startService(intent);button3.setText("正在下载中");buttoncache.put(apkurl3, button3);}}});       button4.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {boolean state=true;boolean netflag =isNetworkAvailable();Log.i("TAG", "网络状况netflag:   "+netflag);Map<String, MyThread> threadcache ;threadcache =DownloadService.threadcache;String filename=geturlfilename(apkurl4);if(threadcache.containsKey(apkurl4)| !netflag){if(!netflag)Toast.makeText(getApplicationContext(), "网络异常,请检查你的网络", 1).show();else Toast.makeText(getApplication(), filename+"下载运行中---", 1).show();}else{Intent intent=new Intent(ServiceDownLoadActivity.this, DownloadService.class);  //由intent启动service,后台运行下载进程,在服务中调用notifycation状态栏显示进度条intent.putExtra("url", apkurl4);startService(intent);button4.setText("正在下载中");buttoncache.put(apkurl4, button4);}}});           button5.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {boolean state=true;boolean netflag =isNetworkAvailable();Log.i("TAG", "网络状况netflag:   "+netflag);Map<String, MyThread> threadcache ;threadcache =DownloadService.threadcache;String filename=geturlfilename(apkurl5);if(threadcache.containsKey(apkurl5)| !netflag){if(!netflag)Toast.makeText(getApplicationContext(), "网络异常,请检查你的网络", 1).show();else Toast.makeText(getApplication(), filename+"下载运行中---", 1).show();}else{Intent intent=new Intent(ServiceDownLoadActivity.this, DownloadService.class);  //由intent启动service,后台运行下载进程,在服务中调用notifycation状态栏显示进度条intent.putExtra("url", apkurl5);startService(intent);button5.setText("正在下载中");buttoncache.put(apkurl5, button5);}}});  IntentFilter mFilter =new IntentFilter();mFilter.addAction("android.basic.button.click.cancel");mFilter.addAction("android.basic.button.click.pause");mFilter.addAction("android.basic.button.click.restart");registerReceiver(clickReceiver, mFilter);}// NETWORKpublic boolean isNetworkAvailable() {Context context = getApplicationContext();ConnectivityManager connect = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);if (connect == null) {return false;} else// get all network info{NetworkInfo[] info = connect.getAllNetworkInfo();if (info != null) {for (int i = 0; i < info.length; i++) {if (info[i].getState() == NetworkInfo.State.CONNECTED) {return true;}}}}return false;}public String geturlfilename(String url){String filename;int first = url.lastIndexOf("/") + 1;filename = url.substring(first, url.length()).toLowerCase();return filename;}private BroadcastReceiver clickReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();String url = intent.getStringExtra("url");if (action.equals("android.basic.button.click.cancel")) {Log.i("avi", "cancel   button中         接收到的 url: "+ url);     if(buttoncache.containsKey(url))    {    Button button=buttoncache.get(url);    button.setText("开始下载");}}if (action.equals("android.basic.button.click.pause")) {Log.i("avi", "pause   button中         接收到的 url: "+ url);     if(buttoncache.containsKey(url))    {    Button button=buttoncache.get(url);    button.setText("下载暂停");}}if (action.equals("android.basic.button.click.restart")) {Log.i("avi", "restart   button中         接收到的 url: "+ url);     if(buttoncache.containsKey(url))    {    Button button=buttoncache.get(url);    button.setText("正在下载中");}}}};}


首先是service服务文件


2 0