您的位置:首页 > 移动开发 > Android开发

Android源码解析四大组件系列(八)---广播几个问题的深入理解

2017-07-24 15:29 1191 查看
接上篇文章,这篇文章主要是总结前面知识,并且了解一些细节问题,加深对广播机制的理解,比如有播有序是怎么保证有序的?广播拦截机制是怎么实现的?广播发送超时了是怎么处理的?registerReceiver方法发返回值有什么用?粘性广播等等。

Android源码解析四大组件系列(五)—广播的注册过程

Android源码解析四大组件系列(六)—广播的处理过程

Android源码解析四大组件系列(七)—广播的发送过程

1、广播相关数据结构的再次理解

ReceiverDispatcher: 客户端广播分发者对象,第一篇讲的很清楚了,ReceiverDispatcher的内部类InnerReceiver为binder对象,用于与AMS的传递与通信。

ReceiverList: 继承自ArrayList,存放了Receiver的binder对象以及其注册的BroadcastFilter列表。AMS中定义了

final HashMap

2、有序广播是怎么保证有序的

上一篇文章中说了processNextBroadcast()只会处理一个BroadcastRecord的一个receiver,那怎么将广播传递给下一个receiver呢?广播接受者有“动态”和“静态”之分,广播消息也有“串行”和“并行”之分,或者叫“有序”和“无序”之分。广播的处理方式跟广播的接收者和广播消息类型有关系。有序广播是怎么保证有序的这个问题,得分情况讨论,对于动态注册的receiver,先回到最终onReceive回调的地方,分析如下:

static final class ReceiverDispatcher {

.....

final class Args extends BroadcastReceiver.PendingResult implements Runnable {
.....
public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
boolean ordered, boolean sticky, int sendingUser) {
//mRegistered传进来的是true
super(resultCode, resultData, resultExtras,
mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED, ordered,
sticky, mIIntentReceiver.asBinder(), sendingUser, intent.getFlags());
mCurIntent = intent;
mOrdered = ordered;
}
public void run() {
.....
try {
ClassLoader cl =  mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
intent.prepareToEnterProcess();
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
//广播的onReceive方法回调
receiver.onReceive(mContext, intent);
} catch (Exception e) {
if (mRegistered && ordered) {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing failed broadcast to " + mReceiver);
sendFinished(mgr);
}
if (mInstrumentation == null ||
!mInstrumentation.onException(mReceiver, e)) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
throw new RuntimeException(
"Error receiving broadcast " + intent
+ " in " + mReceiver, e);
}
}

if (receiver.getPendingResult() != null) {
finish();
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
}


因为在调用onReceive之前,执行了 receiver.setPendingResult(this),所以在下面receiver.getPendingResult()就不是null,则就进入BroadcastReceiver的内部类PendingResult的finish方法。

public final void finish() {
if (mType == TYPE_COMPONENT) {
final IActivityManager mgr = ActivityManagerNative.getDefault();
if (QueuedWork.hasPendingWork()) {
......
QueuedWork.singleThreadExecutor().execute( new Runnable() {
@Override public void run() {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast after work to component " + mToken);
sendFinished(mgr);
}
});
} else {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast to component " + mToken);
sendFinished(mgr);
}
} else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast to " + mToken);
final IActivityManager mgr = ActivityManagerNative.getDefault();
sendFinished(mgr);
}
}


finish方法中根据mType的值有两个分支。mType是PendingResult的成员变量,在PendingResult的构造函数中进行赋值的。

public PendingResult(int resultCode, String resultData, Bundle resultExtras, int type,
boolean ordered, boolean sticky, IBinder token, int userId, int flags) {
mResultCode = resultCode;
mResultData = resultData;
mResultExtras = resultExtras;
mType = type;
mOrderedHint = ordered;
mInitialStickyHint = sticky;
mToken = token;
mSendingUser = userId;
mFlags = flags;
}


这个构造方法是在BroadcastReceiver.PendingResult的子类Args中调用的

final class Args extends BroadcastReceiver.PendingResult implements Runnable {
private Intent mCurIntent;
private final boolean mOrdered;
private boolean mDispatched;

public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
boolean ordered, boolean sticky, int sendingUser) {
super(resultCode, resultData, resultExtras,
mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED, ordered,
sticky, mIIntentReceiver.asBinder(), sendingUser, intent.getFlags());
mCurIntent = intent;
mOrdered = ordered;
}
}


由于mRegistered是动态注册广播接收者传进来的,值是true,所以上面mType的值是TYPE_REGISTERED,由于是有序广播ordered值是true,那么mOrderedHint为true,所以要走第二个分支:

if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast to " + mToken);
final IActivityManager mgr = ActivityManagerNative.getDefault();
sendFinished(mgr);


BroadcastReceiver的sendFinished方法如下:

public void sendFinished(IActivityManager am) {
synchronized (this) {
if (mFinished) {
throw new IllegalStateException("Broadcast already finished");
}
mFinished = true;

try {
if (mResultExtras != null) {
mResultExtras.setAllowFds(false);
}
if (mOrderedHint) {
am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
mAbortBroadcast, mFlags);
} else {
// This broadcast was sent to a component; it is not ordered,
// but we still need to tell the activity manager we are done.
am.finishReceiver(mToken, 0, null, null, false, mFlags);
}
} catch (RemoteException ex) {
}
}
}


有序广播mOrderedHint值为true,所以进入到AMS的finishReceiver方法。

public void finishReceiver(IBinder who, int resultCode, String resultData,
Bundle resultExtras, boolean resultAbort, int flags) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Finish receiver: " + who);

// Refuse possible leaked file descriptors
if (resultExtras != null && resultExtras.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Bundle");
}

final long origId = Binder.clearCallingIdentity();
try {
boolean doNext = false;
BroadcastRecord r;

synchronized(this) {
BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
? mFgBroadcastQueue : mBgBroadcastQueue;
r = queue.getMatchingOrderedReceiver(who);
if (r != null) {
doNext = r.queue.finishReceiverLocked(r, resultCode,
resultData, resultExtras, resultAbort, true);
}
}

if (doNext) {
//再次执行processNextBroadcast处理广播
r.queue.processNextBroadcast(false);
}
trimApplications();
} finally {
Binder.restoreCallingIdentity(origId);
}
}


上面是分析了动态的广播接收者是怎么按照一个接着一个处理的。在看看静态注册的receiver,回到静态广播回调onReceive方法的地方。

private void handleReceiver(ReceiverData data) {
....
IActivityManager mgr = ActivityManagerNative.getDefault();

BroadcastReceiver receiver;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
data.intent.setExtrasClassLoader(cl);
data.intent.prepareToEnterProcess();
data.setExtrasClassLoader(cl);
//反射出BroadcastReceiver
receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
} catch (Exception e) {
....
}

try {
Application app = packageInfo.makeApplication(false, mInstrumentation);
....
ContextImpl context = (ContextImpl)app.getBaseContext();
sCurrentBroadcastIntent.set(data.intent);
receiver.setPendingResult(data);
     //回调广播的onReceive方法
receiver.onReceive(context.getReceiverRestrictedContext(),data.intent);

} catch (Exception e) {
....
}
} finally {
sCurrentBroadcastIntent.set(null);
}

if (receiver.getPendingResult() != null) {
data.finish();
}
}


在回调onReceiver方法之前, 执行了 receiver.setPendingResult(data),所以下面receiver.getPendingResult() != null成立,走 data.finish(),data是ReceiverData对象,handleReceiver方法传进来的,在scheduleReceiver方法中初始化。

public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
boolean sync, int sendingUser, int processState) {
updateProcessState(processState, false);
ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
sync, false, mAppThread.asBinder(), sendingUser);
r.info = info;
r.compatInfo = compatInfo;
sendMessage(H.RECEIVER, r);
}


我们看 data.finish()方法

public final void finish() {
if (mType == TYPE_COMPONENT) {
final IActivityManager mgr = ActivityManagerNative.getDefault();
if (QueuedWork.hasPendingWork()) {
QueuedWork.singleThreadExecutor().execute( new Runnable() {
@Override public void run() {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast after work to component " + mToken);
sendFinished(mgr);
}
});
} else {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast to component " + mToken);
sendFinished(mgr);
}
} else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast to " + mToken);
final IActivityManager mgr = ActivityManagerNative.getDefault();
sendFinished(mgr);
}
}


此时mType分析后值是TYPE_COMPONENT,同样会走sendFinished,后面AMS的处理逻辑是一样的,不赘述。

3、广播超时是怎么处理的?

AMS维护了两个广播队列BroadcastQueue,mFgBroadcastQueue,前台队列的超时时间是10秒,mBgBroadcastQueue,后台队列的超时时间是60秒,如果广播没有在规定的时间内处理完就会发生ANR,如果你想你的广播进入前台广播队列,那么在发送的时候,在intent中加入Intent.FLAG_RECEIVER_FOREGROUND标记,如果不加,系统默认是后台广播。mFgBroadcastQueue会有更高的权限,被优先处理。

在processNextBroadcast方法中有下面一段代码,与广播超时有关系,一旦超时就会出现ANR。

do {
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
//广播消息的第一个ANR监测机制
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
Slog.w(TAG, "Hung broadcast ["
+ mQueueName + "] discarded after timeout failure:"
+ " now=" + now
+ " dispatchTime=" + r.dispatchTime
+ " startTime=" + r.receiverTime
+ " intent=" + r.intent
+ " numReceivers=" + numReceivers
+ " nextReceiver=" + r.nextReceiver
+ " state=" + r.state);
broadcastTimeoutLocked(false); // 超时处理
forceReceive = true;
r.state = BroadcastRecord.IDLE;
}
}
//判断广播有没有处理完毕
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
// No more receivers for this broadcast!  Send the final
// result if requested...
if (r.resultTo != null) {
try {
performReceiveLocked(r.callerApp, r.resultTo, new Intent(r.intent), r.resultCode r.resultData, r.resultExtras, false, false, r.userId);
r.resultTo = null;
} catch (RemoteException e) {
......
}
}

} while (r == null);


广播的超时机制是针对有序广播来说的,无序广播一次性全部处理了,肯定不会超时,超时的这段逻辑都在broadcastTimeoutLocked中,首先判断是否超时,公式:r.dispatchTime + 2×mTimeoutPeriod×numReceivers,现在解释一下这几个时间:

- dispatchTime的意义是标记实际处理BroadcastRecord的起始时间,有序广播是一个接着一个进行处理的,第一次dispatchTime=0,并不会进入该条件判断

mTimeoutPeriod由当前BroadcastQueue的类型决定(mFgBroadcastQueue为10秒,mBgBroadcastQueue为60秒)

// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;

mFgBroadcastQueue = new BroadcastQueue(this, mHandler,  "foreground", BROADCAST_FG_TIMEOUT, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,  "background", BROADCAST_BG_TIMEOUT, true);


所以上面公式翻译过来就是:实际处理BroadcastRecord的起始时间+广播默认的超时时间*广播接收者的数量。话说回来,这个公式为什么要这么设计呢?如果一个前台的广播消息有两个接收者,那么在20秒(2 x 10)之内搞定就可以了,也可能第一个消息执行了15秒,第二个消息执行4.99秒,即使第一消息超过了10秒的规定,也不会出现ANR。但是系统任务繁忙,可能有其他活要干,我们要尽可能的减少ANR的发生,所以前面乘以2倍。

假设现在广播超时还没处理,满足if条件,就会进入,打印Hung broadcast [“+ mQueueName + “] discarded after timeout failure….的log,然后执行 broadcastTimeoutLocked(false)强制停止广播,broadcastTimeoutLocked相关代码代码如下:

final void broadcastTimeoutLocked(boolean fromMsg) {
.....
long timeoutTime = r.receiverTime + mTimeoutPeriod;
if (timeoutTime > now) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Premature timeout ["
+ mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
+ timeoutTime);
setBroadcastTimeoutLocked(timeoutTime);
return;
}
}

.....
}


内部调用setBroadcastTimeoutLocked()设置一个延迟消息

final void setBroadcastTimeoutLocked(long timeoutTime) {
if (! mPendingBroadcastTimeoutMessage) {
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
}
}


如果广播消息能够处理完毕,就会执行cancelBroadcastTimeoutLocked,将超时的Message移除掉。

final void cancelBroadcastTimeoutLocked() {
if (mPendingBroadcastTimeoutMessage) {
mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
mPendingBroadcastTimeoutMessage = false;
}


如果广播消息没有在timeout时间内处理掉,下面BroadcastHandler发送的消息就会执行。

private final class BroadcastHandler extends Handler {
.....
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
.....
case BROADCAST_TIMEOUT_MSG: {
synchronized (mService) {
broadcastTimeoutLocked(true);
}
} break;
.....
}
}
}


再次进入broadcastTimeoutLocked方法里面

final void broadcastTimeoutLocked(boolean fromMsg) {
//传进来是ture
if (fromMsg) {
mPendingBroadcastTimeoutMessage = false;
}
//队列没有广播处理了,返回
if (mOrderedBroadcasts.size() == 0) {
return;
}

long now = SystemClock.uptimeMillis();
BroadcastRecord r = mOrderedBroadcasts.get(0);
if (fromMsg) {
    //正在执行dexopt,返回
if (mService.mDidDexOpt) {
// Delay timeouts until dexopt finishes.
mService.mDidDexOpt = false;
long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
setBroadcastTimeoutLocked(timeoutTime);
return;
}
    //系统还没有进入ready状态
if (!mService.mProcessesReady) {
// Only process broadcast timeouts if the system is ready. That way
// PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
// to do heavy lifting for system up.
return;
}
//如果当前正在执行的receiver没有超时,则重新设置广播超时
long timeoutTime = r.receiverTime + mTimeoutPeriod;
if (timeoutTime > now) {
// We can observe premature timeouts because we do not cancel and reset the
// broadcast timeout message after each receiver finishes.  Instead, we set up
// an initial timeout then kick it down the road a little further as needed
// when it expires.
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Premature timeout ["
+ mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
+ timeoutTime);
setBroadcastTimeoutLocked(timeoutTime);
return;
}
}

//当前正在执行的receiver没有超时,则重新设置广播超时,处理下一条广播
BroadcastRecord br = mOrderedBroadcasts.get(0);
if (br.state == BroadcastRecord.WAITING_SERVICES) {
// In this case the broadcast had already finished, but we had decided to wait
// for started services to finish as well before going on.  So if we have actually
// waited long enough time timeout the broadcast, let's give up on the whole thing
// and just move on to the next.
Slog.i(TAG, "Waited long enough for: " + (br.curComponent != null
? br.curComponent.flattenToShortString() : "(null)"));
br.curComponent = null;
br.state = BroadcastRecord.IDLE;
processNextBroadcast(false);
return;
}

Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r. receiver
+ ", started " + (now - r.receiverTime) + "ms ago");
r.receiverTime = now;
r.anrCount++;

// Current receiver has passed its expiration date.
if (r.nextReceiver <= 0) {
Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0");
return;
}

ProcessRecord app = null;
String anrMessage = null;

Object curReceiver = r.receivers.get(r.nextReceiver-1);
r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
Slog.w(TAG, "Receiver during timeout: " + curReceiver);
logBroadcastReceiverDiscardLocked(r);
if (curReceiver instanceof BroadcastFilter) {
BroadcastFilter bf = (BroadcastFilter)curReceiver;
if (bf.receiverList.pid != 0
&& bf.receiverList.pid != ActivityManagerService.MY_PID) {
synchronized (mService.mPidsSelfLocked) {
app = mService.mPidsSelfLocked.get(
bf.receiverList.pid);
}
}
} else {
app = r.curApp;
}

//进程存在,anrMessage赋值
if (app != null) {
anrMessage = "Broadcast of " + r.intent.toString();
}

if (mPendingBroadcast == r) {
mPendingBroadcast = null;
}

// Move on to the next receiver.
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
        //处理下一条广播
scheduleBroadcastsLocked();

if (anrMessage != null) {
// Post the ANR to the handler since we do not want to process ANRs while
// potentially holding our lock.
mHandler.post(new AppNotResponding(app, anrMessage));
}
}


所以当一个receiver超时后,系统会放弃继续处理它给出ANR提示,并再次调用scheduleBroadcastsLocked(),尝试处理下一个receiver,

private final class AppNotResponding implements Runnable {
private final ProcessRecord mApp;
private final String mAnnotation;

public AppNotResponding(ProcessRecord app, String annotation) {
mApp = app;
mAnnotation = annotation;
}

@Override
public void run() {
//内部创建ANR显示的Dialog
mService.mAppErrors.appNotResponding(mApp, null, null, false, mAnnotation);
}
}


4、广播拦截处理分析

广播消息可以有多个接收者,对于有序广播是一个接着一个处理的,优先级高的接收者可以优先执行,并且可以调用BroadcastReceiver的abortBroadcast()方法拦截广播,如果我们在receiver的onReceive()中调用这个方法,那么它后面的接收者就不会收到广播。

public abstract class BroadcastReceiver {
private PendingResult mPendingResult;

  public final void abortBroadcast() {
  checkSynchronousHint();
   mPendingResult.mAbortBroadcast = true;
   }
 }


把BroadcastReceiver::PendingResult的成员变量mAbortBroadcast设置成true,

final class Args extends BroadcastReceiver.PendingResult implements Runnable {
private Intent mCurIntent;
private final boolean mOrdered;
private boolean mDispatched;

public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
boolean ordered, boolean sticky, int sendingUser) {
super(resultCode, resultData, resultExtras,
mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED, ordered,
sticky, mIIntentReceiver.asBinder(), sendingUser, intent.getFlags());
mCurIntent = intent;
mOrdered = ordered;
}

public void run() {
.....
try {
ClassLoader cl =  mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
intent.prepareToEnterProcess();
setExtrasClassLoader(cl);
      //设置PendingResult,这个PendingResult中mAbortBroadcast为true
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);
} catch (Exception e) {
.....
}

if (receiver.getPendingResult() != null) {
//告知AMS处理下一个广播
finish();
}

}
}


finish()会告知AMS处理下一个广播,在第一小节已经分析过,最终进入AMS的finishReceiver方法

public void finishReceiver(IBinder who, int resultCode, String resultData,
Bundle resultExtras, boolean resultAbort, int flags) {
.....
try {
boolean doNext = false;
BroadcastRecord r;

synchronized(this) {
BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
? mFgBroadcastQueue : mBgBroadcastQueue;
r = queue.getMatchingOrderedReceiver(who);
if (r != null) {
          //resultAbort传进来是true,
doNext = r.queue.finishReceiverLocked(r, resultCode,
resultData, resultExtras, resultAbort, true);
}
}
   //调用processNextBroadcast处理广播
if (doNext) {
r.queue.processNextBroadcast(false);
}
trimApplications();
} finally {
Binder.restoreCallingIdentity(origId);
}
}
```
processNextBroadcast方法中有一个检查广播有没有发送完毕的逻辑。
```
do {
.....
     r = mOrderedBroadcasts.get(0);
 //检查广播有没有发送完,resultAbort为=ture
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
    .....
//mOrderedBroadcasts里删除广播消息
mOrderedBroadcasts.remove(0);
r = null;
looped = true;
continue;
}
} while (r == null);


当resultAbort为=ture时候,广播消息从mOrderedBroadcasts删除,后面也就收不到广播了。

5、理解粘性广播

sticky广播通过Context.sendStickyBroadcast()函数来发送,用此函数发送的广播会一直滞留,当有匹配此广播的广播接收器被注册后,该广播接收器就会收到此条信息。使用此函数需要发送广播时,需要获得BROADCAST_STICKY权限。粘性广播可以使用广播接收器进行接收,但是正确的接收方式是调用registerReceiver能接受广播,信息将在调用registerReceiver的返回值中给出。对于粘性广播的发送,和普通广播的发送方式是一致的,例子来自与Android 粘性广播StickyBroadcast的使用

private void sendStickyBroadcast(){
Intent i = new Intent();
i.setAction(StickyBroadcastReceiver.Action);
i.putExtra("info", "sticky broadcast has been receiver");
sendStickyBroadcast(i);
Log.i("Other","sticky broadcast send ok!");
}


可以使用BroadcastReceiver来接收

public class StickyBroadcastReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
    //收到广播
}
}


<!--使用粘性广播发送权限-->
<uses-permission android:name="android.permission.BROADCAST_STICKY" />


IntentFilter intentFilter = new IntentFilter(StickyBroadcastReceiver.Action);
Intent data = registerReceiver(null, intentFilter);
if(data!=null&&StickyBroadcastReceiver.Action.equals(data.getAction()))  {
Toast.makeText(this, data.getStringExtra("info"), Toast.LENGTH_SHORT).show();
}


好了广播的四篇文章写完了,准备在分析一波Service吧
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐