最近查看一下电脑资料,找到一个印象很深刻的项目。记得几年前刚进入公司两个星期,第一次接触游戏SDK玩意,还在熟悉代码的过程中,产品经理开了个需求会,意思是现在和三七互娱在对接一个游戏,公司想在PC端实现多渠道打包和替换背景图(目前背景图是拉伸的,要替换三七互娱给的背景图),就是运营人员在PC端上能操作多渠道打包。类似易接那样。我脑海里十万个为什么,什么鬼?什么东西?什么情况?这不是要C语言来实现的吗?不是要C来生成exe执行程序吗?按我的经验我实现不了,产品经理回复,我不管,只要很简单实现就可以了。
回到座位上脑袋一片空白,完全没有思路。那时很想放弃,但后来想想,还是尝试一下。
想到要解决的问题
- java如何在PC端可运行?
- 操作界面如何绘画?
- 如何实现替换渠道号和背景图?
通过求助百度,大致有了思路
- eclipse中打包成jar包可选择可运行jar。(习惯使用Android Studio开发,eclipse使用不熟悉,没办法,回归到eclipse)
- java中有个swing的界面库(java中不经常使用,几乎抛弃了)
- 幸好渠道文件是放在asset中,不被编译成二进制,只要将asset文件下的渠道文件替换渠道号就可,那么整体就是解包--替换渠道号和背景图-->打包并签名。
以下是部分实现代码,希望给遇到类似的问题的同学们提供个思路。
绘制页面代码:
private static final long serialVersionUID = 1L;
private JTextField apkUploadName;
private JButton upLoadBtn;// 上传按钮
private JTextField apkFilePath;// 显示上传路径
private JTextField channelName;
private JTextField channelField;
private JButton changeChannelBtn;// 更改渠道号
private JTextField appidTextField;// 修改appid
private JTextField appKeyTextField;// 修改appKey
private JTextField bgField;
private JButton changeBgImageBtn;// 更改背景图
private JTextField bgTextUrlField;
private JTextField splashText;
private JButton changeSplashImageBtn;// 更改背景图
private JTextField splashTextUrlField;
private JButton signButton;// 打包签名
private JProgressBar progressBar;
private JTextField signTextUrl;// 路径
private JTextField signText;// 签名路径
private JButton sureBtn;// 确认按钮
private JButton canclBtn;// 取消按钮
public static final int WIDTH = 800;
public static final int HEIGTH = 600;
private JTextField appid;
private JTextField appKey;
private ArrayList<String> channelkey = new ArrayList<String>();
private JPanel biggerJPanel;
/*public static void main(String[] args) {
ToolDemo toolDemo = new ToolDemo();
toolDemo.setVisible(true);
}*/
public ToolDemo() {
super("游戏母包分包工具");
this.setSize(WIDTH, HEIGTH);
this.setLocation(500, 300); // 设置窗体在屏幕的位置
Container contentPane = getContentPane();
contentPane.setBackground(Color.CYAN);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 设置JVM退出后,窗体的退出属性
initView();
initJPane();
}
private void initView() {
apkUploadName=new JTextField(Constants.APKUPLOADNAME);
apkUploadName.setBounds(50, 50, 90, 25);
setStyle(apkUploadName);
apkFilePath = new JTextField();
apkFilePath.setHorizontalAlignment(JTextField.LEFT);
apkFilePath.setBounds(150, 50, 450, 25);
upLoadBtn = new JButton(Constants.UPLOADAPK);
upLoadBtn.addActionListener(this);
upLoadBtn.setBounds(620, 50, 80, 25);
channelName = new JTextField(Constants.CHANGECHANNEL);
channelName.setBounds(50, 100, 80, 25);
setStyle(channelName);
channelField = new JTextField();
channelField.setEditable(true);
channelField.setBounds(150, 100, 450, 25);
changeChannelBtn = new JButton(Constants.CHANGECHANNEL);
changeChannelBtn.addActionListener(this);
changeChannelBtn.setBounds(620, 100, 80, 25);
appid = new JTextField(Constants.APPID);
appid.setBounds(50, 150, 50, 25);
setStyle(appid);
appidTextField = new JTextField();
appidTextField.setEditable(true);
appidTextField.setBounds(130, 150, 100, 25);
appKey = new JTextField(Constants.APPKEY);
appKey.setBounds(250, 150, 60, 25);
setStyle(appKey);
appKeyTextField = new JTextField();
appKeyTextField.setEditable(true);
appKeyTextField.setBounds(320, 150, 200, 25);
bgField = new JTextField(Constants.UPDATEBACKGROUD);
bgField.setBounds(50, 200, 80, 25);
setStyle(bgField);
bgTextUrlField = new JTextField();
bgTextUrlField.setHorizontalAlignment(JTextField.LEFT);
bgTextUrlField.setBounds(150, 200, 450, 25);
changeBgImageBtn = new JButton(Constants.LIULAN);
changeBgImageBtn.addActionListener(this);
changeBgImageBtn.setBounds(620, 200, 80, 25);
splashText = new JTextField(Constants.UPDATESPLASH);
splashText.setBounds(50, 250, 80, 25);
setStyle(splashText);
splashTextUrlField = new JTextField();
splashTextUrlField.setHorizontalAlignment(JTextField.LEFT);
splashTextUrlField.setBounds(150, 250, 450, 25);
changeSplashImageBtn = new JButton(Constants.LIULANIMAGE);
changeSplashImageBtn.addActionListener(this);
changeSplashImageBtn.setBounds(620, 250, 80, 25);
signText = new JTextField(Constants.SIGNURL);
signText.setBounds(50, 300, 80, 25);
setStyle(signText);
signTextUrl = new JTextField();
signTextUrl.setHorizontalAlignment(JTextField.LEFT);
signTextUrl.setBounds(150, 300, 450, 25);
signButton = new JButton(Constants.CHOOSE);
signButton.addActionListener(this);
signButton.setBounds(620, 300, 80, 25);
progressBar=new JProgressBar();
progressBar.setBounds(150, 310, 450, 30);
progressBar.setMinimum(0);
progressBar.setMaximum(100);
progressBar.setStringPainted(true);
progressBar.setBorderPainted(true);
progressBar.setUI(new MetalProgressBarUI());
progressBar.setBackground(Color.CYAN);
progressBar.setForeground(Color.BLUE);
sureBtn = new JButton(Constants.SURE);
sureBtn.addActionListener(this);
sureBtn.setBounds(200, 400, 100, 25);
canclBtn = new JButton(Constants.CANCLE);
canclBtn.addActionListener(this);
canclBtn.setBounds(400, 400, 100, 25);
}
private void initJPane() {
biggerJPanel = new JPanel();
biggerJPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
//biggerJPanel.setBackground(new Color(0, 0, 0,1));
setContentPane(biggerJPanel);
biggerJPanel.setLayout(null);
biggerJPanel.add(apkUploadName);
biggerJPanel.add(apkFilePath);
biggerJPanel.add(upLoadBtn);
biggerJPanel.add(changeChannelBtn);
biggerJPanel.add(channelField);
biggerJPanel.add(channelName);
biggerJPanel.add(appidTextField);
biggerJPanel.add(appKeyTextField);
biggerJPanel.add(appid);
biggerJPanel.add(appKey);
biggerJPanel.add(bgField);
biggerJPanel.add(bgTextUrlField);
biggerJPanel.add(changeBgImageBtn);
biggerJPanel.add(splashText);
biggerJPanel.add(changeSplashImageBtn);
biggerJPanel.add(splashTextUrlField);
/*biggerJPanel.add(signButton);
biggerJPanel.add(signText);
biggerJPanel.add(signTextUrl);*/
//biggerJPanel.add(progressBar);
biggerJPanel.add(sureBtn);
biggerJPanel.add(canclBtn);
}
private void setStyle(JTextField text) {
text.setBorder(null);
text.setHorizontalAlignment(JTextField.LEFT);
text.setFont(new Font("楷体", Font.PLAIN, 16));
text.setEditable(false);
}
解包:
// 解压
@SuppressWarnings("rawtypes")
public static void unZip(String fileName, String filePath) throws Exception {
ZipFile zipFile = new ZipFile(fileName);
Enumeration emu = zipFile.entries();
while (emu.hasMoreElements()) {
ZipEntry entry = (ZipEntry) emu.nextElement();
if (entry.isDirectory()) {
new File(filePath + entry.getName()).mkdirs();
continue;
}
BufferedInputStream bis = new BufferedInputStream(
zipFile.getInputStream(entry));
File file = new File(filePath + entry.getName());
File parent = file.getParentFile();
if (parent != null && (!parent.exists())) {
parent.mkdirs();
}
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos, BUFFER);
byte[] buf = new byte[BUFFER];
int len = 0;
while ((len = bis.read(buf, 0, BUFFER)) != -1) {
fos.write(buf, 0, len);
}
bos.flush();
bos.close();
bis.close();
}
zipFile.close();
}
读取文件和修改文件
// 读取渠道文件
public static void setChannelFile(String txtPath,ArrayList<String> channelkey) {
File f = new File(txtPath);
if (f.exists() && f.isFile()) {
try {
BufferedReader br = new BufferedReader(new FileReader(f));
String line = null;
while ((line = br.readLine()) != null) {
channelkey.add(line.trim());
}
System.out.println(channelkey.toString());
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "渠道文件有误", "错误", JOptionPane.ERROR_MESSAGE);
}
}
}
//读取、修改、写出txt文件
public static void updateChannelkey(String path, String key)throws Exception {
BufferedReader br = new BufferedReader(new FileReader(path));
String lineKey = null;
StringBuffer sb = new StringBuffer();
while ((lineKey = br.readLine()) != null) {
System.out.println(lineKey);
lineKey = lineKey.replaceAll(lineKey.trim(), key);
System.out.println(lineKey);
sb.append(lineKey + "\n");
}
br.close();
FileWriter fw = new FileWriter(path);
fw.write(sb.toString());
fw.close();
}
最后打包和签名
//打包及签名
public static void compressAndSignApk(StringBuffer buffer, String signPath,
String apkSrcPath, String apkName, String prefixName,
String targetPath, String resultPath, String key) throws Exception {
// 判断创建文件夹
File targetFile = new File(targetPath);
if (!targetFile.exists()) {
targetFile.mkdir();
}
//打包
ZipUtils.compress(apkSrcPath+ Constants.SOURE_PATH_NAME + prefixName,targetPath + apkName);
// 判断创建文件夹
File resultFile = new File(resultPath);
if (!resultFile.exists()) {
resultFile.mkdir();
}
// 组合签名命令
String apkSign = apkName.split(".apk")[0] + "_signed_"+ key + ".apk";
buffer.setLength(0);
buffer.append("cmd.exe /c jarsigner -digestalg SHA1 -sigalg MD5withRSA -tsa https://timestamp.geotrust.com/tsa -keystore ")
.append(signPath).append(" -storepass ")
.append(Constants.SIGN_PASSWORD)
.append(" -signedjar ").append(resultPath)
.append(apkSign).append(" ") // 签名保存路径应用名称
.append(targetPath).append(apkName).append(" ") // 打包保存路径应用名称
.append(Constants.SIGN_NAME);
System.out.println(buffer.toString());
if (buffer.toString()!=null) {
CommanUtils.runCmd(buffer.toString());
}else {
JOptionPane.showMessageDialog(null, "签名有误", "错误", JOptionPane.ERROR_MESSAGE);
}
}
// 执行cmd命令
public static void runCmd(String cmd) {
Runtime rt = Runtime.getRuntime();
try {
Process p = rt.exec(cmd,null);
// p.waitFor();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String msg = null;
while ((msg = br.readLine()) != null) {
System.out.println(msg);
}
/*if(p.waitFor()!=0){
JOptionPane.showInputDialog("打包失败!");
}*/
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "cmd命令运行错误", "错误", JOptionPane.ERROR_MESSAGE);
}
}
最后说一下,用了SwingSets2(BeautyEyeLNFDemo).jar实现外观精致。实测打包50个,大概用了15分钟(系甘上下)。我不是专科出生的,没多大经验,能做出这样的一个需求,自己也觉得不可思议,但最终产品没用这个工具。希望这样的实现思路能给遇到类似的问题的伙伴们提供思路。