Notification使用详解之四:由后台服务向Activity发送进度信息

时间:2023-01-13 11:17:47

上次讲到了如何在Activity中监听后台服务的进度信息,实现的方式是让Activity与后台服务绑定,通过中间对象Binder的实例操作后台服务。从效果上来讲,这种方式是可行的,不过这种实现有个缺点,那就是Activity的任务太重了,为了监听服务的状态,我们不得不绑定服务,然后还需不断地定时的获取最新的进度,我们为何不换一下形式呢,让Service主动将进度发送给Activity,我们在Activity中只需拿到进度数据,然后更新UI界面。这种新形式就像上次结尾提到的,就像两个男人同时喜欢一个女人,都通过自己的手段试图从那个女人那里获取爱情,现在我们要让那个女人变为主动方,将爱情同时传递给那两个男人。

要实现以上方式,我们需要用到BroadcastReceiver,如果不太了解的朋友们,可以查阅相关资料补充一下。

关于整个流程的的截图,我在这里就不在贴出了,大家可以参看Notification详解之三的流程截图。布局文件也没有变化,所以这里也不在贴出。

我们主要看一下MainActivity、DownloadService、FileMgrActivity这几个组件的实现形式。

首先是MainActivity:

package com.scott.notification;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {

	private MyReceiver receiver;
	private TextView text;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		text = (TextView) findViewById(R.id.text);
		
		receiver = new MyReceiver();
		IntentFilter filter = new IntentFilter();
		filter.addAction("android.intent.action.MY_RECEIVER");
		//注册
		registerReceiver(receiver, filter);
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		//不要忘了这一步
		unregisterReceiver(receiver);
	}

	public void start(View view) {
		Intent intent = new Intent(this, DownloadService.class);
		//这里不再使用bindService,而使用startService
		startService(intent);
	}

	/**
	 * 广播接收器
	 * @author user
	 *
	 */
	private class MyReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {
			Bundle bundle = intent.getExtras();
			int progress = bundle.getInt("progress");
			text.setText("downloading..." + progress + "%");
		}
	}
}

上面的代码中,我们的MyReceiver类是继承了BroadcastReceiver,在onReceive方法中,定义了收到进度信息并更新UI的逻辑,在onCreate中,我们注册了这个接受者,并指定action为android.intent.action.MY_RECEIVER,如此一来,如果其他组件向这个指定的action发送消息,我们就能够接收到;另外要注意的是,不要忘了在Activity被摧毁的时候调用unregisterReceiver取消注册。

然后再来看一下DownloadService有什么变化:

package com.scott.notification;

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.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.widget.RemoteViews;

public class DownloadService extends Service {

	private static final int NOTIFY_ID = 0;
	private boolean cancelled;
	
	private Context mContext = this;
	private NotificationManager mNotificationManager;
	private Notification mNotification;
	
	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case 1:
				int rate = msg.arg1;
				if (rate < 100) {
					//更新进度
					RemoteViews contentView = mNotification.contentView;
					contentView.setTextViewText(R.id.rate, rate + "%");
					contentView.setProgressBar(R.id.progress, 100, rate, false);
				} else {
					//下载完毕后变换通知形式
					mNotification.flags = Notification.FLAG_AUTO_CANCEL;
					mNotification.contentView = null;
					Intent intent = new Intent(mContext, FileMgrActivity.class);
					// 告知已完成
					intent.putExtra("completed", "yes");
					//更新参数,注意flags要使用FLAG_UPDATE_CURRENT
					PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
					mNotification.setLatestEventInfo(mContext, "下载完成", "文件已下载完毕", contentIntent);
				}

				// 最后别忘了通知一下,否则不会更新
				mNotificationManager.notify(NOTIFY_ID, mNotification);
				
				if (rate >= 100) {
					stopSelf();	//停止服务
				}
				
				break;
			case 0:
				// 取消通知
				mNotificationManager.cancel(NOTIFY_ID);
				break;
			}
		};
	};
	
	@Override
	public void onCreate() {
		super.onCreate();
		mNotificationManager = (NotificationManager) getSystemService(android.content.Context.NOTIFICATION_SERVICE);
	}
	
	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
		
		int icon = R.drawable.down;
		CharSequence tickerText = "开始下载";
		long when = System.currentTimeMillis();
		mNotification = new Notification(icon, tickerText, when);

		// 放置在"正在运行"栏目中
		mNotification.flags = Notification.FLAG_ONGOING_EVENT;

		RemoteViews contentView = new RemoteViews(mContext.getPackageName(), R.layout.download_notification_layout);
		contentView.setTextViewText(R.id.fileName, "AngryBird.apk");
		// 指定个性化视图
		mNotification.contentView = contentView;

		Intent intnt = new Intent(this, FileMgrActivity.class);
		PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intnt, PendingIntent.FLAG_UPDATE_CURRENT);
		// 指定内容意图
		mNotification.contentIntent = contentIntent;

		mNotificationManager.notify(NOTIFY_ID, mNotification);
		
		new Thread() {
			public void run() {
				startDownload();
			};
		}.start();
	}
	
	@Override
	public void onDestroy() {
		super.onDestroy();
		cancelled = true;	//停止下载线程
	}
	
	private void startDownload() {
		cancelled = false;
		int rate = 0;
		while (!cancelled && rate < 100) {
			try {
				//模拟下载进度
				Thread.sleep(500);
				rate = rate + 5;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			Message msg = handler.obtainMessage();
			msg.what = 1;
			msg.arg1 = rate;
			handler.sendMessage(msg);
			
			//发送特定action的广播
			Intent intent = new Intent();
			intent.setAction("android.intent.action.MY_RECEIVER");
			intent.putExtra("progress", rate);
			sendBroadcast(intent);
		}
		if (cancelled) {
			Message msg = handler.obtainMessage();
			msg.what = 0;
			handler.sendMessage(msg);
		}
	}
	
	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}
}

可以看到,我们在onBind方法里不在返回自定义的Binder实例了,因为现在的Service和Activitys之间并没有绑定关系了,他们是独立的;在下载过程中,我们会调用sendBroadcast方法,向指定的action发送一个附带有进度信息的intent,这样的话,所有注册过action为android.intent.action.MY_RECEIVER的Activity都能收到这条进度消息了。

最后再来看一下FileMgrActivity:

package com.scott.notification;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.ProgressBar;

public class FileMgrActivity extends Activity {
	
	private MyReceiver receiver;
	private ProgressBar progressBar;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.filemgr);
        progressBar = (ProgressBar) findViewById(R.id.progress);
        
        if ("yes".equals(getIntent().getStringExtra("completed"))) {
        	progressBar.setProgress(100);
        }
        
        receiver = new MyReceiver();
		IntentFilter filter = new IntentFilter();
		filter.addAction("android.intent.action.MY_RECEIVER");
		//注册
		registerReceiver(receiver, filter);
    }
    
    public void cancel(View view) {
    	Intent intent = new Intent(this, DownloadService.class);
    	stopService(intent);
    }
    
    @Override
    protected void onDestroy() {
    	super.onDestroy();
    	//不要忘了这一步
    	unregisterReceiver(receiver);
    }
    
    /**
	 * 广播接收器
	 * @author user
	 *
	 */
	private class MyReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {
			Bundle bundle = intent.getExtras();
			int progress = bundle.getInt("progress");
			progressBar.setProgress(progress);
		}
	}
	
}

我们发现,FileMgrActivity的模式和MainActivity差不多,也是注册了相同的广播接收者,对于DownloadService来说自己是广播基站,MainActivity和FileMgrActivity就是听众,信号能够同时到达多个听众,对于代码而言,与之前的代码比较一下,发现简洁了许多,显然这种方式更好一些。

对于我们上面提到的男女关系,DownloadService就是那个女人,然后两个男人将自己的手机号告知了女人,等于注册了接收器,然后女人将短信群发给两个男人。不过在这种情况下,两个男人互相都不知道对方的存在,以为女人深深的爱着自己,等到发现一切的时候,就是痛苦的时候。相信大家如果遇到这种情况都会很痛苦吧,至于你们信不信,我反正是信的。哈哈。

其实关于Notification的讲解最后两篇涉及到Notification的不多,主要是围绕Notification做一些实际的应用示例,希望对于朋友们平时遇到的问题会有所帮助,如果这方面有了新的研究总结,我会再及时更新的,谢谢大家。