kangshifu


  • Startseite

  • Archiv

  • Tags

Unbenannt

Veröffentlicht am 2016-08-24   |     |   visitors

title: 关于android打包混淆你必须知道的事
date: 2016-08-24 15:48:08
tags:

混淆是我们的应用上线前的一个重要环节,android中使用proguard,可以起到压缩,混淆,预检,优化的作用

  • 先说压缩(shrink):移除代码中无用的类,字段,方法和特性。

  • 混淆:使用a,b,c,d这样简短而无意义的名称对类,字段,方法重新命名。

  • 预检:在java平台对处理后的代码进行预检。

  • 优化:对字节码进行优化,移除无用的指令。

在as的gradle文件中:

1
2
3
4
5
6
7
8
9
buildTypes {
release {
minifyEnabled true
proguardFiles
getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}

找到项目根目录下的proguard-ruls.pro文件

基本上我们混淆主要分两大部分

  • 定制化的部分

  • 基本设置部分,这一部分是android每个apk打包混淆相同的部分

    先来说说基本设置部分,主要包括一些基本的指令:

——————————-基本指令区———————————

-optimizationpasses 5

-dontusemixedcaseclassnames

-dontskipnonpubliclibraryclasses

-dontskipnonpubliclibraryclassmembers

-dontpreverify

-verbose

-printmapping proguardMapping.txt

-optimizations !code/simplification/cast,!field/,!class/merging/

-keepattributes Annotation,InnerClasses

-keepattributes Signature

-keepattributes SourceFile,LineNumberTable

——————————默认保留区———————————

-keep public class * extends android.app.Activity

-keep public class * extends android.app.Application

-keep public class * extends android.app.Service

-keep public class * extends android.content.BroadcastReceiver

-keep public class * extends android.content.ContentProvider

-keep public class * extends android.app.backup.BackupAgentHelper

-keep public class * extends android.preference.Preference

-keep public class * extends android.view.View

-keep public class com.android.vending.licensing.ILicensingService

-keep class android.support.* {;}

-keepclasseswithmembernames class * {
native ;
}

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

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

-keep public class extends android.view.View{
**
get();
void set
(*);
public (android.content.Context);
public (android.content.Context, android.util.AttributeSet);
public (android.content.Context, android.util.AttributeSet, int);
}

-keepclasseswithmembers class * {
public (android.content.Context, android.util.AttributeSet);
public (android.content.Context, android.util.AttributeSet, int);
}

-keep class implements android.os.Parcelable {
public static final android.os.Parcelable$Creator
;
}

-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}

-keep class *.R$ {
*;
}

-keepclassmembers class {
void
(*OnEvent);
}

————————-webview—————————————

-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public *;
}

-keepclassmembers class extends android.webkit.webViewClient {
public void
(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}

-keepclassmembers class extends android.webkit.webViewClient {
public void
(android.webkit.webView, jav.lang.String);

————————–end——————————————–

以上命令就是在各个不同apk打包混淆相同的命令,下面就相关命令做相关探讨:

-optimizationpasses 5

代码压缩比例0-7之间

-dontusemixedcaseclassnames

混淆之后类名都为小写

-dontskipnonpubliclibraryclasses

指定不去忽略非公共的库的类

-dontskipnonpubliclibraryclassmembers

指定不去忽略非公共的库的类的成员

-dontpreverify

不做预校验的操作,android 不需要做预校验,这样可以提高混淆的速度

-verbose

-printmapping proguardMapping.txt

生成原类名和混淆后的类名的映射文件

-optimizations !code/simplification/cast,!field/,!class/merging/

指定混淆所采用的算法,后面的参数是一个过滤器,这个过滤器是谷歌推荐的算法一般不改变

-keepattributes Annotation,InnerClasses

不混淆Anotation

-keepattributes Signature

不混淆泛型,这个主要是在JSON实体映射时非常重要,比如fastJson

-keepattributes SourceFile,LineNumberTable

抛出异常时显示代码的行号

-keep class XXXX

保持类名不混淆

-keepclasseswithmembers class XXXX

保持类名和其中的成员名不混淆

####By the way 需要保留的东西:

  • 保留所有本地的native方法不被混淆
1
2
3
print -keepclasseswithmembernames class * {
native <methods>;
}
  • 保留Activity中的方法参数是view的方法,
    从而我们在layout里面编写onClick就不会影响
1
2
3
print -keepclassmembers class * extends android.app.Activity {
public void * (android.view.View);
}
  • 枚举类不能被混淆
1
2
3
4
print -keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
  • 保留自定义控件(继承自View)不能被混淆
1
2
3
4
5
6
7
print -keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(***);
*** get* ();
}
  • 保留Parcelable序列化的类不能被混淆
1
2
3
-keep class * implements android.os.Parcelable{
public static final android.os.Parcelable$Creator *;
}
  • 保留Serializable 序列化的类不被混淆
1
2
3
4
5
6
7
8
9
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
  • 对R文件下的所有类及其方法,都不能被混淆
1
2
3
-keepclassmembers class **.R$* {
*;
}
  • 对于带有回调函数onXXEvent的,不能混淆
1
2
3
-keepclassmembers class * {
void *(**On*Event);
}
  • 保留所有的实体类不被混淆最好放在一个包下面
1
2
3
4
-keep class com.null.test.entities.** {
//全部忽略
*;
}
  • 内嵌类容易被混淆,如果在一个类里面用了,eg:在MainActivity
1
2
3
4
5
keep class com.null.test.MainActivity$* {
*;
}
$这个符号就是用来分割内嵌类与其母体的标志
  • 对WebView的处理
1
2
3
4
5
6
7
8
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String);
}
  • 对javaScript的处理
1
2
3
-keepclassmembers class com.null.test.MainActivity$JSInterfacel {
<methods>;
}

#Last But most Important:很多第三方包都已混淆过使用的时候会有混淆说明,请添加到混淆文件中

“关于工厂方法秒懂的讲解”

Veröffentlicht am 2016-08-08   |     |   visitors

七夕不单身,巧用工厂方法创建你想要的女朋友,炸哇(java)面向对象。

1.简单工厂,什么是简单工厂?

下面就要实例来阐述下吧

(1)定义女朋友的类型

public enum gfType{

HU_NAN,GUANG_DONG,BEIJING;

}

(2)定义女朋友的共有方法抽象类

public abstract class BaseGF{

abstract void eat();

void shopping(){
   System.out.println("都是购物狂人");     
}

}

(3)创建湖南的女朋友

public class HuNanGf extends BaseGf{

@Override
public void eat(){
  System.out.println("湖南的女朋友喜欢吃辣的");
}

}

创建广东的女朋友

public class GuangDongGf extends GF{

@Override 
public void eat(){
  System.out.println("山东的女朋友喜欢吃面食类的");
}

}

创建北京的女朋友

public class BeiJingGf extends GF{

@Override
public void eat(){
  System.out.println("北京的女朋友吃卤煮");

}

}

(4)创建生产女朋友的工厂类

public BaseGF GFFatroy{

public static void createGf(GFtype gfType){
   switch(gfType){
      case HU_NAN:

        return new HuNanGF();

      case GUANG_DONG:

        return new GuangDongGf();

      case BEIJING:

        return BeiJingGf();

   }
   return null;
}

}

(5)测试想要得到的女朋友
public class test{

public static void main(String args []){
   BaseGF gf = new GFFactroy.createGf(Gftype.HU_NAN);
   gf.eat();
   gf.shoping();
}

}

总结:简单工厂方法提供了一个生产所有产品实例的工厂类,并且这些产品的实例都有一个共同的父类

2.工厂方法

假如现在我们突然想要一个云南的女朋友,那么按照简单工厂方法的模式,我们需要在GFType添加新的女朋友类型,我们需要新建云南女朋友的具体产品类。这样也不符合代码的开闭原则,因此我们创建一个生产女朋友的接口,这样想要哪个地方的女朋友就新建一个自己的工厂类不会影响别的已创建的工厂

(1)创建生产女朋友的接口

public interface IGFactroy{
   BaseGF crateGf();
}

(2)新建云南女朋友的具体产品类(这个不变)
public class yunNanGf extends BaseGF{

   @Override
    public void eat(){
      Ststem.out.println("云南女朋友的口味");
    }

}

(2)创建云南女朋友的工厂类

public class YunNanFactroy implements IGFactroy{

@Override
public BaseGF createGf(){
      return new yunNanGf();    
}

}

(3)测试新产生的女朋友
public class Test{

public static void main(String [] args){

BaseGf  gf = new YunNanFactroy.createGf();
gf.eat();
gf.shoping();

}
}

总结:工厂方法提供了一个可以创建任意产品的接口,让子类决定实例化哪一个类,工厂方法让一个类的实例化延迟到了子类

3.抽象工厂方法

多年之后你和女朋友准备结婚了,需要见家长,需要家长的意见该如何处理呢

public interface IGFather{

void sugesstion();

}

public interface IGFactroy{
BaseGf createGf();

IGFather createFather();

}

public class YunNanGFFactroy implements IGFFactroy{
@Override
public BaseGf createGf(){
return new yunNanGf();
}

@Override
public IGFFather createGFFactroy(){
   return new YuNanGFFactroy();
}

}

public class YunNanGFFather implements IGFather{

@Override
public void suggestion(){
  System.out.println("已经同意你们结婚");
}

}

public class Test{
public static void main(String [] args){
IGFFactroy yunNanGFFactroy = new YunNanGFFactroy();
BaseGf yunNanGf = yunNanGFFactroy.createGF();
yunNanGf.eat();
yunNanGf.shoping();

IGFFather yunNanGFFather =           yunNanGFFactroy.createGFFactroy();

yunNanGFFactroy.suggestion();

}
}

结论:抽象工厂为我们提供了一系列相关或者相互依赖对象的接口而无需指定他们的具体类。

千里之行,始于足下,自己敲一遍之后是不是对简单工厂(静态工厂),工厂方法,和抽象工厂有了一定的认识。

那些年我们遇到的坑

Veröffentlicht am 2016-08-04   |     |   visitors

### 从eclipse到idea再到想在的as转变,都曾添加过很多的lib,从以前的add jar到现在complie lib,在这其中相信大家有踩到过很多的坑,今天我们就把这些踩过的坑跟大家讨论讨论


1.当我们从eclipse中把项目导入到as中很多.9图片出现问题的时候:
在gradel中添加如下代码:

aaptOptions.cruncherEnable = false
aaptOptions.useNewCruncher = false

以此来关闭as对PNG的合法性检查

2.as出错:非法字符:”\ufeff”:需要class,interface,enum

原因:

eclipse只能的把UTF-8+BOM文件转为普通的UTF-8文件,AS还没有这个功能,所以当我们在使用as编译UTF-8+BOM编码的文件时会出现”非法字符:’\ufeff’之类的错误

解决方案:

手动的把UTF-8+BOM编码的文件转化为普通的UTF-8文件,这个可以借助别的编辑器,比如EditPlus.

3.将项目导入AS出现以下问题:

Error:Execution failed for task
‘:app:transformResourcesWithMergeJavaResForDebug’
. > com.android.bui

解决方法:
在build.grade中添加以下代码:
android{
packagingOptions {
exclude ‘META-INF/DEPENDENCIES.txt’
exclude ‘META-INF/NOTICE’
exclude ‘META-INF/NOTICE.txt’
exclude ‘META-INF/LICENSE’
exclude ‘META-INF/LICENSE.txt’
}
}

4.修改了Android项目的minSDKVersion后很过style文件找不到
解决方案
compileSdkVersion 是多少版本的
那么compile ‘com.android.support:appcompat-v7:23.2.1’ 就是啥版本的。

5.AS编译问题:finished with non-zero exit value 2

Error:Execution failed for task ‘
:androidShopNC2014Moblie:transformClassesWithDexForDebug’
.>com.android.build.api.transform.TransformException:
com.android.ide.common.process.ProcessException:
java.util.concurrent.ExecutionException:
com.android.ide.common.process.ProcessException:
org.gradle.process.internal.ExecException:
Process ‘command ‘/Library/Java/JavaVirtualMachines/
jdk1.7.0_79.jdk/Contents/Home/bin/java’’
finished with non-zero exit value 2

解决方案:
这个只需要在gradel中添加这句:
android{
defalutConfig{
multiDexEnable true
}
}

6.AS编译问题:finished with non-zero exit value 1(由于导入依赖出现了重复)

问题日志:

Error:Execution failed for task ‘
:app:transformClassesWithDexForDebug’.
com.Android.build.api.
transform.TransformException:
com.android.ide.common.process.ProcessException:
org.gradle.process.internal.ExecException:
Process ‘command ‘F:\Program Files (x86)[Java]
(http://lib.csdn.net/base/17)\jdk1.8.0_31\bin\java.exe‘
‘ finished with non-zero exit value 1

解决方案:

这个是依赖包重复了,比如很多库都各自添加了v4造成了冲突,可以exclue一个

7.错误日志:

Error:Execution failed for task
‘:app:transformClassesWithJarMergingForDebug’.>
com.android.build.api.transform.TransformException:
java.util.zip.ZipException:duplicate entry:
org/apache/http/ConnectionClosedException.class

通常这个是在几个库中存在了相同的类删掉其中一个或者更改其中一个类的名称

8.添加第三方依赖出现的问题:

Error:Execution failed for task ‘:
app:processDebugManifest’.

Manifest merger failed :
uses-sdk:minSdkVersion 14 cannot be smaller than version
19 declared in library [com.github.meikoz:basic:2.0.3]
/AndroidStudioCode/EnjoyLife/app/build/intermediates/
exploded-aar/
com.github.meikoz/basic/2.0.3/AndroidManifest.xml
Suggestion: use tools:overrideLibrary=”com.android.core”
to force usage

错误原因:

出现这个错误的原因就是您的minSdkVersion小于第三方库中的minSdkVersion
解决方案:

在manifest中添加如下代码

其中xxx为包名,如果存在多个库的异常可以用逗号隔开他们,

这样做可以使项目的manifest文件和第三方的manifest文件忽略掉对最低版本的限制

9.AS的编译问题:finished with non-zero exit value 1
错误日志:

Error:Execution failed for task ‘:app:
transformClassesWithDexForDebug’.
com.android.ide.common.process.ProcessException:
org.gradle.process.internal.ExecException:
Process ‘command ‘/Library/Java/JavaVirtualMachines/
jdk1.7.0_79.jdk/Contents/Home/bin/java’’
finished with non-zero exit value 1

解决方案:
由于buildToolsVersion版本太高,调低buildToolsVersion

10.Android studio编译问题:Gradle DSL not found ‘android()’
问题描述:
error(51,52)错误:-source 1.6不支持diamond运算符(请使用-source 7或更高版来启动diamond运算符)

解决方法:
android {
compileOptions {sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}

Android之ActivityThread

Veröffentlicht am 2016-08-03   |     |   visitors

ActivityThread

我们都知道android应用程序有一个主activity作为应用的页面启动入口,通常这个activity在manifest我们声明为launcher,但是这是整个apk的启动入口吗,其实不然,类似于java的main方法,android中也有一个main方法,它才是应用的最开始入口,而这个main方法就在ActivityThread类中。

关于ActivityThread的几个理解点

1.它是一个final类所以不能被继承。

2.(1)它不是一个线程,但是它管理着应用程序主线程的执行,也就是相当于java中的main方法,并且根据AMS的要求(通过IApplication Threada接口,AMS为client,ActivityThread.ApplicationThread为server,复制调度Activities,broadcasts和其他的操作。

(2)在android系统中默认的所有activity,service,broadCast都运行在一个进程当中,并由此进程的[主进程]负责执行。

3.android应用程序的载体是apk文件,本质上是一个资源和组件的容器,apk和其他可执行文件的区别就是每个可执行的文件是运行在一个进程中,但是apk文件可能运行在一个单独的进程也可以和其他的apk运行在一个进程中,可以理解为android系统的设计理念就是弱化进程,而用组件的思想来替代。

public final class ActivityThread{

......

public static void main(String [] args){

  ......

  Looper.prepareMainLooper;//创建MessageQueue的消息队列

  ActivityThread thread = new ActivityThread();

  thread.attach(false);

  if(sMainThreadHandler == null){

      sMainThreadHandler = thread.getHandler();
  }

 ......

 }


 final ApplicationThread mAppThread = new ApplicationThread();

 final Looper  mLooper = Looper.myLooper();

 final H mH = new H();

}

程序首先在main()函数执行,调用prepareMainLooper在主线程中创建一个消息队列(MessageQueue)。

然后创建ActivityThread对象,ApplicationThread对象,Handler对象。

ApplicationThread:它是Binder对象,负责接收远程的AMS的IPC调用。

Hander:复制发送消息到消息队列,UI线程会异步的从消息队列中取消息并做相应的操作。

当有了这两个对象之后我们可以接收AMS发送来的远程消息,(start,stop,pause)并且可以毁掉到ActivityThread,当ApplicationThread对象接收到AMSf发送start某个activity,就通过handler异步发送消息,ActivityThread.handleMessage取出相应的消息,就会创建指定的activity。

public void handleMessage(Message msg)
{
     switch(msg.what)
     {
        case LAUNCH_ACTIVITY:

     ......
         handleLaunchActivity(r,null);

     ......

     break;
   }
 }

进入到handleLaunchActivity中:

Activity  a = performLaunchActivity(r,cumstomIntent);

if(a != null){
 ......

 handleResumeActivity();
 }

进入到performLaunchActivity返回了Activity对象,看看Activity是怎么被创建的:

private Activity performLuanchActivity(ActivityClientRecord r,Intent customIntent){

  ......

  Activity activity = null;

  ......

   java.lang.ClassLoader cl = r.packageInfo.getClassLoader();

   activity = mInstrumentation.newActivity(cl,component.getClassName(),r.intent);
   ......

   return activity;

   }


当activty被创建了之后,又会创建相关的类:

contextImlp---->phoneWindow--->WindowManageImpl---->DecorView---->相应的View,ViewGroup(xml布局文件);

创建完毕之后需要把它显示在屏幕上:

WindowManager------>ViewRoot------>WMS远程接口这样一个窗口就被加到屏幕上

因此我们可以得到android应用程序的窗口模型:

Android应用程序窗口=Activity+PhoneWindow+DecorView;
DecorView = TitleView + contentView;(View的绘制)

####至此我们可以得出的结论android应用程序的运行主要包括:

1.View的绘制

2.用户消息事件的分发

4 Artikel
© 2016
Erstellt mit Hexo
Theme - NexT.Mist
  |   总访问量: