之前在做Android AppWidget这方面的开发,本人菜鸟一个,刚接触android不久。所以在开发的过程中不免遇到诸多难处,不过在解决问题中收获知识是一种非常刺激的体验。接下来是本人在开发开关android系统移动网络的过程所收获的知识,希望能够帮助有需要的爱好编程者(呵呵..本人是Java语言的忠实粉丝)。
其实开启和关闭移动数据网络有两种方法:一种是通过操作系统的数据库改变APN(网络接入点),从而实现开启和关闭移动数据网络,另一种是通过反射调用系统(ConnectivityManager)的setMoblieDataEnabled方法,通过操作该方法开启和关闭系统移动数据,同时也可以通过反射调用getMoblieDataEnabled方法获取当前的开启和关闭状态。
第一种方式:
通过APN的方式开启和关闭很威猛啊,为什么这么说呢,废话不多说,先看代码:
1. 匹配类:
//创建一个匹配类,用于匹配移动、电信、联通的APN public final class APNMatchTools { // 中国移动cmwap public static String CMWAP = "cmwap"; // 中国移动cmnet public static String CMNET = "cmnet"; // 中国联通3gwap APN public static String GWAP_3 = "3gwap"; // 中国联通3gnet APN public static String GNET_3 = "3gnet"; // 中国联通uni wap APN public static String UNIWAP = "uniwap"; // 中国联通uni net APN public static String UNINET = "uninet"; // 中国电信 ct wap APN public static String CTWAP = "ctwap"; // 中国电信ct net APN public static String CTNET = "ctnet"; public static String matchAPN(String currentName) { if ("".equals(currentName) || null == currentName) { return ""; } // 参数转为小写 currentName = currentName.toLowerCase(); // 检查参数是否与各APN匹配,返回匹配值 if (currentName.startsWith(CMNET)) return CMNET; else if (currentName.startsWith(CMWAP)) return CMWAP; else if (currentName.startsWith(GNET_3)) return GNET_3; else if (currentName.startsWith(GWAP_3)) return GWAP_3; else if (currentName.startsWith(UNINET)) return UNINET; else if (currentName.startsWith(UNIWAP)) return UNIWAP; else if (currentName.startsWith(CTWAP)) return CTWAP; else if (currentName.startsWith(CTNET)) return CTNET; else if (currentName.startsWith("default")) return "default"; else return ""; } }
2. 开启和关闭APN的方法在ApnSwitchTest类中实现,如下:
import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.util.Log; public class ApnSwitchTest extends Activity { Uri uri = Uri.parse("content://telephony/carriers/preferapn"); // 开启APN public void openAPN() { List<APN> list = getAPNList(); for (APN apn : list) { ContentValues cv = new ContentValues(); // 获取及保存移动或联通手机卡的APN网络匹配 cv.put("apn", APNMatchTools.matchAPN(apn.apn)); cv.put("type", APNMatchTools.matchAPN(apn.type)); // 更新系统数据库,改变移动网络状态 getContentResolver().update(uri, cv, "_id=?", new String[] { apn.id }); } } // 关闭APN public void closeAPN() { List<APN> list = getAPNList(); for (APN apn : list) { // 创建ContentValues保存数据 ContentValues cv = new ContentValues(); // 添加"close"匹配一个错误的APN,关闭网络 cv.put("apn", APNMatchTools.matchAPN(apn.apn) + "close"); cv.put("type", APNMatchTools.matchAPN(apn.type) + "close"); // 更新系统数据库,改变移动网络状态 getContentResolver().update(uri, cv, "_id=?", new String[] { apn.id }); } } public static class APN { String id; String apn; String type; } private List<APN> getAPNList() { // current不为空表示可以使用的APN String projection[] = { "_id, apn, type, current" }; // 查询获取系统数据库的内容 Cursor cr = getContentResolver().query(uri, projection, null, null, null); // 创建一个List集合 List<APN> list = new ArrayList<APN>(); while (cr != null && cr.moveToNext()) { Log.d("ApnSwitch", "id" + cr.getString(cr.getColumnIndex("_id")) + " \n" + "apn" + cr.getString(cr.getColumnIndex("apn")) + "\n" + "type" + cr.getString(cr.getColumnIndex("type")) + "\n" + "current" + cr.getString(cr.getColumnIndex("current"))); APN a = new APN(); a.id = cr.getString(cr.getColumnIndex("_id")); a.apn = cr.getString(cr.getColumnIndex("apn")); a.type = cr.getString(cr.getColumnIndex("type")); list.add(a); } if (cr != null) cr.close(); return list; } }<span style="font-family: 'Comic Sans MS'; "> </span>
最后,别忘了在AndroidManifext.xml文件中添加访问权限<uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />
亲们,从上面的代码中看出什么来了么,没错,通过APN的方式就是修改数据库,关闭APN其实就是给它随便匹配一个错误的APN。为什么说这种方法很生猛呢,当你通过这个方式关闭APN后,你在通过手机上的快捷开关开启移动数据网络时,是没效果的,也就是说开启不了,除非你再用同样的方法开启APN。
这就奇怪了,关闭APN后,为什么再通过手机上的快捷开关(AppWidget)开启不了呢,这个问题就值得思考了,说明快捷开关其实并不是通过这个方式来开启和关闭移动网络的。道理很简单,想想那些快捷开关是怎么样根据开启和关闭移动网络,然后更换亮和暗的图标的呢(更新UI)。这里肯定会涉及到一个获取系统当前开启和关闭移动数据状态的问题。那到底是怎样获取的,是通过什么样的形式的?其实道理很简单,就是通过调用系统的getMoblieDataEnabled和setMoblieDataEnabled(我是这么知道它是调用到这个方法的呢?亲们,如果你有android手机,把它插到电脑上,然后开启已经搭建好的android开发环境的eclpise,打开logcat面板,相应地在你手机的快捷开关上开启和关闭移动网络,然后看看在logcat面板上出现什么了)。
既然知道是调用上面这两个方法了,我们是不是就可以直接调用这个两个方法实现了?NO,没这么简单,这个两个方法不能直接调用,必须通过反射机制调用(呵呵,没接触过java有关反射的知识的,或者是忘了的,可以去学习和温习一下)。
第二种方式:
废话不多说,看下面的代码:
import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import android.app.Activity; import android.content.Context; import android.net.ConnectivityManager; public class MobileDataSwitchTest extends Activity { // 移动数据开启和关闭 public void setMobileDataStatus(Context context,boolean enabled) { ConnectivityManager conMgr = (ConnectivityManager)this.getSystemService(Context.CONNECTIVITY_SERVICE); //ConnectivityManager类 Class<?> conMgrClass = null; //ConnectivityManager类中的字段 Field iConMgrField = null; //IConnectivityManager类的引用 Object iConMgr = null; //IConnectivityManager类 Class<?> iConMgrClass = null; //setMobileDataEnabled方法 Method setMobileDataEnabledMethod = null; try { //取得ConnectivityManager类 conMgrClass = Class.forName(conMgr.getClass().getName()); //取得ConnectivityManager类中的对象Mservice iConMgrField = conMgrClass.getDeclaredField("mService"); //设置mService可访问 iConMgrField.setAccessible(true); //取得mService的实例化类IConnectivityManager iConMgr = iConMgrField.get(conMgr); //取得IConnectivityManager类 iConMgrClass = Class.forName(iConMgr.getClass().getName()); //取得IConnectivityManager类中的setMobileDataEnabled(boolean)方法 setMobileDataEnabledMethod = iConMgrClass.getDeclaredMethod("setMobileDataEnabled", Boolean.TYPE); //设置setMobileDataEnabled方法是否可访问 setMobileDataEnabledMethod.setAccessible(true); //调用setMobileDataEnabled方法 setMobileDataEnabledMethod.invoke(iConMgr, enabled); } catch(ClassNotFoundException e) { e.printStackTrace(); } catch(NoSuchFieldException e) { e.printStackTrace(); } catch(SecurityException e) { e.printStackTrace(); } catch(NoSuchMethodException e) { e.printStackTrace(); } catch(IllegalArgumentException e) { e.printStackTrace(); } catch(IllegalAccessException e) { e.printStackTrace(); } catch(InvocationTargetException e) { e.printStackTrace(); } } //获取移动数据开关状态 public boolean getMobileDataStatus(String getMobileDataEnabled) { ConnectivityManager cm; cm = (ConnectivityManager)this.getSystemService(Context.CONNECTIVITY_SERVICE); Class cmClass = cm.getClass(); Class[] argClasses = null; Object[] argObject = null; Boolean isOpen = false; try { Method method = cmClass.getMethod(getMobileDataEnabled, argClasses); isOpen = (Boolean)method.invoke(cm, argObject); }catch(Exception e) { e.printStackTrace(); } return isOpen; } }
最后,别忘了在AndroidMannifest.xml文件里添加访问权限 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />, <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
通过上面的代码可以知道,当开启移动网络时调用setMobileDataStatus(context,true),关闭调用setMobileDataStatus(context,false),通过getMobileDataStatus(String getMobileDataEnabled)方法返回的布尔值判断当移动数据网络前状态的开启和关闭。
注:本人第一次发博客,如有不足之处恳请多多谅解,诚心接受大家的批评及采纳大家的意见!大家共同努力学习!!