即使提供了通知,也无法 运行 在前台服务中进行蓝牙扫描超过 10 - 15 秒

Cannot able run Bluetooth Scanning in Foreground service more than 10 - 15 secs even though Notification is provided

我已经使用服务来保持对 android 手机的扫描,即使在应用程序关闭时也是如此。我使用广播接收器在被杀时重新启动我的服务。它重新开始扫描,仅工作约 15 秒,然后停止 当我在 {MainActivity} 中单击 button1 时,我启动了服务并在 {ExampleService} 的 startCommand 方法中调用了 startdiscovery() 请帮助我 运行 后台应用

MainActivity.java

import android.Manifest;
import android.app.ActivityManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.bluetooth.le.ScanResult;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.ResolvableApiException;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResponse;
import com.google.android.gms.location.LocationSettingsStatusCodes;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.firestore.FirebaseFirestore;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class MainActivity extends AppCompatActivity {
   ListView scanListView;
    ArrayList<String> stringArrayList = new ArrayList<String>();
    ArrayAdapter<String> arrayAdapter;
    static BluetoothAdapter myAdapter = BluetoothAdapter.getDefaultAdapter();
    FirebaseFirestore db = FirebaseFirestore.getInstance();

    static int count=0;
    ScanResult sc;
    String Uuid;
    Date currentTime;
    private LocationSettingsRequest.Builder builder;
    private final int REQUEST_CHECK_CODE=8989;
    public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
    LocationManager locationm;
    String provider;
    Button button1;
    BluetoothAdapter bluetoothAdapter;
    Intent btEnablingIntent;
    int requestCodeForEnable;
    Button button;
    Button scanButton1;
    RegisterActivity registerActivity=new RegisterActivity();
    String email ;
    String phone ;

///
Intent discoverableIntenet = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
Intent mServiceIntent;

    private ExampleService mYourService;
    Context ctx;
    public Context getCtx() {
        return ctx;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);



   currentTime = Calendar.getInstance().getTime();
        setContentView(R.layout.activity_main);

        checkLocationPermission();

        btEnablingIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        requestCodeForEnable=1;
        discoverableIntenet.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 3000);
        startActivity(discoverableIntenet);
        button=(Button) findViewById(R.id.button4);
        Intent intent = new Intent(MainActivity.this,ble.class);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                Intent intent = new Intent(MainActivity.this,ble.class);
                MainActivity.this.startActivity(intent);
            }
        });
        button=(Button) findViewById(R.id.button6);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                open(view);
            }
        });
        button=(Button) findViewById(R.id.button3);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                pdfs(view);
            }
        });
        button=(Button) findViewById(R.id.button5);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myths(view);
            }
        });


        button1=(Button) findViewById(R.id.button2);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                analytics(view);
            }
        });
        scanListView = (ListView) findViewById(R.id.scannedListView);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        scanButton1=(Button) findViewById(R.id.Button1);
        scanButton1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("disc","discoveeery!");
                Log.i("hiiiiiii", String.valueOf(myAdapter.isDiscovering()));
                Intent serviceIntent = new Intent(MainActivity.this, ExampleService.class);
                serviceIntent.putExtra("inputExtra", "hi");
//                startService( serviceIntent);

                mYourService = new ExampleService();
                mServiceIntent = new Intent(MainActivity.this, mYourService.getClass());
                if (!isMyServiceRunning(mYourService.getClass())) {
                    startService(mServiceIntent);
                }

            }
        });
        bluetoothOnMethod();
        BluetoothFunctions();


   }
    public void BluetoothFunctions(){


        Log.d("disc", "discovery!");
        IntentFilter intentFilter;
        intentFilter=new IntentFilter(BluetoothDevice.ACTION_FOUND);
        registerReceiver(myreceiver, intentFilter);
        intentFilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        registerReceiver(myreceiver, intentFilter);
        arrayAdapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, stringArrayList);
        scanListView.setAdapter(arrayAdapter);
        Uuid="02.020202.020.02";
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == requestCodeForEnable) {
            if (resultCode == RESULT_OK) {
                Toast.makeText(getApplicationContext(), "Bluetooth is enabled", Toast.LENGTH_LONG).show();
            }
            else if(resultCode==RESULT_CANCELED) {
                Toast.makeText(getApplicationContext(), "Bluetooth enabling cancelled", Toast.LENGTH_LONG).show();
            }
        }
    }


    final BroadcastReceiver myreceiver = new BroadcastReceiver() {

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if (device.getName() == null) {
                    return;
                }
                String address = device.getName();
                int N = 2;
                float type = intent.getFloatExtra((BluetoothDevice.EXTRA_UUID),Float.MIN_VALUE);
                int rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
                String rssi_val = String.valueOf(rssi);
                String data = device.getAddress() + "   " + rssi_val;
                Vibrator v1 = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
                stringArrayList.add(data);
                if (rssi >= -50) {
                   v1.vibrate(VibrationEffect.createOneShot(1000, VibrationEffect.DEFAULT_AMPLITUDE));
                    count++;
                    if (device.getAddress() != null) {
                        Firebasepush(device.getAddress(), rssi_val);
                    }
                }
                Log.i("lll", device.getAddress());
                Log.i("lll", device.getName());
                Log.i("+>>>>>>>>>>>>>", BluetoothDevice.EXTRA_UUID);
                Log.i("lll", rssi_val);
                arrayAdapter.notifyDataSetChanged();
                //  Toast.makeText(getApplicationContext(),"  TX power: " +sc.getTxPower() + "dBm", Toast.LENGTH_SHORT).show();
                //startThread();
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                Log.v("ggggggggg", "Entered the Finished ");
                myAdapter.startDiscovery();
            }
        }
    };

public void Firebasepush(String uuid,String rrsi){
    Map<String, Object> updateMap = new HashMap();
    updateMap.put("RSSI", rrsi);
    updateMap.put("time", currentTime);
    Map<String, Object> countMap = new HashMap();
    countMap.put("Count", count);
    countMap.put("time", currentTime);
    countMap.put("Name",registerActivity.NameString);
    countMap.put("Email",registerActivity.EmailId);
   countMap.put("Phone Number",registerActivity.PhoneNumber);
// Add a new document with a generated ID
    db.collection("users").document(Uuid).collection("contacts").document(uuid)
            .set(updateMap);
    db.collection("users").document(Uuid).
            set(countMap);
}
    void bluetoothOnMethod() {
        if(bluetoothAdapter==null){
            Toast.makeText(getApplicationContext(), "Bluetooth does not support ",Toast.LENGTH_LONG).show();
        }
        else {
            if(!bluetoothAdapter.isEnabled()){
                startActivityForResult(btEnablingIntent,requestCodeForEnable);
            }
        }
    }
    public boolean checkLocationPermission() {
        LocationRequest request = new LocationRequest()
                .setFastestInterval(1500)
                .setInterval(3000)
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        builder = new LocationSettingsRequest.Builder()
                .addLocationRequest(request);
        Task<LocationSettingsResponse> result = LocationServices.getSettingsClient( this).checkLocationSettings(builder.build());
        result.addOnCompleteListener(new OnCompleteListener<LocationSettingsResponse>() {
            @Override
            public void onComplete(@NonNull Task<LocationSettingsResponse> task) {
                try{
                    task.getResult(ApiException.class);
                }catch (ApiException e){
                    switch (e.getStatusCode())
                    {
                        case LocationSettingsStatusCodes
                                .RESOLUTION_REQUIRED:
                            try {
                                ResolvableApiException resolvableApiException= (ResolvableApiException) e;
                                resolvableApiException.startResolutionForResult(MainActivity.this,REQUEST_CHECK_CODE);
                            } catch (IntentSender.SendIntentException ex) {
                                ex.printStackTrace();
                            }catch (ClassCastException ex){
                            }break;
                        case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                        { break;}
                    }
                }
            }
        });
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)) {
                // Show an explanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.
                new AlertDialog.Builder(this)
                        .setTitle(R.string.title_location_permission)
                        .setMessage(R.string.text_location_permission)
                        .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                //Prompt the user once explanation has been shown
                                ActivityCompat.requestPermissions(MainActivity.this,
                                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                                        MY_PERMISSIONS_REQUEST_LOCATION);
                            }
                        })
                        .create()
                        .show();
            } else {
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        MY_PERMISSIONS_REQUEST_LOCATION);
            }
            return false;
        } else {
            return true;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,String permissions[], int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_LOCATION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    // permission was granted, yay! Do the
                    // location-related task you need to do.
                    if (ContextCompat.checkSelfPermission(this,
                            Manifest.permission.ACCESS_FINE_LOCATION)
                            == PackageManager.PERMISSION_GRANTED) {
                        //Request location updates:
                        //  locationm.requestLocationUpdates(provider, 400, 1,this);
                    }
                } else {
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                }
                return;
            }

        }
    }

    private boolean isMyServiceRunning(Class<?> serviceClass) {
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (serviceClass.getName().equals(service.service.getClassName())) {
                Log.i ("Service status", "Running");
                return true;
            }
        }
        Log.i ("Service status", "Not running");
        return false;
    }


    @Override
    protected void onDestroy() {

        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction("restartservice");
        broadcastIntent.setClass(this, Restarter.class);
        this.sendBroadcast(broadcastIntent);
        super.onDestroy();

        // Don't forget to unregister the ACTION_FOUND receiver.
    //    unregisterReceiver(myreceiver);
    }
    public void openservices(){
        Intent intnt=new Intent(this ,Services.class);
        startActivity(intnt);
    }
    public void open(View view)
    {
        Intent browserIntent=new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.hogist.com/#/"));
        startActivity(browserIntent);
    }
    public void pdfs(View view)
    {
        Intent browserIntent=new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.mohfw.gov.in/pdf/Illustrativeguidelineupdate.pdf"));
        startActivity(browserIntent);
    }
    public void myths(View view)
    {
        Intent browserIntent=new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.mohfw.gov.in/pdf/Illustrativeguidelineupdate.pdf"));
        startActivity(browserIntent);
    }
    public void analytics(View view){
        final Intent intent1 = new Intent(MainActivity.this, Analytics.class);
        startActivity(intent1);
    }

}

ExampleService.java

import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;

import java.util.Timer;
import java.util.TimerTask;

public class ExampleService extends Service {
      BluetoothAdapter myAdapter = BluetoothAdapter.getDefaultAdapter();
      MainActivity mainActivity=new MainActivity();
    public int counter=0;

    @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
            startMyOwnForeground();
        else
            startForeground(1, new Notification());
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private void startMyOwnForeground()
    {
        String NOTIFICATION_CHANNEL_ID = "example.permanence";
        String channelName = "Background Service";
        NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
        chan.setLightColor(Color.BLUE);
        chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);

        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        assert manager != null;
        manager.createNotificationChannel(chan);

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
        Notification notification = notificationBuilder.setOngoing(true)
                .setContentTitle("App is running in background")
                .setPriority(NotificationManager.IMPORTANCE_MIN)
                .setCategory(Notification.CATEGORY_SERVICE)
                .build();
        startForeground(2, notification);
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
    //    startTimer();
        myAdapter.startDiscovery();
        return START_STICKY;
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
       // stoptimertask();
myAdapter.cancelDiscovery();
        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction("restartservice");
        broadcastIntent.setClass(this, Restarter.class);
        this.sendBroadcast(broadcastIntent);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}```

Restarter.java


import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
import android.widget.Toast;

public class Restarter extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i("Broadcast Listened", "Service tried to stop");
        Toast.makeText(context, "Service restarted", Toast.LENGTH_SHORT).show();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Log.i(Restarter.class.getSimpleName(), "Service Stops! Oooooooooooooppppssssss!!!!");
            context.startForegroundService(new Intent(context, ExampleService.class));
        } else {
            context.startService(new Intent(context, ExampleService.class));

        }
    }
}

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.hogist_social_distancing">

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

    <application
        android:name=".App"
        android:fullBackupContent="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity
            android:name=".Analytics"
            android:screenOrientation="portrait" />
        <activity
            android:name=".Services"
            android:screenOrientation="portrait" />
        <activity
            android:name=".Permissions"
            android:screenOrientation="portrait" />
        <activity
            android:name=".MainActivity"
            android:screenOrientation="portrait" />
        <activity android:name=".RegisterActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <service
            android:name=".ExampleService"
            android:enabled="true" />

        <receiver
            android:name="Restarter"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="restartservice" />
            </intent-filter>
        </receiver>

    </application>

</manifest>```
  1. 您确定您的蓝牙代码可以在 activity(无服务)上正常工作吗?
  2. 当你的服务被杀死时,你不能通过调用广播来重启你的服务,因为当你的服务被OS系统杀死时,它会杀死你的应用程序进程,所以你的广播将无法工作。当您调用 return START_STICKY; 时,这意味着系统将在有可用资源时自动重启您的服务

Android 努力节省电池电量,因此预计您的服务会停止,尤其是当它 运行 进行蓝牙扫描等耗电操作时。

此外,请记住,从 Android 7 开始,应用每 30 秒启动 BLE 扫描的次数不能超过 5 次,如前所述 here. Other issues related to BLE scanning are listed here

如果你确实想运行在后台进行蓝牙扫描,我建议你使用JobIntentService,在其中启动BLE扫描几秒钟。

JobIntentServiceIntentService 非常相似,但没有什么好处,例如持有唤醒锁以防止 CPU 进入睡眠状态

此外,此类服务不需要向您的用户显示通知。更多信息:https://developer.android.com/reference/androidx/core/app/JobIntentService