分享一种最简单的Android打渠道包的方法

2018年7月12日补充:
此文章写的早了,现在安卓出了v2签名,以下方法已经不适用了。推荐适用腾讯出的支持v1+v2且更高效打包方式:
腾讯的多渠道打包:https://github.com/Tencent/VasDolly

=======================================================

做Android开发一转眼就四年了,以前是用ant打包的,习惯了也没觉得慢。

今年年初加入了新公司,新公司用的是Android studio开发,用的是gradle构建项目。

由于gradle构建每次都是重新编译项目,所以打包时就特别慢了,16个渠道包要打一个小时吧。

然后我们的项目负责人就交给我一个任务,研究下有什么快的打包方法,

并发给我一篇参考文章:http://tech.meituan.com/mt-apk-packaging.html

我一边写代码一边测试,终于找到了一种很快的打渠道包的方法。

因为APK其实就是ZIP的格式,所以,解压apk后,会看到里面有个META-INF目录。

由于META-INF目录并不会影响到APK的签名和运行,所以我们可以在META-INF目录里添加一个空文件,

不同的渠道就添加不同的空文件,文件名代表不同的渠道。

代码是java写的:

public class Tool {

	private static final String CHANNEL_PREFIX = "/META-INF/";
	private static final String CHANNEL_PATH_MATCHER = "regex:/META-INF/mtchannel_[0-9a-zA-Z]{1,5}";
	private static String source_path;
	private static final String channel_file_name = "channel_list.txt";
	private static final String channel_flag = "channel_";

	public static void main(String[] args) throws Exception {
		if (args.length <= 0) {
			System.out.println("请输入文件路径作为参数");
			return;
		}


		final String source_apk_path = args[0];//main方法传入的源apk的路径,是执行jar时命令行传入的,不懂的往下看。
		int last_index = source_apk_path.lastIndexOf("/") + 1;
		source_path = source_apk_path.substring(0, last_index);
		final String source_apk_name = source_apk_path.substring(last_index, source_apk_path.length());

		System.out.println("包路径:" + source_path);
		System.out.println("文件名:" + source_apk_name);

		ArrayList<String> channel_list = getChannelList(source_path + channel_file_name);
		final String last_name = ".apk";
		for (int i = 0; i < channel_list.size(); i++) {
			final String new_apk_path = source_path + source_apk_name.substring(0, source_apk_name.length() - last_name.length()) //
					+ "_" + channel_list.get(i) + last_name;
			copyFile(source_apk_path, new_apk_path);
			changeChannel(new_apk_path, channel_flag + channel_list.get(i));
		}
	}

	/**
	 * 修改渠道号,原理是在apk的META-INF下新建一个文件名为渠道号的文件
	 */
	public static boolean changeChannel(final String zipFilename, final String channel) {
		try (FileSystem zipfs = createZipFileSystem(zipFilename, false)) {

			final Path root = zipfs.getPath("/META-INF/");
			ChannelFileVisitor visitor = new ChannelFileVisitor();
			Files.walkFileTree(root, visitor);

			Path existChannel = visitor.getChannelFile();
			Path newChannel = zipfs.getPath(CHANNEL_PREFIX + channel);
			if (existChannel != null) {
				Files.move(existChannel, newChannel, StandardCopyOption.ATOMIC_MOVE);
			} else {
				Files.createFile(newChannel);
			}

			return true;

		} catch (IOException e) {
			System.out.println("添加渠道号失败:" + channel);
			e.printStackTrace();
		}

		return false;
	}

	private static FileSystem createZipFileSystem(String zipFilename, boolean create) throws IOException {
		final Path path = Paths.get(zipFilename);
		final URI uri = URI.create("jar:file:" + path.toUri().getPath());

		final Map<String, String> env = new HashMap<>();
		if (create) {
			env.put("create", "true");
		}
		return FileSystems.newFileSystem(uri, env);
	}

	private static class ChannelFileVisitor extends SimpleFileVisitor<Path> {
		private Path channelFile;
		private PathMatcher matcher = FileSystems.getDefault().getPathMatcher(CHANNEL_PATH_MATCHER);

		public Path getChannelFile() {
			return channelFile;
		}

		@Override
		public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
			if (matcher.matches(file)) {
				channelFile = file;
				return FileVisitResult.TERMINATE;
			} else {
				return FileVisitResult.CONTINUE;
			}
		}
	}

	/** 得到渠道列表 */
	private static ArrayList<String> getChannelList(String filePath) {
		ArrayList<String> channel_list = new ArrayList<String>();

		try {
			String encoding = "UTF-8";
			File file = new File(filePath);
			if (file.isFile() && file.exists()) { // 判断文件是否存在
				InputStreamReader read = new InputStreamReader(new FileInputStream(file), encoding);// 考虑到编码格式
				BufferedReader bufferedReader = new BufferedReader(read);
				String lineTxt = null;
				while ((lineTxt = bufferedReader.readLine()) != null) {
					// System.out.println(lineTxt);
					if (lineTxt != null && lineTxt.length() > 0) {
						channel_list.add(lineTxt);
					}
				}
				read.close();
			} else {
				System.out.println("找不到指定的文件");
			}
		} catch (Exception e) {
			System.out.println("读取文件内容出错");
			e.printStackTrace();
		}

		return channel_list;
	}

	/** 复制文件 */
	private static void copyFile(final String source_file_path, final String target_file_path) throws IOException {

		File sourceFile = new File(source_file_path);
		File targetFile = new File(target_file_path);

		BufferedInputStream inBuff = null;
		BufferedOutputStream outBuff = null;
		try {
			// 新建文件输入流并对它进行缓冲
			inBuff = new BufferedInputStream(new FileInputStream(sourceFile));

			// 新建文件输出流并对它进行缓冲
			outBuff = new BufferedOutputStream(new FileOutputStream(targetFile));

			// 缓冲数组
			byte[] b = new byte[1024 * 5];
			int len;
			while ((len = inBuff.read(b)) != -1) {
				outBuff.write(b, 0, len);
			}
			// 刷新此缓冲的输出流
			outBuff.flush();
		} catch (Exception e) {
			System.out.println("复制文件失败:" + target_file_path);
			e.printStackTrace();
		} finally {
			// 关闭流
			if (inBuff != null)
				inBuff.close();
			if (outBuff != null)
				outBuff.close();
		}
	}
}


1、新建一个java工程,把上面的代码复制进去。 

2、对着这个类点右键,选择Export-java-Runnable JAR file

3、在Launch configuration中,选择你所要导出的类(如果这里不能选择,那么你要run一下你的工程,run成功了才能选择你要导为jar的类),

假设导出的jar的名字是apktool.jar

然后在命令行输入:

java -jar /Users/company/Documents/apk/apktool.jar /Users/company/Documents/apk/test.apk

我用的mac电脑,路径和windows不一样,上面的路径都是拖拽进命令行的。

/Users/company/Documents/apk/apktool.jar 表示jar包所在路径;
/Users/company/Documents/apk/test.apk表示你源apk路径,这个是作为命令行参数传入main方法的。

 

test.apk就是你已经打包成功的一个apk,就是源apk,在你源apk的基础上生成渠道包。

channel_list.txt一定要和这个源apk在同一个目录下。

比如channel_list.txt里面的数据结构如下:

360
xiaomi
anzhi
baidu

运行命令后,你会发现在channel_list.txt和源apk目录下,会生成你想要的渠道包。

你可以把扩展名改为.zip,然后解压看看是否在META-INF目录下生成你想要的渠道名文件。


最后,就是读取这个渠道标识了,代码是写在Android工程里的,代码如下:

private static String channel = null;    

public static String getChannel(Context context) {
        if (channel != null) {
            return channel;
        }

        final String start_flag = "META-INF/channel_";
        ApplicationInfo appinfo = context.getApplicationInfo();
        String sourceDir = appinfo.sourceDir;
        ZipFile zipfile = null;
        try {
            zipfile = new ZipFile(sourceDir);
            Enumeration<?> entries = zipfile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = ((ZipEntry) entries.nextElement());
                String entryName = entry.getName();
                if (entryName.contains(start_flag)) {
                    channel = entryName.replace(start_flag, "");
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (zipfile != null) {
                try {
                    zipfile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        if (channel == null || channel.length() <= 0) {
            channel = "guanwang";//读不到渠道号就默认是官方渠道
        }
        return channel;
    }


如果你用的友盟统计,可以在主Activity里这么写:AnalyticsConfig.setChannel("获取到的渠道");

好了,结束了,有问题留言。

===============================

如果你觉得帮到了你,请给作者打赏一口饭吃:


### 回答1: 很高兴为您解答。您可以使用Android Studio开发电子书应用,首先需要安装Android Studio,然后创建新的项目,使用XML和Java语言,编写你的代码,然后编译,打,发布应用程序,完成电子书App开发。 ### 回答2: 使用Android Studio编写一个电子书应用程序相对较简单。以下是一个简要的步骤: 1. 创建一个新的Android项目:在Android Studio中,选择“新建项目”。然后,填写应用程序的名称、名等基本信息。 2. 设计应用程序的界面:使用Android Studio提供的布局编辑器设计应用程序的界面布局。可以在界面中添加文本视图、图像视图和按钮等组件来显示电子书的内容以及操作元素。 3. 添加必要的资源文件:在应用程序的资源文件夹中,以正确的格式存储电子书的内容。这可以是一个单独的文件夹,存放电子书的页面内容和相关的图像和音频等资源。 4. 创建与电子书相关的类:为了管理电子书的加载和显示,可以创建一个Book类,其中含电子书的信息和内容。还可以创建一个BookReader类,用于将电子书的内容呈现在应用程序的界面上。 5. 实施读取电子书的功能:在BookReader类中,编写方法来读取电子书的内容,并通过文本视图和图像视图等组件来显示。 6. 添加用户交互功能:为了提供更好的用户体验,可以添加一些用户交互功能。例如,通过按钮或滑动手势来翻页,或者通过搜索功能来查找特定的内容等。 7. 进行测试和调试:使用Android Studio的模拟器或连接真实设备进行应用程序的测试和调试。确保应用程序在各种情况下都能正常运行,并处理异常情况。 8. 优化和打应用程序:根据需要,对应用程序进行性能优化和界面美化。最后使用Android Studio的打工具将应用程序打为APK文件,以便在真实设备上安装和分发。 虽然以上步骤提供了一个大致的概述,但实际上在创建一个电子书应用程序时可能还会涉及其他更复杂的功能,如书签、目录、字体大小调整等。因此,根据应用程序需求,可能需要更多的开发工作。但Android Studio提供了强大的工具和支持,使开发者能够轻松地构建功能丰富的电子书应用程序。 ### 回答3: 使用Android Studio编写电子书应用是一种相对简单而强大的方法。以下是一些步骤: 1. 首先,安装并配置Android Studio。在官方网站上下载并安装Android Studio。一旦安装完毕,打开它并设置相关的SDK和虚拟设备。 2. 创建一个新项目。选择“新建项目”选项并填写项目的名称和名。选择适当的最低和目标API级别,然后点击“下一步”。 3. 设计应用的用户界面。在“activity_main.xml”文件中创建应用的布局。可以使用不同的布局元素和小部件来设计主屏幕。 4. 创建相关类和方法。在“MainActivity.java”文件中创建一个类,继承自“AppCompatActivity”。在类中定义相关的方法,比如读取电子书文件和展示电子书内容等。 5. 添加必要的权限和依赖项。为了使应用能够读取和展示电子书,需要添加适当的权限。这可以通过在“AndroidManifest.xml”文件中添加权限声明来实现。 6. 将电子书文件添加到应用的资源中。将电子书文件(一般为PDF、ePub等格式)添加到项目中的“res/”目录或“assets/”目录下。 7. 实现电子书阅读功能。使用相应的库或API来读取电子书文件,并将其内容展示在应用的界面上。这可以通过编写特定的方法和逻辑来实现。 8. 测试应用。在虚拟设备或真机上进行测试,确保电子书能够正常加载和显示,并且用户界面与预期一致。 9. 修复和改进。根据测试结果和用户反馈,通过修改代码和增添功能来改进应用的性能和用户体验。 10. 打和发布。完成应用的开发和测试后,将其打成APK文件,并发布到Google Play商店或其他应用分发渠道。 以上是使用Android Studio编写电子书应用的一般步骤。根据具体需求和功能,还可以进行更多的定制和扩展。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值