本文为原创文章,转载请注明出处。
文章最后会附带源码下载地址,有需要的朋友可下载。
通常我们编写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模拟第三方项目)。