原文地址:http://blog.****.net/wy819/article/details/51454769
Activity、Service和线程应该是Android编程中最常见的几种类了,几乎大多数应用程序都会涉及到这几个类的编程,自然而然的,也就会涉及到三者之间的相互通信,本文就试图简单地介绍一下这三者通信的方式。
想写这篇文章的起因是,笔者跟几个同学在做一个Android上的应用,起初代码写得很凌乱,因为我在Activity中直接创建了线程,去执行某些任务。但是我们知道线程可能需要运行的时间比较长,而Android在内存不足的时候,会将一些Activity销毁,这样线程就会失去了管理的对象,从而使程序发生意想不到的结果。此外,在Activity中创建线程,线程跟Activity的通信也比较麻烦,一般借助Handler类来进行通信(
http://blog.sina.com.cn/s/blog_3fe961ae0100mvc5.html)。
与Activity相比,Service一般“默默”地运行在后台,生命周期比较长,所以它更合适去为主程序提供某些服务,创建线程并管理线程。因此,笔者将原程序改成三层架构,从高到低分别为:Activity层--Service层--Thread层。Activity将需要的服务“告诉”Service层,Service层创建Thread去完成服务。Thread将任务的进度、状态、错误信息反馈给Service,Service将这些消息反馈给相关的Activity,并且还可以利用Notification更新通知栏消息。大体的结构就是这样。
1 Activity和Service之间的通信
1.1 利用Handler通信:
http://blog.sina.com.cn/s/blog_3fe961ae0100mvc5.html
:
Android在子线程中更新Activity中UI的方法:
在
Android平台下,进行多线程编程时,经常需要在主线程之外的一个单独的线程中进行某些处理,然后更新用户界面显示。但是,在主线线程之外的线程中直接更新页面显示的问题是:系统会报这个异常:ERROR/AndroidRuntime(1222):android.view.ViewRoot$CalledFromWrongThreadException:
Only theoriginal thread that created a view hierarchy can touch itsviews.
或许编程人员会在线程中调用Toast.makeText()方法,试图在UI中显示某些提示信息,这样也会报如下的错误:
Can't create handler insidethread that has not called Looper.prepare()
解决方法:子线程中无法直接去更新Activity中的UI,一般的作法是子线程向Activity传递消息,然后Activity根据这些消息自己来更新UI。Android中有一个类叫android.os.Handler,就是用来做这件事的。
1. 在需要被线程更新UI的Activity 中声明一个android.os.Handler 类的变量,privateHandler handler;
2. onCreate函数中加入handler的初始化:
@Override
public void onCreate(Bundle savedInstanceState) {
//其他代码……
//……
//……
handler=new Handler(){
public void handleMessage(Message msg){
String message=(String)msg.obj;//obj不一定是String类,可以是别的类,看用户具体的应用
//根据message中的信息对主线程的UI进行改动
//…… }
}
};
另外Activity中需要提供handler的get函数,这样线程才能得到handler,进而传递消息。
public HandlergetHandler(){
returnthis.handler;
}
3.子线程类中需要持有表示上下文的Context类对象,实际应用中这个引用就是指向要更新UI的Activity对象,一般声明为:
private Contextctx;
然后在子线程类构造函数或其它函数中初始化ctx,这一步是为了能够得到Activity对象中的Handler对象。(或者用别的方法也行,只要子线程能得到Activity中的这个handler对象就可以。)
4. 最后一步,在子线程运行到某个地方,需要向Activity传递消息的时候,创建一个android.os.Message 类的对象,将要传送的对象加入message ,通过Handler发布传送给主线程,代码示例如下:
String str_temp="要传给主线程的消息"
Message message = Message.obtain();
message.obj=str_temp;
//通过Handler发布传送消息,handler
handler.sendMessage(message);
记住,这里的handler跟Activity中的handler是同一个对象噢,这样才是把消息送到那个Activity中了。
另外,这种方法不但可以让子线程更新UI,还可以有别的用途。现在我们假设子线程可能抛出某些错误,这个应该是很正常的,那么如何让错误信息能够让用户知道呢?很简单,在catch语句块中,将catch到的错误对象,放入message.obj中,传递给Activity,Activity中用Toast.makeText()方法将错误信息显示出来就可以了。
1.2 Activity调用startService (Intentservice)方法,将消息添加到Intent对象中,这样Service对象可以在调用onStartCommand (Intentintent, int flags, intstartId)的时候可以得到这些消息。这种方法很简单,但如果有大量的信息要传递的话,就很麻烦了。因为Service端还要判断一下消息是什么,才能作进一步的动作。
1.3 Activity调用bindService (Intentservice, ServiceConnection conn, intflags)方法,得到Service对象的一个引用,这样Activity可以直接调用到Service中的方法。具体代码:
http://blog.****.net/liuhe688/article/details/6623924。
上次我们讲到如何实现一个可更新的进度通知,实现的方式是启动一个线程模拟一个下载任务,然后根据任务进度向UI线程消息队列发送进度消息,UI线程根据进度消息更新通知的UI界面。可是在实际应用中,我们一般会将上传、下载等比较耗时的后台任务以服务的形式运行,更新进度通知也是交由后台服务来完成的。 不过有的时候,除了在通知里面显示进度信息,我们也要在Activity中显示当前进度,很多下载系统都有这样的功能,例如Android自带浏览器的下载系统、QQ浏览器的下载系统等等。那么如何实现这一功能呢?实现方式有很多,我们今天先来介绍其中的一种:在Activity中主动监听服务的进度。
具体的思路是:让Activity与后台服务绑定,通过中间对象Binder的实例操作后台服务,获取进度信息和服务的状态以及在必要的时候停止服务。
关于服务的生命周期,如果有些朋友们不太熟悉的话,可以去查阅相关资料;如果以后有时间,我可能也会总结一些与服务相关的知识。
为了让大家对这个过程更清晰一些,在上代码之前,我们先来看看几个截图:




整个过程如上图所示:在我们点击开始按钮后,下载任务开始运行,同事更新通知上的进度,当前Activity也从后台服务获取进度信息,显示到按钮下方;当我们点击通知后,跳转到下载管理界面,在这里我们也从后台服务获取进度,还可以做取消任务等操作。
了解了整个过程的情况后,我们就来分析一下具体的代码实现。
首先是/res/main.xml布局文件:
-
<?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的代码:
-
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代码如下:
-
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如下:
-
<?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具体的代码:
-
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为主动方,让一个女人脚踏两只船的方式。
1.4Service向Activity发送消息,除了可以利用Handler外,还可以使用广播,当然Activity要注册相应的接收器。比如Service要向多个Activity发送同样的消息的话,用这种方法就更好。具体方法可以看一下这篇文章:
http://blog.****.net/liuhe688/article/details/6641806。
上次讲到了如何在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做一些实际的应用示例,希望对于朋友们平时遇到的问题会有所帮助,如果这方面有了新的研究总结,我会再及时更新的,谢谢大家。
2 Service跟Thread之间的通信
2.1Service创建Thread后,如果要对线程进行控制(启动,暂停,停止等),那么Service中应该保留线程的引用,这不用多说。那么有了这个引用,Service就可以直接调用Thread的其它方法了。运行的线程要向Service发送消息的话,通常使用Handler就可以了:
3 Activity和Thread之间的通信
不用多想了,直接使用Handler吧。不推荐Activity直接去创建线程,因为不好管理线程。