您的位置:首页 > 移动开发 > Android开发

Android Studio 打包Jar

2017-07-17 17:43 274 查看

晨鸣的博客–Android Studio 打包Jar

最近公司一个新需求,需要将项目中某一个模块打包成SDK提供给第三方公司使用。



打包SDK?我只会用啊!!没办法,硬着头皮也得上!

选择打包方式

说干就干,撸起袖子就Google。。。

通过Google可以确定常见的几种打包SDK的方式

直接以Library Module的方式引入

优点

简单方便,直接把模块抠出来放进一个Library中扔给第三方公司用就行了

缺点

只能适合于Android Studio开发的项目,假如第三方公司用的是Eclipse,那不就懵逼了.

不安全,相当于把源码完全暴露给第三方。泄露资料事小,代码写的差被嘲笑事大啊!!

以aar包的方式引入

优点

生成简单,Android Studio的项目在编译完成后,Library Module 的build目录中会自动生成 aar包,不用做额外操作

缺点

还是比较适合Android Studio开发的项目,虽然Eclipse 也能引入aar包,但是比较复杂,我可不想写一堆接入文档,还不一定说的清。

以jar包的方式引入

优点

接入方便,是个Android开发应该都会引入jar包吧。。

缺点

打包比较麻烦,而且Jar包中关于一些资源文件的引用比较麻烦

经过一番比较,最终我选择以Jar包的形式提供一个对外的SDK。毕竟是给别人使用的东西,用户的使用体验最重要,通过jar包方式可以以最简单的方式集成SDK,减少接入SDK的成本以及时间。

打包Jar

相关知识

Android Studio 生成Jar包,还是需要借助 Library Module来操作。创建一个Library Module ,然后在主项目中依赖这个Module,当项目经过编译后,我们会发现在Library Module 的
build/intermediates/bundles/default/
目录下会有一个
class.jar
文件



我们生成Jar文件就是需要将这个
class.jar
文件进行打包处理

操作

将项目中需要生成SDK的模块分离出来,单独放入一个Module中,并将这个Module切换为Library,使主项目依赖这个Library。

在Library Module 的build.gradle 文件的最后添加如下代码

//打jar包
def SDK_BASENAME = "*****";  //打包后的名称
def sdkJarPath = "build";  //打包输出目录
def zipFile = file('build/intermediates/bundles/default/classes.jar') //将编译生成的classes.jar文件打包

task makeJar(type: Jar) {
from zipTree(zipFile)
from fileTree(dir: 'src/main', includes: ['assets/**']) //需要保留的资源文件
baseName = SDK_BASENAME
destinationDir = file(sdkJarPath)
}
makeJar.dependsOn(build)


rebuild 项目,确保
build/intermediates/bundles/default/
目录下存在
class.jar
文件。

打开Terminal命令行,或者直接用系统命令行进入项目目录,输入
gradlew makeJar
回车,开始进行打包。第一次进行打包,可能会需要下载一些文件,需要一些时间,请耐心等待。

当出现
BUILD SUCCESSFUL
时代表打包完成,打开输出目录就会有打包生成的jar包。



一些问题及注意事项

如果Jar包中包含Activity,在项目中引入这个Jar后,还需要在项目的
AndroidManifest.xml
中声明这个Activity,以及添加一些必要的权限声明。

打包时可以选择保留项目中的资源文件,但仅仅只有assets下的资源文件在jar包中可以正常使用。其他的资源文件,如drawable、layout等,都不可以用常见的方法引用。我们在正常使用某一个资源文件时,是通过
R.**.**
方法引用,而打包Jar时并不会保留资源文件id对应的映射R文件,所以这些资源文件就不能正常调用了。

如果Jar包中需要使用一些资源文件,可以将需要的资源文件,例如图片、布局等单独拿出来,项目中引入Jar时,同时添加这些必需的资源文件(类似友盟、腾讯等第三方jar的引入),这样Jar包中可以通过反射读取项目中资源文件。反射读取资源文件的代码如下:

/**
* ****************************************************************
* Author: LCM
* Date: 2017/6/2 上午11:09
* Desc: 通过反射获取资源文件
* *****************************************************************
*/

public class MResource {
/**
*
* @param context 上下文
* @param className 资源文件的类型 layout、id、drawable
* @param name 资源文件的名字
* @return
*/
public static int getIdByName(Context context, String className, String name) {
String packageName = context.getPackageName();
Class r = null;
int id = 0;
try {
r = Class.forName(packageName + ".R");
Class[] classes = r.getClasses();
Class desireClass = null;
for (int i = 0; i < classes.length; ++i) {
if (classes[i].getName().split("\\$")[1].equals(className)) {
desireClass = classes[i];
break;
}
}
if (desireClass != null)
id = desireClass.getField(name).getInt(desireClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return id;
}

/**
* 从Assets中读取图片
*/
public static Bitmap getImageFromAssetsFile(Context context, String fileName) {
Bitmap image = null;
AssetManager am = context.getResources().getAssets();
try {
InputStream is = am.open(fileName);
image = BitmapFactory.decodeStream(is);
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return image;

}
}


//代码中调用
setContentView(MResource.getIdByName(getApplicationContext(), "layout", "layout_demo"));

tvTitle = (TextView) findViewById(MResource.getIdByName(getApplicationContext(), "id", "demo_tv_title"));


额外发现

生成一个Jar,还需要另外提供一些资源文件给别人,对我这种有代码洁癖的人来说是不能忍的。我的理想状态是,就一个jar包,你想用的都在里面,干净简洁。

本着不作死就不会死的精神,竟然真的被我发现了一种从assets中加载布局文件的方式!!!!!!

我们知道生成布局文件一般有两种方式

findViewById


直接new一个布局文件,代码中添加属性

其实还有一种容易被我们忽略的方法
LayoutInflater
,我们常在ListView或者RecyclerView 的Adapter中通过
Layoutinflater
加载Item项的布局,那么能不能通过它来加载一个Activity的布局呢?

我们通常使用的是这两个

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)


public View inflate(@LayoutRes int resource, @Nullable ViewGroup root)


方法来加载布局

但其实
LayoutInflater
还有两个重载的方法

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)


public View inflate(XmlPullParser parser, @Nullable ViewGroup root)


看见
XmlPullParser
是不是感觉到希望了呢?

没错!
XmlPullParser
可以用来解析Xml文件,这样我们就不用通过R文件来映射资源文件,而是直接通过解析Xml文件来加载布局。

解析资源布局文件需要用
XmlResourceParser


public static XmlResourceParser getIsFromAssetsFile(Context context, String fileName) {
AssetManager am = context.getResources().getAssets();

XmlResourceParser xmlResourceParser = null;
try {
xmlResourceParser = am.openXmlResourceParser("assets/" + fileName);
} catch (IOException e) {
e.printStackTrace();
}
return xmlResourceParser;
}


LinearLayout layout = (LinearLayout) LayoutInflater.from(this).inflate(MResource.getIsFromAssetsFile(this, "layout_demo.xml"), null,false);
setContentView(layout);


把布局文件扔进assets目录,来来来!接下来就是见证奇迹的时刻!





f**k ………

淡定淡定。。发现问题还是要解决问题嘛。。。

经过一番艰苦卓越的google ,发现这里
XmlResourceParser
解析的布局必需是编译过后的布局文件。

那么怎么获取编译后的布局文件呢,很简单也很无脑,把你需要的布局文件放到任意一个正常项目的layout目录下,编译项目,在
build/output/apk
文件夹中会有一个 apk包,解压就可在layout目录下获得编译后的布局文件

重新将编译后的布局文件扔到assets目录中,运行



布局文件能找到,那么布局中的控件呢,肯定不能
findViewById
了,别担心,android中还有另一个findview的方法
findViewByTag
,你可以在布局中给每一个控件添加一个
android:tag=""
属性,然后你就可以通过
findViewByTag
方法来找到指定的控件了。

终于我成功的将SDK封装的只剩一个Jar包!!



然鹅!!我最终还是放弃了这种方式!!!因为这种方式打包的jar包在Android Studio中可以正常使用,但是在Eclipse中不能使用!!!解析不了这个布局!!!!网上关于这方面的资料又比较少,我只能猜测这是因为Eclipse 与Android Studio 的加载机制不同导致的。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息