本文为原创文章,转载请注明出处。
文章最后会附带源码下载地址,有需要的朋友可下载。
通常我们编写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模块
用户登录信息实体:
class="code_img_closed" src="/Upload/Images/2016062105/0015B68B3C38AA5B.gif" alt="" />logs_code_hide('7deffe40-4f6d-4ac4-a165-dc1449cdb1d5',event)" src="/Upload/Images/2016062105/2B1B950FA3DF188F.gif" alt="" />
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文件
DBA1F132E913BA90.png" alt="" />
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模拟第三方项目)。