转载请说明出处http://blog.csdn.net/andywuchuanlong

记得上次在南昌中兴的一个项目中遇到过一个这种需求:一个app能够给多个渠道商去运营,渠道商推广出去能够获得对应的推广金额。

那么这种情况下就必需要使得这个app能够唯一的标志一个渠道商。

那个时候我们在这个项目中的解决方式是:让用户在app中手动填入渠道商的工号。我如今想想这种方式也是醉了,真不知道那个时候项目经理是怎么想的,居然会给出这种方案。

这次的项目中又遇到了这个问题:需求是这个app可以给多个渠道商去推广。渠道商可以获得推广金额。这次我提出的解决方式是:先把打包后的app解压,然后在assets文件夹中写入渠道商的唯一标识id。然后压缩app,压缩完成又一次签名app,之后就大工告成。用户在第一次进入app的时候。会把assets中的id读出来,提交到server,就完美的攻克了这个用户是此渠道商的推广所获得的用户。

首先第一步:把app解压。删除META-INF目录中的CERT.RSA和CERT.SF两个文件

第二步:读取解压后的assets文件夹中的id.txt文件,写入渠道商的id

[java] view
plain
copy

  1. File file = new File("d:/app/assets/id.txt");
  2. OutputStream outputStream = new FileOutputStream(file);
  3. outputStream.write(user.getId().toString().getBytes());
  4. outputStream.flush();
  5. outputStream.close();

第三步:压缩写入渠道商id后的全部app文件

[java] view
plain
copy

  1. ZipCompressor zc = new  ZipCompressor("d:/play.apk");
  2. zc.compressExe("d:/app/");

详细的压缩代码例如以下:

[java] view
plain
copy

  1. package com.xyc.signSystem.utils;
  2. import java.io.BufferedInputStream;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileOutputStream;
  6. import java.util.zip.CRC32;
  7. import java.util.zip.CheckedOutputStream;
  8. import org.apache.tools.zip.ZipEntry;
  9. import org.apache.tools.zip.ZipOutputStream;
  10. /**
  11. * @ClassName: ZipCompressor
  12. * @author :andywuchuanlong   QQ:312037487
  13. * @Description: 压缩文件的通用工具类-採用org.apache.tools.zip.ZipOutputStream实现。较复杂。
  14. *
  15. */
  16. public class ZipCompressor {
  17. static final int BUFFER = 8192;
  18. private File zipFile;
  19. /**
  20. * 压缩文件构造函数
  21. *
  22. * @param pathName
  23. *            压缩的文件存放文件夹
  24. */
  25. public ZipCompressor(String pathName) {
  26. zipFile = new File(pathName);
  27. }
  28. /**
  29. * 运行压缩操作
  30. *
  31. * @param srcPathName
  32. *            被压缩的文件/目录
  33. */
  34. public void compressExe(String srcPathName) {
  35. File file = new File(srcPathName);
  36. if (!file.exists()) {
  37. throw new RuntimeException(srcPathName + "不存在!");
  38. }
  39. try {
  40. FileOutputStream fileOutputStream = new FileOutputStream(zipFile);
  41. CheckedOutputStream cos = new CheckedOutputStream(fileOutputStream,
  42. new CRC32());
  43. ZipOutputStream out = new ZipOutputStream(cos);
  44. String basedir = "";
  45. compressByType(file, out, basedir);
  46. out.close();
  47. } catch (Exception e) {
  48. e.printStackTrace();
  49. throw new RuntimeException(e);
  50. }
  51. }
  52. /**
  53. * 推断是文件夹还是文件。依据类型(文件/文件夹)运行不同的压缩方法
  54. *
  55. * @param file
  56. * @param out
  57. * @param basedir
  58. */
  59. private void compressByType(File file, ZipOutputStream out, String basedir) {
  60. if (basedir.equals("play/")) {
  61. basedir = "";
  62. }
  63. /* 推断是文件夹还是文件 */
  64. if (file.isDirectory()) {
  65. this.compressDirectory(file, out, basedir);
  66. } else {
  67. this.compressFile(file, out, basedir);
  68. }
  69. }
  70. boolean isFirst = true;
  71. /**
  72. * 压缩一个文件夹
  73. *
  74. * @param dir
  75. * @param out
  76. * @param basedir
  77. */
  78. private void compressDirectory(File dir, ZipOutputStream out, String basedir) {
  79. if (!dir.exists()) {
  80. return;
  81. }
  82. if (basedir.equals("play/")) {
  83. basedir = "";
  84. }
  85. File[] files = dir.listFiles();
  86. for (int i = 0; i < files.length; i++) {
  87. /* 递归 */
  88. compressByType(files[i], out, basedir + dir.getName() + "/");
  89. }
  90. }
  91. /**
  92. * 压缩一个文件
  93. *
  94. * @param file
  95. * @param out
  96. * @param basedir
  97. */
  98. private void compressFile(File file, ZipOutputStream out, String basedir) {
  99. if (!file.exists()) {
  100. isFirst = false;
  101. return;
  102. }
  103. if (basedir.equals("play/")) {
  104. basedir = "";
  105. }
  106. try {
  107. BufferedInputStream bis = new BufferedInputStream(
  108. new FileInputStream(file));
  109. ZipEntry entry = new ZipEntry(basedir + file.getName());
  110. out.putNextEntry(entry);
  111. int count;
  112. byte data[] = new byte[BUFFER];
  113. while ((count = bis.read(data, 0, BUFFER)) != -1) {
  114. out.write(data, 0, count);
  115. }
  116. bis.close();
  117. } catch (Exception e) {
  118. throw new RuntimeException(e);
  119. }
  120. }
  121. }

第四步:压缩完成之后。此时的包是没有签名过的,所以还须要签名。签名能够使用jarsigner工具,首先我们要寻找到java的安装文件夹

[java] view
plain
copy

  1. <span style="white-space:pre">    </span>public  String getJavaPath() {
  2. String javaPath = (String) System.getenv("Path");
  3. String paths[]= javaPath.split(";");
  4. String myPath = null;
  5. for(String path:paths){
  6. if (path.contains("Java")&&!path.contains("jre")
  7. &&path.contains("bin") ){
  8. myPath = path;
  9. break;
  10. }
  11. }
  12. return myPath+"\\";
  13. }

签名:

[java] view
plain
copy

  1. <span style="white-space:pre">    </span>String javaPath = getJavaPath();
  2. Runtime rt = Runtime.getRuntime();
  3. String cmd = javaPath
  4. + "jarsigner -verbose"
  5. + " -keystore "+ keystorePath
  6. + " -storepass player"// password
  7. + " -signedjar "+signedApkPath // 签名后的apk存放位置
  8. + " -digestalg SHA1  -sigalg  MD5withRSA "
  9. + unsignedApkPath//未签名的apk
  10. + " player";// 别名
  11. Process child = rt.exec(cmd);

OK,签名成功。

更改已经签名的app中的内容的更多相关文章

  1. 解决“iOS 7 app自动更新,无法在app中向用户展示更新内容”问题

    转自cocoachina iOS 7能在后台自动app,这对开发者来说和用户都很方便,但是还是有一些缺点.用户不会知道app本次更新的内容,除非他们上到app的App Store页面去查看.开发者也会 ...

  2. 嵌入式表单字段中的内容可能被server更改以删除不安全的内容。是否要又一次载入您的页面以查看保存结果?

    嵌入式表单字段中的内容可能被server更改以删除不安全的内容.是否要又一次载入您的页面以查看保存结果?         近期有朋友问到,当他在SharePoint首页上进行编辑时.插入一段代码. 完 ...

  3. android APP 中微信分享功能实现 的总结

    //花了很长时间最终完成了微信分享功能,中间走了很多弯路,在此做一下小结,希望对在应用中使用到微信分享的朋友有所帮助. 主要问题就是下面两个: 1.为什么运行了项目之后,微信分享只是闪了一下就没有了? ...

  4. [Phonegap+Sencha Touch] 移动开发77 Cordova Hot Code Push插件实现自己主动更新App的Web内容

    原文地址:http://blog.csdn.net/lovelyelfpop/article/details/50848524 插件地址:https://github.com/nordnet/cord ...

  5. 全面分析:APP中的消息功能设计

    一.定义 APP的“消息”模块,是通过APP或手机这个客户端,围绕某个产品的功能进行交流.沟通的重要方式.这种沟通,一方是运营人员或商家,也可以是产品或系统本身,为方便说明笔者这里姑且统一简称为B端, ...

  6. ViewPager封装工具类: 轻松实现APP导航或APP中的广告栏

    相信做app应用开发的,绝对都接触过ViewPager,毕竟ViewPager的应用可以说无处不在:APP第一次启动时的新手导航页,APP中结合Fragment实现页面滑动,APP中常见的广告栏的自动 ...

  7. 替换excel模板中的内容并使用JavaMail发送邮件

    由于在公司工作,常年出差,每天都要以日报的形式向公司汇报当天的工作内容.而日报的内容大体上就只有当天工作的主要内容时变化的,其余的都是不变 的. 而我的电脑刚打开excel有点卡,因此决定使用Java ...

  8. 分享 Ionic 开发 Hybrid App 中遇到的问题以及后期发布 iOS/Android 的方方面面

    此篇文章主要整理了最近在使用 Ionic 开发 Hybrid App 过程中遇到的一些疑难点以及后期发布生成 iOS 和 Android 版本过程中的种种问题. 文章目录 Ionic 简介和项目需求介 ...

  9. 【iOS开发】在 App 中加入 AdMob 广告 - 入门介绍与编程技巧

    前言 虽然在App中加入广告来盈利是比较低级的商业化方式,但对于个人开发者或者小团队开发者来说,做出一个简单易用的免费小工具(举个栗子),在里面加入一些广告,如果用的人多,也是可以带来一些可观的收入的 ...

随机推荐

  1. Selenium WebDriver- 通过源码中的关键字找到我们要操作的句柄,用于多个窗口之间切换

    #encoding=utf-8 import unittest import time from selenium import webdriver from selenium.webdriver i ...

  2. Linux安装Scala

    下载Scala地址http://downloads.typesafe.com/scala/2.10.6/scala-2.10.6.tgz然后解压Scala到指定目录 tar -zxvf scala-2 ...

  3. day04_05 逻辑运算符、表达式

    num += 1 等价于 num = num + 1 逻辑运算符 and   全true则true 条件1 and 条件2 5>3 and 3>2   ===> true 5> ...

  4. [python][oldboy]关键字参数和位置参数,默认参数,可变长参数(无名,有名)

    关键字参数和位置参数是在函数调用的时候定义 关键字参数是以键-值对出现,无序,多一个不行,少一个不行 位置参数,有序,多一个不行,少一个不行 混搭:所有位置参数必须在关键字参数的左边 默认参数是在函数 ...

  5. 在SAE搭建微信公众账号服务

    让我们回到2014年11月,从公司请假回成都,在天府软件园B区旁边的小区里,那个10多平米的出租屋里,闲来无事,我想找个事情做一做,好让我这漂浮的心静下来.大约在半年前就申请了微信的一个公众账号,一直 ...

  6. 天气预报接口:SmartWeather API中key的计算方法

    这个代码大家都蛮感兴趣,我在git开源了一个用于收集天气信息的系统,基于python语言的,请大家参考: https://github.com/BerlinSun/WeatherForecast -- ...

  7. 广东工业大学2016校赛决赛重现——E积木积水(方法据说很多)

    Problem E: 积木积水 Description 现有一堆边长为1的已经放置好的积木,小明(对的,你没看错,的确是陪伴我们成长的那个小明)想知道当下雨天来时会有多少积水.小明又是如此地喜欢二次元 ...

  8. 【CCF】有趣的数 数位dp

    [思路] dp[i][j]表示前i个数为第j种状态,考虑6种状态 0: 出现且仅出现 2 1: 出现且仅出现 2 0 2: 出现且仅出现 2 3 3: 出现且仅出现 2 0 1 4: 出现且仅出现 2 ...

  9. Dumb Bones(uva 10529)

    题意:给定n,表示要放n个骨牌,每次放下骨牌,有可能向左倒的概率为pl,向右倒的概率为pr,如果倒下,会将那一侧的骨牌全部推倒,可以选择位置先后放骨牌,问说一种放骨牌次数最少的期望是多少. /* 设d ...

  10. 10个JavaScript难点--摘抄

    1. 立即执行函数 立即执行函数,即Immediately Invoked Function Expression (IIFE),正如它的名字,就是创建函数的同时立即执行.它没有绑定任何事件,也无需等 ...