1. 程式人生 > >實現開啟和關閉android行動網路

實現開啟和關閉android行動網路

開啟和關閉移動資料網路有兩種方法:一種是通過作業系統的資料庫改變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 { //APN的資料庫表所在的URI 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; } }最後,別忘了在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)方法返回的布林值判斷當移動資料網路前狀態的開啟和關閉。


注:本人第一次發部落格,也是轉載了前輩的並且更改了說明,如有不足之處懇請多多諒解,誠心接受大家的批評及採納大家的意見!大家共同努力學習!!