标签: Eclipse

java版本不匹配解决办法

java版本不匹配解决办法

问题描述:eclipse加载新的项目后报一个错误,具体描述如下:

Description Resource PathLocation

Type Java compiler level does notmatch the version of the installed Java project facet.webattemp

Unknown FacetedProject Problem (Java Version Mismatch)

问题分析:       java版本不匹配:Facted Project 中的Java 版本设定与项目的Java 版本设定不一致。

问题解决:

在当前项目上点右键,属性–Project Facets中,配置编译版本与java compiler的版本一致。

1、选中项目后按下alt+enter组合键或者右键Project | Properties | Java Compiler(type filter text输入compiler可快速定位),如下图所示:

%title插图%num

2、修改Project Facets的Java值,使之和Compiler compliance level相同:打开方式参考1,如下图:

%title插图%num

总结:

刚开始用的是eclipse比较老的一个版本,常用的文件定位的插件装上就是识别不出来,换了一个高版本的eclipse加载项目后就报出上面的错误,因为是*次见到这个错误,以为是用的这个项目不支持高版本的eclipse呢,头说不行就用那个老版本得了,不要在这个上浪费时间,可是提高班培养出来的习惯,这样感觉就是不爽,还是想查一下再说,上网一查,这样的问题很多,当然很快就找到了解决的办法,工欲善其事,必先利其器,有了好的工具开发的时候心情好多了,自然效率也就提高了。

打开eclipse出现Incompatible JVM解决方法

安装了oracle10g的客户端后,eclipse打不开了。所以检查了一下,发现是以下原因。
运行eclipse出现以下错误:

Incompatible JVM

Version 1.3.1_01 of the JVM is not suitable for this product.Version:1.4.1 or greater is required.


Version 1.4.1_02 of the JVM is not suitable for this product.Version:1.5 or greater is required.

%title插图%num
一,系统上的JAVA JDK版本过低,去装高版本的JDK.

二,这个是*有可能是装了oracle了,因为oracle是使用1.3JVM的而eclipse是需要1.4或1.5以上JVM的。

解决方法:就是在环境变量中把JDK的的路径放到Oracle JDK的前面就可以解决

————————————————

eclipse中调试时无法进入jdk源码

转载自:https://blog.csdn.net/u013352983/article/details/50633637

亲试有效

今天在eclipse中进行断点调试时,无法进入jdk源码中跟踪代码,eclipse默认是使用的java环境是JRE(Java Runtime Environment 即java运行时环境)环境,jre环境是不支持调试的;需要将eclipse的环境换成JDK的。查看eclipse运行环境方法如下: window  –>  preference  –>  java  –>  Installed JREs –> 右侧 会看到 eclipse的java环境了。

既然JRE不允许调试那就将eclipse的环境换成jdk的配置如下:

*步:找到eclipse环境如下图:

%title插图%num%title插图%num

 

 

第二步:点击add找到本地安装jdk的目录

点击add按钮之后如图:

%title插图%num%title插图%num

点击Next:如图:

%title插图%num%title插图%num

点击Directory….选择jdk的安装文件(是jdk不是jre)选择完成后点击Finish,设置jdk为默认环境如图:

%title插图%num

第三步:将要调试的工程的引用的环境换成jdk的(即  Buil Path)如图:

%title插图%num

现在可以进行jdk源码的调试和跟踪了

eclipse使用maven新建类目录时,提示The folder is already a source folder

 

我们有时候新建一个webapp的maven项目时,生成的目录结构是这样子的:

%title插图%num

缺少maven规范必须的src/main/java 和 src/test/java

但是当我们新建这两个目录时,却报The folder is already a source folder.这个错误,错误意思是这个目录已经存在了!

这个目录确实存在,只是missing了(不知这样说对不对)

解决办法:

右键项目 build path->configure build path

%title插图%num

 

把这两个目录删了,然后自己新建即可!)

Eclipse中设置作者日期等Java注释模板

转载自:https://jingyan.baidu.com/article/1612d500856cb1e20e1eeed5.html

Eclipse作为JavaIDE(Integrated Development Environment,集成开发环境),可以通过设置自动添加Javadoc注释信息,如@author 作者名、@version 版本标识、@date 日期等,在创建类或新增方法时会自动添加注释信息。关于java如何生成javadoc文档可参考下文。下面将会为大家介绍如何在Eclipse中设置Java注释模板。

4Eclipse规范注释及注释文档的生成

工具/原料

  • Eclipse Oxygen Release (4.7.0)

方法/步骤

  1. 1

    首先介绍几个常用的注解:

    @author 作者名

    @date 日期

    @version 版本标识

    @parameter 参数及其意义

    @return 返回值

    @throws 异常类及抛出条件

    @deprecated 引起不推荐使用的警告

    @override 重写

    这个注解我们在java代码中经常可以看到。

  2. 2

    设置注释模板的步骤:点击菜单栏上的Window –>Preferences–>Java–>Code Style –>Code Templates,对右侧Comments选项中具体的注释信息进行编辑即可。可勾选上自动添加注释信息,在生成java文件时便会自动生成注释,当然也可以手动插入注释。设置的界面如下:

    Eclipse中设置作者日期等Java注释模板

  3. 3

    点击Edit按钮,进入编辑页面,全部编辑完成后点击“Apply And Close”即可设置完成,并关闭设置页面。

    Eclipse中设置作者日期等Java注释模板

    Eclipse中设置作者日期等Java注释模板

  4. 4

    下面介绍具体的Comment如何设置:

    1. 点击Comments下的Files可对整个Java文件进行注释:包括公司名称,版权所属,作者信息,日期等。

     

    /**

    * <p>Title: ${file_name}</p>

    * <p>Description: </p>

    * <p>Copyright: Copyright (c) 2017</p>

    * <p>Company: www.baidudu.com</p>

    * @author shenlan

    * @date ${date}

    * @version 1.0

    */

  5. 5

    2. 点击Types对类进行注释:

     

    /**

    * <p>Title: ${type_name}</p>

    * <p>Description: </p>

    * @author shenlan

    * @date ${date}

    */

     

    3. 点击Fields对字段进行注释:

     

    /** ${field}*/

     

    4. 点击Constructors对构造方法进行注释:

     

    /**

    * <p>Title: </p>

    * <p>Description: </p>

    * ${tags}

    */

     

    5. 点击Methods对方法进行注释:

     

    /**

     

    * <p>Title: ${enclosing_method}</p>

     

    * <p>Description: </p>

     

    * ${tags}

     

    */

  6. 6

    6. 点击Overriding Methods对重写方法进行注释:

     

    /* (non-Javadoc)

     

    * <p>Title: ${enclosing_method}</p>

     

    * <p>Description: </p>

     

    * ${tags}

     

    * ${see_to_overridden}

     

    */

     

    7. Delegate methods对代表方法进行注释:

     

    /**

    * ${tags}

    * ${see_to_target}

    */

     

    8. Getters对get方法进行注释:

     

    /**

     

    * @return the ${bare_field_name}

     

    */

     

    9. Setters对set方法进行注释:

     

    /**

     

    * @param ${param} the ${bare_field_name} to set

     

    */

  7. 7

    注释模板的导入和导出:点击Import和Export按钮即可。

    Eclipse中设置作者日期等Java注释模板

    END

Java注释模板的使用

  1. 1

    在设置模板时如果勾选了自动添加注释信息,则在创建Java文件时会自动生成文档和类的注释信息,若没有勾选,按Shift+Alt+J快捷键也可生成。

    Eclipse中设置作者日期等Java注释模板

  2. 2

    在对类中的方法进行注释是:在方法上方输入/** 后点击回车,即可生成方法注释;或将光标放在方法名上,按住Shift+Alt+J快捷键也可;或在方法上右击,source》Generate Element Comment也可生成注释。

    Eclipse中设置作者日期等Java注释模板

    END

注意事项

  • 注释模板设置完一定要点击Appy保存

Android安全攻防战,反编译与混淆技术完全解析(下)

混淆

本篇文章中介绍的混淆技术都是基于Android Studio的,Eclipse的用法也基本类似,但是就不再为Eclipse专门做讲解了。
我们要建立一个Android Studio项目,并在项目中添加一些能够帮助我们理解混淆知识的代码。这里我准备好了一些,我们将它们添加到Android Studio当中。
首先新建一个MyFragment类,代码如下所示:

  1. public class MyFragment extends Fragment {
  2. private String toastTip = “toast in MyFragment”;
  3. @Nullable
  4. @Override
  5. public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
  6. View view = inflater.inflate(R.layout.fragment_layout, container, false);
  7. methodWithGlobalVariable();
  8. methodWithLocalVariable();
  9. return view;
  10. }
  11. public void methodWithGlobalVariable() {
  12. Toast.makeText(getActivity(), toastTip, Toast.LENGTH_SHORT).show();
  13. }
  14. public void methodWithLocalVariable() {
  15. String logMessage = “log in MyFragment”;
  16. logMessage = logMessage.toLowerCase();
  17. System.out.println(logMessage);
  18. }
  19. }

 

可以看到,MyFragment是继承自Fragment的,并且MyFragment中有一个全局变量。onCreateView()方法是Fragment的生命周期函数,这个不用多说,在onCreateView()方法中又调用了methodWithGlobalVariable()和methodWithLocalVariable()方法,这两个方法的内部分别引用了一个全局变量和一个局部变量。
接下来新建一个Utils类,代码如下所示:

  1. public class Utils {
  2. public void methodNormal() {
  3. String logMessage = “this is normal method”;
  4. logMessage = logMessage.toLowerCase();
  5. System.out.println(logMessage);
  6. }
  7. public void methodUnused() {
  8. String logMessage = “this is unused method”;
  9. logMessage = logMessage.toLowerCase();
  10. System.out.println(logMessage);
  11. }
  12. }

 

这是一个非常普通的工具类,没有任何继承关系。Utils中有两个方法methodNormal()和methodUnused(),它们的内部逻辑都是一样的,唯一的据别是稍后methodNormal()方法会被调用,而methodUnused()方法不会被调用。
下面再新建一个NativeUtils类,代码如下所示:

  1. public class NativeUtils {
  2. public static native void methodNative();
  3. public static void methodNotNative() {
  4. String logMessage = “this is not native method”;
  5. logMessage = logMessage.toLowerCase();
  6. System.out.println(logMessage);
  7. }
  8. }

 

这个类中同样有两个方法,一个是native方法,一个是非native方法。
*后,修改MainActivity中的代码,如下所示:

  1. public class MainActivity extends AppCompatActivity {
  2. private String toastTip = “toast in MainActivity”;
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. getSupportFragmentManager().beginTransaction().add(R.id.fragment, new MyFragment()).commit();
  8. Button button = (Button) findViewById(R.id.button);
  9. button.setOnClickListener(new View.OnClickListener() {
  10. @Override
  11. public void onClick(View v) {
  12. methodWithGlobalVariable();
  13. methodWithLocalVariable();
  14. Utils utils = new Utils();
  15. utils.methodNormal();
  16. NativeUtils.methodNative();
  17. NativeUtils.methodNotNative();
  18. Connector.getDatabase();
  19. }
  20. });
  21. }
  22. public void methodWithGlobalVariable() {
  23. Toast.makeText(MainActivity.this, toastTip, Toast.LENGTH_SHORT).show();
  24. }
  25. public void methodWithLocalVariable() {
  26. String logMessage = “log in MainActivity”;
  27. logMessage = logMessage.toLowerCase();
  28. System.out.println(logMessage);
  29. }
  30. }

 

可以看到,MainActivity和MyFragment类似,也是定义了methodWithGlobalVariable()和methodWithLocalVariable()这两个方法,然后MainActivity对MyFragment进行了添加,并在Button的点击事件里面调用了自身的、Utils的、以及NativeUtils中的方法。注意调用native方法需要有相应的so库实现,不然的话就会报UnsatisefiedLinkError,不过这里其实我也并没有真正的so库实现,只是演示一下让大家看看混淆结果。点击事件的*后一行调用的是LitePal中的方法,因为我们还要测试一下引用第三方Jar包的场景,到LitePal项目的主页去下载*新的Jar包,然后放到libs目录下即可。
完整的build.gradle内容如下所示:

  1. apply plugin: ‘com.android.application’
  2. android {
  3. compileSdkVersion 23
  4. buildToolsVersion “23.0.2”
  5. defaultConfig {
  6. applicationId “com.example.guolin.androidtest”
  7. minSdkVersion 15
  8. targetSdkVersion 23
  9. versionCode 1
  10. versionName “1.0”
  11. }
  12. buildTypes {
  13. release {
  14. minifyEnabled false
  15. proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’
  16. }
  17. }
  18. }
  19. dependencies {
  20. compile fileTree(dir: ‘libs’, include: [‘*.jar’])
  21. compile ‘com.android.support:appcompat-v7:23.2.0’
  22. }

 

好的,到这里准备工作就已经基本完成了,接下来我们就开始对代码进行混淆吧。

混淆APK

在Android Studio当中混淆APK实在是太简单了,借助SDK中自带的Proguard工具,只需要修改build.gradle中的一行配置即可。可以看到,现在build.gradle中minifyEnabled的值是false,这里我们只需要把值改成true,打出来的APK包就会是混淆过的了。如下所示:

  1. release {
  2. minifyEnabled true
  3. proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’
  4. }

 

其中minifyEnabled用于设置是否启用混淆,proguardFiles用于选定混淆配置文件。注意这里是在release闭包内进行配置的,因此只有打出正式版的APK才会进行混淆,Debug版的APK是不会混淆的。当然这也是非常合理的,因为Debug版的APK文件我们只会用来内部测试,不用担心被人破解。
那么现在我们来打一个正式版的APK文件,在Android Studio导航栏中点击Build->Generate Signed APK,然后选择签名文件并输入密码,如果没有签名文件就创建一个,*终点击Finish完成打包,生成的APK文件会自动存放在app目录下。除此之外也可以在build.gradle文件当中添加签名文件配置,然后通过gradlew assembleRelease来打出一个正式版的APK文件,这种方式APK文件会自动存放在app/build/outputs/apk目录下。
那么现在已经得到了APK文件,接下来就用上篇文章中学到的反编译知识来对这个文件进行反编译吧,结果如下图所示:

%title插图%num

很明显可以看出,我们的代码混淆功能已经生效了。
下面我们尝试来阅读一下这个混淆过后的代码,*顶层的包名结构主要分为三部分,*个a.a已经被混淆的面目全非了,但是可以猜测出这个包下是LitePal的所有代码。第二个android.support可以猜测出是我们引用的android support库的代码,第三个com.example.guolin.androidtest则很明显就是我们项目的主包名了,下面将里面所有的类一个个打开看一下。
首先MainActivity中的代码如下所示:

%title插图%num

可以看到,MainActivity的类名是没有混淆的,onCreate()方法也没有被混淆,但是我们定义的方法、全局变量、局部变量都被混淆了。
再来打开下一个类NativeUtils,如下所示:

%title插图%num

NativeUtils的类名没有被混淆,其中声明成native的方法也没有被混淆,但是非native方法的方法名和局部变量都被混淆了。
接下来是a类的代码,如下所示:

%title插图%num

很明显,这个是MainActivity中按钮点击事件的匿名类,在onClick()方法中的调用代码虽然都被混淆了,但是调用顺序是不会改变的,对照源代码就可以看出哪一行是调用的什么方法了。
再接下来是b类,代码如下所示:

%title插图%num

虽然被混淆的很严重,但是我们还是可以看出这个是MyFragment类。其中所有的方法名、全局变量、局部变量都被混淆了。
*后再来看下c类,代码如下所示:

%title插图%num

c类中只有一个a方法,从字符串的内容我们可以看出,这个是Utils类中的methodNormal()方法。
我为什么要创建这样的一个项目呢?因为从这几个类当中很能看出一些问题,接下来我们就分析一下上面的混淆结果。
首先像Utils这样的普通类肯定是会被混淆的,不管是类名、方法名还是变量都不会放过。除了混淆之外Utils类还说明了一个问题,就是minifyEnabled会对资源进行压缩,因为Utils类中我们明明定义了两个方法,但是反编译之后就只剩一个方法了,因为另外一个方法没有被调用,所以认为是多余的代码,在打包的时候就给移除掉了。不仅仅是代码,没有被调用的资源同样也会被移除掉,因此minifyEnabled除了混淆代码之外,还可以起到压缩APK包的作用。
接着看一下MyFragment,这个类也是混淆的比较彻底的,基本没有任何保留。那有些朋友可能会有疑问,Fragment怎么说也算是系统组件吧,就算普通方法名被混淆了,至少像onCreateView()这样的生命周期方法不应该被混淆吧?其实生命周期方法会不会被混淆和我们使用Fragment的方式有关,比如在本项目中,我使用的是android.support.v4.app.Fragment,support-v4包下的,就连Fragment的源码都被一起混淆了,因此生命周期方法当然也不例外了。但如果你使用的是android.app.Fragment,这就是调用手机系统中预编译好的代码了,很明显我们的混淆无法影响到系统内置的代码,因此这种情况下onCreateView()方法名就不会被混淆,但其它的方法以及变量仍然会被混淆。
接下来看一下MainActivity,同样也是系统组件之一,但MainActivity的保留程度就比MyFragment好多了,至少像类名、生命周期方法名都没有被混淆,这是为什么呢?根据我亲身测试得出结论,凡是需要在AndroidManifest.xml中去注册的所有类的类名以及从父类重写的方法名都自动不会被混淆。因此,除了Activity之外,这份规则同样也适用于Service、BroadcastReceiver和ContentProvider。
*后看一下NativeUtils类,这个类的类名也没有被混淆,这是由于它有一个声明成native的方法。只要一个类中有存在native方法,它的类名就不会被混淆,native方法的方法名也不会被混淆,因为C++代码要通过包名+类名+方法名来进行交互。 但是类中的别的代码还是会被混淆的。
除此之外,第三方的Jar包都是会被混淆的,LitePal不管是包名还是类名还是方法名都被完完全全混淆掉了。
这些就是Android Studio打正式APK时默认的混淆规则。
那么这些混淆规则是在哪里定义的呢?其实就是刚才在build.gradle的release闭包下配置的proguard-android.txt文件,这个文件存放于<Android SDK>/tools/proguard目录下,我们打开来看一下:

 

  1. # This is a configuration file for ProGuard.
  2. # http://proguard.sourceforge.net/index.html#manual/usage.html
  3. -dontusemixedcaseclassnames
  4. -dontskipnonpubliclibraryclasses
  5. -verbose
  6. # Optimization is turned off by default. Dex does not like code run
  7. # through the ProGuard optimize and preverify steps (and performs some
  8. # of these optimizations on its own).
  9. -dontoptimize
  10. -dontpreverify
  11. # Note that if you want to enable optimization, you cannot just
  12. # include optimization flags in your own project configuration file;
  13. # instead you will need to point to the
  14. # “proguard-android-optimize.txt” file instead of this one from your
  15. # project.properties file.
  16. -keepattributes *Annotation*
  17. -keep public class com.google.vending.licensing.ILicensingService
  18. -keep public class com.android.vending.licensing.ILicensingService
  19. # For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
  20. -keepclasseswithmembernames class * {
  21. native <methods>;
  22. }
  23. # keep setters in Views so that animations can still work.
  24. # see http://proguard.sourceforge.net/manual/examples.html#beans
  25. -keepclassmembers public class * extends android.view.View {
  26. void set*(***);
  27. *** get*();
  28. }
  29. # We want to keep methods in Activity that could be used in the XML attribute onClick
  30. -keepclassmembers class * extends android.app.Activity {
  31. public void *(android.view.View);
  32. }
  33. # For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
  34. -keepclassmembers enum * {
  35. public static **[] values();
  36. public static ** valueOf(java.lang.String);
  37. }
  38. -keepclassmembers class * implements android.os.Parcelable {
  39. public static final android.os.Parcelable$Creator CREATOR;
  40. }
  41. -keepclassmembers class **.R$* {
  42. public static <fields>;
  43. }
  44. # The support library contains references to newer platform versions.
  45. # Dont warn about those in case this app is linking against an older
  46. # platform version. We know about them, and they are safe.
  47. -dontwarn android.support.**

 

这个就是默认的混淆配置文件了,我们来一起逐行阅读一下。
-dontusemixedcaseclassnames 表示混淆时不使用大小写混合类名。
-dontskipnonpubliclibraryclasses 表示不跳过library中的非public的类。
-verbose 表示打印混淆的详细信息。
-dontoptimize 表示不进行优化,建议使用此选项,因为根据proguard-android-optimize.txt中的描述,优化可能会造成一些潜在风险,不能保证在所有版本的Dalvik上都正常运行。
-dontpreverify 表示不进行预校验。这个预校验是作用在Java平台上的,Android平台上不需要这项功能,去掉之后还可以加快混淆速度。
-keepattributes *Annotation* 表示对注解中的参数进行保留。

  1. -keep public class com.google.vending.licensing.ILicensingService
  2. -keep public class com.android.vending.licensing.ILicensingService

 

表示不混淆上述声明的两个类,这两个类我们基本也用不上,是接入Google原生的一些服务时使用的。

  1. -keepclasseswithmembernames class * {
  2. native <methods>;
  3. }

 

表示不混淆任何包含native方法的类的类名以及native方法名,这个和我们刚才验证的结果是一致的。

  1. -keepclassmembers public class * extends android.view.View {
  2. void set*(***);
  3. *** get*();
  4. }

 

表示不混淆任何一个View中的setXxx()和getXxx()方法,因为属性动画需要有相应的setter和getter的方法实现,混淆了就无法工作了。

  1. -keepclassmembers class * extends android.app.Activity {
  2. public void *(android.view.View);
  3. }

 

表示不混淆Activity中参数是View的方法,因为有这样一种用法,在XML中配置android:onClick=”buttonClick”属性,当用户点击该按钮时就会调用Activity中的buttonClick(View view)方法,如果这个方法被混淆的话就找不到了。

  1. -keepclassmembers enum * {
  2. public static **[] values();
  3. public static ** valueOf(java.lang.String);
  4. }

 

表示不混淆枚举中的values()和valueOf()方法,枚举我用的非常少,这个就不评论了。

  1. -keepclassmembers class * implements android.os.Parcelable {
  2. public static final android.os.Parcelable$Creator CREATOR;
  3. }

 

表示不混淆Parcelable实现类中的CREATOR字段,毫无疑问,CREATOR字段是*对不能改变的,包括大小写都不能变,不然整个Parcelable工作机制都会失败。

  1. -keepclassmembers class **.R$* {
  2. public static <fields>;
  3. }

 

表示不混淆R文件中的所有静态字段,我们都知道R文件是通过字段来记录每个资源的id的,字段名要是被混淆了,id也就找不着了。
-dontwarn android.support.** 表示对android.support包下的代码不警告,因为support包中有很多代码都是在高版本中使用的,如果我们的项目指定的版本比较低在打包时就会给予警告。不过support包中所有的代码都在版本兼容性上做足了判断,因此不用担心代码会出问题,所以直接忽略警告就可以了。
好了,这就是proguard-android.txt文件中所有默认的配置,而我们混淆代码也是按照这些配置的规则来进行混淆的。经过我上面的讲解之后,相信大家对这些配置的内容基本都能理解了。不过proguard语法中还真有几处非常难理解的地方,我自己也是研究了好久才搞明白,下面和大家分享一下这些难懂的语法部分。
proguard中一共有三组六个keep关键字,很多人搞不清楚它们的区别,这里我们通过一个表格来直观地看下:

关键字 描述
keep 保留类和类中的成员,防止它们被混淆或移除。
keepnames 保留类和类中的成员,防止它们被混淆,但当成员没有被引用时会被移除。
keepclassmembers 只保留类中的成员,防止它们被混淆或移除。
keepclassmembernames 只保留类中的成员,防止它们被混淆,但当成员没有被引用时会被移除。
keepclasseswithmembers 保留类和类中的成员,防止它们被混淆或移除,前提是指名的类中的成员必须存在,如果不存在则还是会混淆。
keepclasseswithmembernames 保留类和类中的成员,防止它们被混淆,但当成员没有被引用时会被移除,前提是指名的类中的成员必须存在,如果不存在则还是会混淆。

除此之外,proguard中的通配符也比较让人难懂,proguard-android.txt中就使用到了很多通配符,我们来看一下它们之间的区别:

通配符 描述
<field> 匹配类中的所有字段
<method> 匹配类中的所有方法
<init> 匹配类中的所有构造函数
* 匹配任意长度字符,但不含包名分隔符(.)。比如说我们的完整类名是com.example.test.MyActivity,使用com.*,或者com.exmaple.*都是无法匹配的,因为*无法匹配包名中的分隔符,正确的匹配方式是com.exmaple.*.*,或者com.exmaple.test.*,这些都是可以的。但如果你不写任何其它内容,只有一个*,那就表示匹配所有的东西。
** 匹配任意长度字符,并且包含包名分隔符(.)。比如proguard-android.txt中使用的-dontwarn android.support.**就可以匹配android.support包下的所有内容,包括任意长度的子包。
*** 匹配任意参数类型。比如void set*(***)就能匹配任意传入的参数类型,*** get*()就能匹配任意返回值的类型。
匹配任意长度的任意类型参数。比如void test(…)就能匹配任意void test(String a)或者是void test(int a, String b)这些方法。

虽说上面表格已经解释的很详细了,但是很多人对于keep和keepclasseswithmembers这两个关键字的区别还是搞不懂。确实,它们之间用法有点太像了,我做了很多次试验它们的结果都是相同的。其实唯一的区别就在于类中声明的成员存不存在,我们还是通过一个例子来直接地看一下,先看keepclasseswithmember关键字:

  1. -keepclasseswithmember class * {
  2. native <methods>;
  3. }

 

这段代码的意思其实很明显,就是保留所有含有native方法的类的类名和native方法名,而如果某个类中没有含有native方法,那就还是会被混淆。
但是如果改成keep关键字,结果会完全不一样:

  1. -keep class * {
  2. native <methods>;
  3. }

 

使用keep关键字后,你会发现代码中所有类的类名都不会被混淆了,因为keep关键字看到class *就认为应该将所有类名进行保留,而不会关心该类中是否含有native方法。当然这样写只会保证类名不会被混淆,类中的成员还是会被混淆的。
比较难懂的用法大概就这些吧,掌握了这些内容之后我们就能继续前进了。
回到Android Studio项目当中,刚才打出的APK虽然已经成功混淆了,但是混淆的规则都是按照proguard-android.txt中默认的规则来的,当然我们也可以修改proguard-android.txt中的规则,但是直接在proguard-android.txt中修改会对我们本机上所有项目的混淆规则都生效,那么有没有什么办法只针对当前项目的混淆规则做修改呢?当然是有办法的了,你会发现任何一个Android Studio项目在app模块目录下都有一个proguard-rules.pro文件,这个文件就是用于让我们编写只适用于当前项目的混淆规则的,那么接下来我们就利用刚才学到的所有知识来对混淆规则做修改吧。
这里我们先列出来要实现的目标:

  • 对MyFragment类进行完全保留,不混淆其类名、方法名、以及变量名。
  • 对Utils类中的未调用方法进行保留,防止其被移除掉。
  • 对第三方库进行保留,不混淆android-support库,以及LitePal库中的代码。

下面我们就来逐一实现这些目标。
首先要对MyFragment类进行完全保留可以使用keep关键字,keep后声明完整的类名,然后保留类中的所有内容可以使用*通配符实现,如下所示:

  1. -keep class com.example.guolin.androidtest.MyFragment {
  2. *;
  3. }

 

然后保留Utils类中的未调用方法可以使用keepclassmembers关键字,后跟Utils完整类名,然后在内部声明未调用的方法,如下所示:

  1. -keepclassmembers class com.example.guolin.androidtest.Utils {
  2. public void methodUnused();
  3. }

 

*后不要混淆第三方库,目前我们使用了两种方式来引入第三方库,一种是通过本地jar包引入的,一种是通过remote引入的,其实这两种方式没什么区别,要保留代码都可以使用**这种通配符来实现,如下所示:

  1. -keep class org.litepal.** {
  2. *;
  3. }
  4. -keep class android.support.** {
  5. *;
  6. }

 

所有内容都在这里了,现在我们重新打一个正式版的APK文件,然后再反编译看看效果:

%title插图%num

可以看到,现在android-support包中所有代码都被保留下来了,不管是包名、类名、还是方法名都没有被混淆。LitePal中的代码也是同样的情况:

%title插图%num

再来看下MyFragment中的代码,如下所示:

%title插图%num

可以看到,MyFragment中的代码也没有被混淆,按照我们的要求被完全保留下来了。
*后再来看一下Utils类中的代码:

%title插图%num

很明显,Utils类并没有被完全保留下来,类名还是被混淆了,methodNormal()方法也被混淆了,但是methodUnused()没有被混淆,当然也没有被移除,因为我们的混淆配置生效了。
经过这些例子的演示,相信大家已经对Proguard的用法有了相当不错的理解了,那么根据自己的业务需求来去编写混淆配置相信也不是什么难事了吧?
Progaurd的使用非常灵活,基本上能够覆盖你所能想到的所有业务逻辑。这里再举个例子,之前一直有人问我使用LitePal时的混淆配置怎么写,其实真的很简单,LitePal作为开源库并不需要混淆,上面的配置已经演示了如何不混淆LitePal代码,然后所有代码中的Model是需要进行反射的,也不能混淆,那么只需要这样写就行了:

 

  1. -keep class * extends org.litepal.crud.DataSupport {
  2. *;
  3. }

 

因为LitePal中所有的Model都是应该继承DataSupport类的,所以这里我们将所有继承自DataSupport的类都进行保留就可以了。
关于混淆APK的用法就讲这么多,如果你还想继续了解关于Proguard的更多用法,可以参考官方文档:http://proguard.sourceforge.net/index.html#manual/usage.html

混淆Jar

在本篇文章的第二部分我想讲一讲混淆Jar包的内容,因为APK不一定是我们交付的唯一产品。就比如说我自己,我在公司是负责写SDK的,对于我来说交付出去的产品就是Jar包,而如果Jar包不混淆的话将会很容易就被别人反编译出来,从而泄漏程序逻辑。
实际上Android对混淆Jar包的支持在很早之前就有了,不管你使用多老版本的SDK,都能在 <Android SDK>/tools目录下找到proguard这个文件夹。然后打开里面的bin目录,你会看到如下文件:

%title插图%num

其中proguardgui.bat文件是允许我们以图形化的方式来对Jar包进行混淆的一个工具,今天我们就来讲解一下这个工具的用法。
在开始讲解这个工具之前,首先我们需要先准备一个Jar包,当然你从哪里搞到一个Jar包都是可以的,不过这里为了和刚才的混淆逻辑统一,我们就把本篇文章中的项目代码打成一个Jar包吧。
Eclipse中导出Jar包的方法非常简单,相信所有人都会,可是Android Studio当中就比较让人头疼了,因为Android Studio并没有提供一个专门用于导出Jar包的工具,因此我们只能自己动手了。
我们需要知道,任何一个Android Studio项目,只要编译成功之后就会在项目模块的build/intermediates/classes/debug目录下生成代码编译过后的class文件,因此只需通过打包命令将这些class文件打包成Jar包就行了,打开cmd,切换到项目的根目录,然后输入如下命令:

 

jar -cvf androidtest.jar -C app/build/intermediates/classes/debug .

 

在项目的根目录下就会生成androidtest.jar这个文件,这样我们就把Jar包准备好了。
现在双击proguardgui.bat打开混淆工具,如果是Mac或Ubuntu系统则使用sh proguardgui.sh命令打开混淆工具,界面如下图所示:

%title插图%num

其实从主界面上我们就能看出,这个Proguard工具支持Shrinking、Optimization、Obfuscation、Preverification四项操作,在左侧的侧边栏上也能看到相应的这些选项。Proguard的工作机制仍然还是要依赖于配置文件,当然我们也可以通过proguardgui工具来生成配置文件,不过由于配置选项太多了,每个都去一一设置太复杂,而且大多数还都是我们用不到的配置。因此*简单的方式就是直接拿现有的配置文件,然后再做些修改就行了。
那么我们从<Android SDK>/tools/proguard目录下将proguard-android.txt文件复制一份出来,然后点击主界面上的Load configuration按钮来加载复制出来的这份proguard-android.txt文件,完成后点击Next将进入Input/Output界面。
Input/Output界面是用于导入要混淆的Jar包、配置混淆后文件的输出路径、以及导入该Jar包所依赖的所有其它Jar包的。我们要混淆的当然就是androidtest.jar这个文件,那么这个Jar包又依赖了哪些Jar包呢?这里就需要整理一下了。

 

  • 首先我们写的都是Java代码,Java代码的运行要基于Jre基础之上,没有Jre计算机将无法识别Java的语法,因此*个要依赖的就是Jre的rt.jar。
  • 然后由于我们导出的Jar包中有Android相关的代码,比如Activity、Fragment等,因此还需要添加Android的编译库,android.jar。
  • 除此之外,我们使用的AppCompatActivity和Fragment分别来自于appcompat-v7包和support-v4包,那么这两个Jar包也是需要引入的。
  • *后就是代码中还引入了litepal-1.3.1.jar。

整理清楚了之后我们就来一个个添加,Input/Output有上下两个操作界面,上面是用于导入要混淆的Jar包和配置混淆后文件的输出路径的,下面则是导入该Jar包所依赖的所有其它Jar包的,全部导入后结果如下图所示:

%title插图%num

这些依赖的Jar包所存在的路径每台电脑都不一样,你所需要做的就是在你自己的电脑上成功找到这些依赖的Jar包并导入即可。
不过细心的朋友可能会发现,我在上面整理出了五个依赖的Jar包,但是在图中却添加了六个。这是我在写这篇文章时碰到的一个新的坑,也是定位了好久才解决的,我觉得有必要重点提一下。由于我平时混淆Jar包时里面很少会有Activity,所以没遇到过这个问题,但是本篇文章中的演示Jar包中不仅包含了Activty,还是继承自AppCompatActivity的。而AppCompatActivity的继承结构并不简单,如下图所示:

%title插图%num

其中AppCompatActivity是在appcompat-v7包中的,它的父类FragmentActivity是在support-v4包中的,这两个包我们都已经添加依赖了。但是FragmentActivity的父类就坑爹了,如果你去看BaseFragmentActivityHoneycomb和BaseFragmentActivityDonut这两个类的源码,你会发现它们都是在support-v4包中的:

%title插图%num

 

%title插图%num

可是如果你去support-v4的Jar包中找一下,你会发现压根就没有这两个类,所以我当时一直混淆报错就是因为这两个类不存在,继承结构在这里断掉了。而这两个类其实被规整到了另外一个internal的Jar包中,所以当你要混淆的Jar包中有Activity,并且还是继承自AppCompatActivity或FragmentActivity的话,那么就一定要记得导入这个internal Jar包的依赖,如下图所示:

%title插图%num

接下来点击Next进入Shrink界面,这个界面没什么需要配置的东西,但记得要将Shrink选项钩掉,因为我们这个Jar包是独立存在的,没有任何项目引用,如果钩中Shrink选项的话就会认为我们所有的代码都是无用的,从而把所有代码全压缩掉,导出一个空的Jar包。
继续点击Next进入Obfuscation界面,在这里可以添加一些混淆的逻辑,和混淆APK时不同的是,这里并不会自动帮我们排除混淆四大组件,因此必须要手动声明一下才行。点击*下方的Add按钮,然后在弹出的界面上编写排除逻辑,如下图所示:

%title插图%num

很简单,就是在继承那一栏写上android.app.Activity就行了,其它的组件原理也相同。
继续点击Next进入Optimiazation界面,不用修改任何东西,因为我们本身就不启用Optimization功能。继续点击Next进入Information界面,也不用修改任何东西,因为我们也不启用Preverification功能。
接着点击Next,进入Process界面,在这里可以通过点击View configuration按钮来预览一下目前我们的混淆配置文件,内容如下所示:

 

  1. -injars /Users/guolin/AndroidStudioProjects/AndroidTest/androidtest.jar
  2. -outjars /Users/guolin/androidtest_obfuscated.jar
  3. -libraryjars /Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/rt.jar
  4. -libraryjars /Users/guolin/Library/Android/sdk/platforms/android-23/android.jar
  5. -libraryjars /Users/guolin/AndroidStudioProjects/AndroidTest/app/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.2.0/jars/classes.jar
  6. -libraryjars /Users/guolin/AndroidStudioProjects/AndroidTest/app/build/intermediates/exploded-aar/com.android.support/support-v4/23.2.0/jars/classes.jar
  7. -libraryjars /Users/guolin/AndroidStudioProjects/AndroidTest/app/build/intermediates/exploded-aar/com.android.support/support-v4/23.2.0/jars/libs/internal_impl-23.2.0.jar
  8. -libraryjars /Users/guolin/AndroidStudioProjects/AndroidTest/app/libs/litepal-1.3.1.jar
  9. -dontshrink
  10. -dontoptimize
  11. -dontusemixedcaseclassnames
  12. -keepattributes *Annotation*
  13. -dontpreverify
  14. -verbose
  15. -dontwarn android.support.**
  16. -keep public class com.google.vending.licensing.ILicensingService
  17. -keep public class com.android.vending.licensing.ILicensingService
  18. # keep setters in Views so that animations can still work.
  19. # see http://proguard.sourceforge.net/manual/examples.html#beans
  20. -keepclassmembers public class * extends android.view.View {
  21. void set*(***);
  22. *** get*();
  23. }
  24. # We want to keep methods in Activity that could be used in the XML attribute onClick
  25. -keepclassmembers class * extends android.app.Activity {
  26. public void *(android.view.View);
  27. }
  28. -keepclassmembers class * extends android.os.Parcelable {
  29. public static final android.os.Parcelable$Creator CREATOR;
  30. }
  31. -keepclassmembers class **.R$* {
  32. public static <fields>;
  33. }
  34. -keep class * extends android.app.Activity
  35. -keep class * extends android.app.Service
  36. -keep class * extends android.content.BroadcastReceiver
  37. -keep class * extends android.content.ContentProvider
  38. # Also keep – Enumerations. Keep the special static methods that are required in
  39. # enumeration classes.
  40. -keepclassmembers enum * {
  41. public static **[] values();
  42. public static ** valueOf(java.lang.String);
  43. }
  44. # Keep names – Native method names. Keep all native class/method names.
  45. -keepclasseswithmembers,allowshrinking class * {
  46. native <methods>;
  47. }

 

恩,由此可见其实GUI工具只是给我们提供了一个方便操作的平台,背后工作的原理还是通过这些配置来实现的,相信上面的配置内容大家应该都能看得懂了吧。
接下来我们还可以点击Save configuration按钮来保存一下当前的配置文件,这样下次混淆的时候就可以直接Load进来而不用修改任何东西了。
*后点击Process!按钮来开始混淆处理,中间会提示一大堆的Note信息,我们不用理会,只要看到*终显示Processing completed successfully,就说明混淆Jar包已经成功了,如下图所示:

%title插图%num

混淆后的文件我将它配置在了/Users/guolin/androidtest_obfuscated.jar这里,如果反编译一下这个文件,你会发现和刚才反编译APK得到的结果是差不多的:MainActivity的类名以及从父类继承的方法名不会被混淆,NativeUtils的类名和其中的native方法名不会被混淆,Utils的methodUnsed方法不会被移除,因为我们禁用了Shrink功能,其余的代码都会被混淆。由于结果实在是太相似了,我就不再贴图了,参考本篇文章*部分的截图即可。

 

安装JDK和Eclipse

上篇的Android SDK下载安装及配置教程。

总结起来,Android开发环境搭建可以分为以下四步:

*步、安装JDK

第二步、安装Eclipse

第三步、下载并安装AndroidSDK;

第四步、为Eclipse安装ADT插件

今天再来补充一下安装JDK和Eclipse:

安装JDK
官网:http://www.Oracle.com/technetwork/Java/javase/downloads/index.html

按照以下步骤操作即可:

(1)点击图中箭头所指的任意一个都可以。

%title插图%num

(2)接受

%title插图%num

(3)找到适合自己电脑系统的JDK版本,点击下载

%title插图%num

(4)自己选择路径安装完,注:不可以保存在中文路径下。

(5)变量环境的设置:我的电脑/计算机——属性——高级系统设置——环境变量。

①、新建一个系统环境变量,变量名为JAVA_HOME,变量值为JDK的安装路径

%title插图%num

②、在系统变量中找到Path,将;%JAVA_HOME%\bin; %JAVA_HOME%\jre\bin添加到变量值后面

注:在变量的*末尾添加时,需要加上分号;

③、检测是否配置成功。打开命令行窗口,输入javac -version。安装成功则看到oracle JDK版本号,如下图所示:

%title插图%num

 

安装Eclipse
官网:http://www.eclipse.org/downloads/

1、下载Eclipse,选择EclipseIDE for Java EE Developers,根据自己的系统选择32位或者64位的安装包,

2、将下载好的安装包解压缩至自己想要的位置,得到如图效果:

%title插图%num

3、双击eclipse/eclipse.exe。自己选择工作空间存放位置,出现以下图标则安装成功,若无请检查步骤一JDK是否正确安装和配置。

%title插图%num %title插图%num

 

 

什么是主流的开发安卓APP的方式?

IDE
曾经的主流,Eclipse+Genymotion

1. 安装Genymotion

默认的虚拟机启动及其困难,也较为卡顿,这时候我们需要一个替代的虚拟机Genymotion了

当然前提是你要有个账号。

下载安装过程就不细说了,选择教育|学习|个人使用版本就行了。

安装完毕后,打开,用账户登陆(没有就去注册),下载自己想要的虚拟机版本,设置分辨路和内存。


2. Genymotion和Eclipse链接

至于链接就需要装个插件了,和安装ADTPlugin一样,
01

接着输入插件地址:http://plugins.genymotion.com/eclipse

02

然后等待以下,把下面选项 Group items by category 选项勾弄掉,接着就出现插件包了,选上就Next,同意,无乱什么都选择Yes,等待下载安装直到完成。

下载安装完成重启Eclipse后会发现多了个这个图标
04

点击以下,会弹出警告,让你设置Genymotion和安装目录,设置你刚才安装的Genymotion的目录,接着确定就行了。

剩下的就一样的了,点击上面那个图标打开虚拟机,然后选择启动虚拟机。

在运行项目之前,先设置以下运行配置
05

选择如下
06


现在的主流,AndroidStudio+Genymotion

1. 安装Android Studio

AndroidStudio的下载地址在下面 ↓ (不要怀疑(→_→),下面那个就是是谷歌官网下载地址)

https://dl.google.com/dl/android/studio/install/2.2.0.12/android-studio-bundle-145.3276617-windows.exe

安装过程同样不在多说,一直下一步到完成就行了。

特别注意!
安装路径不要出现任何中文!!!
不要出现任何中文!!!
不要出现任何中文!!!

2. Android Studio和Genymotion链接

Genymotion安装上面已讲,Android Studio链接Genymotino同样是安装插件,很简单,打开设置
07

在Plugins选项中输入genymotion,搜索到后点击旁边的install,因为我安装了所以显示“Uninstall”
08

如果搜不到就点击下面搜索进行安装
09

一直同意,Next,等待安装完毕后重启。

重启后出现下面的图标。
10

点击打开,会弹出错误,说没设置Genymotion目录,和上面一样,到设置里设置。
11

后面就一样的了,点击小图标来选择启动虚拟机,接着运行项目。

两款好用的代码Review工具

*近团队内部呼吁代码质量的声音越来越高,项目组引入了代码Review机制。上次我由于不了解主站URL替换的规则,险些造成客户端图片读取错误,而代码Review则有助于帮我们发现代码潜在的问题。

主讲的同学介绍了两款代码Review工具,都是Eclipse下免费的插件。一款是ReviewClipse,另外一款是Jupiter。通过参加分享的同学们讨论以后,*终觉得ReviewClipse可能更适合目前团队的开发工作,所以对于Jupiter,我们不做详细介绍。

ReviewClipse
这是一个基于SVN版本比较机制进行Review的工具, 官方地址:http://www.inso.tuwien.ac.at/projects/reviewclipse/

安装好插件后,会在右键菜单多出一个Review的选项,在*次使用的时候,我们要对ReviewClipse进行配置。

%title插图%num

配置项目:

点击点击Review—》Review Project…

%title插图%num

该插件会从SVN的Repository中发现项目成员:

%title插图%num

继续“下一步”,点击“Add”,可以分配reviewer给谁review,如下:

%title插图%num

配置完成之后,接可以对代码进行审查。

它比较适合在对代码改动比较敏感的阶段使用,如版本测试后期bug修改阶段或者mantis上小版本bug修改。或者进行较频繁的代码评审时使用,如项目经理想看新人每天提交的代码,让新人快速提升代码质量及改善代码风格。

这个工具应该说是项目经理进行代码修改影响分析的好工具,无需再自己对着SVN log,查谁改了哪些文件。

%title插图%num

同时ReviewClipse还支持多人协同的代码评审,被评审人可以看到评审人给出的评审意见。

ReviewClipse的不足就是它只支持文件级别的代码评审,不能提供代码行级别的评审。儿另一款工具Jupiter则能够支持代码行级别的评审,但是由于使用起来比较繁琐,项目组没有采用,这里就不细说了。有兴趣的同学可以自己去试一下。

FindBugs
FindBugs是个专门用来发现Java代码中的Bug的Eclipse小插件,与Eclipse无缝结合,可以实现敲入代码、保存文件、编译时即时反馈给用户Bug信息,比Eclipse自带的Java Bug管理功能要强。FindBugs一共有三个版本,除过Eclipse插件板本外,还提供一个独立版本的Java程序(基于SWing)和一个Ant Task版本;下面介绍Eclipse的插件版。

使用非常简单:安装完成后,项目邮件,选择FindBugs,,插件会自动查找项目下的所有bug

%title插图%num

有bug的代码行,前面会有一个小虫子,点击小虫子,FindBugs会给出该代码的修改意见。当然了,仅仅是建议,酌情参考即可。

%title插图%num

【Android】短信应用——短信信息实时获取

我们知道,只需通过代码就可以读到收件箱中的短信,发件箱中的短信;但是却没办法在短信发来的瞬间获取;如果我们在短信发来的一瞬间能得到相应的信息内容,那么我们就可以依次来展开很多应用了——也就是通过短信去远程操作一部手机。

 

如果想实时获取,就需要调用receiver了,写一个监听类,这样我们就可以实时获取短息信息了。

 

预览图:%title插图%num

 

还是来看看代码吧。

首先,我们需要创建一个监听类SMSBroadcastReceiver,让他去继承BroadcastReceiver。

再来初始化一个常量ACTION,并赋短信相关参数值。

 

android.provider.Telephony.SMS_RECEIVED
接着创建onReceive方法。

然后用getAction去监听手机短信相关动态,利用StringBuffer来保存短信信息。

 

再然后主要代码了。

%title插图%num
代码中的SMSAddress为发送短信的号码,SMSContent为短信内容。

 

要想看到是否成功获取,*简单的方法就是把这两个参数打印出来。

1|System.out.println(“发送号码:” + SMSAddress + “\n” + “短信内容:”
2|                                         + SMSContent);

不过要把他们加入for循环中,因为当新信息发来时,SMSAddress和SMSContent将被替换。

因此如果要是做应用时,也是在for循环中判断的。

*后要记得在Manifest.xml中注册监听器。

 

<receiver android:name=”cn.etzmico.SMSBroadcastReceiver”>
<intent-filter>
<action android:name=”android.provider.Telephony.SMS_RECEIVED”></action>
</intent-filter>
</receiver>

 

同时要加上权限。

 

<uses-permission android:name=”android.permission.RECEIVE_SMS”></uses-permission>
这样,我们运行程序后,只要有短信接收,SMSAddress和SMSContent就会被赋值。

 

这里顺便补充一个知识点,关于Eclipse程序的。

相信很多初学者不知道,Eclipse自带一个发短信插件,可以实现给虚拟机发送短信。这样,我们在做短信应用的时候,就不用同时启动多台虚拟机了……

如何操作呢?方法如下。

1.点击菜单栏中的 Window 窗口。

2.找到哦啊其中的 Show View 目录。

3. 选择 Other…。

%title插图%num

然后我们发现会弹出一个窗口。

%title插图%num

4,为了便于操作,我们在弹出的窗口的搜索栏中,直接输入 Emulator Control。

%title插图%num

5.点击列表中的 Emulator Control,再点OK;或者直接双击。

 

这样就出现了一个窗口,其中有很多参数。

其他的以后有机会再做介绍,我们这次至用到其中4个。

%title插图%num

如图所示,我们只需要输入对应的参数,选择需要的类型,*后点发送就可以了。

 

PS:有的人奇怪为什么灰色,没法输入,没法选择,那是因为你没有选中模拟器。这个插件只能同时给一个模拟器发送消息。关于模拟器的选择,和调用Emulator Control的方法差不多,区别只是在输入Emulator Control的时候输入 Devices 就可以了。你当前选中哪个模拟器了,就会给哪个模拟器发送消息,不需要输入模拟器号码。

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速