2015年5月23日星期六

解决error receiving broadcast intent act=android.net.conn.connectivity_change引起的程序崩溃

学习BroadcastReceiver,尝试使用动态监听网络变化,又遇到了一次程序崩溃。记录如下,解决方案直接跳最后。

首先只是简单显示网络是否变化,代码如下,


public class MainActivity extends Activity {
 
 private IntentFilter intentFilter;
 
 private NetworkChangeReceiver networkChangeReceiver;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  intentFilter = new IntentFilter();
  intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");//网络发生变化时,系统发出android.net.conn.CONNECTIVITY_CHANGE广播;想监听什么,就在这里添加相应action值即可
  networkChangeReceiver = new NetworkChangeReceiver();
  //将NetworkChangeReceiver的实例和IntentFilter实例都传进去,故前者就收到所有值为android.net.conn.CONNECTIVITY_CHANGE的广播;实现了监听网络变化的功能
  registerReceiver(networkChangeReceiver, intentFilter);   
 }
 
 @Override
 protected void onDestroy() {
  super.onDestroy();
  unregisterReceiver(networkChangeReceiver); //动态注册的广播接收器一定逗得取消注册
 }
 
 class NetworkChangeReceiver extends BroadcastReceiver {
  @Override
  //每当网络发生变化,onReceive会得到执行
  public void onReceive(Context context, Intent intent) {
    Toast.makeText(context, "network changes", Toast.LENGTH_SHORT).show();
    
  }  
  
 }

 
}

运行正常,当网络变化时,toast会显示“network is changed”,但是依旧不知道此时是有网络还是没有网络。

为了准确告诉是有网络还是没有网络,引入ConnectivityManager,创建其实例,调用它的getActiveNetworkInfo()方法得到NetworkInfo实例,然后通过使用NetworkInfo的isActivity()方法判断当前是否有网络。代码如下,只修改了NetworkChangeReceiver类部分。


public class MainActivity extends Activity {
 
 private IntentFilter intentFilter;
 
 private NetworkChangeReceiver networkChangeReceiver;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  intentFilter = new IntentFilter();
  intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");//网络发生变化时,系统发出android.net.conn.CONNECTIVITY_CHANGE广播;想监听什么,就在这里添加相应action值即可
  networkChangeReceiver = new NetworkChangeReceiver();
  //将NetworkChangeReceiver的实例和IntentFilter实例都传进去,故前者就收到所有值为android.net.conn.CONNECTIVITY_CHANGE的广播;实现了监听网络变化的功能
  registerReceiver(networkChangeReceiver, intentFilter);   
 }
 
 @Override
 protected void onDestroy() {
  super.onDestroy();
  unregisterReceiver(networkChangeReceiver); //动态注册的广播接收器一定逗得取消注册
 }
 
 class NetworkChangeReceiver extends BroadcastReceiver {
  @Override
  //每当网络发生变化,onReceive会得到执行
  public void onReceive(Context context, Intent intent) {
   // Toast.makeText(context, "network changes", Toast.LENGTH_SHORT).show();
   
    //准确告诉是有网络还是没有网络
   ConnectivityManager connectionManager = (ConnectivityManager) 
     getSystemService(Context.CONNECTIVITY_SERVICE);
   NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
   if (networkInfo != null && networkInfo.isAvailable()) {
    Toast.makeText(context, "network is available", Toast.LENGTH_SHORT).show();
   }
   else {
    Toast.makeText(context, "network is not available", Toast.LENGTH_SHORT).show();
   }
  }  
  
 }

 
}

再次运行之,应用崩溃,熟悉的Unfortunately, APP has stopped映入眼帘,娘希匹的!问题如LogCat所示,


05-23 23:57:48.666: E/AndroidRuntime(16389): FATAL EXCEPTION: main
05-23 23:57:48.666: E/AndroidRuntime(16389): Process: com.example.broadcasttest, PID: 16389
05-23 23:57:48.666: E/AndroidRuntime(16389): java.lang.RuntimeException: Error receiving broadcast Intent { act=android.net.conn.CONNECTIVITY_CHANGE flg=0x4000010 (has extras) } in com.example.broadcasttest.MainActivity$NetworkChangeReceiver@41fd2280
05-23 23:57:48.666: E/AndroidRuntime(16389):  at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:769)
05-23 23:57:48.666: E/AndroidRuntime(16389):  at android.os.Handler.handleCallback(Handler.java:733)
05-23 23:57:48.666: E/AndroidRuntime(16389):  at android.os.Handler.dispatchMessage(Handler.java:95)
05-23 23:57:48.666: E/AndroidRuntime(16389):  at android.os.Looper.loop(Looper.java:136)
05-23 23:57:48.666: E/AndroidRuntime(16389):  at android.app.ActivityThread.main(ActivityThread.java:5086)
05-23 23:57:48.666: E/AndroidRuntime(16389):  at java.lang.reflect.Method.invokeNative(Native Method)
05-23 23:57:48.666: E/AndroidRuntime(16389):  at java.lang.reflect.Method.invoke(Method.java:515)
05-23 23:57:48.666: E/AndroidRuntime(16389):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
05-23 23:57:48.666: E/AndroidRuntime(16389):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
05-23 23:57:48.666: E/AndroidRuntime(16389):  at dalvik.system.NativeStart.main(Native Method)
05-23 23:57:48.666: E/AndroidRuntime(16389): Caused by: java.lang.SecurityException: ConnectivityService: Neither user 10118 nor current process has android.permission.ACCESS_NETWORK_STATE.
05-23 23:57:48.666: E/AndroidRuntime(16389):  at android.os.Parcel.readException(Parcel.java:1465)
05-23 23:57:48.666: E/AndroidRuntime(16389):  at android.os.Parcel.readException(Parcel.java:1419)
05-23 23:57:48.666: E/AndroidRuntime(16389):  at android.net.IConnectivityManager$Stub$Proxy.getActiveNetworkInfo(IConnectivityManager.java:817)
05-23 23:57:48.666: E/AndroidRuntime(16389):  at android.net.ConnectivityManager.getActiveNetworkInfo(ConnectivityManager.java:604)
05-23 23:57:48.666: E/AndroidRuntime(16389):  at com.example.broadcasttest.MainActivity$NetworkChangeReceiver.onReceive(MainActivity.java:47)
05-23 23:57:48.666: E/AndroidRuntime(16389):  at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:759)
05-23 23:57:48.666: E/AndroidRuntime(16389):  ... 9 more


很明显,是修改了NetworkChangeReceiver类后出现的问题。那就对这几行代码肉眼排查。
基本没有发现问题。如果非要鸡蛋挑骨头的话,那就是networkInfo可能会是null,而null.isActivityable()是行不通的,但是我的条件语句杜绝了这样的情况发生。那是怎么回事儿呢?

再仔细查看LogCat输出内容,可见一行,


05-23 23:57:48.666: E/AndroidRuntime(16389): Caused by: java.lang.SecurityException: ConnectivityService: Neither user 10118 nor current process has android.permission.ACCESS_NETWORK_STATE.

这就是问题所在了!

解决方案

在AndroidManifest中的<manifest> </manifest>间添加如下代码,重新运行,问题解决了!LogCat输出的刷刷绿色看起来真漂亮!

  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>



可是,这是为什么呢?

谷歌了一番,了解到,Android系统为了保证安全,在程序需要访问一些系统关键信息,如本次访问系统网络情况,必须在配置文件中声明权限,否则程序会崩溃

LOL!

没有评论:

发表评论