android APK动态添加数据
2017-07-14 01:47
323 查看
前言:
前段时间遇到个需求: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 stub byte[] 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的长度来获取comment String 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 block e.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 block e.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 stub File 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渠道信息。
相关文章推荐
- android ListView内数据的动态添加与删除
- android listview scroll滑屏显示sqlite分页,类似聊天记录,listview动态添加sqlite分页数据
- Android 动态添加view或item并获取数据的实例
- android ListView内数据的动态添加与删除
- Android 广告(banner)图片轮播、图片浏览、仿微信大图查看控件(支持视频和gif图片)、支持动态添加数据
- Android统计图表MPAndroidChart:动态添加数据更新【6】
- Android表格布局TableLayout简单实现(Java动态添加,设置边框,删除数据(单行,多行))
- Android -- ListView 动态添加数据
- android 将Json数据动态的添加到String[]数组当中
- android ListView内数据的动态添加与删除
- Android MPAndroidChart:动态添加统计数据线【8】
- Android25闹钟项目——ArrayAdapter动态添加数据,显示数据,删除数据SharedPreferences存储数据,读取数据
- Android学习--动态向SPinner控件中添加数据
- android开发--详解ListView,动态添加,删除Adapter中的数据项
- android ListView内数据的动态添加与删除实例代码
- Android图表控件MPAndroidChart——曲线图LineChart(多条曲线)动态添加数据^a!HsMa0cWDc
- Android 动态添加view或item并获取数据
- Android ListView分页,动态添加数据
- 【Android基础篇】SimpleAdapter动态添加数据时的ListView刷新
- android ListView内数据的动态添加与删除