问题:
所以问题是我有一个应用程序,当连接WiFi(连接的SSID和其他信息)或断开连接(通过移动网络)时,它会向我们的后端发送请求。但是,随着Android 7 / N及更高版本的更改,CONNECTIVITY_CHANGE和CONNECTIVITY_ACTION不再在后台运行。现在大多数情况下人们滥用这个广播,因此我完全理解为什么要做出改变。但是,我不知道如何在当前状态下解决这个问题。
现在我不是一个Android开发人员(这是一个Cordova插件),所以我指望你们!
预期行为 只要WiFi切换连接,即使应用程序被杀/在后台,也会唤醒应用程序并发送请求。
当前行为: 应用程序仅在应用程序位于前台时发送请求。
到目前为止尝试过: 到目前为止,我已经移动了隐藏的意图,从清单中侦听CONNECTIVITY_ACTION,并在应用程序的主要部分(插件)中手动注册它。这使得它只要应用程序在内存中就可以工作,但不能用于冷启动或实际背景
已经看过: 大多数答案都谈到使用预定的工作来代替丢失的广播。我看到它是如何工作的,例如,重试下载或类似,但不适用于我的情况(但如果我错了请纠正我)。以下是我已经看过的SO帖子:
Detect connectivity changes on Android 7.0 Nougat when app is in foreground
ConnectivityManager.CONNECTIVITY_ACTION deprecated
答案 0 :(得分:48)
牛轧糖及以上: 我们必须使用JobScheduler和JobService进行连接更改。
我只能将其分为三个步骤。
在活动中注册JobScheduler。另外,启动JobService( 处理来自JobScheduler的回调的服务。请求已安排 随着JobScheduler最终落在这项服务的开始,#on; onStartJob" 方法。)
public class NetworkConnectionActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_network_connection);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
scheduleJob();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void scheduleJob() {
JobInfo myJob = new JobInfo.Builder(0, new ComponentName(this, NetworkSchedulerService.class))
.setRequiresCharging(true)
.setMinimumLatency(1000)
.setOverrideDeadline(2000)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.build();
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(myJob);
}
@Override
protected void onStop() {
// A service can be "started" and/or "bound". In this case, it's "started" by this Activity
// and "bound" to the JobScheduler (also called "Scheduled" by the JobScheduler). This call
// to stopService() won't prevent scheduled jobs to be processed. However, failing
// to call stopService() would keep it alive indefinitely.
stopService(new Intent(this, NetworkSchedulerService.class));
super.onStop();
}
@Override
protected void onStart() {
super.onStart();
// Start service and provide it a way to communicate with this class.
Intent startServiceIntent = new Intent(this, NetworkSchedulerService.class);
startService(startServiceIntent);
}
}
开始和完成工作的服务。
public class NetworkSchedulerService extends JobService implements
ConnectivityReceiver.ConnectivityReceiverListener {
private static final String TAG = NetworkSchedulerService.class.getSimpleName();
private ConnectivityReceiver mConnectivityReceiver;
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Service created");
mConnectivityReceiver = new ConnectivityReceiver(this);
}
/**
* When the app's NetworkConnectionActivity is created, it starts this service. This is so that the
* activity and this service can communicate back and forth. See "setUiCallback()"
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand");
return START_NOT_STICKY;
}
@Override
public boolean onStartJob(JobParameters params) {
Log.i(TAG, "onStartJob" + mConnectivityReceiver);
registerReceiver(mConnectivityReceiver, new IntentFilter(Constants.CONNECTIVITY_ACTION));
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
Log.i(TAG, "onStopJob");
unregisterReceiver(mConnectivityReceiver);
return true;
}
@Override
public void onNetworkConnectionChanged(boolean isConnected) {
String message = isConnected ? "Good! Connected to Internet" : "Sorry! Not connected to internet";
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
}
最后,检查网络连接的接收器类 变化。
public class ConnectivityReceiver extends BroadcastReceiver {
private ConnectivityReceiverListener mConnectivityReceiverListener;
ConnectivityReceiver(ConnectivityReceiverListener listener) {
mConnectivityReceiverListener = listener;
}
@Override
public void onReceive(Context context, Intent intent) {
mConnectivityReceiverListener.onNetworkConnectionChanged(isConnected(context));
}
public static boolean isConnected(Context context) {
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
}
public interface ConnectivityReceiverListener {
void onNetworkConnectionChanged(boolean isConnected);
}
}
不要忘记在清单文件中添加权限和服务。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yourpackagename">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!-- Always required on api < 21, needed to keep a wake lock while your job is running -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!-- Required on api < 21 if you are using setRequiredNetworkType(int) -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!-- Required on all api levels if you are using setPersisted(true) -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:allowBackup="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=".connectivity.NetworkConnectionActivity"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Define your service, make sure to add the permision! -->
<service
android:name=".connectivity.NetworkSchedulerService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE"/>
</application>
</manifest>
请参阅以下链接以获取更多信息。
https://github.com/jiteshmohite/Android-Network-Connectivity
https://github.com/evant/JobSchedulerCompat
答案 1 :(得分:2)
这就是我做到的。我创建了IntentService
和onCreate
方法,我已注册networkBroadacst
,用于检查互联网连接。
public class SyncingIntentService extends IntentService {
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
networkBroadcast=new NetworkBroadcast();
registerReceiver(networkBroadcast,
new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onHandleIntent(intent);
return START_STICKY;
}
}
这是我的广播课
public class NetworkBroadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Constants.isInternetConnected(context)) {
// Toast.makeText(context, "Internet Connect", Toast.LENGTH_SHORT).show();
context.startService(new Intent(context, SyncingIntentService.class));
}
else{}
}
}
通过这种方式,您可以检查互联网连接,确认您的应用是在牛轧糖的前景还是背景中。
答案 2 :(得分:2)
针对Android 7.0(API级别24)及更高版本的应用无法接收 如果CONNECTIVITY_ACTION声明广播接收器,则广播 在他们的清单中。应用仍会收到CONNECTIVITY_ACTION 广播,如果他们注册他们的BroadcastReceiver Context.registerReceiver()和该上下文仍然有效。
因此,您将获得此广播,直到您的上下文在Android N&amp;通过明确注册相同的内容。
启动已完成:
您可以收听android.intent.action.BOOT_COMPLETED
广播
您将需要此权限。
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
App Killed Scenario:
你不会收到它。
这是非常期待的,并且由于各种原因
Android Oreo has limitations,因此您可能会在O设备上遇到此问题
Doze mode可以导致这种情况,它将停止所有网络操作本身&amp;拿走CPU唤醒锁
虽然“打盹”模式有requesting whitelisting of apps的一种机制,但这可能对您有用。
答案 3 :(得分:2)
获取连接更改的最佳方法是Android Os 7及更高版本,如下所示在Application类中注册您的ConnectivityReceiver广播,这也将帮助您获取后台更改,直到您的应用生效。
public class MyApplication extends Application {
private ConnectivityReceiver connectivityReceiver;
private ConnectivityReceiver getConnectivityReceiver() {
if (connectivityReceiver == null)
connectivityReceiver = new ConnectivityReceiver();
return connectivityReceiver;
}
@Override
public void onCreate() {
super.onCreate();
registerConnectivityReceiver();
}
// register here your filtters
private void registerConnectivityReceiver(){
try {
// if (android.os.Build.VERSION.SDK_INT >= 26) {
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
//filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
//filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
//filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
registerReceiver(getConnectivityReceiver(), filter);
} catch (Exception e) {
MLog.e(TAG, e.getMessage());
}
}
}
然后在清单中
<application
android:name=".app.MyApplication"/>
这是您的ConnectivityReceiver.java
public class ConnectivityReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, final Intent intent) {
MLog.v(TAG, "onReceive().." + intent.getAction());
}
}
答案 4 :(得分:0)
使用registerNetworkCallback (NetworkRequest, PendingIntent)
时更简便的另一种方法:
NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
builder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
builder.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
Intent intent = new Intent(this, SendAnyRequestService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
if (connectivityManager != null) {
NetworkRequest networkRequest = builder.build();
connectivityManager.registerNetworkCallback(networkRequest, pendingIntent);
}
SendAnyRequestService.class
是哪个服务类,您可以在其中调用API。
此代码适用于Android 6.0(API 23)及更高版本
参考文件为here
答案 5 :(得分:-2)
因此,我想出了一个替代方案,该替代方案是在我的应用程序中使用 Kotlin Coroutines 和 EventBus 实现的。
//connection listener
fun connectivityListener(context: Context) {
launch {
while (true) {
delay(1500) // any time delay you wish to check for network
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork: NetworkInfo? = cm.activeNetworkInfo
val isConnected: Boolean = activeNetwork?.isConnected == true
// well in this part I used the EventBus
// which will help me to listen to this event in any activity
val obj = JSONObject()
obj.put(C.TYPE, C.NETWORK) // C is my own custom class
obj.put(C.NETWORK, isConnected)
EventBus.getDefault().post(Event(obj))
}
}
}
我在扩展 Application 的类中调用了此函数。
由于我的应用程序仅基于前台,因此此代码在前台场景中可以完美地工作。通过使用服务等进行一些修改,我相信尽管不确定,它也可以在后台使用。
如果有人在后台尝试此操作,则也请编辑此答案,包括该答案。