您的位置:首页 > 其它

Notification使用详解之三:通过服务更新进度通知&在Activity中监听服务进度

2012-07-28 19:32 597 查看
上次我们讲到如何实现一个可更新的进度通知,实现的方式是启动一个线程模拟一个下载任务,然后根据任务进度向UI线程消息队列发送进度消息,UI线程根据进度消息更新通知的UI界面。可是在实际应用中,我们一般会将上传、下载等比较耗时的后台任务以服务的形式运行,更新进度通知也是交由后台服务来完成的。 不过有的时候,除了在通知里面显示进度信息,我们也要在Activity中显示当前进度,很多下载系统都有这样的功能,例如Android自带浏览器的下载系统、QQ浏览器的下载系统等等。那么如何实现这一功能呢?实现方式有很多,我们今天先来介绍其中的一种:在Activity中主动监听服务的进度。

具体的思路是:让Activity与后台服务绑定,通过中间对象Binder的实例操作后台服务,获取进度信息和服务的状态以及在必要的时候停止服务。

关于服务的生命周期,如果有些朋友们不太熟悉的话,可以去查阅相关资料;如果以后有时间,我可能也会总结一些与服务相关的知识。

为了让大家对这个过程更清晰一些,在上代码之前,我们先来看看几个截图:









整个过程如上图所示:在我们点击开始按钮后,下载任务开始运行,同事更新通知上的进度,当前Activity也从后台服务获取进度信息,显示到按钮下方;当我们点击通知后,跳转到下载管理界面,在这里我们也从后台服务获取进度,还可以做取消任务等操作。

了解了整个过程的情况后,我们就来分析一下具体的代码实现。

首先是/res/main.xml布局文件:

[html] view
plaincopy

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<Button

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="start"

android:onClick="start"/>

<TextView

android:id="@+id/text"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:gravity="center"/>

</LinearLayout>

其中Button是用来启动服务的,TextView是用来显示进度信息的。

然后再在看一下MainActivity.java的代码:

[java] view
plaincopy

package com.scott.notification;

import android.app.Activity;

import android.content.ComponentName;

import android.content.Context;

import android.content.Intent;

import android.content.ServiceConnection;

import android.os.Bundle;

import android.os.Handler;

import android.os.IBinder;

import android.os.Message;

import android.view.View;

import android.widget.TextView;

public class MainActivity extends Activity {

private DownloadService.DownloadBinder binder;

private TextView text;

private boolean binded;

private Handler handler = new Handler() {

public void handleMessage(android.os.Message msg) {

int progress = msg.arg1;

text.setText("downloading..." + progress + "%");

};

};

private ServiceConnection conn = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName name, IBinder service) {

binder = (DownloadService.DownloadBinder) service;

binded = true;

// 开始下载

binder.start();

// 监听进度信息

listenProgress();

}

@Override

public void onServiceDisconnected(ComponentName name) {

}

};

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

text = (TextView) findViewById(R.id.text);

}

@Override

protected void onDestroy() {

super.onDestroy();

if (binded) {

unbindService(conn);

}

}

public void start(View view) {

if (binded) {

binder.start();

listenProgress();

return;

}

Intent intent = new Intent(this, DownloadService.class);

startService(intent); //如果先调用startService,则在多个服务绑定对象调用unbindService后服务仍不会被销毁

bindService(intent, conn, Context.BIND_AUTO_CREATE);

}

/**

* 监听进度

*/

private void listenProgress() {

new Thread() {

public void run() {

while (!binder.isCancelled() && binder.getProgress() <= 100) {

int progress = binder.getProgress();

Message msg = handler.obtainMessage();

msg.arg1 = progress;

handler.sendMessage(msg);

if (progress == 100) {

break;

}

try {

Thread.sleep(200);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

};

}.start();

}

}

我们可以看到,当点击开始按钮后,以bindService的方式绑定服务,用获取到的DownloadService.DownloadBinder实例启动服务,并在Activity中启动一个线程监听服务的进度信息,及时的显示到按钮下方。

服务类DownloadService.java代码如下:

[java] view
plaincopy

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.Binder;

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 int progress;

private Context mContext = this;

private NotificationManager mNotificationManager;

private Notification mNotification;

private DownloadBinder binder = new DownloadBinder();

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);

stopSelf();//停掉服务自身

}

// 最后别忘了通知一下,否则不会更新

mNotificationManager.notify(NOTIFY_ID, mNotification);

break;

case 0:

// 取消通知

mNotificationManager.cancel(NOTIFY_ID);

break;

}

};

};

@Override

public void onCreate() {

super.onCreate();

mNotificationManager = (NotificationManager) getSystemService(android.content.Context.NOTIFICATION_SERVICE);

}

@Override

public IBinder onBind(Intent intent) {

// 返回自定义的DownloadBinder实例

return binder;

}

@Override

public void onDestroy() {

super.onDestroy();

cancelled = true; // 取消下载线程

}

/**

* 创建通知

*/

private void setUpNotification() {

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 intent = new Intent(this, FileMgrActivity.class);

PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

// 指定内容意图

mNotification.contentIntent = contentIntent;

mNotificationManager.notify(NOTIFY_ID, mNotification);

}

/**

* 下载模块

*/

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);

this.progress = rate;

}

if (cancelled) {

Message msg = handler.obtainMessage();

msg.what = 0;

handler.sendMessage(msg);

}

}

/**

* DownloadBinder中定义了一些实用的方法

*

* @author user

*

*/

public class DownloadBinder extends Binder {

/**

* 开始下载

*/

public void start() {

//将进度归零

progress = 0;

//创建通知

setUpNotification();

new Thread() {

public void run() {

//下载

startDownload();

};

}.start();

}

/**

* 获取进度

*

* @return

*/

public int getProgress() {

return progress;

}

/**

* 取消下载

*/

public void cancel() {

cancelled = true;

}

/**

* 是否已被取消

*

* @return

*/

public boolean isCancelled() {

return cancelled;

}

}

}

我们看到,在服务中有个DownloadBinder类,它继承自Binder,定义了一系列方法,获取服务状态以及操作当前服务,刚才我们在MainActivity中获取的就是这个类的实例。最后,不要忘了在AndroidManifest.xml中配置该服务。关于进度通知的布局文件/res/layout/download_notification_layout.xml,在这里就不需贴出了,朋友们可以参考一下Notification使用详解之二中进度通知布局的具体代码。

下面我们来介绍一下FileMgrActivity,它就是点击通知之后跳转到的界面,布局文件/res/filemgr.xml如下:

[html] view
plaincopy

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<ProgressBar

android:id="@+id/progress"

style="?android:attr/progressBarStyleHorizontal"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:max="100"

android:progress="0"/>

<Button

android:id="@+id/cancel"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="cancel"

android:onClick="cancel"/>

</LinearLayout>

我们来看一下FileMgrActivity.java具体的代码:

[java] view
plaincopy

package com.scott.notification;

import android.app.Activity;

import android.content.ComponentName;

import android.content.Context;

import android.content.Intent;

import android.content.ServiceConnection;

import android.os.Bundle;

import android.os.Handler;

import android.os.IBinder;

import android.os.Message;

import android.view.View;

import android.widget.Button;

import android.widget.ProgressBar;

public class FileMgrActivity extends Activity {

private DownloadService.DownloadBinder binder;

private ProgressBar progressBar;

private Button cancel;

private boolean binded;

private Handler handler = new Handler() {

public void handleMessage(android.os.Message msg) {

int progress = msg.arg1;

progressBar.setProgress(progress);

if (progress == 100) {

cancel.setEnabled(false);

}

};

};

private ServiceConnection conn = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName name, IBinder service) {

binder = (DownloadService.DownloadBinder) service;

//监听进度信息

listenProgress();

}

@Override

public void onServiceDisconnected(ComponentName name) {

}

};

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.filemgr);

progressBar = (ProgressBar) findViewById(R.id.progress);

cancel = (Button) findViewById(R.id.cancel);

if ("yes".equals(getIntent().getStringExtra("completed"))) {

//如果已完成,则不需再绑定service

progressBar.setProgress(100);

cancel.setEnabled(false);

} else {

//绑定service

Intent intent = new Intent(this, DownloadService.class);

bindService(intent, conn, Context.BIND_AUTO_CREATE);

binded = true;

}

}

@Override

protected void onDestroy() {

super.onDestroy();

//如果是绑定状态,则取消绑定

if (binded) {

unbindService(conn);

}

}

public void cancel(View view) {

//取消下载

binder.cancel();

}

/**

* 监听进度信息

*/

private void listenProgress() {

new Thread() {

public void run() {

while (!binder.isCancelled() && binder.getProgress() <= 100) {

int progress = binder.getProgress();

Message msg = handler.obtainMessage();

msg.arg1 = progress;

handler.sendMessage(msg);

try {

Thread.sleep(200);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

};

}.start();

}

}

我们发现,它和MainActivity实现方式很相似,恩,他们都是通过和服务绑定后获取到的Binder对象来跟服务通信的,都是主动和服务打招呼来获取信息和控制服务的。

这两个Activity和一个Service似乎像是复杂的男女关系,两个男人同时喜欢一个女人,都通过自己的手段试图从那个女人获取爱情,两个男人都很主动,那个女人显得很被动。

以上就是今天的全部内容,也许朋友们会有疑问,能不能让Service主动告知Activity当前的进度信息呢?答案是可以。下一次,我就会和大家分享一下,如何变Service为主动方,让一个女人脚踏两只船的方式。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐