android APK动态添加数据
来源:互联网 发布:安心360定位软件 编辑:程序博客网 时间:2024/05/23 13:10
前言:
前段时间遇到个需求:
1.需要在不安装apk的前提下,获取当前apk的渠道信息。
2.用户在特定的页面下载的apk,需要跳到与app中对应的页面,从而让app的用户体验更好。
第一个需求好处理,只需要解析xml文件就可以获取到渠道信息了,如果不清楚的可以看我这篇博客android 解析未安装apk中的AndroidManifest.xml以及系统源码分析。
第二个需求就不好处理,虽然也可以通过写入androidManifest.xml,但是如果这样的页面过多的话,就需要打很多个包,可以说是一种极端的处理方法。后来又想到过插件化的方式来处理,但是一想,这样也不太好,会导致内部逻辑复杂,同样的服务端要准备多个包来供客户端实现插件化。后来通过公司的一个技术文档了解到,原来zip文件能动态写入信息,我们的apk也就是一个zip文件,它有个Comment属性能动态往里面写入信息,这样就能在不破坏apk的结构的基础上,动态添加需要跳转的页面的信息。那我们只需要把我们的渠道包给到后台,然后后台往里面添加数据就可以了。
在这之前需要先了解下zip的文件结构:
这里有个链接--- ZIP文件结构
它的整体结构是这样的:
Overall .ZIP file format: [local file header 1] [file data 1] [data descriptor 1] . . . [local file header n] [file data n] [data descriptor n] [archive decryption header] (EFS) [archive extra data record] (EFS) [central directory] [zip64 end of central directory record] [zip64 end of central directory locator] [end of central directory record]
基本上是由 (文件头+数据+目录结构)....+整体目录结构+末尾信息 组成的。这里就不详细说明了,网上有许多关于Zip文件结构的分析。而我们的Coment就.end of central directory record 中。
end of central directory record:
End of central directory record: end of central dir signature 4 bytes (0x06054b50) number of this disk 2 bytes number of the disk with the start of the central directory 2 bytes total number of entries in the central directory on this disk 2 bytes total number of entries in the central directory 2 bytes size of the central directory 4 bytes offset of start of central directory with respect to the starting disk number 4 bytes .ZIP file comment length 2 bytes .ZIP file comment (variable size)
这里只需要看最后两个字段,zip文件comment的长度,占2个字节,而我们的short类型也是占2个字节,所以comment的长度不能超过short的最大长度。并且每个zip文件这个comment信息在没有添加之前是为null的,所以不需要担心我们打包的apk中会有comment信息,并不会影响我们添加数据进去的完整性。
整个demo就两个类,一个是AddMessage,一个是GetMessage
先看下我们的AddMessage
public class AddMessage {public static void main(String[] args) {// TODO Auto-generated method stubbyte[] bytes = new byte[2];ZipFile zipFile = null;ByteArrayOutputStream baos = null;RandomAccessFile ranFile = null;File file = null;try {//获取apk文件file = new File("XXX.apk");zipFile = new ZipFile(file);//直接拿到comment,这个方法仅仅在JAVA7中存在//而android4.4之前是不支持的,所以我们只能根据comment的长度来获取commentString zipComment = zipFile.getComment();System.out.println("zipComment : " + zipComment);if(zipComment != null){return;}String comment = "123456789";byte[] byteComment = comment.getBytes();baos = new ByteArrayOutputStream();//这里值得注意的是,我们在客户端获取comment的时候并不知道comment有多长//所以在comment的末尾也把我们写的comment的长度追加进去//这样在客户端获取comment信息的时候,我们把最后的两字节信息获取到就是我们的comment长度信息了baos.write(byteComment);baos.write(shortToByte((short)byteComment.length));byte[] data = baos.toByteArray();ranFile = new RandomAccessFile(file, "rw");ranFile.seek(file.length() - 2);ranFile.write(shortToByte((short) data.length));ranFile.write(data);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally {try {if(zipFile!=null){zipFile.close();}if(baos != null){baos.close();}if(ranFile!=null){ranFile.close();}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}//short转成byte[]public static byte[] shortToByte(short number) { int temp = number; byte[] b = new byte[2]; for (int i = 0; i < b.length; i++) { b[i] = new Integer(temp & 0xff).byteValue();// 将最低位保存在最低位 temp = temp >> 8; // 向右移8位 } return b; } }
然后就是我们GetMessage的代码:
public class GetMessage {public static void main(String[] args) {// TODO Auto-generated method stubFile file = null;file = new File("XXX.apk");getComment(file);}public static String getComment(File file) { byte[] bytes = null; try { RandomAccessFile accessFile = new RandomAccessFile(file, "r"); long index = accessFile.length(); bytes = new byte[2]; index = index - bytes.length; accessFile.seek(index); accessFile.readFully(bytes); //拿到我们追加到comment中的comment长度 int contentLength = byteToShort(bytes); bytes = new byte[contentLength]; index = index - bytes.length; accessFile.seek(index); accessFile.readFully(bytes); System.out.println("comment-String : " + new String(bytes, "utf-8")); return new String(bytes, "utf-8"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null;}public static short byteToShort(byte[] b) { short s = 0; short s0 = (short) (b[0] & 0xff);// 最低位 short s1 = (short) (b[1] & 0xff); s1 <<= 8; s = (short) (s0 | s1); return s; } }
最后得到的comment也就是我们写入的123456789,具体该注意的地方已经在代码中写出来了,所以这里就不做详细说明了。
zip的动态写入数据就说到这了,这里值得一说的是,真正在应用中使用的时候,我们的apk的路径可以通过context.getPackageCodePath()获取到,这是在安装的时候原apk复制到我们/data/app目录下的复制文件路径,所以并不要担心用户在安装之后把我们的apk删除了这种方法就没效果了。当然它也能完成我们的需求1,这样在不需要解析androidManifest.xml文件也能获取到apk渠道信息。
阅读全文
0 0
- android APK动态添加数据
- Android -- ListView 动态添加数据
- android动态加载apk
- android动态加载apk
- Android 动态加载 APK
- android添加第三方apk
- 动态添加数据
- datagridview 数据动态添加
- ListView动态添加数据
- highchart 动态添加数据
- DataGridView动态添加数据
- 动态添加数据(一)
- 动态添加数据(二)
- 动态添加数据(三)
- 动态添加数据
- Android apk动态加载机制
- MSF android apk动态分析
- android 动态加载apk实现
- 深入研究java.lang.Runtime类
- Android 蓝牙通信及自定义消息协议的解析和生成
- char*以及char*p
- 快学scala之Array、List、tuple模式匹配
- ORACLE数据库分页的集中方法
- android APK动态添加数据
- 快速排序的原理
- [LeetCode] Longest Valid Parentheses
- JAVA JBDC连接MySql数据库示例心得一
- 数组-leetcode 1. Two Sum
- [Algorithm] Combination问题
- JFreeChart 制作3D饼状图分析数据
- ARouter源码解析03-路由跳转
- RabbitMQ 学习笔记(四):Routing