关于打Android aar离线包的一些经验分享

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于打Android aar离线包的一些经验分享相关的知识,希望对你有一定的参考价值。

参考技术A 几经思考后,本来想手动把a,b,c那几个库编译成jar放到aar的lib里,但是找到那些a,b,c库一看他们又分别依赖了公司另外的几个库d,e,f,顿时头大了,真要自己一个个手动编译就头大了,后来在网上找了一圈发现一个插件是解决这种难题的完美方案。就是fat-aar(胖子aar),GitHub地址:

该插件提供的功能主要是帮我们把sdk的依赖库集成到aar里,使用上还需自行灵活选择哪些依赖库需要集成进aar,哪些不需要,比如我在打完全量依赖的aar后(配置transitive为true,只embed标记公司的库依赖,三方库不打标记)就发现会出现很多类/模块重复,一看都是官方的一些库文件,原因是全量打包会把所有支付Sdk里embed标记的库依赖的依赖(树结构遍历一直到底)都集成进aar,这样肯定会出现很多重复的模块/类。
所以后来又打了一个只引入直属依赖jar包的aar((配置transitive为false,也是只embed标记公司的库依赖,三方库不打标记),这样就只会把支付Sdk里的embed标记依赖库集成进aar,而不会继续遍历下去集成更底层的依赖,如此确实解决了模块/类冲突的问题,但是因为没有把所有的依赖集成进aar,使用的时候会报很多ClassNotFound的错误。

在这里卡了很久,虽然知道问题原因,但是处理起来着实麻烦,好在fat-aar插件打包aar的时候把大部分直属/公司库依赖都是放在classes.jar里,而很多知名三方/android官方依赖放在libs目录下,如此,我便想到可以把全量aar里的classes.jar文件里的com包(里面大都是公司的依赖jar)复制替换掉直属依赖aar包里面的com包,删掉android和androidx包,最后使用修改过的直属依赖aar放到demo工程libs里跑起来后,还是报了一些知名三方库(retrofit,gson等)没有找到的错误,这都是预期会出现的问题,在demo里重新依赖一下即可。

到此,我们提供的支付Sdk的aar包里面包含的基本是我们公司自己的库相关集成,知名的三方库让使用者自行添加依赖即可,这样对集成方也更友好,因为他们自己的工程很多可能会自行集成那些知名三方库,这样就不会出现包名/类冲突的问题,但是需要集成哪些知名 三方库需要告知集成者。(期间遇到因为公司加密系统导致360压缩编辑aar和jar包时损坏文件,后面改用rar压缩解决了问题)

unity打aar包工具

需求:

unity将游戏导出android工程之后,打成aar包的工具

第一种:

高版本的unity导出的android工程是android studio版的,那么打成aar的流程就是

1.build.gradle文件中把apply plugin: ‘com.android.application‘改成apply plugin: ‘com.android.library‘
2.build.gradle文件中buildToolsVersion改为25.0.2
3.注释掉applicationId这一行
4.清单文件AndroidManifest.xml中启动Activity,一般是UnityPlayerActiity,标签内全部删除
5.src.java下类UnityPlayerActiity共三个都删除
6.然后在主目录下执行命令gradlew.bat assembleDebug生成aar

把上述流程做成java自动的工具,代码如下

package com.jinkejoy.build_aar;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;

public class ASBuildAarFile {

    private static final String CACHE_PATH = "C:\\build-apk-path.properties";
    private JFrame jFrame;

    private JTextField sourcePath_text;
    private JButton sourcePath_button;
    private File sourceFile;

    private JTextField output_text;
    private JButton output_button;
    private File outputFile;

    private JTextField sdk_text;
    private JButton sdk_button;
    private File sdkFile;

    private JTextField ndk_text;
    private JButton ndk_button;
    private File ndkFile;

    private JButton buildAar_button;
    private JButton outputAar_button;

    public static void main(String[] args) {
        new ASBuildAarFile();
    }

    public ASBuildAarFile() {
        openFileWindow();
    }

    private void openFileWindow() {
        jFrame = new JFrame();
        jFrame.setTitle("将android工程打成aar");
        jFrame.setBounds(500, 500, 700, 200);
        jFrame.setVisible(true);
        FlowLayout layout = new FlowLayout();
        layout.setAlignment(FlowLayout.LEFT);
        //选择文件
        JLabel filePath_label = new JLabel("工程路径:");
        sourcePath_text = new JTextField(50);
        sourcePath_button = new JButton("浏览");
        //输出路径
        JLabel output_label = new JLabel("出包路径:");
        output_text = new JTextField(50);
        output_button = new JButton("浏览");
        //sdk
        JLabel sdk_label = new JLabel("本地sdk路径:");
        sdk_text = new JTextField(48);
        sdk_button = new JButton("浏览");
        //ndk
        JLabel ndk_label = new JLabel("本地ndk路径(sdk路径下):");
        ndk_text = new JTextField(42);
        ndk_button = new JButton("浏览");
        //构建
        buildAar_button = new JButton("构建aar");
        outputAar_button = new JButton("弹出aar(构建aar成功后再点)");
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jFrame.setLayout(layout);
        jFrame.setResizable(false);
        jFrame.add(filePath_label);
        jFrame.add(sourcePath_text);
        jFrame.add(sourcePath_button);
        jFrame.add(output_label);
        jFrame.add(output_text);
        jFrame.add(output_button);
        jFrame.add(sdk_label);
        jFrame.add(sdk_text);
        jFrame.add(sdk_button);
        jFrame.add(ndk_label);
        jFrame.add(ndk_text);
        jFrame.add(ndk_button);
        jFrame.add(buildAar_button);
        jFrame.add(outputAar_button);
        choosePath();
        outputPath();
        sdkPath();
        ndkPath();
        buildAar();
        outputAar();
        getCachePath();
    }

    public void choosePath() {
        sourcePath_button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                JFileChooser chooser = new JFileChooser();
                chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
                chooser.showDialog(new JLabel(), "选择");
                sourceFile = chooser.getSelectedFile();
                sourcePath_text.setText(sourceFile.getAbsolutePath().toString());
            }
        });
    }

    private void outputPath() {
        output_button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                JFileChooser chooser = new JFileChooser();
                chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
                chooser.showDialog(new JLabel(), "选择");
                outputFile = chooser.getSelectedFile();
                output_text.setText(outputFile.getAbsolutePath().toString());
            }
        });
    }

    private void sdkPath() {
        sdk_button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                JFileChooser chooser = new JFileChooser();
                chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
                chooser.showDialog(new JLabel(), "选择");
                sdkFile = chooser.getSelectedFile();
                sdk_text.setText(sdkFile.getAbsolutePath().toString());
            }
        });
    }

    private void ndkPath() {
        ndk_button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                JFileChooser chooser = new JFileChooser();
                chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
                chooser.showDialog(new JLabel(), "选择");
                ndkFile = chooser.getSelectedFile();
                ndk_text.setText(ndkFile.getAbsolutePath().toString());
            }
        });
    }

    private void buildAar() {
        buildAar_button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                buildAarImpl();
            }
        });
    }

    private void outputAar() {
        outputAar_button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                outputAarImpl(sourcePath_text.getText().toString());
            }
        });
    }

    private void cachePath() {
        String cache = "sourcePath=" + sourcePath_text.getText().toString().replace("\\", "\\\\") + "
" +
                "outputPath=" + output_text.getText().toString().replace("\\", "\\\\") + "
" +
                "sdkPath=" + sdk_text.getText().toString().replace("\\", "\\\\") + "
" +
                "ndkPath=" + ndk_text.getText().toString().replace("\\", "\\\\") + "
";
        File cacheFile = new File(CACHE_PATH);
        if (!cacheFile.exists()) {
            try {
                cacheFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            FileOutputStream fop = new FileOutputStream(cacheFile);
            fop.write(cache.getBytes());
            fop.flush();
            fop.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void getCachePath() {
        File cacheFile = new File(CACHE_PATH);
        if (cacheFile.exists()) {
            try {
                FileInputStream fip = new FileInputStream(cacheFile);
                Properties properties = new Properties();
                properties.load(fip);
                Iterator<Map.Entry<Object, Object>> iterator = properties.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<Object, Object> entry = iterator.next();
                    switch (entry.getKey().toString()) {
                        case "sourcePath":
                            sourcePath_text.setText(entry.getValue().toString());
                            break;
                        case "outputPath":
                            output_text.setText(entry.getValue().toString());
                            break;
                        case "sdkPath":
                            sdk_text.setText(entry.getValue().toString());
                            break;
                        case "ndkPath":
                            ndk_text.setText(entry.getValue().toString());
                            break;
                    }
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void buildAarImpl() {
        if (checkInput()) return;
        cachePath();
        String filePath = sourcePath_text.getText().toString();
        findUpdateFile(filePath);
        gradleBuildAar();
    }

    private void gradleBuildAar() {
        String command = "cmd /c start gradle clean assembleDebug";
        File cmdPath = new File(sourcePath_text.getText().toString());
        Runtime runtime = Runtime.getRuntime();
        try {
            Process process = runtime.exec(command, null, cmdPath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void outputAarImpl(String filePath) {
        if (checkInput()) return;
        findAarFile(filePath);
        String command = "cmd /k start " + output_text.getText().toString();
        Runtime runtime = Runtime.getRuntime();
        try {
            runtime.exec(command);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void findAarFile(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            return;
        }
        File[] files = file.listFiles();
        for (File outputFile : files) {
            if (outputFile.isDirectory()) {
                findAarFile(outputFile.getAbsolutePath());
            } else {
                String fileName = outputFile.getName().toString();
                if (fileName.endsWith(".aar")) {
                    String time = timeStamp2Date(String.valueOf(System.currentTimeMillis()));
                    String aarName = fileName.substring(0, fileName.length() - 4);
                    File aarFile = new File(output_text.getText().toString() + "\\" + aarName + time + ".aar");
                    outputFile.renameTo(aarFile);
                }
            }
        }
    }

    private void findUpdateFile(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            return;
        }
        File[] files = file.listFiles();
        for (File updateFile : files) {
            if (updateFile.isDirectory()) {
                findUpdateFile(updateFile.getAbsolutePath());
            } else {
                switch (updateFile.getName().toString()) {
                    case "build.gradle":
                        updateBuildGradle(updateFile.getAbsolutePath());
                        break;
                    case "AndroidManifest.xml":
                        updateManifestFile(updateFile.getAbsolutePath());
                        break;
                    case "local.properties":
                        updateSdkFile(updateFile.getAbsolutePath());
                        break;
                    case "UnityPlayerActivity.java":
                    case "UnityPlayerNativeActivity.java":
                    case "UnityPlayerProxyActivity.java":
                        updateFile.delete();
                        break;
                }
            }
        }
    }

    private void updateSdkFile(String filePath) {
        try {
            RandomAccessFile sdkFile = new RandomAccessFile(filePath + "\\local.properties", "rw");
            String line;
            long lastPoint = 0;
            while ((line = sdkFile.readLine()) != null) {
                final long point = sdkFile.getFilePointer();
                if (line.contains("sdk.dir")) {
                    String s = line.substring(0);
                    String sdkStr = sdk_text.getText().toString();
                    String sdkPan = sdkStr.substring(0, 1);
                    sdkStr = sdkStr.substring(1).replace("\\", "\\\\");
                    String ndkStr = ndk_text.getText().toString();
                    String ndkPan = ndkStr.substring(0, 1);
                    ndkStr = ndkStr.substring(1).replace("\\", "\\\\");
                    String replaceStr = "sdk.dir=" + sdkPan + "\\" + sdkStr + "
" + "ndk.dir=" + ndkPan + "\\" + ndkStr + "
                      ";
                    String str = line.replace(s, replaceStr);
                    sdkFile.seek(lastPoint);
                    sdkFile.writeBytes(str);
                }
                lastPoint = point;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void updateManifestFile(String filePath) {
        try {
            RandomAccessFile manifestFile = new RandomAccessFile(filePath, "rw");
            String line;
            long lastPoint = 0;
            while ((line = manifestFile.readLine()) != null) {
                final long ponit = manifestFile.getFilePointer();
                if (line.contains("<activity") && line.contains("UnityPlayerActivity") && !line.contains("<!--<activity")) {
                    String str = line.replace("<activity", "<!--<activity");
                    manifestFile.seek(lastPoint);
                    manifestFile.writeBytes(str);
                }
                if (line.contains("</activity>") && !line.contains("</activity>-->")) {
                    String str = line.replace("</activity>", "</activity>-->
");
                    manifestFile.seek(lastPoint);
                    manifestFile.writeBytes(str);
                }
                lastPoint = ponit;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void updateBuildGradle(String filePath) {
        try {
            RandomAccessFile buildGradleFile = new RandomAccessFile(filePath, "rw");
            String line;
            long lastPoint = 0;
            while ((line = buildGradleFile.readLine()) != null) {
                final long ponit = buildGradleFile.getFilePointer();
                if (line.contains("classpath ‘com.android.tools.build:gradle")) {
                    String s = line.substring(line.indexOf("classpath"));
                    String str = line.replace(s, "classpath ‘com.android.tools.build:gradle:2.3.0‘  
");
                    buildGradleFile.seek(lastPoint);
                    buildGradleFile.writeBytes(str);
                }
                if (line.contains("com.android.application")) {
                    String str = line.replace("‘com.android.application‘", "‘com.android.library‘    
");
                    buildGradleFile.seek(lastPoint);
                    buildGradleFile.writeBytes(str);
                }
                if (line.contains("compileSdkVersion") && !line.contains("compileSdkVersion 25")) {
                    String s = line.substring(line.indexOf("compileSdkVersion")).toString();
                    String str = line.replace(s, "compileSdkVersion 25
");
                    buildGradleFile.seek(lastPoint);
                    buildGradleFile.writeBytes(str);
                }
                if (line.contains("buildToolsVersion") && !line.contains("buildToolsVersion ‘25.0.2‘")) {
                    String s = line.substring(line.indexOf("buildToolsVersion")).toString();
                    String str = line.replace(s, "buildToolsVersion ‘25.0.2‘
");
                    buildGradleFile.seek(lastPoint);
                    buildGradleFile.writeBytes(str);
                }
                if (line.contains("targetSdkVersion") && !line.contains("targetSdkVersion 25")) {
                    String s = line.substring(line.indexOf("targetSdkVersion")).toString();
                    String str = line.replace(s, "targetSdkVersion 25
");
                    buildGradleFile.seek(lastPoint);
                    buildGradleFile.writeBytes(str);
                }
                if (line.contains("applicationId") && !line.contains("//applicationId")) {
                    String s = line.substring(line.indexOf("applicationId")).toString();
                    String str = line.replace(s, "//" + s + "
");
                    buildGradleFile.seek(lastPoint);
                    buildGradleFile.writeBytes(str);
                }
                lastPoint = ponit;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private boolean checkInput() {
        if ("".equals(sourcePath_text.getText().toString())) {
            JOptionPane.showMessageDialog(jFrame, "请输入源工程文件路径");
            return true;
        }
        if ("".equals(output_text.getText().toString())) {
            JOptionPane.showMessageDialog(jFrame, "请输入apk输出路径");
            return true;
        }
        if ("".equals(sdk_text.getText().toString())) {
            JOptionPane.showMessageDialog(jFrame, "请输入本地sdk路径");
            return true;
        }
        if ("".equals(ndk_text.getText().toString())) {
            JOptionPane.showMessageDialog(jFrame, "请输入本地ndk路径");
            return true;
        }
        return false;
    }

    public static String timeStamp2Date(String time) {
        Long timeLong = Long.parseLong(time);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");//要转换的时间格式
        Date date;
        try {
            date = sdf.parse(sdf.format(timeLong));
            return sdf.format(date);
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }
}

方法名写的很清楚,我就不加注释了。

jar包打成可运行程序参考android studio打可执行jar包

效果图

技术分享图片

第二种:

低版本的unity导出的android工程是Eclipse版的

Eclipse版的android工程和android studio版的android工程还是有不小区别的,所以我增加了一个母包,用于把eclipse版变成android studio版,代码如下

package com.jinkejoy.build_aar;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;

public class EcBuildAarFile {

    private static final String CACHE_PATH = "C:\\build-apk-path.properties";
    private JFrame jFrame;

    private JTextField sourcePath_text;
    private JButton sourcePath_button;
    private File sourceFile;

    private JTextField output_text;
    private JButton output_button;
    private File outputFile;

    private JTextField sdk_text;
    private JButton sdk_button;
    private File sdkFile;

    private JTextField ndk_text;
    private JButton ndk_button;
    private File ndkFile;

    private JTextField main_text;
    private JButton main_button;
    private File mainFile;

    private JButton buildAar_button;
    private JButton outputAar_button;

    public static void main(String[] args) {
        new EcBuildAarFile();
    }

    public EcBuildAarFile() {
        openFileWindow();
    }

    private void openFileWindow() {
        jFrame = new JFrame();
        jFrame.setTitle("将android工程打成aar");
        jFrame.setBounds(500, 500, 700, 250);
        jFrame.setVisible(true);
        FlowLayout layout = new FlowLayout();
        layout.setAlignment(FlowLayout.LEFT);
        //选择文件
        JLabel filePath_label = new JLabel("工程路径:");
        sourcePath_text = new JTextField(50);
        sourcePath_button = new JButton("浏览");
        //输出路径
        JLabel output_label = new JLabel("出包路径:");
        output_text = new JTextField(50);
        output_button = new JButton("浏览");
        //sdk
        JLabel sdk_label = new JLabel("本地sdk路径:");
        sdk_text = new JTextField(48);
        sdk_button = new JButton("浏览");
        //ndk
        JLabel ndk_label = new JLabel("本地ndk路径(sdk路径下):");
        ndk_text = new JTextField(42);
        ndk_button = new JButton("浏览");
        //母包
        JLabel main_label = new JLabel("本地母包文件路径:");
        main_text = new JTextField(45);
        main_button = new JButton("浏览");
        //构建
        buildAar_button = new JButton("构建aar");
        outputAar_button = new JButton("弹出aar(构建aar成功后再点)");
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jFrame.setLayout(layout);
        jFrame.setResizable(false);
        jFrame.add(filePath_label);
        jFrame.add(sourcePath_text);
        jFrame.add(sourcePath_button);
        jFrame.add(output_label);
        jFrame.add(output_text);
        jFrame.add(output_button);
        jFrame.add(sdk_label);
        jFrame.add(sdk_text);
        jFrame.add(sdk_button);
        jFrame.add(ndk_label);
        jFrame.add(ndk_text);
        jFrame.add(ndk_button);
        jFrame.add(main_label);
        jFrame.add(main_text);
        jFrame.add(main_button);
        jFrame.add(buildAar_button);
        jFrame.add(outputAar_button);
        choosePath();
        outputPath();
        sdkPath();
        ndkPath();
        mainPath();
        buildAar();
        outputAar();
        getCachePath();
    }

    public void choosePath() {
        sourcePath_button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                JFileChooser chooser = new JFileChooser();
                chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
                chooser.showDialog(new JLabel(), "选择");
                sourceFile = chooser.getSelectedFile();
                sourcePath_text.setText(sourceFile.getAbsolutePath().toString());
            }
        });
    }

    private void outputPath() {
        output_button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                JFileChooser chooser = new JFileChooser();
                chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
                chooser.showDialog(new JLabel(), "选择");
                outputFile = chooser.getSelectedFile();
                output_text.setText(outputFile.getAbsolutePath().toString());
            }
        });
    }

    private void sdkPath() {
        sdk_button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                JFileChooser chooser = new JFileChooser();
                chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
                chooser.showDialog(new JLabel(), "选择");
                sdkFile = chooser.getSelectedFile();
                sdk_text.setText(sdkFile.getAbsolutePath().toString());
            }
        });
    }

    private void ndkPath() {
        ndk_button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                JFileChooser chooser = new JFileChooser();
                chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
                chooser.showDialog(new JLabel(), "选择");
                ndkFile = chooser.getSelectedFile();
                ndk_text.setText(ndkFile.getAbsolutePath().toString());
            }
        });
    }

    private void mainPath() {
        main_button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                JFileChooser chooser = new JFileChooser();
                chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
                chooser.showDialog(new JLabel(), "选择");
                mainFile = chooser.getSelectedFile();
                main_text.setText(mainFile.getAbsolutePath().toString());
            }
        });
    }

    private void buildAar() {
        buildAar_button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                buildAarImpl();
            }
        });
    }

    private void outputAar() {
        outputAar_button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                outputAarImpl(main_text.getText().toString());
            }
        });
    }

    private void cachePath() {
        String cache = "sourcePath=" + sourcePath_text.getText().toString().replace("\\", "\\\\") + "
" +
                "outputPath=" + output_text.getText().toString().replace("\\", "\\\\") + "
" +
                "sdkPath=" + sdk_text.getText().toString().replace("\\", "\\\\") + "
" +
                "ndkPath=" + ndk_text.getText().toString().replace("\\", "\\\\") + "
" +
                "mainPath=" + main_text.getText().toString().replace("\\", "\\\\");
        File cacheFile = new File(CACHE_PATH);
        if (!cacheFile.exists()) {
            try {
                cacheFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            FileOutputStream fop = new FileOutputStream(cacheFile);
            fop.write(cache.getBytes());
            fop.flush();
            fop.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void getCachePath() {
        File cacheFile = new File(CACHE_PATH);
        if (cacheFile.exists()) {
            try {
                FileInputStream fip = new FileInputStream(cacheFile);
                Properties properties = new Properties();
                properties.load(fip);
                Iterator<Map.Entry<Object, Object>> iterator = properties.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<Object, Object> entry = iterator.next();
                    switch (entry.getKey().toString()) {
                        case "sourcePath":
                            sourcePath_text.setText(entry.getValue().toString());
                            break;
                        case "outputPath":
                            output_text.setText(entry.getValue().toString());
                            break;
                        case "sdkPath":
                            sdk_text.setText(entry.getValue().toString());
                            break;
                        case "ndkPath":
                            ndk_text.setText(entry.getValue().toString());
                            break;
                        case "mainPath":
                            main_text.setText(entry.getValue().toString());
                            break;
                    }
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void buildAarImpl() {
        if (checkInput()) return;
        cachePath();
        createAs();
        findUpdateFile(main_text.getText().toString());
        gradleBuildAar();
    }

    private void createAs() {
        String sourcePath = sourcePath_text.getText().toString();
        String mainPath = main_text.getText().toString();
        //assets
        String assets = sourcePath + "\\assets";
        String newAssets = mainPath + "\\app\\src\\main\\assets";
        copyFolder(assets, newAssets);
        //unity-classes.jar
        String unity = sourcePath + "\\libs\\unity-classes.jar";
        String newUnity = mainPath + "\\app\\libs\\unity-classes.jar";
        copyFolder(unity, newUnity);
        //libs
        String libs = sourcePath + "\\libs";
        String jniLibs = mainPath + "\\app\\src\\main\\jniLibs";
        copyFolder(libs, jniLibs);
        //res
        String res = sourcePath + "\\res";
        String newRes = mainPath + "\\app\\src\\main\\res";
        copyFolder(res, newRes);
        //src
        String src = sourcePath + "\\src";
        String java = mainPath + "\\app\\src\\main\\java";
        copyFolder(src, java);
        //AndroidManifest.xml
        String manifest = sourcePath + "\\AndroidManifest.xml";
        String newManifest = mainPath + "\\app\\src\\main\\AndroidManifest.xml";
        copyFolder(manifest, newManifest);
    }

    public static void copyFolder(String oldPath, String newPath) {
        try {
            // 如果文件夹不存在,则建立新文件夹
            (new File(newPath)).mkdirs();
            // 读取整个文件夹的内容到file字符串数组,下面设置一个游标i,不停地向下移开始读这个数组
            File filelist = new File(oldPath);
            String[] file = filelist.list();
            // 要注意,这个temp仅仅是一个临时文件指针
            // 整个程序并没有创建临时文件
            File temp = null;
            for (int i = 0; i < file.length; i++) {
                // 如果oldPath以路径分隔符/或者结尾,那么则oldPath/文件名就可以了
                // 否则要自己oldPath后面补个路径分隔符再加文件名
                // 谁知道你传递过来的参数是f:/a还是f:/a/啊?
                if (oldPath.endsWith(File.separator)) {
                    temp = new File(oldPath + file[i]);
                } else {
                    temp = new File(oldPath + File.separator + file[i]);
                }

                // 如果游标遇到文件
                if (temp.isFile()) {
                    FileInputStream input = new FileInputStream(temp);
                    // 复制并且改名
                    FileOutputStream output = new FileOutputStream(newPath
                            + "/" + (temp.getName()).toString());
                    byte[] bufferarray = new byte[1024 * 64];
                    int prereadlength;
                    while ((prereadlength = input.read(bufferarray)) != -1) {
                        output.write(bufferarray, 0, prereadlength);
                    }
                    output.flush();
                    output.close();
                    input.close();
                }
                // 如果游标遇到文件夹
                if (temp.isDirectory()) {
                    copyFolder(oldPath + "/" + file[i], newPath + "/" + file[i]);
                }
            }
        } catch (Exception e) {
            System.out.println("复制整个文件夹内容操作出错");
        }
    }

    private void gradleBuildAar() {
        String command = "cmd /c start gradle clean assembleDebug";
        File cmdPath = new File(main_text.getText().toString());
        Runtime runtime = Runtime.getRuntime();
        try {
            Process process = runtime.exec(command, null, cmdPath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void outputAarImpl(String filePath) {
        if (checkInput()) return;
        findAarFile(filePath);
        String command = "cmd /k start " + output_text.getText().toString();
        Runtime runtime = Runtime.getRuntime();
        try {
            runtime.exec(command);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void findAarFile(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            return;
        }
        File[] files = file.listFiles();
        for (File outputFile : files) {
            if (outputFile.isDirectory()) {
                findAarFile(outputFile.getAbsolutePath());
            } else {
                String fileName = outputFile.getName().toString();
                if (fileName.endsWith(".aar")) {
                    String time = timeStamp2Date(String.valueOf(System.currentTimeMillis()));
                    String aarName = fileName.substring(0, fileName.length() - 4);
                    File aarFile = new File(output_text.getText().toString() + "\\" + aarName + time + ".aar");
                    outputFile.renameTo(aarFile);
                }
            }
        }
    }

    private void findUpdateFile(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            return;
        }
        File[] files = file.listFiles();
        for (File updateFile : files) {
            if (updateFile.isDirectory()) {
                findUpdateFile(updateFile.getAbsolutePath());
            } else {
                switch (updateFile.getName().toString()) {
                    case "build.gradle":
                        updateBuildGradle(updateFile.getAbsolutePath());
                        break;
                    case "AndroidManifest.xml":
                        updateManifestFile(updateFile.getAbsolutePath());
                        break;
                    case "local.properties":
                        updateSdkFile(updateFile.getAbsolutePath());
                        break;
                    case "UnityPlayerActivity.java":
                    case "UnityPlayerNativeActivity.java":
                    case "UnityPlayerProxyActivity.java":
                        updateFile.delete();
                        break;
                }
            }
        }
    }

    private void updateSdkFile(String filePath) {
        try {
            RandomAccessFile sdkFile = new RandomAccessFile(filePath + "\\local.properties", "rw");
            String line;
            long lastPoint = 0;
            while ((line = sdkFile.readLine()) != null) {
                final long point = sdkFile.getFilePointer();
                if (line.contains("sdk.dir")) {
                    String s = line.substring(0);
                    String sdkStr = sdk_text.getText().toString();
                    String sdkPan = sdkStr.substring(0, 1);
                    sdkStr = sdkStr.substring(1).replace("\\", "\\\\");
                    String ndkStr = ndk_text.getText().toString();
                    String ndkPan = ndkStr.substring(0, 1);
                    ndkStr = ndkStr.substring(1).replace("\\", "\\\\");
                    String replaceStr = "sdk.dir=" + sdkPan + "\\" + sdkStr + "
" + "ndk.dir=" + ndkPan + "\\" + ndkStr + "
                      ";
                    String str = line.replace(s, replaceStr);
                    sdkFile.seek(lastPoint);
                    sdkFile.writeBytes(str);
                }
                lastPoint = point;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void updateManifestFile(String filePath) {
        try {
            RandomAccessFile manifestFile = new RandomAccessFile(filePath, "rw");
            String line;
            long lastPoint = 0;
            while ((line = manifestFile.readLine()) != null) {
                final long ponit = manifestFile.getFilePointer();
                if (line.contains("<activity") && line.contains("UnityPlayerActivity") && !line.contains("<!--<activity")) {
                    String str = line.replace("<activity", "<!--<activity");
                    manifestFile.seek(lastPoint);
                    manifestFile.writeBytes(str);
                }
                if (line.contains("</activity>") && !line.contains("</activity>-->")) {
                    String str = line.replace("</activity>", "</activity>-->
");
                    manifestFile.seek(lastPoint);
                    manifestFile.writeBytes(str);
                }
                lastPoint = ponit;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void updateBuildGradle(String filePath) {
        try {
            RandomAccessFile buildGradleFile = new RandomAccessFile(filePath, "rw");
            String line;
            long lastPoint = 0;
            while ((line = buildGradleFile.readLine()) != null) {
                final long ponit = buildGradleFile.getFilePointer();
                if (line.contains("classpath ‘com.android.tools.build:gradle")) {
                    String s = line.substring(line.indexOf("classpath"));
                    String str = line.replace(s, "classpath ‘com.android.tools.build:gradle:2.3.0‘  
");
                    buildGradleFile.seek(lastPoint);
                    buildGradleFile.writeBytes(str);
                }
                if (line.contains("com.android.application")) {
                    String str = line.replace("‘com.android.application‘", "‘com.android.library‘    
");
                    buildGradleFile.seek(lastPoint);
                    buildGradleFile.writeBytes(str);
                }
                if (line.contains("compileSdkVersion") && !line.contains("compileSdkVersion 25")) {
                    String s = line.substring(line.indexOf("compileSdkVersion")).toString();
                    String str = line.replace(s, "compileSdkVersion 25
");
                    buildGradleFile.seek(lastPoint);
                    buildGradleFile.writeBytes(str);
                }
                if (line.contains("buildToolsVersion") && !line.contains("buildToolsVersion ‘25.0.2‘")) {
                    String s = line.substring(line.indexOf("buildToolsVersion")).toString();
                    String str = line.replace(s, "buildToolsVersion ‘25.0.2‘
");
                    buildGradleFile.seek(lastPoint);
                    buildGradleFile.writeBytes(str);
                }
                if (line.contains("targetSdkVersion") && !line.contains("targetSdkVersion 25")) {
                    String s = line.substring(line.indexOf("targetSdkVersion")).toString();
                    String str = line.replace(s, "targetSdkVersion 25
");
                    buildGradleFile.seek(lastPoint);
                    buildGradleFile.writeBytes(str);
                }
                if (line.contains("applicationId") && !line.contains("//applicationId")) {
                    String s = line.substring(line.indexOf("applicationId")).toString();
                    String str = line.replace(s, "//" + s + "
");
                    buildGradleFile.seek(lastPoint);
                    buildGradleFile.writeBytes(str);
                }
                lastPoint = ponit;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private boolean checkInput() {
        if ("".equals(sourcePath_text.getText().toString())) {
            JOptionPane.showMessageDialog(jFrame, "请输入源工程文件路径");
            return true;
        }
        if ("".equals(output_text.getText().toString())) {
            JOptionPane.showMessageDialog(jFrame, "请输入apk输出路径");
            return true;
        }
        if ("".equals(sdk_text.getText().toString())) {
            JOptionPane.showMessageDialog(jFrame, "请输入本地sdk路径");
            return true;
        }
        if ("".equals(ndk_text.getText().toString())) {
            JOptionPane.showMessageDialog(jFrame, "请输入本地ndk路径");
            return true;
        }
        return false;
    }

    public static String timeStamp2Date(String time) {
        Long timeLong = Long.parseLong(time);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");//要转换的时间格式
        Date date;
        try {
            date = sdf.parse(sdf.format(timeLong));
            return sdf.format(date);
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }
}

效果图

技术分享图片

以上是关于关于打Android aar离线包的一些经验分享的主要内容,如果未能解决你的问题,请参考以下文章

visual studio xamarin 离线安装文件以及 android 模拟器

NPM离线包的制作和使用

排查指南 | 两个案例学会从埋点排查 iOS 离线包

NPM离线包的制作和使用

unity打aar包工具

排查指南 | 两个案例学会从埋点排查 iOS 离线包