android oreo 和 pie 中的广播接收器不在后台工作

Broadcast Receivers not working in background in android oreo and pie

在我的应用程序中,我使用广播接收器获取传入的 phone 号码并将其显示在对话框中,然后在单击按钮时将其保存在应用程序中。每次通话结束后都会得到号码。 Oreo 和 Pie 以下的所有 android 版本都运行良好。在 Oreo 和 Pie 中,当应用程序为 closed/killed 时,结束通话后不会出现对话框。但是当应用程序是 运行 时,对话框完美出现。我认为当我们终止应用程序时,广播接收器也会被终止。我一直在寻找几天,但没有任何帮助。

这里是我的代码的一些介绍:

Manifest.xml :

<receiver
            android:name="com.softixtechnologies.phonemanager.callhelpers.CallReciever"
        android:enabled="true">
        <intent-filter>
            <action android:name="android.intent.action.PHONE_STATE" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
        </intent-filter>
    </receiver>

MainActivity.java :

public class MainActivity extends AppCompatActivity {

LinearLayout btncat, btnlog, btnIgnore, btnExit;
SwitchCompat checkBoxMissed, checkBoxUnknown ;
boolean isCheckedValue = true;
boolean isCheckedUnknown = true;
DatabaseHelper databaseHelper ;
public static final String SHARED_PREFS = "shared_prefs" ;
public final static int MY_PERMISSIONS_REQUEST_READ_PHONE_STATE = 11;
public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 5469;
public static final int CALL_LOG_PERMISSION = 1 ;
PhoneCallReceiver phoneCallReceiver ;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    databaseHelper = new DatabaseHelper(this) ;
    btncat = findViewById(R.id.button_Categories);
    btnlog = findViewById(R.id.button_logs) ;
    btnIgnore = findViewById(R.id.button_Ignore_contacts) ;
    checkBoxMissed= findViewById(R.id.checkbox_missed) ;
    checkBoxUnknown= findViewById(R.id.checkbox_unknown);
    btnExit = findViewById(R.id.button_exit);
    phoneCallReceiver = new PhoneCallReceiver();

    IntentFilter intentFilter = new IntentFilter();
//        intentFilter.addAction("android.intent.action.PHONE_STATE");
//        intentFilter.addAction("android.intent.action.NEW_OUTGOING_CALL");
    IntentFilter intentFilter1 = new IntentFilter("android.intent.action.NEW_OUTGOING_CALL");
    intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
    intentFilter1.addCategory(Intent.CATEGORY_DEFAULT);
    registerReceiver(phoneCallReceiver, intentFilter);
    registerReceiver(phoneCallReceiver, intentFilter1);

    checkPermission();
    requestCallLogPermission();

    //permission check for pie 23-july-2019
    if (ContextCompat.checkSelfPermission(this,Manifest.permission.READ_CALL_LOG) == PackageManager.PERMISSION_GRANTED){
    }else {
        requestCallLogPermission();
    }

    if (ContextCompat.checkSelfPermission(getApplicationContext(),
            Manifest.permission.READ_PHONE_STATE)
            != PackageManager.PERMISSION_GRANTED)
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.READ_CONTACTS)) {
        } else {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_PHONE_STATE, Manifest.permission.SYSTEM_ALERT_WINDOW},
                    MY_PERMISSIONS_REQUEST_READ_PHONE_STATE);
        }

    SharedPreferences sharedPreferenceCAT = getSharedPreferences(SHARED_PREFS, MODE_PRIVATE);
    boolean isFirstTimeCAT =sharedPreferenceCAT.getBoolean("isFirstTimeCAT", true);
    if (isFirstTimeCAT){
        try{
        databaseHelper.insertCAT("New Customer");
        databaseHelper.insertCAT("Complaint");
        databaseHelper.insertCAT("Reminder");
        sharedPreferenceCAT.edit().putBoolean("isFirstTimeCAT",false).commit();}catch (Exception e){
            Toast.makeText(this, ""+ e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }

    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
    final SharedPreferences.Editor editor = preferences.edit();
    checkBoxMissed.setChecked(preferences.getBoolean("checked",false));
    checkBoxMissed.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            isCheckedValue = isChecked;
            editor.putBoolean("checked", isChecked);
            editor.apply();
        }
    });

    SharedPreferences preferencesUnknown = PreferenceManager.getDefaultSharedPreferences(this);
    final SharedPreferences.Editor editorUnknown = preferencesUnknown.edit();
    checkBoxUnknown.setChecked(preferencesUnknown.getBoolean("checkedunknown",false));
    checkBoxUnknown.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            isCheckedUnknown = isChecked;
            editorUnknown.putBoolean("checkedunknown", isChecked);
            editorUnknown.apply();
        }
    });
private void requestCallLogPermission() {
    if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CALL_LOG)){
        new AlertDialog.Builder(this).setTitle("Permission Needed")
                .setMessage("This permission is needed by App to work properly !")
                .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ActivityCompat.requestPermissions(MainActivity.this, new String[] {Manifest.permission.READ_CALL_LOG},
                                CALL_LOG_PERMISSION);
                    }
                }).setCancelable(false)
                .create().show();
    }else {
        ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.READ_CALL_LOG},
                CALL_LOG_PERMISSION);
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch(requestCode){
        case MY_PERMISSIONS_REQUEST_READ_PHONE_STATE:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){

            }else {
                return;
            }
        case CALL_LOG_PERMISSION:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "Call Log Permission Granted !", Toast.LENGTH_SHORT).show();
            }
    }
}
@TargetApi(Build.VERSION_CODES.M)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE) {
        if (!Settings.canDrawOverlays(this)) {
            // You don't have permission
            checkPermission();
        } else {
            // Do as per your logic
        }

    }
    if (requestCode == CALL_LOG_PERMISSION) {
        if (!Settings.canDrawOverlays(this)) {
            // You don't have permission
            requestCallLogPermission();
        } else {
            // Do as per your logic
        }
    }
}

public void checkPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (!Settings.canDrawOverlays(this)) {
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + getPackageName()));
            startActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
        }
    }
}

@Override
protected void onStart() {
    super.onStart();
    IntentFilter intentFilter = new IntentFilter("android.intent.action.PHONE_STATE");
    IntentFilter intentFilter1 = new IntentFilter("android.intent.action.NEW_OUTGOING_CALL");
    intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
    intentFilter1.addCategory(Intent.CATEGORY_DEFAULT);
    registerReceiver(phoneCallReceiver, intentFilter);
    registerReceiver(phoneCallReceiver, intentFilter1);
}

@Override
protected void onStop() {
    super.onStop();
//        unregisterReceiver(phoneCallReceiver);
}

@Override
protected void onDestroy() {
    super.onDestroy();
}
}

广播接收器:

public class PhoneCallReceiver extends BroadcastReceiver {
private static int lastState = TelephonyManager.CALL_STATE_IDLE;
private static Date callStartTime;
private static boolean isIncoming;
private static String savedNumber;

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

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
        TelephonyManager tm = (TelephonyManager)context.getSystemService(Service.TELEPHONY_SERVICE);

        switch (tm.getCallState()) {

            case TelephonyManager.CALL_STATE_IDLE:
                Toast.makeText(context, "IDLE", Toast.LENGTH_SHORT).show();
                final String phoneNr= intent.getStringExtra("incoming_number");
                Toast.makeText(context, phoneNr,Toast.LENGTH_LONG).show();

                SharedPreferences preferencesUnknown = PreferenceManager.getDefaultSharedPreferences(context);
                final SharedPreferences.Editor editorUnknown = preferencesUnknown.edit();
                boolean checkedUnknown = preferencesUnknown.getBoolean("checkedunknown", false);
                if (checkedUnknown) {
                    boolean isUnknown = contactExists(context, phoneNr);
                    if (isUnknown == false) {
                        AlertDialog.Builder alerDialog = new AlertDialog.Builder(context);
                        alerDialog.setIcon(R.drawable.calllogo);
                        alerDialog.setTitle(phoneNr);
                        alerDialog.setMessage("Save this number ?");
                        alerDialog.setCancelable(false);
                        alerDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Intent intent = new Intent(context, CategoryPhoneActivity.class);
                                intent.putExtra("Phone", phoneNr);
                                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                context.startActivity(intent);
                            }
                        });
                        alerDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.cancel();
                            }
                        });
                        AlertDialog aldialog = alerDialog.create();
                        aldialog.setCanceledOnTouchOutside(false);
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                            aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } else {
                            aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
                        }
                        aldialog.show(); } else {
                        Toast.makeText(context, "Number is already saved", Toast.LENGTH_SHORT).show(); }
                }else{
                    AlertDialog.Builder alerDialog = new AlertDialog.Builder(context);
                    alerDialog.setIcon(R.drawable.calllogo);
                    alerDialog.setTitle(phoneNr);
                    alerDialog.setCancelable(false);
                    alerDialog.setMessage("Save this number ?");
                    alerDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Intent intent = new Intent(context, CategoryPhoneActivity.class);
                            intent.putExtra("Phone", phoneNr);
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            context.startActivity(intent);
                        }
                    });
                    alerDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.cancel();
                        }
                    });
                    AlertDialog aldialog = alerDialog.create();
                    aldialog.setCanceledOnTouchOutside(false);
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } else {
                        aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
                    }
                    aldialog.show(); }
                break;
        }
    }else {

        if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
            savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
        } else {
            String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
            String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);

            int state = 0;
            if (stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
                state = TelephonyManager.CALL_STATE_IDLE;
            } else if (stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
                state = TelephonyManager.CALL_STATE_OFFHOOK;
            } else if (stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
                state = TelephonyManager.CALL_STATE_RINGING;
            }
            onCallStateChanged(context, state, number);
        }
    }
}

protected void onIncomingCallStarted(Context ctx, String number, Date start){}

protected void onOutgoingCallStarted(Context ctx, String number, Date start){}

protected void onIncomingCallEnded(Context ctx, String number, Date start, Date end){}

protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end){}

protected void onMissedCall(Context ctx, String number, Date start){}

public void onCallStateChanged(Context context, int state, String number) {
    if(lastState == state){
        return;
    }
    switch (state) {
        case TelephonyManager.CALL_STATE_RINGING:
            isIncoming = true;
            callStartTime = new Date();
            savedNumber = number;
            onIncomingCallStarted(context, number, callStartTime);
            break;

        case TelephonyManager.CALL_STATE_OFFHOOK:
            if(lastState != TelephonyManager.CALL_STATE_RINGING){
                isIncoming = false;
                callStartTime = new Date();
                onOutgoingCallStarted(context, savedNumber, callStartTime);
            }
            break;

        case TelephonyManager.CALL_STATE_IDLE:
            if(lastState == TelephonyManager.CALL_STATE_RINGING){
                onMissedCall(context, savedNumber, callStartTime);
            }
            else if(isIncoming){
                onIncomingCallEnded(context, savedNumber, callStartTime, new Date());
            }
            else{
                onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());
            }
            break;
    }
    lastState = state;
}

呼叫接收者:

public class CallReciever extends PhoneCallReceiver {

DatabaseHelper databaseHelper ;

@Override
protected void onIncomingCallStarted(final Context ctx, final String number, Date start) {
}

@Override
protected void onOutgoingCallStarted(Context ctx, String number, Date start) {
}

@Override
protected void onIncomingCallEnded(final Context ctx, final String number, Date start, Date end) {

        SharedPreferences preferencesUnknown = PreferenceManager.getDefaultSharedPreferences(ctx);
        final SharedPreferences.Editor editorUnknown = preferencesUnknown.edit();
        boolean checkedUnknown = preferencesUnknown.getBoolean("checkedunknown", false);
        if (checkedUnknown) {
            boolean isUnknown = contactExists(ctx, number);
            if (isUnknown == false) {
                AlertDialog.Builder alerDialog = new AlertDialog.Builder(ctx);
                alerDialog.setIcon(R.drawable.calllogo);
                alerDialog.setTitle(number);
                alerDialog.setMessage("Save this number ?");
                alerDialog.setCancelable(false);
                alerDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Intent intent = new Intent(ctx, CategoryPhoneActivity.class);
                        intent.putExtra("Phone", number);
                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        ctx.startActivity(intent);
                    }
                });
                alerDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
                AlertDialog aldialog = alerDialog.create();
                aldialog.setCanceledOnTouchOutside(false);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } else {
                    aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
                }
                aldialog.show(); } else {
                Toast.makeText(ctx, "Number is already saved", Toast.LENGTH_SHORT).show(); }
        }else{
            AlertDialog.Builder alerDialog = new AlertDialog.Builder(ctx);
            alerDialog.setIcon(R.drawable.calllogo);
            alerDialog.setTitle(number);
            alerDialog.setCancelable(false);
            alerDialog.setMessage("Save this number ?");
            alerDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Intent intent = new Intent(ctx, CategoryPhoneActivity.class);
                    intent.putExtra("Phone", number);
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    ctx.startActivity(intent);
                }
            });
            alerDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.cancel();
                }
            });
            AlertDialog aldialog = alerDialog.create();
            aldialog.setCanceledOnTouchOutside(false);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } else {
                aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
            }
            aldialog.show(); }
    }
@Override
protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end) {
}

@Override
protected void onMissedCall(final Context ctx, final String number, Date start) {

    Toast.makeText(ctx, "Missed call from " + number, Toast.LENGTH_SHORT).show();

    databaseHelper = new DatabaseHelper(ctx);

    if (databaseHelper.searchContact(number)){
        return;
    }

    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(ctx);
    final SharedPreferences.Editor editor = preferences.edit();
    boolean checked = preferences.getBoolean("checked", false);
    if (checked) {
        SharedPreferences preferencesUnknown = PreferenceManager.getDefaultSharedPreferences(ctx);
        final SharedPreferences.Editor editorUnknown = preferencesUnknown.edit();
        boolean checkedUnknown = preferencesUnknown.getBoolean("checkedunknown", false);
        if (checkedUnknown) {
            boolean isUnknown = contactExists(ctx, number);
            if (isUnknown == false) {
                AlertDialog.Builder alerDialog = new AlertDialog.Builder(ctx);
                alerDialog.setIcon(R.drawable.calllogo);
                alerDialog.setTitle(number);
                alerDialog.setMessage("Save this number ?");
                alerDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Intent intent = new Intent(ctx, CategoryPhoneActivity.class);
                        intent.putExtra("Phone", number);
                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        ctx.startActivity(intent);
                    }
                });
                alerDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
                AlertDialog aldialog = alerDialog.create();
                aldialog.setCanceledOnTouchOutside(false);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } else {
                    aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
                }
                aldialog.show(); } else {
                Toast.makeText(ctx, "Number is already saved", Toast.LENGTH_SHORT).show(); }
        }else{
            AlertDialog.Builder alerDialog = new AlertDialog.Builder(ctx);
            alerDialog.setIcon(R.drawable.calllogo);
            alerDialog.setTitle(number);
            alerDialog.setMessage("Save this number ?");
            alerDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Intent intent = new Intent(ctx, CategoryPhoneActivity.class);
                    intent.putExtra("Phone", number);
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    ctx.startActivity(intent);
                }
            });
            alerDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.cancel();
                }
            });
            AlertDialog aldialog = alerDialog.create();
            aldialog.setCanceledOnTouchOutside(false);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } else {
                aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
            }
            aldialog.show(); }
    }else{
        Toast.makeText(ctx, "", Toast.LENGTH_SHORT).show(); }

}


public boolean contactExists(Context context, String number) {
    Uri lookupUri = Uri.withAppendedPath(
            ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
            Uri.encode(number));
    String[] mPhoneNumberProjection = { ContactsContract.PhoneLookup._ID, ContactsContract.PhoneLookup.NUMBER, ContactsContract.PhoneLookup.DISPLAY_NAME };
    Cursor cur = context.getContentResolver().query(lookupUri,mPhoneNumberProjection, null, null, null);
    try {
        if (cur.moveToFirst()) {
            return true;
        }
    } finally {
        if (cur != null)
            cur.close();
    }
    return false;
}
}

Beginning with Android 8.0 (API level 26), the system imposes additional restrictions on manifest-declared receivers.

If your app targets Android 8.0 or higher, you cannot use the manifest to declare a receiver for most implicit broadcasts (broadcasts that don't target your app specifically). You can still use a context-registered receiver when the user is actively using your app.

我猜系统会在您关闭应用程序时自动注销您的 broadcast receivers解决方案: 创建粘性或前台服务并在服务中注册您的广播接收器。当您接收广播时,启动 activity 并显示来自广播接收器 onReceive 的对话框。