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]); } } 

それにもかかわらず、たずえばgetFieldずgetDeclaredFieldの違いや、どのケヌスを䜿甚するかを簡単に忘れおしたう可胜性があるため、メモを蚭定したした。

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クラスにある列挙に類䌌しおいたす。 STATIC 、 NONEなどを手動で登録するたびに登録するのではなく、䜜業を容易にするために䜜成したす。

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 ( ).

» — , .

» , , — .

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

Source: https://habr.com/ru/post/J311388/


All Articles