Gradle混淆+打包Jar包基础

本文为原创文章,转载请注明出处。  

文章最后会附带源码下载地址,有需要的朋友可下载。

通常我们编写Android APP时有这样的需求:(1)代码混淆;(2)模块化;(3)向第三方提供JAR包。下面将以实例的形式向初学Android或开始使用AndroidStudio(AS)的朋友介绍下这几部分。

引述:

(1)AS采用了Gradle的构建工具,可以让我们很方便的对我们的APP进行配置,比如版本、支持最低API level 、代码混淆文件、第三方库等等,具体语法请查考其他朋友的文章。

(2)AS提供了模块编程,便于我们对APP进行分层和理清架构,个人推荐初学者可以参考下这篇文章

实例正文:

本文实例仅作为演示使用,重点是演示代码混淆和打包JAR,所以功能都进行了简化并省略了很多逻辑代码。

基本需求:接受用户的登录请求,模拟完成向服务端发起登录请求,并提示登录结果。

一、创建项目

项目目录结构:

个人习惯将各模块创建为平级,模块(android library)描述:

app:用户模块,用户交互界面、用户资源等   model:实体模块     player:核心业务模块  utils:辅助类模块。

提示:创建模块时注意模块类型为Android Library,否则会对后面的一些功能会有影响。

 

二、代码编写

(1)model模块

用户登录信息实体:

 

 1 package xiaoshubao.model;
 2 
 3 /**
 4  * 作者: 小书包
 5  * 日期: 2016/6/16
 6  * 版本: V1.0
 7  * 说明:
 8  */
 9 public class UserModel {
10     String userName;
11     String pwd;
12 
13     public void setUserName(String userName) {
14         this.userName = userName;
15     }
16 
17     public void setPwd(String pwd) {
18         this.pwd = pwd;
19     }
20 
21     public String getUserName() {
22         return userName;
23     }
24 
25     public String getPwd() {
26         return pwd;
27     }
28 }
View Code

 

  其他实体不再贴代码,model层最终的代码结构如下:

HttpMsgCallback:http回调请求接口

Parent:无实际意义类,代码混淆时需要

UserLoginCallback:用户登录结果回调接口

(2)utils模块:

网络访问辅助类(HttpUtils):

 1 package xiaoshubao.utils;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 
 6 import xiaoshubao.model.HttpMsgCallback;
 7 import xiaoshubao.model.Parent;
 8 
 9 /**
10  * 作者:  小书包
11  * 日期: 2015/12/18
12  * 版本:V1.0
13  * 说明:与服务端Http通信
14  */
15 public class HttpUtils implements  Parent {
16     private static final String TAG = "HttpUtils";
17 
18     /**
19      * 发送Post请求到服务器 HTTP
20      *
21      * @param strUrlPath 服务器地址
22      * @param params     请求体参数
23      * @return 错误码
24      */
25     private static String httpPostData(String strUrlPath, Map<String, String> params) {
26 
27         return "true";
28     }
29 
30     /**
31      * 向http服务器发出注册消息
32      * @param serverUrl 服务器地址
33      * @param params 请求体参数
34      * @param httpMsgCallback 执行结果回调
35      */
36     public static void sendPostMsgToServer(final String serverUrl, final HashMap params, final HttpMsgCallback httpMsgCallback) {
37         Thread thread = new Thread(new Runnable() {
38             @Override
39             public void run() {
40                 try {
41                     Thread.sleep(2*1000);//当前线程睡眠两秒钟模拟发送网络请求
42                 } catch (InterruptedException e) {
43                     e.printStackTrace();
44                 }
45                 String result = HttpUtils.httpPostData(serverUrl, params);
46                 httpMsgCallback.httpPostCallBack(result);
47             }
48         });
49         thread.start();
50     }
51   }
View Code

 

(3)player模块

 UserLogin类(用户登录业务类):

 

 1 package xiaoshubao.player;
 2 
 3 import java.util.HashMap;
 4 
 5 import xiaoshubao.model.HttpMsgCallback;
 6 import xiaoshubao.model.Parent;
 7 import xiaoshubao.model.UserLoginCallback;
 8 import xiaoshubao.model.UserModel;
 9 import xiaoshubao.utils.HttpUtils;
10 
11 /**
12  * 作者: 小书包
13  * 日期: 2016/6/16
14  * 版本: V1.0
15  * 说明:
16  */
17 public class UserLogin implements Parent {
18     UserLoginCallback userLoginCallback;
19     public UserLogin(UserLoginCallback userLoginCallback){
20         this.userLoginCallback=userLoginCallback;
21     }
22 
23     /**
24      * 用户登录
25      * @param user 用户信息
26      */
27     public void login(UserModel user){
28         userLogin(user);
29     }
30     private void userLogin(UserModel user){
31         HashMap hashMap=new HashMap();
32         hashMap.put("userName",user.getUserName());
33         hashMap.put("pwd",user.getPwd());
34         HttpUtils.sendPostMsgToServer("XXXXX", hashMap, httpMsgCallback);
35     }
36     HttpMsgCallback httpMsgCallback=new HttpMsgCallback() {
37         @Override
38         public void httpPostCallBack(String json) {
39             if (json.contains("true")&&null!=userLoginCallback){
40                 userLoginCallback.loginResult(true);
41             }else if (null!=userLoginCallback){
42                 userLoginCallback.loginResult(false);
43             }
44         }
45     };
46     private void fun1(){}
47     private void fun2(){}
48 }
View Code

 

三、代码混淆

  AS中进行代码混淆需要在build.gradle文件和proguard-rules.pro文件中进行设置(可以通过jd-gui工具对比混淆前后效果):

(1)build.gradle文件

minifyEnabled:表示是否开启混淆,默认为false

proguardFiles:混淆配置文件,一般就采用项目中默认的proguard-rules.pro文件。

(2)proguard-rules.pro文件

混淆设置,具体可参考progurad官网

注意图中红框部分,因为所有jar包都要求有对外接口(没有对外接口的模块一般也没什么意义),有多种种方式设置对外接口类:

a:-keep public class *,例如:

-keep public class * {
public protected *;
}

b:如图所示。

因为一个模块一般有很多类文件,混淆时我们希望除对外接口类的其他所有类文件的类名也进行混淆,那么就可以单独创建一个基类或接口,让对外的接口类继承该基类或接口。

c:-keep public class XXX,特定类不混淆,例如:

-keep public class xiaoshubao.player.UserLogin{
public protected *;
}

四、打包JAR包

(1)proguard-rules.pro配置

配置生成JAR包的基本属性,如下:

上述代码很简单不再叙述。

(2)生成JAR包

CMD命令行中切换到当前项目目录下,执行gradlew makeJar 命令。

顺利的话会生成JAR包,如果是第一次采用gradlew生成,可能需要在线更新相关包,大约几分钟时间。

如果配置、类引用出现错误,CMD窗口会提示,请根据具体的错误提示做修改。

(3)JAR包合并

gradlew makeJar命令会在model、uitls、palyer目录下分别生成这三个模块的JAR包,那么如果我们需要向第三方提供SDK,三个JAR包可能会不太方便,所以就有了合并为一个JAR包的需求。

我们知道JAR包其实就是普通的压缩包而已,所以对三个JAR包进行解压后文件如下:

注意:META-INF配置文件,该项目对palyer、utils模块进行了混淆而model模块未混淆(也可通过配置进行混淆),所以只有一个META-INF文件生成,如果有多个模块未混淆时生成了多个META-INF文件,采用本文方法进行JAR包合并会出问题。

xiaoshubao文件夹下的目录文件如下:

 

 一起压缩META-INF、xiaoshubao文件生成zip文件,重命名为.jar文件,结果如下:

五、第三方使用

 将MyUserManager.jar包导入测试项目(非JAR包源码项目)中,如下:

提示:有时在JAR包前面没有向下的三角符号也无法点开JAR包查看里面的类文件,且使用JAR包里的类时会报错,此时重启该项目应该就可以出现如上图所示的效果。

(1)登录界面:

(2)登录代码:

 1 package xiaoshubao.jartest;
 2 
 3 import android.content.Context;
 4 import android.os.Bundle;
 5 import android.os.Message;
 6 import android.support.v7.app.AppCompatActivity;
 7 import android.view.View;
 8 import android.widget.EditText;
 9 import android.widget.Toast;
10 
11 import xiaoshubao.model.UserLoginCallback;
12 import xiaoshubao.model.UserModel;
13 import xiaoshubao.player.UserLogin;
14 
15 public class MainActivity extends AppCompatActivity {
16     Context context;
17     MyHandler handler;
18     @Override
19     protected void onCreate(Bundle savedInstanceState) {
20         super.onCreate(savedInstanceState);
21         setContentView(R.layout.activity_main);
22         context = this;
23         handler = new MyHandler();
24     }
25     public void btn_loginClick(View v) {
26         UserLogin userLogin = new UserLogin(userLoginCallback);
27         UserModel userModel = new UserModel();
28         String userName = ((EditText) findViewById(R.id.etUserName)).getText().toString().trim();
29         userModel.setUserName(userName);
30         String pwd = ((EditText) findViewById(R.id.etPwd)).getText().toString().trim();
31         userModel.setPwd(pwd);
32         userLogin.login(userModel);
33     }
34 
35     UserLoginCallback userLoginCallback = new UserLoginCallback() {
36         @Override
37         public void loginResult(boolean result) {
38             Message msg = Message.obtain();
39             msg.what = 7634;
40             if (result) {
41                 msg.obj = "登录成功!";
42             } else {
43                 msg.obj = "登录失败!";
44             }
45             handler.sendMessage(msg);
46         }
47     };
48 
49     public class MyHandler extends android.os.Handler {
50         @Override
51         public void handleMessage(Message msg) {
52             switch (msg.what) {
53                 case 7634:
54                     Toast.makeText(context, msg.obj.toString(), Toast.LENGTH_LONG).show();
55             }
56         }
57     }
58 }
View Code

 

(3)运行效果

 

代码混淆是最简单、最基础的Android APP安全保障,后续将还会介绍其他的关于APP安全相关技术。

本实例DEMO下载地址(MyApplication4 源码项目,JarTest模拟第三方项目)。

posted @ 2016-06-20 23:09 小书包 阅读(...) 评论(...) 编辑 收藏