未啓動的應用無法監聽到系統廣播

先說下問題和解決方案:

Q:在 3.1 版本以後,新安裝而從未啓動過的app不能收到系統的廣播(啓動完成,網絡狀態變化之類的);

解決方案:
1. 將app做成系統應用,直接安裝在 system/app 目錄下
2. 通過第三方應用,發送帶 FLAG_INCLUDE_STOPPED_PACKAGES 的廣播給stop狀態的自己


下文轉載自 http://www.cnblogs.com/fanfeng/p/3236178.html ,很好的講解了這個問題的原因

Android 開機自啓動

首先實現開機自啓動:

第一步創建一個廣播接收者,如MyBootBroadcastReceiver.java

package com.example;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class MyBootBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent startServiceIntent = new Intent(context, MyService.class);
        context.startService(startServiceIntent);
        //啓動應用
        //Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
        //context.startActivity(intent);
    }
}

第二步給receiver配置對應intent-filter

<receiver android:name="MyBootBroadcastReceiver">  
    <intent-filter>  
        <action android:name="android.intent.action.BOOT_COMPLETED" />  
    </intent-filter>  
</receiver>

第三步添加權限,缺少這步則無法在Android 3.0及其之前版本上自啓動

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

上面的實現有以下【限制】:

1.由於Android系統在外置SD卡加載前廣播ACTION_BOOT_COMPLETED,於是如果應用安裝在外置SD卡上,它無法接收到此廣播,原文見Broadcast Receivers listening for "boot completed"

2.從Android 3.1(API level對應NDK版本)開始,系統的包管理器保持跟蹤處於停止狀態(stopped state)的應用程序,提供了一種控制其從後臺進程和其它應用程序啓動的方式。這種停止狀態的應用程序指那些安裝了但從未啓動過的apk,或被用戶在程序管理中force stop的apk。Android系統爲防止廣播無意或不必要開啓停止狀態的組件,它給所有廣播intent添加了FL​​AG_EXCLUDE_STOPPED_PACKAGES標誌(不設置和同FLAG_INCLUDE_STOPPED_PACKAGES一起設置結果都是此標誌),

Intent.java

public boolean isExcludingStopped() {
        return (mFlags&(FLAG_EXCLUDE_STOPPED_PACKAGES|FLAG_INCLUDE_STOPPED_PACKAGES))
                == FLAG_EXCLUDE_STOPPED_PACKAGES;
    }

IntentResolver.java

/**
     * Returns whether the object associated with the given filter is
     * "stopped," that is whether it should not be included in the result
     * if the intent requests to excluded stopped objects.
     */
    protected boolean isFilterStopped(F filter, int userId) {
        return false;
    }

private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
            boolean debug, boolean defaultOnly,
            String resolvedType, String scheme, F[] src, List<R> dest, int userId) {
        ...

        final boolean excludingStopped = intent.isExcludingStopped();

        final int N = src != null ? src.length : 0;
        boolean hasNonDefaults = false;
        int i;
        F filter;
        for (i=0; i<N && (filter=src[i]) != null; i++) {
            int match;
            if (debug) Slog.v(TAG, "Matching against filter " + filter);

            if (excludingStopped && isFilterStopped(filter, userId)) {
                if (debug) {
                    Slog.v(TAG, "  Filter's target is stopped; skipping");
                }
                continue;
            }
        ...

此標誌指廣播intent排除停止狀態的應用,原文見Launch controls on stopped applications。用戶可以給自己的應用或者後臺服務添加FLAG_INCLUDE_STOPPED_PACKAGES標誌以喚醒停止狀態的應用,但系統自帶的廣播intent,用戶無法修改,只能接受;注意系統級應用都不是停止狀態

PackageManagerService.java中重寫IntentResolver

final class ActivityIntentResolver
            extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
        ...
@Override
        protected boolean isFilterStopped(PackageParser.ActivityIntentInfo filter, int userId) {
            if (!sUserManager.exists(userId)) return true;
            PackageParser.Package p = filter.activity.owner;
            if (p != null) {
                PackageSetting ps = (PackageSetting)p.mExtras;
                if (ps != null) {
                    // System apps are never considered stopped for purposes of
                    // filtering, because there may be no way for the user to
                    // actually re-launch them.
                    return (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0
                            && ps.getStopped(userId);
                }
            }
            return false;
        }

private final class ServiceIntentResolver
            extends IntentResolver<PackageParser.ServiceIntentInfo, ResolveInfo> {
        ...
@Override
        protected boolean isFilterStopped(PackageParser.ServiceIntentInfo filter, int userId) {
            if (!sUserManager.exists(userId)) return true;
            PackageParser.Package p = filter.service.owner;
            if (p != null) {
                PackageSetting ps = (PackageSetting)p.mExtras;
                if (ps != null) {
                    // System apps are never considered stopped for purposes of
                    // filtering, because there may be no way for the user to
                    // actually re-launch them.
                    return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0
                            && ps.getStopped(userId);
                }
            }
            return false;
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章