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



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

主要活动.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);
}
}

示例服务.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;
}
}```

重新启动器.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));
}
}
}

安卓清单.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>```

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

另外,请记住,从Android 7开始,应用程序每30秒启动BLE扫描的次数不能超过5次,如此处所述。此处列出了与BLE扫描相关的其他问题。

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

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

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

  1. 您确定您的蓝牙代码可以在活动(没有服务)上很好地工作吗?
  2. 当您的服务被终止时,
  3. 您无法通过调用广播重新启动您的服务,因为当您的服务作系统杀死时,它将终止您的应用程序进程,因此您的广播将不起作用。当您调用return START_STICKY;时,这意味着系统将在有可用资源时自动重新启动您的服务

最新更新