AndroidのソフトウェアProxyChangingの機能。 パート1:ジェリービーンからロリポップまで

かつて、私自身の便宜のために、AndroidのWifiネットワークの構成でプロキシ設定を変更するアプリケーションを作成したかったのです。 そのとき私に思えたように、タスクは時々吐き出すことでしたが、実際には、いつものように、予期しない困難が生じました。







将来的に解決策を知ることが有用であると思う場合、自分のために何かを学びたい場合、または好奇心が目覚めたばかりの場合-猫へようこそ。 そこには、AndroidのさまざまなバージョンでのWifi構成を担当するクラスの内部構造、Javaコードの小さなカップ、Reflectionのピンチがあります。



もちろん、「プログラムでアンドロイドのWi-Fiプロキシ設定を変更する」というトピックに関するGoogleとのちょっとしたやり取りがStackOverflowにもたらされ、そこではReflectionを介したソリューションが存在しました。 考え直すことなく、コードをコピーしてデバイスで起動しました。



結果
画像



失望はすぐに興味と興奮に変わり、この時点で著者はすでに上記の質問に採点するのに時間がかかりすぎていると判断し、このタスクにはよりグローバルなアプローチが必要であることに気付きました。 行きましょう。



ステップ1. android.netライブラリの内部構造とJelly Beanの違いを調査します-KitkatとLollipop



以下で何が起こっているかをより良く理解するために知りたいことのリスト。
  • あなたの英語レベルは、少なくとも中級以上でなければなりません。
  • コンテキストとは何ですか?」という質問は避けてください。また、この時点で複雑さが似ている場合は、 developer.android.comまたはAlexander Klimovを読むことができます。
  • また、アノテーション@Before@Test@Afterなど、テストに関連するものは恥ずべきではありません。 繰り返しますが、リンク: developer.android.com


私はこれをすべて確認したいと思いますが、あなたは理解しています-それから私の記事は本に成長します。



また、いくつかのより一般的な説明をしたいと思います。



  • 私のコードにはコメントがほとんどありません。 私はこの問題を長い間考え、非常に長い間疑っていましたが、最終的にはjavaをまったく知らない女の子にそれを与えることにしました、私は彼らをほとんど放棄したので、彼らと彼らの方法で何が起こるかを言うことは非常に正確です。
  • コメント、追加、質問、コメント(たとえば、前の段落)がある場合-作成者はそれらを待っています。
  • 記事は現在非常に一般化されています。作業量に応じていくつかのポイントをより詳細に表示したい場合は、コメントを書くか、興味のあるトピックに関する別の記事を書きます。
  • パッケージの名前には謙虚な使用人のニックネームが含まれているため、コードにはインポートがありません。ルールから判断すると、サンドボックスの記事ではそれらを照らすことはできません。


Googleでさらに質問をすると、 android.googlesourceに移動しました



プロキシ設定(および他のいくつか)は、このネットワークのWifiConfigurationインスタンス( Kitkat mr2.2クラスへのリンク)に含まれています。 このクラスを勉強したとき、StackOverflowを使用したソリューションがデバイス上で機能しなかった理由についての回答を得ました。 Androidの5番目のバージョンであるWifiConfigurationクラスのデバイスとandroid.netパッケージには大きな変更が加えられ、上記のコードが機能するLinkPropetiesオブジェクトはこのクラスには存在しないことが判明しました。 ただし、 ProxyInfoオブジェクトを持つIpConfiguraionオブジェクトがあります。



Androidのこれらのバージョンがさまざまなデバイスの80%をカバーしていることを考えると、タスクは次のように単純に記述することでした。



public void changeProxySettings(String host, int port){ if(Build.VERSION.SDK_INT > 14 && Build.VERSION.SDK_INT < 20){ changeProxyWithLikProperties(String host, int port); }else if(Build.VERSION.SDK_INT > 20 && Build.VERSION.SDK_INT < 23){ changeProxyWithProxyInfo(String host, int port); }else{ throw new Exception("Sorry, android version not supported") } }
      
      





where changeProxyXXX-巨大なメソッド、数ページ。 最もエレガントなソリューションではありません。



ステップ2. AndroidでWifiプロキシを設定するためのライブラリを開発します



そのため、著者は、多数のメソッドを持つかさばるクラスにこだわらないことにしました。 自由な時間があるので(私が参加したプロジェクトの資金を削減するために早急に休暇を取る)、なぜグローバルにタスクに取り組んでみませんか。



モジュールのアーキテクチャ


Androidのバージョンごとにさまざまな実装があり、プロキシ設定を変更し、 WifiConfigurationオブジェクトを操作するための単一のインターフェイスが必要です。 これらの要件を可能な限り満たそうとすると、最初の段階で炎症を起こした意識が次のようになりました。







上の写真に関する説明的解説
  • BaseWifiConfigurationクラスは、実際にはWifiConfigurationオブジェクトを格納し、 Contextを介して作成されたときに最新のネットワーク構成を取得する実装を含んでいます。
  • ProxyChangerインターフェイスは、それぞれ、プロキシネットワーク構成を操作するメソッドの可用性を保証します。
  • Reflectionを使用する必要があります。このためのメインメソッドは、頻繁に使用されるため、別のクラスに配置することが望ましいです。 したがって、 ReflectionHelperクラスを作成します。


Androidのさまざまなバージョンのクラスは、関心のあるネットワークのWifiConfigurationインスタンスに簡単にアクセスし、 作業を容易するためにBaseWifiConfigurationから継承され、 ProxyChangerで宣言されたメソッドの実装が必要です



PS
これが最良のアーキテクチャソリューションではないかもしれないとは言いません。改善の提案やコメントがあれば、コメントでお待ちください。



たとえば、ReflectionHelperに非常に興味があります。ご覧のとおり、abstractと宣言されています。これは、特定の実装が必要ないという理由で行われ、目的のメソッドへの構造化と簡単なアクセスにのみ使用されます。 このアプローチがどれほど正しいかはわかりません。したがって、この問題(または他の問題)についてコメントがあれば、それを聞いて非常に感謝します。


もちろん、これは一般的なフレームワークにすぎず、リファクタリングを数回繰り返した後、新しい詳細が得られます。



テスト


それで、アーキテクチャについて考えました。今度は数行のコードを書きます。 もちろん、労働者ではなく、プロジェクトをテストしています。



特定のapiのProxyChangerを選択する小さなクラスを作成します。プロキシ設定の変更に関して上記のオブジェクトを操作するクラスと、現在のネットワークの設定に関する情報を取得する別のクラスを作成します。



WifiProxyChangerTest.java
 @RunWith(AndroidJUnit4.class) public class WifiProxyChangerTest { @Rule public ExpectedException expectedException = ExpectedException.none(); @Rule public ActivityTestRule mActivityRule = new ActivityTestRule<>( MainActivity.class); Context context; @Before public void prepare() throws Exception { context = mActivityRule.getActivity(); ExceptionsPreparer.prepareExceptions(expectedException, context); } @Test public void testChangeWifiStaticProxySettings() throws Exception { String testIp = RandomValuesGenerator.randomIp(); int testPort = RandomValuesGenerator.randomPort(); WifiProxyChanger.changeWifiStaticProxySettings(testIp, testPort, context); assertEquals(testIp, WifiProxyInfo.getHost(context)); assertEquals(testPort, WifiProxyInfo.getPort(context)); } @Test public void testProxySettingsClear() throws Exception { String testIp = RandomValuesGenerator.randomIp(); int testPort = RandomValuesGenerator.randomPort(); WifiProxyChanger.changeWifiStaticProxySettings(testIp, testPort, context); WifiProxyChanger.clearProxySettings(context); assertEquals(ProxySettings.NONE, CurrentProxyChangerGetter .chooseProxyChangerForCurrentApi(context) .getProxySettings()); } @After public void learSettings() throws Exception { if (NetworkHelper.isWifiConnected(context) && ApiChecker.isSupportedApi()) WifiProxyChanger.clearProxySettings(context); } }
      
      







WifiProxyInfoTest.java
 @RunWith(AndroidJUnit4.class) public class WifiProxyInfoTest { @Rule public ExpectedException expectedException = ExpectedException.none(); @Rule public ActivityTestRule mActivityRule = new ActivityTestRule<>( MainActivity.class); Context context; @Before public void prepareAndPresetProxy() throws Exception { context = mActivityRule.getActivity(); ExceptionsPreparer.prepareExceptions(expectedException, context); if (ApiChecker.isSupportedApi()) { WifiProxyChanger.clearProxySettings(context); WifiProxyChanger.changeWifiStaticProxySettings("localhost", 3030, context); } } @Test public void testGetHost() throws Exception { assertEquals("localhost", WifiProxyInfo.getHost(context)); } @Test public void testGetPort() throws Exception { assertEquals(3030, WifiProxyInfo.getPort(context)); } @Test public void testGetProxySettings() throws Exception { assertEquals(ProxySettings.STATIC, WifiProxyInfo.getProxySettings(context)); } @After public void learSettings() throws Exception { if (NetworkHelper.isWifiConnected(context) && ApiChecker.isSupportedApi()) WifiProxyChanger.clearProxySettings(context); } }
      
      







CurrentProxyChangerGetterTest .java
 @RunWith(AndroidJUnit4.class) public class CurrentProxyChangerGetterTest { @Rule public ExpectedException expectedException = ExpectedException.none(); @Rule public ActivityTestRule mActivityRule = new ActivityTestRule<>( MainActivity.class); Context context; @Before public void prepare() throws Exception { context = mActivityRule.getActivity(); ExceptionsPreparer.prepareExceptions(expectedException, context); } @Test public void testChooseProxyChangerForCurrentApi() throws Exception { ProxyChanger proxyChanger = CurrentProxyChangerGetter.chooseProxyChangerForCurrentApi(context); WifiManager manager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); assertEquals(manager.getConnectionInfo().getNetworkId(), proxyChanger.getWifiConfiguration().networkId); if (ApiChecker.isJellyBeanOrKitkat()) { assertTrue(proxyChanger instanceof WifiConfigurationForApiFrom15To19); } else if (ApiChecker.isLolipop()) { assertTrue(proxyChanger instanceof WifiConfigurationForApiFrom21To22); } } }
      
      







ExceptionsPreparer.java
 public abstract class ExceptionsPreparer { public static void prepareExceptions(ExpectedException expectedException, Context context) throws Exception { if (!ApiChecker.isSupportedApi()) { expectedException.expect(ApiNotSupportedException.class); } else if (!NetworkHelper.isWifiConnected(context)) { expectedException.expect(NullWifiConfigurationException.class); } else if (!CurrentProxyChangerGetter.chooseProxyChangerForCurrentApi(context).isProxySetted()) { expectedException.expect(WifiProxyNotSettedException.class); } } }
      
      







私が言及しなかった奇妙なことがたくさんある場所を説明するコードにコメントしてください。
理にかなった質問を読んだ後、この「ProxySettings.STATIC」とは何か、それが責任を負うのはどこか、例外はどこから来たのか、これまで言及されていなかった、などだと思います。

残念ながら、残念ながら、初期バージョンはありませんでした。また、bitbucketには、リファクタリングのいくつかの反復に合格したテストクラスしかありません。



テストの実行は失敗しますが、今度はそれを修正する必要があります。



ステップ3.実装



最後に、何を書くべきかを考え、必要な準備をすべて行ったので、プロジェクトの実装を進めることができます。



パート1:準備作業とヘルパークラス


始めるために、約束されたReflectionのピンチに取りかかりましょう。


Reflection APIについて少し
リフレクションは、Java仮想マシンで実行されているアプリケーションの実行時の動作を検査または変更する機能を必要とするプログラムで一般的に使用されます。

Oracle Java Turtorial


クラスですが、それについて正確に何ができますか?



小さな例
小さなライブラリがあり、Webページを取得して保存するためのクラスがいくつかあるとします。 その場で接続して使用したいです。 これのために小さなクラスを書きましょう:



 public class LibLoader { //      ,   2   . URLClassLoader urlClassLoader; String page; LibLoader(File myJar) throws MalformedURLException { urlClassLoader = new URLClassLoader(new URL[]{myJar.toURL()}, this.getClass().getClassLoader()); } public void loadPage(URL url) throws Exception { Class classToLoad = Class.forName("com.company.HtmlPageGetter", true, urlClassLoader); Method method = classToLoad.getDeclaredMethod("getPageFromURL", URL.class); Object instance = classToLoad.newInstance(); Object result = method.invoke(instance, url); page = (String) result; } public String getCurrentPage() { return page; } public void saveCurrentPage(String name) throws Exception { List<String> content = new ArrayList<>(); content.add(page); Class classToLoad = Class.forName("com.company.HtmlPageSaver", true, urlClassLoader); Method method = classToLoad.getDeclaredMethod("savePageToFile", String.class, List.class); Object instance = classToLoad.newInstance(); method.invoke(instance, name, content); } }
      
      





今それを使用します:



  public static void main(String[] args) throws Exception { File lib = new File("htmlgetandsave.jar"); LibLoader libLoader = new LibLoader(lib); libLoader.loadPage(new URL("https://habrahabr.ru/post/69552/")); System.out.println(libLoader.getCurrentPage()); libLoader.saveCurrentPage("   -  reflection     "); }
      
      





結果を公開してお楽しみください:







さらに、ライブラリファイルの場所のみを知ることができ、その構造については何も知りません。Reflectionapiを使用すると、実行時にこの問題を直接調査し、その後使用できます。



ただし、私たちにとって重要なのは、とりわけReflectionのおかげで、プライベートのフィールドとメソッドにアクセスできることと、アノテーションhideを使用できることです。



そこで、上記のReflectionHelperを作成します。



ReflectionHelper.java
 public abstract class ReflectionHelper { /** * Used for getting public fields with @hide annotation */ public static Object getField(Object object, String name) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { Field field = object.getClass().getField(name); return field.get(object); } /** * Used for getting private fields */ public static Object getDeclaredField(Object object, String name) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { Field declaredField = object.getClass().getDeclaredField(name); declaredField.setAccessible(true); return declaredField.get(object); } /** * Used for setting private fields */ public static void setDeclaredField(Object object, String name, Object value) throws NoSuchFieldException, IllegalAccessException { Field declaredField = object.getClass().getDeclaredField(name); declaredField.setAccessible(true); declaredField.set(object, value); } /** * Used for setting Enum fields */ public static void setEnumField(Object object, String value, String name) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { Field field = object.getClass().getField(name); field.set(object, Enum.valueOf((Class<Enum>) field.getType(), value)); } /** * Used for simplifying process of invoking private method * Automatically detects args types and founds method to get and invoke */ public static Object getMethodAndInvokeIt(Object object, String methodName, Object... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Method method = object.getClass().getDeclaredMethod(methodName, parameterTypes(args)); method.setAccessible(true); return method.invoke(object, args); } private static Class[] parameterTypes(Object... args) { ArrayList<Class> classes = new ArrayList<>(); for (Object arg : args) { classes.add(arg.getClass()); } return classes.toArray(new Class[args.length]); } }
      
      





それにもかかわらず、たとえばgetFieldgetDeclaredFieldの違いや、どのケースを使用するかを簡単に忘れてしまう可能性があるため、メモを設定しました。



Reflectionのほとんどの作業は別のクラスに移動されました。残りの部分の実装を扱います。



3つのケースの例外を作成します。




さまざまなAPIでWifiConfiguration操作するサブクラスのベースとして機能するクラスを実装します。


BaseWifiConfiguration.java
 public class BaseWifiConfiguration { protected WifiConfiguration wifiConfiguration; protected BaseWifiConfiguration(WifiConfiguration wifiConfiguration) throws NullWifiConfigurationException { if (wifiConfiguration == null) throw new NullWifiConfigurationException(); this.wifiConfiguration = wifiConfiguration; } protected BaseWifiConfiguration(Context context) throws NullWifiConfigurationException { this(getCurrentWifiConfigurationFromContext(context)); } public WifiConfiguration getWifiConfiguration() { return wifiConfiguration; } private static WifiConfiguration getCurrentWifiConfigurationFromContext(Context context) { final WifiManager manager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); List<WifiConfiguration> wifiConfigurationList = manager.getConfiguredNetworks(); if (!manager.isWifiEnabled() || wifiConfigurationList == null || wifiConfigurationList.isEmpty()) return null; return findWifiConfigurationByNetworkId(wifiConfigurationList, manager.getConnectionInfo().getNetworkId()); } private static WifiConfiguration findWifiConfigurationByNetworkId(List<WifiConfiguration> wifiConfigurationList, int networkId) { for (WifiConfiguration wifiConf : wifiConfigurationList) { if (wifiConf.networkId == networkId) return wifiConf; } return null; } }
      
      







ProxyChangerインターフェースの発表


Proxychanger.java
 public interface ProxyChanger { void setProxySettings(ProxySettings proxySettings) throws NoSuchFieldException, IllegalAccessException; ProxySettings getProxySettings() throws NoSuchFieldException, IllegalAccessException; void setProxyHostAndPort(String host, int port) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException; String getProxyHost() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ApiNotSupportedException, NoSuchFieldException; int getProxyPort() throws ApiNotSupportedException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, NoSuchFieldException; boolean isProxySetted() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ApiNotSupportedException, NoSuchFieldException; WifiConfiguration getWifiConfiguration(); }
      
      





はい、Reflectionを使用する場合の例外リストは何かです。



すべてが好きですか? そして、いいえ、別の小さなサブアイテムがあります:



ProxySettings.java-それは何であり、なぜ必要なのですか?


これは、 AndroidライブラリのWifiConfigurationクラスにある列挙に類似しています。 STATICNONEなどを手動で登録するたびに登録するのではなく、作業を容易にするために作成します。



ProxySettings.java
 public enum ProxySettings { /* No proxy is to be used. Any existing proxy settings * should be cleared. */ NONE("NONE"), /* Use statically configured proxy. Configuration can be accessed * with httpProxy. */ STATIC("STATIC"), /* no proxy details are assigned, this is used to indicate * that any existing proxy settings should be retained */ UNASSIGNED("UNASSIGNED"), /* Use a Pac based proxy. */ PAC("PAC"); String value = ""; ProxySettings(String value) { this.value = value; } public String getValue() { return value; } }
      
      







パート2:特定のAPIにProxyChangerを実装するクラスを作成します


プロキシ設定を変更するコードを最終的に作成するときが来ました。 Reflectionを介してそれらに到達するさまざまな方法があることをすぐに言わなければなりません。WifiConfigurationクラスのメソッドを呼び出して、 実際にあるフィールドに到達し、 setDeclaredFieldを介して直接変更できます。



私は現在のネットワークで作業するための一部のみを書きました(これは著者が必要としたものです)、すなわち ただし、 Contextを介してクラスのインスタンスを作成すると、アーキテクチャにより、数行を追加するだけで、これらのクラスを任意のWifiConfigurationオブジェクトで動作するように適合させることができます。



キットカットとジェリービーン


手順1で説明したように、これらのAndroidバージョンでは、 WifiConfigurationにあるLinkPropertiesに格納されているProxyPropertiesオブジェクトが、プロキシ設定を格納します。 はい、はい、卵の中の針、アヒルの中の卵、ウサギの中のアヒルなど。



プロキシ設定を変更するには、必要なパラメーターを使用してProxyPropertiesの新しいインスタンスを作成し、既存のインスタンスをこのオブジェクトに置き換えてからProxySettingsを構成します。



ProxyPropertiesのインスタンスの作成は、別のクラスが担当します。



ProxyPropertiesConstructor.java
 public abstract class ProxyPropertiesConstructor { public static Object proxyProperties(String host, int port) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { return proxyProperties(host, port, null); } public static Object proxyProperties(String host, int port, String exclList) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException { return proxyPropertiesConstructor().newInstance(host, port, exclList); } private static Constructor proxyPropertiesConstructor() throws ClassNotFoundException, NoSuchMethodException { return Class.forName("android.net.ProxyProperties").getConstructor(String.class, int.class, String.class); } }
      
      







このオブジェクトの便利な作業のために、 ProxyPropertiesオブジェクトを含むコンテナークラスも作成し、メインフィールドへのアクセスを提供します(ホストとポートを介してすぐに作成できるようにします)。



ProxyPropertiesContainer.java
 public class ProxyPropertiesContainer { Object proxyProperties; ProxyPropertiesContainer(Object proxyProperties) { this.proxyProperties = proxyProperties; } ProxyPropertiesContainer(String host, int port) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { this(host, port, null); } ProxyPropertiesContainer(String host, int port, String exclList) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { this(ProxyPropertiesConstructor.proxyProperties(host, port, exclList)); } public String getHost() throws NoSuchFieldException, IllegalAccessException { return (String) ReflectionHelper.getDeclaredField(proxyProperties, "mHost"); } public int getPort() throws NoSuchFieldException, IllegalAccessException { return (int) ReflectionHelper.getDeclaredField(proxyProperties, "mPort"); } public String getExclusionList() throws NoSuchFieldException, IllegalAccessException { return (String) ReflectionHelper.getDeclaredField(proxyProperties, "mExclusionList"); } public Object getProxyProperties() { return proxyProperties; } }
      
      







次に、クラス自体の実装を記述します。



WifiConfigurationForApiFrom15To19.java
 public class WifiConfigurationForApiFrom15To19 extends BaseWifiConfiguration implements ProxyChanger { private ProxyPropertiesContainer proxyPropertiesContainer; public WifiConfigurationForApiFrom15To19(Context context) throws NoSuchFieldException, IllegalAccessException, NullWifiConfigurationException { super(context); this.proxyPropertiesContainer = new ProxyPropertiesContainer(getCurrentProxyProperties()); } public static WifiConfigurationForApiFrom15To19 createFromCurrentContext(Context context) throws NoSuchFieldException, IllegalAccessException, NullWifiConfigurationException { return new WifiConfigurationForApiFrom15To19(context); } @Override public void setProxySettings(ProxySettings proxySettings) throws NoSuchFieldException, IllegalAccessException { ReflectionHelper.setEnumField(wifiConfiguration, proxySettings.getValue(), "proxySettings"); } @Override public ProxySettings getProxySettings() throws NoSuchFieldException, IllegalAccessException { return ProxySettings.valueOf(String.valueOf(ReflectionHelper.getDeclaredField(wifiConfiguration, "proxySettings"))); } @Override public void setProxyHostAndPort(String host, int port) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { proxyPropertiesContainer = new ProxyPropertiesContainer(host, port); ReflectionHelper.getMethodAndInvokeIt( getLinkProperties(), "setHttpProxy", proxyPropertiesContainer.getProxyProperties()); } @Override public String getProxyHost() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ApiNotSupportedException, NoSuchFieldException { if (proxyPropertiesContainer == null) throw new WifiProxyNotSettedException(); return proxyPropertiesContainer.getHost(); } @Override public int getProxyPort() throws ApiNotSupportedException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, NoSuchFieldException { if (proxyPropertiesContainer == null) throw new WifiProxyNotSettedException(); return proxyPropertiesContainer.getPort(); } @Override public boolean isProxySetted() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ApiNotSupportedException, NoSuchFieldException { return !(proxyPropertiesContainer == null); } private LinkProperties getLinkProperties() throws NoSuchFieldException, IllegalAccessException { return (LinkProperties) ReflectionHelper.getField(wifiConfiguration, "linkProperties"); } private Object getCurrentProxyProperties() throws NoSuchFieldException, IllegalAccessException { return ReflectionHelper.getDeclaredField(getLinkProperties(), "mHttpProxy"); } }
      
      







このバージョンが終了したら、左:



ロリポップ


繰り返しになりますが、ステップ1を参照すると、このバージョンのApiのプロキシ設定はIpConfigurationに含まれるProxyInfoクラスにあり、 WifiConfigurationがその場所にあると結論付けることができます。 ProxySettings-移動され、上記のIpConfigurationに追加されました



指定されたパラメーターに従ってProxyInfoの新しいインスタンスを作成するクラスを作成しましょう。



ProxyInfoConstructor.java
 public abstract class ProxyInfoConstructor { public static ProxyInfo proxyInfo(String host, int port) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { return proxyInfo(host, port, null); } public static ProxyInfo proxyInfo(String host, int port, String exclude) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Object newProxyInfo = proxyInfoConstructor().newInstance(host, port, exclude); return (ProxyInfo) newProxyInfo; } private static Constructor proxyInfoConstructor() throws ClassNotFoundException, NoSuchMethodException { return Class.forName("android.net.ProxyInfo").getConstructor(String.class, int.class, String.class); } }
      
      







ご覧のとおり、ここではObject、つまりProxyInfoインスタンスをまだ返していません。さらに、このクラスにはgetHost メソッドgetPortメソッドもあることがわかり ます 。 前のケースでは、これを行うことができなかったため、 ProxyPropertiesクラスが非表示になったため、「シェル」を作成しました。



そして、実際には、別の実装のコード:



WifiConfigurationForApiFrom21To22.java
 public class WifiConfigurationForApiFrom21To22 extends BaseWifiConfiguration implements ProxyChanger { public WifiConfigurationForApiFrom21To22(Context context) throws NullWifiConfigurationException { super(context); } public static WifiConfigurationForApiFrom21To22 createFromCurrentContext(Context context) throws NullWifiConfigurationException { return new WifiConfigurationForApiFrom21To22(context); } @Override public ProxySettings getProxySettings() throws NoSuchFieldException, IllegalAccessException { return ProxySettings.valueOf(String.valueOf(ReflectionHelper.getDeclaredField(getIpConfigurationObject(), "proxySettings"))); } @Override public void setProxySettings(ProxySettings proxySettings) throws NoSuchFieldException, IllegalAccessException { ReflectionHelper.setEnumField(getIpConfigurationObject(), proxySettings.getValue(), "proxySettings"); } @Override public void setProxyHostAndPort(String host, int port) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { setProxyInfo(ProxyInfoConstructor.proxyInfo(host, port)); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public String getProxyHost() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ApiNotSupportedException { ProxyInfo info = getProxyInfo(); if (info == null) throw new WifiProxyNotSettedException(); return info.getHost(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public int getProxyPort() throws ApiNotSupportedException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { ProxyInfo info = getProxyInfo(); if (info == null) throw new WifiProxyNotSettedException(); return info.getPort(); } @Override public boolean isProxySetted() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ApiNotSupportedException, NoSuchFieldException { return !(getProxyInfo() == null); } private Object getIpConfigurationObject() throws NoSuchFieldException, IllegalAccessException { return ReflectionHelper.getDeclaredField(wifiConfiguration, "mIpConfiguration"); } private ProxyInfo getProxyInfo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { return (ProxyInfo) ReflectionHelper.getMethodAndInvokeIt(wifiConfiguration, "getHttpProxy"); } private void setProxyInfo(ProxyInfo proxyInfo) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { ReflectionHelper.getMethodAndInvokeIt(wifiConfiguration, "setHttpProxy", proxyInfo); } }
      
      







メインの実装では、これですべてです。フィニッシュラインまであと少しです。



ステップ4.事前起動



テストで前述したクラスを実装します(注:プロキシ設定はそれぞれIPとポートで実装され、ProxySettingsタイプSTATICです)。



CurrentProxyChangerGetter.java
 public abstract class CurrentProxyChangerGetter { public static ProxyChanger chooseProxyChangerForCurrentApi(Context context) throws ApiNotSupportedException, NoSuchFieldException, IllegalAccessException, NullWifiConfigurationException { if (ApiChecker.isJellyBeanOrKitkat()) { return WifiConfigurationForApiFrom15To19.createFromCurrentContext(context); } else if (ApiChecker.isLolipop()) { return WifiConfigurationForApiFrom21To22.createFromCurrentContext(context); } else { throw new ApiNotSupportedException(); } } }
      
      







WifiProxyChanger.java
 public abstract class WifiProxyChanger { public static void changeWifiStaticProxySettings(String host, int port, Context context) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException, ApiNotSupportedException, NullWifiConfigurationException { updateWifiWithNewConfiguration( getCurrentWifiConfiguretionWithUpdatedSettings(host, port, ProxySettings.STATIC, context), context); } public static void clearProxySettings(Context context) throws IllegalAccessException, ApiNotSupportedException, NoSuchFieldException, NullWifiConfigurationException, ClassNotFoundException, NoSuchMethodException, InstantiationException, InvocationTargetException { updateWifiWithNewConfiguration( getCurrentWifiConfiguretionWithUpdatedSettings("", 0, ProxySettings.NONE, context), context); } private static WifiConfiguration getCurrentWifiConfiguretionWithUpdatedSettings(String host, int port, ProxySettings proxySettings, Context context) throws ApiNotSupportedException, IllegalAccessException, NullWifiConfigurationException, NoSuchFieldException, ClassNotFoundException, NoSuchMethodException, InstantiationException, InvocationTargetException { ProxyChanger proxyChanger = CurrentProxyChangerGetter.chooseProxyChangerForCurrentApi(context); proxyChanger.setProxyHostAndPort(host, port); proxyChanger.setProxySettings(proxySettings); return proxyChanger.getWifiConfiguration(); } private static void updateWifiWithNewConfiguration(WifiConfiguration wifiConfiguration, Context context) { WifiManager currentWifiManager = NetworkHelper.getWifiManager(context); currentWifiManager.updateNetwork(wifiConfiguration); currentWifiManager.saveConfiguration(); currentWifiManager.reconnect(); } }
      
      







WifiProxyInfo.java
 public abstract class WifiProxyInfo { public static String getHost(Context context) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ApiNotSupportedException, NoSuchFieldException, NullWifiConfigurationException { return CurrentProxyChangerGetter.chooseProxyChangerForCurrentApi(context).getProxyHost(); } public static int getPort(Context context) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ApiNotSupportedException, NoSuchFieldException, NullWifiConfigurationException { return CurrentProxyChangerGetter.chooseProxyChangerForCurrentApi(context).getProxyPort(); } public static ProxySettings getProxySettings(Context context) throws ApiNotSupportedException, IllegalAccessException, NoSuchFieldException, NullWifiConfigurationException { return CurrentProxyChangerGetter.chooseProxyChangerForCurrentApi(context).getProxySettings(); } }
      
      







APIのバージョンを確認するためのヘルパークラスを実装します。



ApiChecker.java
 public abstract class ApiChecker { public static boolean isJellyBeanOrKitkat() { return Build.VERSION.SDK_INT > 14 && Build.VERSION.SDK_INT < 20; } public static boolean isLolipop() { return Build.VERSION.SDK_INT > 20 && Build.VERSION.SDK_INT < 23; } public static boolean isSupportedApi() { return isJellyBeanOrKitkat() || isLolipop(); } }
      
      







起動テスト



(申し訳ありませんが、この瞬間に見出しで強調することにしました)









...



シャンパン!ワイン!お祭り!拍手!クイーン-私たちは伴奏としてのチャンピオンです!



小さなコメント
, . , - , "!" 。 , 10 , , .


ステップ5.アクティビティのメリットをお楽しみください。



ライブラリをデフォルトで作成されたアプリケーションに接続し、結果を確認します。



MainActivity.java
 public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); changeProxySettings("myhost.com", 12345); } void changeProxySettings(String host, int port) { try { WifiProxyChanger.changeWifiStaticProxySettings(host, port, this); } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | InvocationTargetException | NoSuchFieldException | IllegalAccessException | NullWifiConfigurationException | ApiNotSupportedException e) { e.printStackTrace(); } } }
      
      







始めます。アプリケーション画面には、何も面白いものは表示されません。接続先のネットワークのWi-Fi設定にすぐに移動するからです。



結果。写真が大きすぎます。




結果が達成されました。



まとめ



現時点では、上記のバージョンのAndroidでWi-Fiネットワークのプロキシ設定を変更するために既に使用できる、機能する、簡単に拡張可能なライブラリがあります。



さらなる計画?はい、著者はそれらの全リストを持っています!





そして、もちろん、これはすべてとは程遠いものです。



脚本と著者によるコメントを投稿する
» , , , Bitbucket ( ).



» — , .



» , , — .


記事に関心をお寄せいただき、ありがとうございます。「サンドボックスに残してはいけないニックネーム」



All Articles