私のアプリには、_<receiver>
_タグを通じてコンポーネントとして起動され、_Android.net.conn.CONNECTIVITY_CHANGE
_インテントをフィルタリングするBroadcastReceiver
があります。
私の目標は、Wi-Fi接続がいつ確立されたかを知ることです。したがって、onReceive()
で行うことは次のとおりです。
_NetworkInfo networkInfo = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if(networkInfo.getType() == ConnectivityManager.TYPE_WIFI && networkInfo.isConnected()) {
// Wifi is connected
}
_
正常に動作しますが、Wifi接続が確立されると、約1秒以内に2つの同一のインテントを常に取得するようです。 ConnectivityManager
とWifiManager
というインテントから得られる情報を調べようとしましたが、2つのインテントを区別するものは見つかりません。
ログを見ると、2つの同一のインテントも受け取る少なくとも1つのBroadcastReceiver
があります。
Android 2.2のHTC Desireで実行されています
Wifiが接続したときに「重複した」意図を取得しているように見える理由や、2つの違いは何ですか?
注:最近の最新の回答については、下記の this one を参照してください!
多くのグーグルとデバッグの後、これがWifiが接続されたか切断されたかを判断する正しい方法だと思います。
BroadcastReceiverのonReceive()
メソッド:
_public void onReceive(final Context context, final Intent intent) {
if(intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
NetworkInfo networkInfo =
intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if(networkInfo.isConnected()) {
// Wifi is connected
Log.d("Inetify", "Wifi is connected: " + String.valueOf(networkInfo));
}
} else if(intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
NetworkInfo networkInfo =
intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if(networkInfo.getType() == ConnectivityManager.TYPE_WIFI &&
! networkInfo.isConnected()) {
// Wifi is disconnected
Log.d("Inetify", "Wifi is disconnected: " + String.valueOf(networkInfo));
}
}
}
_
AndroidManifest.xmlの次のレシーバー要素と一緒に
_<receiver Android:name="ConnectivityActionReceiver"
Android:enabled="true" Android:label="ConnectivityActionReceiver">
<intent-filter>
<action Android:name="Android.net.conn.CONNECTIVITY_CHANGE"/>
<action Android:name="Android.net.wifi.STATE_CHANGE"/>
</intent-filter>
</receiver>
_
いくつかの説明:
_ConnectivityManager.CONNECTIVITY_ACTION
_のみを考慮すると、Wifiの接続時に常に同じNetworkInfoインスタンス(getType()== TYPE_WIFIとisConnected()== trueの両方)を含む2つのインテントを取得します-この質問で説明されている問題。
_WifiManager.NETWORK_STATE_CHANGED_ACTION
_のみを使用する場合、Wifiの切断時にブロードキャストされるインテントはありませんが、異なるNetworkInfoインスタンスを含む2つのインテントがあり、Wifiの接続時に1つのイベントを判別できます。
注:intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO)
がnullを返した単一のクラッシュレポート(NPE)を受け取りました。そのため、非常にまれにしか発生しないと思われる場合でも、nullチェックを追加することをお勧めします。
乾杯、トルステン
_WifiManager.NETWORK_STATE_CHANGED_ACTION
_をリッスンしている場合、NetworkInfo
には2つのメソッドがあるため、これを2回受け取ります。
isConnectedOrConnecting()
isConnected()
初めてisConnectedOrConnecting()
はtrue
とisConnected()
false
を返します
2回目isConnectedOrConnecting()
およびisConnected()
return true
乾杯
Torstenのコードを更新し、WIFIが切断されたときに、適切な単一のブロードキャストのみが実行されるようにしました。
NetworkInfo.getDetailedState()== DetailedState.DISCONNECTEDをチェックに使用しました。
public void onReceive(final Context context, final Intent intent) {
if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
NetworkInfo networkInfo = intent
.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
// Wifi is connected
Log.d("Inetify","Wifi is connected: " + String.valueOf(networkInfo));
}
} else if (intent.getAction().equals(
ConnectivityManager.CONNECTIVITY_ACTION)) {
NetworkInfo networkInfo = intent
.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (networkInfo.getDetailedState() == DetailedState.DISCONNECTED) {
// Wifi is disconnected
Log.d("Inetify","Wifi is disconnected: "+String.valueOf(networkInfo));
}
}
}
これは、API 21以降で接続の変更を登録する適切な方法です。次のコードを基本アクティビティに配置すると、アプリのすべての画面(このアクティビティから継承する)がこれらのコールバックを取得できるようになります。
最初に、接続の変更を監視するネットワークコールバックを作成します。
@TargetApi(Build.VERSION_CODES.Lollipop)
private val networkCallback: ConnectivityManager.NetworkCallback = object : ConnectivityManager.NetworkCallback() {
// Implement the callback methods that are relevant to the actions you want to take.
// I have implemented onAvailable for connecting and onLost for disconnecting.
override fun onAvailable(network: Network?) {
super.onAvailable(network)
}
override fun onLost(network: Network?) {
super.onLost(network)
}
}
次に、関連するスポットでこのコールバックを登録および登録解除します。
override fun onResume() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
cm?.registerNetworkCallback(NetworkRequest.Builder().build(), networkCallback)
}
}
必要に応じて登録を解除します。
override fun onPause() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
cm?.unregisterNetworkCallback(networkCallback)
}
}
Build.VERSION_CODES.Lollipop
のチェックがあることに注意してください。この機能は、Lollipop以降でのみ使用できます。アプリでAPI 21未満しかサポートしていない場合、Pre-Lollipopデバイスでネットワークステータスの変更を処理する方法を計画してください。
アクティビティをインテントリスナーとして登録した場合、同じメッセージを2回受信します。具体的には、パッケージレベル(XML)またはプログラムレベルのどちらでリッスンするかを選択する必要があります。
ブロードキャストレシーバーのクラスを設定してリッスンをアタッチし、アクティビティにインテントフィルターをアタッチすると、メッセージは2回複製されます。
これで問題が解決することを願っています。
SharedPrefとTimeを使用して2回の呼び出しを解決しました。
private static final Long SYNCTIME = 800L;
private static final String LASTTIMESYNC = "DATE";
SharedPreferences sharedPreferences;
private static final String TAG = "Connection";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Network connectivity change");
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
final ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkInfo ni = connectivityManager.getActiveNetworkInfo();
if (ni != null && ni.isConnected()) {
if(System.currentTimeMillis()-sharedPreferences.getLong(LASTTIMESYNC, 0)>=SYNCTIME)
{
sharedPreferences.edit().putLong(LASTTIMESYNC, System.currentTimeMillis()).commit();
// Your code Here.
}
}
else if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, Boolean.FALSE)) {
Log.d(TAG, "There's no network connectivity");
}
}
1.callと2.callの間にわずかな遅延があるため(約200ミリ秒)。そのため、IFが時間とともに2番目の呼び出しが停止し、最初の呼び出しが続行されます。
で解決したら
onCreate()
intentFilter.addAction("Android.net.conn.CONNECTIVITY_CHANGE");
intentFilter.addAction("Android.net.wifi.WIFI_STATE_CHANGED");
intentFilter.addAction("Android.net.wifi.STATE_CHANGE");
ctx.registerReceiver(outgoingReceiver, intentFilter);
に
BroadcastReceiver
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
NetworkInfo networkInfo =
intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if(networkInfo.getType() == ConnectivityManager.TYPE_WIFI &&
networkInfo.isConnected()) {
// Wifi is connected
Log.d("Inetify", "Wifi is connected: " + String.valueOf(networkInfo));
Log.e("intent action", intent.getAction());
if (isNetworkConnected(context)){
Log.e("WiFi", "is Connected. Saving...");
try {
saveFilesToServer("/" + ctx.getString(R.string.app_name).replaceAll(" ", "_") + "/Temp.txt");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}}
boolean isNetworkConnected(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
if (ni != null) {
Log.e("NetworkInfo", "!=null");
try{
//For 3G check
boolean is3g = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE)
.isConnectedOrConnecting();
//For WiFi Check
boolean isWifi = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
.isConnected();
Log.e("isWifi", "isWifi="+isWifi);
Log.e("is3g", "is3g="+is3g);
if (!isWifi)
{
return false;
}
else
{
return true;
}
}catch (Exception er){
return false;
}
} else{
Log.e("NetworkInfo", "==null");
return false;
}
}
NetworkInfoの追加のインテントを使用して、この問題を解決しました。次の例では、wifiが接続されているかモバイルの場合、onReceiveイベントが1回だけ発生します。
if (intent.getAction().equalsIgnoreCase(ConnectivityManager.CONNECTIVITY_ACTION)) {
NetworkInfo networkInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
boolean screenIsOn = false;
// Prüfen ob Screen on ist
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (Android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
screenIsOn = pm.isInteractive();
} else {
screenIsOn = pm.isScreenOn();
}
if (Helper.isNetworkConnected(context)) {
if (networkInfo.isConnected() && networkInfo.isAvailable()) {
Log.v(logTAG + "onReceive", "connected");
if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
Log.v(logTAG + "onReceive", "mobile connected");
} else if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
Log.v(logTAG + "onReceive", "wifi connected");
}
}
}
そして私のヘルパー:
public static boolean isNetworkConnected(Context ctx) {
ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
return ni != null;
}
一度だけ受信したい場合は、変数を介して単純に制御できます。
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
NetworkInfo activeNetwork = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (activeNetwork != null) { // connected to the internet
if (activeNetwork.isConnected() && !isUpdated) {
if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
// connected to wifi
} else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
// connected to the mobile provider's data plan
}
isUpdated = true;
} else {
isUpdated = false;
}
}
}
WIFIをオンにすると、
上記の2つの条件下でWIFIをオフにすると、同様の動作が見られます。
2つを区別するには、以下の#2と#3に従ってください。
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "*** Action: " + intent.getParcelableExtra("networkInfo"));
NetworkInfo netInfo = intent.getParcelableExtra("networkInfo");
if(intent.getAction().equalsIgnoreCase("Android.net.conn.CONNECTIVITY_CHANGE")) {
ConnectivityManager connectivityManager
= (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
if (activeNetInfo != null) {
if (netInfo.getType() == ConnectivityManager.TYPE_WIFI) {
if (netInfo.getState().name().contains("DISCONNECTED")
&& activeNetInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
Log.d(TAG, "WIFI disconnect created this broadcast. MOBILE data ON."); // #1
} else if (netInfo.getState().name().contains("CONNECTED")
&& activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI) {
Log.d(TAG, "WIFI connect created this broadcast."); // #2
}
} else if (netInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
if (netInfo.getState().name().contains("DISCONNECTED")
&& activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI) {
Log.d(TAG, "MOBILE data disconnect created this broadcast. WIFI ON."); // #3
} else if (netInfo.getState().name().contains("CONNECTED")
&& activeNetInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
Log.d(TAG, "MOBILE data connect created this broadcast."); // #4
}
}
} else {
Log.d(TAG, "No network available");
}
}
}
インターネットはないが、実際にはあるというネットワーク接続の特殊なケースを見つけました。バッテリーレベルが低くアプリがあったときにネットワークが変更された特定の場合に、getActiveNetworkInfo
は常にあなたを返しますDISCONNECTED/BLOCKEDちょうど切り替えた
この投稿 をご覧ください
私がそれを処理した方法は、単にネットワークの状態を保存し、それを比較してchangeがあるかどうかを確認することでした。
public class ConnectivityChangedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
boolean previouslyConnected = MyApp.getInstance().isNetworkPreviouslyConnected();
boolean currentlyConnected = MyApp.getInstance().isNetworkConnected();
if (previouslyConnected != currentlyConnected) {
// do something and reset
MyApp.getInstance().resetNetworkPreviouslyConnected();
}
}
}
これがあなたがとるアプローチである場合、フラグメントまたはアクティビティのonResume
でそれをリセットして、現在の値を保持することが重要です:
@Override
public void onResume() {
super.onResume();
MyApp.getInstance().resetNetworkPreviouslyConnected();
}
これは、アプリのすべてのフラグメントの親であるBaseFragment
で行いました。
インテントからnetworkTypeを確認し、activeNetworkInfo.getType()を比較します
Bundle bundle = intent.getExtras();
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = manager.getActiveNetworkInfo();
if(ni != null && ni.getState() == NetworkInfo.State.CONNECTED) {
if(bundle.getInt("networkType") == ni.getType()) {
// active network intent
}
}