如何让我的 BroadcastReceiver 保持活力

How to keep alive my BroadcastReceiver

目前我正在开发像 Truecaller 这样的呼叫拦截器应用程序。

我需要的

I want to detect the incoming calls even my app is removed from the recent apps list.

Manifest.xml代码

<receiver android:name=".PhoneStateReceiver">
        <intent-filter>
            <action android:name="android.intent.action.PHONE_STATE" />
        </intent-filter>
    </receiver>

我的广播接收器代码

@Override
public void onReceive(Context context, Intent intent) {
  //my call blocking code
}

我的问题

My BroadcastReceiver wont work in the background as if I removed from the recent apps list.

我的完整清单代码在这里

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ranjith.callblocker">

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

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

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

<application
    android:allowBackup="true"
    android:enabled="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <receiver
        android:name=".PhoneStateReceiver"
        android:enabled="true"
        android:exported="true"
        android:process=":anotherProcess">
        <intent-filter android:priority="1000">
            <action android:name="android.intent.action.PHONE_STATE" />
        </intent-filter>
    </receiver>
    <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:theme="@style/AppTheme.NoActionBar">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

</manifest>

我应该使用服务还是其他什么?

更新:

有了 Suraj 的回答,我在我的接收器中尝试了这个标签

    android:enabled="true"
    android:exported="true"
    android:process=":anotherProcess"

它适用于 kitkat..但不适用于棒棒糖..

更新问题:

Incase 如果无法使广播接收器保持活动状态,即使我的应用已关闭,我如何检测来电?

哪位大侠给个详细的回答..

这里我们从服务通知接收者。

所以做一个服务class作为

    public class MyService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new CountDownTimer(100000,4000)
        {
            @Override
            public void onTick(long millisUntilFinished) {
                sendBroadcast(new Intent("fromservice"));

            }

            @Override
            public void onFinish() {

            }
        }.start();
        return START_STICKY;
    }
}

现在将接收器设置为

    public class MyReceiver extends WakefulBroadcastReceiver {
        @Override
        public void onReceive(final Context context, Intent intent) {

            Toast.makeText(context, "inside receiver", Toast.LENGTH_SHORT).show();
        }
    }

现在从 main 启动服务 activity

    public class MainActivity extends AppCompatActivity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            startService(new Intent(this,MyService.class));

        }
    }

在manifest中声明receiver和service如下

 <receiver android:name=".MyReceiver"
        android:process=":jk"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="fromservice"/>
        </intent-filter>
    </receiver>
    <service android:name=".MyService"
android:process=":ff"
android:enabled="true"
android:exported="true" />

将以下权限添加到您的清单中。 这用于防止 cpu 睡眠。

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

什么是倒计时?

倒计时计时器可以理解为必须方法的迭代

onTick() 和 onFinish()

在 CountDownTimer 构造函数中给定的间隔(持续时间)后多次调用 onTick()。

onFinish() 最后(仅一次)在 longTimeInFuture 到达时被调用。

您必须从清单中添加权限,

<uses-permission android:name="android.permission.READ_PHONE_STATE" >

并且接收方必须声明以下属性,声明名称时也总是尽量使用完整的包名。

<receiver
    android:name="ranjith.callblocker.PhoneStateReceiver"
    android:enabled="true"
    android:exported="true"
    android:permission="android.permission.READ_PHONE_STATE">
    <intent-filter>
        <action android:name="android.intent.action.PHONE_STATE" />
    </intent-filter>
</receiver>

然后尝试在接收块中放置一个日志,

@Override
public void onReceive(Context context, Intent intent) {
  Log.i("TAG", "Receiver is called but might not block call for other reason!");
}

确保您没有通过任何 onPause、onStop、onDestroy 方法按名称注销接收器

这是我的食谱,它实际上适用于 4.4 到 6.0

清单:

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />

<application> 
    <receiver android:name=".utils.PhoneReceiver">
        <intent-filter android:priority="999">
            <action android:name="android.intent.action.PHONE_STATE" />
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
        </intent-filter>
    </receiver>
</application>

然后在 PhoneReceiver.class

public class PhoneReceiver extends BroadcastReceiver 
{

    public void onReceive(Context thecontext, Intent intent) 
    {

        if ("android.provider.Telephony.SMS_RECEIVED".equals(intent.getAction()))
        {
            // handle SMS received
        }
        else if ("android.intent.action.NEW_OUTGOING_CALL".equals(intent.getAction()))
        {
            // handle outgoing call
        }
        else if (intent.getAction().equals("android.intent.action.PHONE_STATE"))
        {
            String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);

            /* from there do your stuff... */

            if (state.equals(TelephonyManager.EXTRA_STATE_RINGING))
                String incomingNumber =  intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);

            /* etc. etc. */
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.phonestatelistener">
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <receiver android:name="com.android.receiver.PhoneStateListener"           android:exported="true"  >

        <intent-filter >
            <action android:name="android.intent.action.PHONE_STATE"/>
            <action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>
    </receiver>
</application>

public class PhoneStateListener extends BroadcastReceiver {

public static String TAG="PhoneStateListener";

public static String ACTION_PHONE_STATE = "android.intent.action.PHONE_STATE";
public static String ACTION_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL";
public static String ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";

@Override
public void onReceive(Context context, Intent intent) {

    if (intent.getAction().equals(ACTION_PHONE_STATE)) {

        String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
        if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {

            Toast.makeText(context, "Incoming call", Toast.LENGTH_SHORT).show();
            Log.d(TAG, "Incoming call");
        }

    } else if (intent.getAction().equals(ACTION_OUTGOING_CALL)) {
        Toast.makeText(context, "Outgoing call", Toast.LENGTH_SHORT).show();

        Log.d(TAG, "Outgoing call");
    } else if (intent.getAction().equals(ACTION_SMS_RECEIVED)) {

        Toast.makeText(context, "Incoming message", Toast.LENGTH_SHORT).show();
        Log.d(TAG, "Incoming message");
    }

}

}

试试这个。它会一直工作,直到您强制关闭应用程序

您可以尝试在 activity 的 onCreate() 中注册广播接收器。

这是代码它对我来说很好用:

在清单中添加权限:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

后续步骤创建一个 Service 在启动 App 时启动(可能 MainActivity) 在 Service

的方法 onCreate 中调用此代码

Code

((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE)).listen(new PhoneStateListener(new PhoneStateListener.PhoneCallListener() {
            @Override
            public void PhoneCall() {
              // Do something
            }
            @Override
            public void PhoneOff() {
               // Do something
            }
            @Override
            public void MissCall() {
               // Do something
            }
        }), PhoneStateListener.LISTEN_CALL_STATE);

PhoneStateListener.java

import android.telephony.TelephonyManager;

/**
 * PhoneStateListener.
 *
 * @author DaoLQ
 */
public class PhoneStateListener extends android.telephony.PhoneStateListener {

    public interface PhoneCallListener {
        void PhoneCall();

        void PhoneOff();

        void MissCall();
    }

    private PhoneCallListener mPhoneCallListener;

    private boolean ring = false;
    private boolean callReceived = false;

    public PhoneStateListener(PhoneCallListener listener) {
        mPhoneCallListener = listener;
    }

    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        super.onCallStateChanged(state, incomingNumber);
        switch (state) {
            case TelephonyManager.CALL_STATE_IDLE:
                mPhoneCallListener.PhoneOff();
                if (ring && !callReceived) {
                    mPhoneCallListener.MissCall();
                }
                break;

            case TelephonyManager.CALL_STATE_OFFHOOK:
                // CALL_STATE_OFFHOOK;
                callReceived = true;
                mPhoneCallListener.PhoneCall();
                break;

            case TelephonyManager.CALL_STATE_RINGING:
                ring = true;
                mPhoneCallListener.PhoneCall();
                break;

            default:
                break;
        }
    }
}

您需要创建一个 Service 并在清单中注册它。之后你应该在服务中注册你的 BroadcastReceiver 而不是清单。

A Service 不会停止,当应用程序从最近的应用程序中删除时,因此您的接收器也将继续工作。当应用程序从最近的应用程序中删除时,您甚至还会通过 Service#onTaskRemoved 收到回调。

虽然您还需要处理其他一些情况,但可以停止 Service

一种情况是当系统内存不足时 android 可以停止您的服务,您可以通过从 onStartCommand 方法返回 START_STICKY 来修复它。

另一种情况是当设备重启时,您需要在 mnifest 中为 ACTION_BOOT_COMPLETED 注册一个广播接收器来解决这个问题。您可以在其 onReceive 方法中重新启动您的服务。

希望下面的示例代码对您有所帮助-

private BroadcastReceiver mReceiver;

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.v(LOG_TAG, "worked");
        }
    };
    registerReceiver(mReceiver, 
            new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));

    return START_STICKY;
}

@Override
public void onDestroy() {
    unregisterReceiver(mReceiver);
    super.onDestroy()
}