一、概述
广播(Broadcast)机制用于进程/线程间通信,其中广播接收者BroadcastReceiver便是Android四大组件之一。因此在我们应用程序内发出的广播,其他的应用程序应该也是可以收到的。广播分为广播发送和广播接收两个过程。
发送广播的方法是使用Intent,而接收广播使用的是广播接收者(Broadcast Receiver)
BroadcastReceiver分为两类:
静态广播接收者:通过AndroidManifest.xml的标签来申明的BroadcastReceiver。
动态广播接收者:通过AMS.registerReceiver()方式注册的BroadcastReceiver,动态注册更为灵活,可在不需要时通过unregisterReceiver()取消注册。
从广播发送方式可分为二类:
标准广播(Normal broadcasts):通过Context.sendBroadcast()发送,是一种完全异步执行的广播
有序广播(Ordered broadcasts):通过Context.sendOrderedBroadcast()发送,是一种同步执行的广播
广播的功能和特征:
1、广播的生命周期很短,经过 调用对象—实现onReceive—结束 整个过程就结束了。从实现的复杂度和代码量来看,广播无疑是最迷你的Android 组件,实现往往只需几行代码。广播对象被构造出来后通常只执行BroadcastReceiver.onReceive方法,便结束了其生命周期。所以有的时候我们可以把它当做函数看也未必不可。
2、和所有组件一样,广播对象也是在应用进程的主线程中被构造,所以广播对象的执行必须是要同步且快速的。也不推荐在里面开子线程,因为往往线程还未结束,广播对象就已经执行完毕被系统销毁。如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由 Service 来完成。
3、每次广播到来时 , 会重新创建 BroadcastReceiver 对象 , 并且调用 onReceive() 方法 , 执行完以后 , 该对象即被销毁 . 当 onReceive() 方法在 10 秒内没有执行完毕, Android 会认为该程序无响应。
应用场景
注册广播有两种方式:动态注册和静态注册
1、非常驻型广播(动态注册):非常驻型广播,广播跟随程序的生命周期,当应用程序结束了,广播自然就没有了,比如在 Activity 中的 onCreate 或者 onResume 中注册广播接收者,在 onDestory 中注销广播接收者。这样你的广播接收者就一个非常驻型的了,这种注册方式也叫动态注册。这种方式可以理解为通过代码注册的广播是和注册者关联在一起的。动态注册的广播,最后必须取消注册。这类广播,只有应用启动了,才能接收到广播。
实现动态注册需要以下内容
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.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private NetworkChangeReceiver networkChangeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter();
//网络发生变化时系统发出的是一条值为android.net.conn.CONNECTIVITY_CHANGE的广播,
//即想监听什么广播就在此添加相应action
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
//创建NetworkChangeReceiver实例
networkChangeReceiver = new NetworkChangeReceiver();
//注册实例
registerReceiver(networkChangeReceiver, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
//动态注册的广播接收器需取消注册
unregisterReceiver(networkChangeReceiver);
}
/**
* 继承自BroadcastReceiver,并重写了onReceive()。当网络状态发生变化时,onReceive()方法便会执行
*/
class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
//判断当前是否有网络
if (networkInfo != null && networkInfo.isAvailable()) {
Toast.makeText(context, "network is available", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();
}
}
}
}
2、常驻型广播(静态注册)
在AndroidManifest.xml文件中注册的广播。这类广播,常驻广播,在应用未启动的时候,即可接收到广播,处理相应的逻辑。
通过< receiver >这个标签进行注册
//指定此广播接收器将用于接收特定的广播类型
//本例中给出的时系统开机后自身发出的广播
标准广播
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
//最普通的发送方式
sendBroadcast(intent);
//附带权限的发送方式,声明此权限的BroadcastReceiver才能接收此广播
sendBroadcast(intent,RECEIVER_PREMISSION);
有序广播
有序广播与标准广播的发送方式,在代码中只有一句代码有差别,就是发送广播的方法不同。发送出去的广播被BroadcastReceiver按照先后循序接收。有序广播的有序广播中的“有序”是针对广播接收者而言的。
对于有序广播,其主要特点总结如下:
1>多个具当前已经注册且有效的BroadcastReceiver接收有序广播时,是按照先后顺序接收的,先后顺序判定标准遵循为:将当前系统中所有有效的动态注册和静态注册的BroadcastReceiver按照priority属性值从大到小排序,对于具有相同的priority的动态广播和静态广播,动态广播会排在前面。
2>先接收的BroadcastReceiver可以对此有序广播进行截断,使后面的BroadcastReceiver不再接收到此广播,也可以对广播进行修改,使后面的BroadcastReceiver接收到广播后解析得到错误的参数值。当然,一般情况下,不建议对有序广播进行此类操作,尤其是针对系统中的有序广播。
Service是Android中实现程序后台运行的解决方案,它非常适用于去执行那些不需要和用户交互而且还要求长期运行的任务。Service默认并不会运行在子线程中,它也不运行在一个独立的进程中,它同样执行在UI线程中,因此,不要在Service中执行耗时的操作,除非你在Service中创建了子线程来完成耗时操作
Service的运行不依赖于任何用户界面,即使程序被切换到后台或者用户打开另一个应用程序,Service仍然能够保持正常运行,这也正是Service的使用场景。当某个应用程序进程被杀掉时,所有依赖于该进程的Service也会停止运行
实际上服务不会自动开启线程,所有代码都是默认在主线程中的。也就是说我们需要在服务内部手动创建子线程。
public class MyService extends Service {
public static final String TAG = "MyService";
//创建服务时调用
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
}
//服务执行的操作
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
//销毁服务时调用
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
我们创建一个MyService类继承自Service。在这里我们可以看到我们重写了onCreate()、onStartCommand()、onDestroy()、onBind()方法。onCreate()在服务创建时候调用,onStartCommand()会在每次服务启动时候被调用,如果我们希望服务一旦启动就立即去执行某个动作,就可以将逻辑写在这里。onDestroy()在服务被销毁时,去回收不再使用的资源。onBind()在bindService()启动时调用,是Service唯一一个抽象方法。
服务的启动方式有两种:一种是startService(),另一种是bindService()
public class MainActivity extends Activity implements OnClickListener {
private Button button1_start_service;
private Button button2_stop_service;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1_start_service = (Button) findViewById(R.id.button1_start_service);
button2_stop_service = (Button) findViewById(R.id.button2_stop_service);
button1_start_service.setOnClickListener(this);
button2_stop_service.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1_start_service:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
break;
case R.id.button2_stop_service:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
break;
default:
break;
}
}
}
我们也可以在MyService中使用stopSelf()自动使服务停止下来。我们总结为
停止一个started服务有两种方法:
(1)在外部使用stopService()
(2)在服务内部(onStartCommand方法内部)使用stopSelf()方法。
服务中的代码默认运行在主线程中,如果直接在服务里执行一些耗时操作,容易造成ANR(Application Not Responding)异常,所以就需要用到多线程的知识了。
为了可以简单地创建一个异步的、会自动停止的服务,Android专门提供了一个IntentService类。IntentService的作用:当我们需要这样一次性完成的任务时,就可以使用IntentService来完成。
我们新建一个MyIntentService类,继承自IntentService,并重写父类的onHandleIntent()方法,代码如下:
package com.example.servicetest;
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
public class MyIntentService extends IntentService{
public MyIntentService() {
super("MyIntentService");//调用父类有参构造函数。这里我们手动给服务起个名字为:MyIntentService
// TODO Auto-generated constructor stub
}
//该方法在会在一个单独的线程中执行,来完成工作任务。任务结束后,该Service自动停止
@Override
protected void onHandleIntent(Intent intent) {
// TODO Auto-generated method stub
for(int i = 0;i<3;i++) {
//打印当前线程的id
Log.d("MyIntentService","IntentService线程的id是:"+Thread.currentThread().getId());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.d("MyIntentService","onDestroy");
}
}
这里首先要提供一个无参的构造方法,并且必须在其内部调用父类的有参构造方法(9至12行),我们在第10行手动将服务的名字改为“MyIntentService”。
然后在子类中实现onHandleIntent()这个抽象方法,可以在这个方法里去处理一些具体的逻辑,我们就用三次for循环,打印当前线程的id,每次延时1秒。
因为这个服务在运行结束后会自动停止,所以我们在onDestroy()方法中打印日志验证一下。
在MainActivity里面加入启动IntentService的逻辑,核心代码如下:
Log.d("MainActivity","主线程的id是:"+Thread.currentThread().getId());
Intent intentService = new Intent(this,MyIntentService.class);
startService(intentService);
由此可见,启动一个IntentService和启动一个普通的Service,步骤是一样的。
Bind Service的介绍:
应用程序组件(客户端)通过调用bindService()方法能够绑定服务,然后Android系统会调用服务的onBind()回调方法,则个方法会返回一个跟服务器端交互的Binder对象。
这个绑定是异步的,bindService()方法立即返回,并且不给客户端返回IBinder对象。要接收IBinder对象,客户端必须创建一个ServiceConnection类的实例,并且把这个实例传递给bindService()方法。ServiceConnection对象包含了一个系统调用的传递IBinder对象的回调方法。
注意:只有Activity、Service、Content Provider能够绑定服务;BroadcastReceiver广播接收器不能绑定服务。
package com.example.servicetest;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service {
public static final String TAG = "MyService";
private MyBinder mBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@Override
public IBinder onBind(Intent intent) {
return mBinder; //在这里返回新建的MyBinder类
}
//MyBinder类,继承Binder:让里面的方法执行下载任务,并获取下载进度
class MyBinder extends Binder {
public void startDownload() {
Log.d("TAG", "startDownload() executed");
// 执行具体的下载任务
}
public int getProgress(){
Log.d("TAG", "getProgress() executed");
return 0;
}
}
}
package com.example.servicetest;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity implements OnClickListener {
private Button button1_start_service;
private Button button2_stop_service;
private Button button3_bind_service;
private Button button4_unbind_service;
private MyService.MyBinder myBinder;
//匿名内部类:服务连接对象
private ServiceConnection connection = new ServiceConnection() {
//当服务异常终止时会调用。注意,解除绑定服务时不会调用
@Override
public void onServiceDisconnected(ComponentName name) {
}
//和服务绑定成功后,服务会回调该方法
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (MyService.MyBinder) service;
//在Activity中调用Service里面的方法
myBinder.startDownload();
myBinder.getProgress();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1_start_service = (Button) findViewById(R.id.button1_start_service);
button2_stop_service = (Button) findViewById(R.id.button2_stop_service);
button3_bind_service = (Button) findViewById(R.id.button3_bind_service);
button4_unbind_service = (Button) findViewById(R.id.button4_unbind_service);
button1_start_service.setOnClickListener(this);
button2_stop_service.setOnClickListener(this);
button3_bind_service.setOnClickListener(this);
button4_unbind_service.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1_start_service:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
break;
case R.id.button2_stop_service:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
break;
case R.id.button3_bind_service:
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);
break;
case R.id.button4_unbind_service:
unbindService(connection);
break;
default:
break;
}
}
}
可以看到,这里我们首先创建了一个ServiceConnection的匿名类(24行),在里面重写了onServiceConnected()方法和onServiceDisconnected()方法,如果当前Activity与服务连接成功后,服务会回调onServiceConnected()方法,
在onServiceConnected()方法中,我们又通过向下转型得到了MyBinder的实例(34行),有了这个实例,Activity和Service之间的关系就变得非常紧密了。现在我们可以在Activity中根据具体的场景来调用MyBinder中的任何public方法(36、37行),即实现了Activity指挥Service干什么Service就去干什么的功能。
started服务与bind服务的区别:
区别一:生命周期
通过started方式的服务会一直运行在后台,需要由组件本身或外部组件来停止服务才会以结束运行
bind方式的服务,生命周期就要依赖绑定的组件
区别二:参数传递
started服务可以给启动的服务对象传递参数,但无法获取服务中方法的返回值
bind服务可以给启动的服务对象传递参数,也可以通过绑定的业务对象获取返回结果
实际开发中的技巧;
第一次先使用started方式来启动一个服务
之后可以使用bind的方式绑定服务,从而可以直接调用业务方法获取返回值