android 源码开发apk

2017-07-13 23:11:18 qq_27540131 阅读数 4120

*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

前言:

前一篇博客分析了一下PackageManagerService是如何解析apk的以及我们如何解析未安装apk中的androidManifest.xml文件。解析完肯定要安装的,索性写一篇关于android系统是如何安装我们apk的流程分析。不过这里仅仅只分析java层面的代码,C层方面的就跳过了。

apk安装起始点-Pm.java run()

apk安装java层的起始位置是在Pm.java的run()中。

这里有2个重点,第一个红框可以看到与我们的PackageManagerService有关,获取PackageManagerService的binder对象,与PackageManagerService进行通信。并且如果这个对象为null 则输出异常信息直接返回,其实也不难理解,PackageManagerService把我们的apk给扫描了,那安装应该也会在其中。所以这里可以肯定的是apk的安装实现就在我们的PackageManagerService中;第二点就是根据这个install标识来执行安装的方法了。那我们就进入到runInstall()中来看下,它的内部是调用了PackageManagerService的什么方法来进行apk安装的。

runInstall()


从上面的runInstall()方法的代码可以看到 有3个标红框的地方需要了解,第一个obs对象,用于接收PackageManagerService安装结果,其实从第三个红框就一目了然的了解INSTALL_SUCCEEDED,安装成功!就输出Success;第二个红框就是通过binder来调起PackageManagerService中的installPackageWithVerificationAndEncryption()方法。

那接下来看下PackageManagerService中的installPackageWithVerificationAndEncryption()方法是如何操作的



这个方法代码并不是很多,重点在最后一部分,通过handler发送一个INIT_COPY的消息,消息的内容是一个InstallParams对象。(这里要注意下InstallParams,等下会说下这个)
那我们就只要找到handleMessage中处理这个INIT_COPY的消息代码就行了


这里值得一提的是,安装apk的操作还需要一个服务,只有这个服务被bind了,才行进行下一步的工作,也就是通过handler继续发送一个消息。(这个服务是com.android.defcontainer.DefaultContainerService这里就不深层次分析了,它的作用就是用来解析APK,以及获取推荐安装路径的,安装的路径与内存情况以及一些标识来决定)
绕来绕去,开启服务之后又发送了一个消息,那只好继续看下这个MCS_BOUND消息是如何处理的。

MCS_BOUND:



从代码中就能明显看到,这里又对服务进行了一次判断,是否已经连接,所以这个服务于我们的apk安装是共存的,其中mPendingInstalls就是用于存储需要安装的请求,只有当这个队列为空时才断开连接。(在INIT_COPY消息处理中被添加到mPendingInstalls中的),然后又调用了HandlerParams的startCopy()方法执行安装。


可以看到以下几个重点
1.HandlerParams是个抽象类
2.箭头那可以知道,这个安装会尝试4次,超过4次就GG了
3.执行handleStartCopy()方法
4.执行handleReturnCode()方法

在这之前值得一提的是前面installPackageWithVerificationAndEncryption()方法中通过handler发送消息,消息的内容是InstallParams,而InstallParams又是继承自HandlerParams这个抽象类,所以具体执行的是handleStartCopy()与handleReturnCode()的是InstallParams。

不过InstallParams这个方法的代码很长,这里大致说下,InstallParams的handleStartCopy()的主要内容是通过com.android.defcontainer.DefaultContainerService来获取apk的推荐安装路径,通过这个路径来确定是内部安装还是SD卡安装,并且在这个方法的末尾,根据路径来创建不同的InstallArgs,分别是FileInstallArgs/SdInstallArgs执行各自的copyApk()方法!




这里就从FileInstallArgs的copyApk()这条路线来分析。

FileInstallArgs.copyApk()



这个方法的重点部分就在红框位置,它的作用就是把我们的APK给复制到/data/app下,这个的路径可以通过context.getPackageCodePath()获取到,命名规则一般都是XXX.base.apk,不过这里是个临时文件,在安装的时候会对其进行改名操作。
到这就分析完了InstallParams的handleStartCopy()方法,还有一个重点部分是handleReturnCode方法,所以接着分析handleReturnCode();

handleReturnCode():




这里很简单,调用了processPendingInstall()方法

processPendingInstall():



这里分为两部分:
第一张图可以看到标红框部分执行了doPreInstall()和installPackageLI(),doPostInstall();
doPreInstall和doPostInstall内部很简单,他的作用就是把我们安装过程中的临时文件删除,installPackageLI就是我们的正真安装操作。
第二张图则是安装之后,发送一个POST_INTALL消息,告诉系统是否安装完毕。

一系列的安装流程终于走到最后了,看下最终的install操作的方法

installPackageLI:



下面两个红框可以看到,分别通过两种不同的方式进行安装,具体的判断逻辑是根据包名来判断的,如果存在包名则是覆盖安装,而不存在就是安装一个新的apk。replacePackageLI()和installNewPackageLI()内部就和扫描系统中的package信息一样,如果不了解的,可以看下我这篇博客android 解析未安装apk中的AndroidManifest.xml以及系统源码分析  它把APK进行扫描,然后把apk中的信息存储到PackageManagerService中。了解4大组件的启动过程就会知道,有一段流程是在PackageManagerService中获取四大组件信息,这些信息就是通过把我们apk扫描安装然后存储到PackageMangerService中的。这样我们的apk就已经安装完成了。

图中还有一个红框,args.doRename()方法(这个args就是我们的FileInstallArgs),前面提到过我们的apk文件会被复制到/data/app下,当复制进来的时候命名格式不是xx.apk,而这里的doRename()方法就是把这个复制进来的文件改名成XXXbase.apk。所以我们context.getPackageCodePath()获取到的路径就是改名后的信息。


APK安装的源码分析就分析到这,大致的流程就是获取我们的安装位置,然后复制我们的apk文件到特定目录,然后安装我们的apk把apk的信息存储到PackageManagerService中。跟着上面的代码走一遍,相信还是很好理解的。










2017-12-21 00:00:00 xJ032w2j4cCjhOW8s8 阅读数 51664

热文导读 | 点击标题阅读

成为 Android 大牛的10大独门秘籍

吊炸天!74款APP完整源码!

一份年薪30万的Android面试宝典,附答案

作者:OCNYang

链接:https://juejin.im/post/5a320ffb51882561a20a7d5d


最近利用闲暇时间,写了一款生活工具类的应用,开始的目的也主要是为了熟练一些老框架和熟悉一些新框架或者第三方库,大家可以把它看成一款练手的 Demo 应用吧!

这里把自己在这个应用中用的库总结一下,还有一些大家感兴趣的实现效果也在这里给大家说明一下,大家在开发中作为一种参考吧。


源码及下载地址


这里是这款应用的下载地址和演示视频,大家可以下载把玩一下:

  • 小秋魔盒下载地址:

    https://mobile.baidu.com/item?type=soft&docid=21930773  

  • 小秋魔盒演示视频:

    http://v.youku.com/v_show/id_XMjcyMDkwMjM2MA==.html

  • 源码:https://github.com/OCNYang/QBox


第三方库

下面进入重点,主要介绍一下开发中采用的库第三方框架和库(截图较多,手机流量党慎入)。


1. 应用的整体框架(Rxjava + Retrofit + okhttp 网络请求框架)

在以前的项目中采用的网络请求框架,基本上都是使用的封装好的 okhttp 框架,不过最近一两年,Rxjava + Retrofit 十分热门势头很盛。

作为开发者,总是要去学习和接受采用这些新出来的技术。在开发的世界里,永远是技术至上,一门新技术的盛行总是有它的优越之处,要么比老框架性能提升不少,要不更加易用等等。



对于 Rxjava + Retrofit + okhttp 相结合的使用,个人建议目前不要去网上找那些所谓的封装好的库什么的,大家还是原生的使用 Rxjava 和 Retrofit,虽然代码上有些繁琐但是这样一来大家能够更快的熟悉它们的各个用法。(同时,表示目前我也没有找到能够把它们封装十分完善的库)。


三个库的官方地址请自行查找,这里提供一个介绍用法的 Demo 地址(个人也是参考这个使用的)


Github地址:
https://github.com/rengwuxian/RxJavaSamples


如果对这三个库的基本用法还不太熟悉的,可以参考一下下面我收集的一些教程:

Rxjava 教程  
给 Android 开发者的 RxJava 详解: 

https://gank.io/post/560e15be2dca930e00da1083#toc_5  
Okhttp 教程:  

http://ocnyang.com/tags/OkHttp/  
Retrofit2 教程:  
http://ocnyang.com/tags/Retrofit2/


2. 一些通用且常用的框架(Glide、leakcanary、Logger等等)


Logger 日志打印  
https://github.com/orhanobut/logger


GsonFormat & Gson json 数据解析  
https://github.com/zzz40500/GsonFormat  
https://github.com/google/gson


butterknife 依赖注入框架  
https://github.com/JakeWharton/butterknife


leakcanary 内存泄漏检测工具  
https://github.com/square/leakcanary


Glide 图片请求库  
https://github.com/bumptech/glide  
教程地址:http://ocnyang.com/tags/Glide/


circleimageview 圆形图片库  
https://github.com/hdodenhof/CircleImageView


greenDAO 数据库  
https://github.com/greenrobot/greenDAO


eventbus 事件发布/订阅框架  
https://github.com/greenrobot/EventBus


3. 常用第三方平台的使用(推送 + 社会化分享登录 + 短信验证)

这里先说一下,其实目前国内市场有相同功能的各个平台基本上趋于同质化,选择哪个也没必要那么纠结,效率相差不会太大。


  1. 小米推送(本来没想用小米的,抵不住前几年小米手机买的好名气大,我就用用试试。这里说一下和其他平台相比的不足点,小米推送目前不支持富文本的但是效率是很不错的。)

  2. ShareSDK Mob 平台的社会化分享和登录

  3. SMSSDK Mob 平台的手机短信验证(感觉 Mob 平台名气稳健,一方面是名字取得好呀,一方面永久免费)

  4. 友盟统计 (这个小应用大部分都会用吧)


4. 一些针对功能的开源库

BaseRecyclerViewAdapterHelper RecycleView 拓展库


无论在哪个项目,可能使用相对比较频繁的就是列表了,这里先说一下自己选取列表库。如果你还在着重用ListView/GridView,你真的需要抽出一点时间还研究研究一下 RecyclerView 的用法了。


RecyclerView 的使用我想大家已经在日常开发中已经频繁使用了,现在针对 RecyclerView 的封装库也已经是遍地都是了,Github 上 star 过千的也很多很多了。


大家可以看看这位朋友总结的 RecyclerView 库的集合,你一定会眼花缭乱的:


《那些酷炫的RecyclerView开源库整理》

 http://www.jianshu.com/p/154891851fe2


而我这当初在对比多个库后最终选择了 BaseRecyclerViewAdapterHelper 这个库,原因这个简单说一下:自己在选择时的标准是:


  • 列表要有的功能要全(添加 Header&Footer,下拉刷新页面,上拉加载更多这些基本的功能都要有);

  • 不能有入侵性,所谓入侵性就是不能对原生态使用有本质的改变,比如有些框架要求你在使用时使用 SuperRecycleView 诸如这些自定义的控件名,更有甚者有时候看这些框架源码才发现,它内部竟然是继承封装的 LinearLayout;有些要求你在使用原有控件时,需要在原有控件外部嵌套一层父布局,这点也是不能容忍的。

  • 这里简介一下 BaseRecyclerViewAdapterHelper,这个框架入侵性非常小,布局时你不需要任何改变,需要下拉刷新时,你可以像从前一样在外部嵌套一层 SwipeRefreshLayout 就好了。同时加载动画,加载失败等的布局你另外设置就行了,添加 header/footer 你新建一个布局通过 .add(header) 就很轻易的添加上了。
    BaseRecyclerViewAdapterHelper 这个框架从命名上你大概就能看出来,它的入侵体现在 Adapter 上,但是 Adapter 的职责是数据适配填充,对它的入侵在我看来是可以接受的。


这里就说这么多,感兴趣的话自己可以到 Github 上了解一下具体用法(说明文档还算友好,建议参考Demo看):
Github 地址:

https://github.com/CymChad/BaseRecyclerViewAdapterHelper


这里向大家展示一下在“小秋魔盒”中的使用:


多布局:



拖曳



添加 Header & Footer


ReactiveNetwork 手机网络连接状态动态监控  

现在的应用中,常常用到例如 WiFi 网络下显示图片,而手机流量下不显示图片的需求,这个时候就需要随时监听手机网络的连接和断开、WiFi和流量的切换,以便随时做出相应的变化。
ReactiveNetwork 这个库能够帮你做到这一点。这个库我也是无意中发现的,具体的用法大家可以自己查看:
Github地址:

https://github.com/pwittchen/ReactiveNetwork

colorpicker 颜色画板选择库  



类似的库也有很多,但还是喜欢这个。

https://github.com/QuadFlask/colorpicker

BGAQRCode-Android 二维码扫描项目



二维码扫描|图片来自Github

作者本身也是改造的别人的一个库,如果你的项目中需要二维码扫描且同时要求不是太高,可以考虑使用此库。

Github 地址:

https://github.com/bingoogolapple/BGAQRCode-Android


MPAndroidChart 图表库



在 Android 开发中使用过图表的,应该都会知道这个库吧。这个库是真心强大的不能强大的开源库,能满足你大多的图标图需求了。


Github 地址:

https://github.com/PhilJay/MPAndroidChart

material-calendarview 材料设计日历控件




改造后支持农历和节假日查询的日历,很漂亮的一个日历控件,可拓展性也很不错。


Github 地址:

https://github.com/prolificinteractive/material-calendarview

flexbox-layout 盒子布局控件


Github 地址:

https://github.com/google/flexbox-layout  

相应的教程也有很多,大家自行 Google 吧。


KenBurnsView 视差图片控件



有时候项目中应用一下,这种效果还是很不错的。

Github 地址:

https://github.com/flavioarfaria/KenBurnsView


materialsearchview 材料设计搜索框




Github 地址:

https://github.com/MiguelCatalan/MaterialSearchView

MagicIndicator 指示器库


这个库,目前发现多多少少还是有一点bug的。

MagicIndicator 地址:

https://github.com/hackware1993/MagicIndicator

Android应用自动更新库(android-auto-update)


一个应用本地自动更新的库,Dialog & Notification 两种形式都支持,还是很不错的。
项目地址:

https://github.com/feicien/android-auto-update


Slidinglayout 果冻弹跳效果




这种效果在QQ上大家应用很常见,一般情况下都是用在 WebView 上吧,当然你还可以用在其他地方,发挥你的想象力。


Github 地址:

https://github.com/HomHomLin/SlidingLayout

SlidingTutorial-Android 引导页






根据Demo直接使用,可定制的东西很少,当然引导页本身也没什么可定制的,就看自己喜欢不喜欢这个引导页的设计。这里给出了两个库,我在应用中采用的是第一个,大家欣赏一下就行了。


SlidingTutorial-Android项目地址:

https://github.com/Cleveroad/slidingtutorial-android  
AppIntro Github 地址:

https://github.com/apl-devs/AppIntro

Android 权限的动态申请

从 Android 6.0 开始,部分高级权限需要动态申请,在以前的版本中是在安装应用时一次性许可所有权限的申请,但是 6.0 以后,安装后只会默认允许部分低权限,高级权限需要你手动申请用户开通,如果你不做申请在应用权限的界面会闪退崩溃。


相信一线开发者早就知道这些了,这里给出几个可以帮助你动态申请权限的库:


  • PermissionsDispatcher 地址(目前应用中使用的是这个):https://github.com/hotchemi/PermissionsDispatcher

  • RxPermissions 地址:

    https://github.com/tbruyelle/RxPermissions

  • easypermissions 地址(貌似大家更青睐这个,不知道为什么):https://github.com/googlesamples/easypermissions

Android 图片手势控件



Github 地址:

https://github.com/boycy815/PinchImageView


结尾


好了,对库就总结这么多吧。如果大家在玩“小秋魔盒”时遇到什么问题都可以在下方留言,如果对应用中的某个功能的实现效果有疑问也可以下方留言,如果我感觉有必要的话,会写一些相应的教程给大家。


目前也已经在 Github 上建了相应的项目,同时也在上面更加详细的总结了用到的第三方库和相关功能的实现,大家可以看看:


小秋魔盒 Github 地址:

https://github.com/OCNYang/QBox   

你有好的文章想和大家分享欢迎投稿,直接向我投递文章链接即可


Java和Android架构

微信扫描或者点击下方二维码领取Android\Python\AI\Java等高级进阶资源

关注后回复“百度”、“阿里”、“腾讯”、“资源”有惊喜

公众号:JANiubility

欢迎加入我们的Java和Android架构圈,已有近1000人加入学习交流,更多学习资源更新,更多交流进步


更多学习资料点击下面的“阅读原文”获取

2018-05-30 10:00:34 qq_32611951 阅读数 9353

在android studio导出的apk分为4种,一种是未签名调试版apk,一种是未签名发行版apk,一种是已签名调试版apk,还有一种是已签名发行版apk。以下将介绍这4种apk如何导出。

一、调试版apk与发行版apk区别

Debug通常称为调试版本,通过一系列编译选项的配合,编译的结果通常包含调试信息,而且不做任何优化,以为开发 人员提供强大的应用程序调试能力。
Release通常称为发布版本,是为用户使用的,一般客户不允许在发布版本上进行调试。所以不保存调试信 息,同时,它往往进行了各种优化,以期达到代码最小和速度最优。为用户的使用提供便利。

(1) debug程序通常比release程序要慢,尤其是处理视频方便release要比debug快很多。在release模式对程序进行调试的时候经常会遇到变量虽然初始化了,但是在查看其值的时候却发现是一个随机的数并不是初始化的值,有时候在对变量进行监视的时候了,会出现找不到变量的情况。
(2) debug跟release在初始化变量时所做的操作是不同的,debug是将每个字节位都赋成0xcc, 而release的赋值近似于随机。在声明变量后马上对其初始化一个默认的值是最简单有效的办法,否则项目大了你找都没地方找。代码存在错误在debug方式下可能会忽略而不被察觉到。debug方式下数组越界也大多不会出错,在release中就暴露出来了,这个找起来就比较难了。
(3) 只有DEBUG版的程序才能设置断点、单步执行、使用 TRACE/ASSERT等调试输出语句。REALEASE不包含任何调试信息,所以体积小、运行速度快。
以上内容来自:https://zhidao.baidu.com/question/620939843767120332.html


二、签名的意义

为了保证每个应用程序开发商合法ID,防止部分开发商可能通过使用相同的包名(pakage name)来混淆替换已经安装的程序,我们需要对发布的APK文件进行签名。每一次所发布的APK应保证所用的签名文件的一致性,否则可能出现无法安装的情况。


三、导出调试版apk

导出调试版apk方法有两种,第一种:


选择菜单Build→ Build APK,点击便可生成调试版apk,等待生成成功,可在模块目录的build/outputs/apk目录下找到xxx-debug.apk的文件,此文件便是调试版apk。



第二种方法:


点击视图右上角的Gradle,弹出以上界面,双击模块目录下Tasks/build目录下的assembleDebug,便可在生成调试版apk,等待生成成功,可在模块目录的build/outputs/apk目录下找到xxx-debug.apk的文件,此文件便是调试版apk。


四、导出未签名发行版apk


点击视图右上角的Gradle,弹出以上界面,双击模块目录下Tasks/build目录下的assembleRelease,便可在生成未签名发行版apk,等待生成成功,可在模块目录的build/outputs/apk目录下找到xxx-release-unsigned.apk的文件,此文件便是未签名发行版apk。



五、生成已签名发行版或调试版apk


选择菜单Build→Generate Signed APK...并点击,出现如下界面


情况一:如果要使用已有的签名文件,那么点击Choose existing...按钮,选择已有的签名文件的路径,在Key store password输入框填写上签名文件的密码,在Key alias输入框填写上别名,在Key password输入框填写上该别名对应的密码,而后点击Next按钮,出现如下界面


Build Type类型中选择release,然后点击Finish按钮即可生成已签名发行版apk。等待生成成功,可在模块目录下找到xxx-release.apk的文件,此文件便是已签名发行版apk。

Build Type类型中选择debug,然后点击Finish按钮即可生成已签名调试版apk。等待生成成功,可在模块目录下找到xxx-debug.apk的文件,此文件便是已签名调试版apk。



情况二:如果还没有签名文件,需要使用新创建的签名文件,则点击Create new...按钮,点击后出现如下界面


箭头指出的部分填写上信息,点击OK,即可生成签名文件。

其余生成签名apk的步骤同情况一。

转载至:https://blog.csdn.net/u013524014/article/details/71537308

2013-08-26 16:37:01 colwer 阅读数 2873

只有一个apk文件,如何添加到Andorid源码中,开机之后这个apk已经安装好。

1.device/amlogic/f20ref/f20ref.mk中copy file到system/app目录下。

PRODUCT_COPY_FILES += \
  device/amlogic/f20ref/hello.apk:system/app/hello.apk


2. make 源代码。

 *** Prebuilt apk found in PRODUCT_COPY_FILES: device/amlogic/f16ref/hello.apk:/system/app/hello.apk, use BUILD_PREBUILT instead!.  Stop.



原因是build/core/Makefile中对copy file作了检测,如果是apk文件,会出错。

解决办法:注释掉build/core/Makefile文件中检测apk代码

#define check-product-copy-files
#$(if $(filter %.apk, $(1)),$(error \
     Prebuilt apk found in PRODUCT_COPY_FILES: $(1), use BUILD_PREBUILT inst     ead!))
#endef


3.在make otapackage时,报错:

File "/build/tools/releasetools/edify_generator.py", line 213, in SetPermissions
    self.script.append('set_perm(%d, %d, 0%o, "%s");' % (uid, gid, mode, fn))
TypeError: %d format: a number is required, not NoneType


可能是你的apk名字中包含非法字符,如中文,"-",等.

2016-08-18 17:13:09 sgmenghuo 阅读数 3827

0、前言

    作为android开发人员,经常面对这样的问题:网上下载的apk预制到系统,第三方oem提供编译好的库或者jar包等,你要将这些编制到你的系统中该如何做,那么这就不得不要去熟悉android编译环境,即一系列以LOCAL_XXX这样的变量。其实我们实际上碰到的编译MODULE就那么几样,下面我一一列出,可能不全面但是够用了。

1、第三方jar静态编译

    源码环境使用第三方jar,需要关注三个LOCAL_XXX:
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES值格式为  别名:库相对路径
LOCAL_STATIC_JAVA_LIBRARIES
BUILD_MULTI_PREBUILT第三方包,编译类型为BUILD_MULTI_PREBUILT
其中zxing-1.6-core.jar放在apk源码目录的libs目录下
Android.mk如下:
LOCAL_PATH :=$(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
#LOCAL_JAVA_LIBRARIES := ScannerAPI  telephony-common telephony-msim 
LOCAL_STATIC_JAVA_LIBRARIES :=zxing android-support-v4 android-support-v13
LOCAL_SRC_FILES :=$(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := SetInput2
LOCAL_CERTIFICATE := platform   #签名为platform


include $(BUILD_PACKAGE)


include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES :=zxing:libs/zxing-1.6-core.jar
include $(BUILD_MULTI_PREBUILT)

2、可执行程序bin

    比如我们想内置某些已编译好的busybox工具
Android.mk如下:
LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE        := busybox
LOCAL_MODULE_TAGS   := optional
LOCAL_MODULE_CLASS  := EXECUTABLES
LOCAL_SRC_FILES     := busybox
LOCAL_MODULE_PATH   := $(PRODUCT_OUT)/system/xbin
include $(BUILD_PREBUILT)

3、不带so的apk

    我在源码环境内置apk时发现有的可以运行,有的不能运行(说缺少lib64下的库),所以我分开说明如何内置这2中apk。
RAR工具解压apk没有lib文件夹的就是不带so的apk,最简单。
Android.mk如下:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := rsota
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_CLASS  := APPS
LOCAL_MODULE_PATH := $(PRODUCT_OUT)/system/app
LOCAL_CERTIFICATE := platform   
LOCAL_MODULE_SUFFIX := .apk

include $(BUILD_PREBUILT)


4、带so的apk

    解压apk,发现lib文件夹含有xxx.so就是带so的apk,一般这些so默认是32位的,当前我们使用的大量的设备都是64位cpu,apk一运行就曝出找不到lib64类型的库,我们只需要让他调用apk解压的那些32位的so库就行了。

4.1、so库架构类型

    这里穿插下关于lib下so的一些故事。如下是youku.apk中lib目录下的:
    
从这个图可以看出,这个youku.apk支持3种类型的cpu架构:arm,mips,x86,如果这里只有armeabi,那么是绝对不能正常安装到x86的android设备上的(比如使用x86架构的联想K900,好像还是科比代言,卖的好惨,反正我没看到有人用)。如果要提高多架构兼容,就得放入多架构的库,坏处就是apk好大啊吃手机内存啊,所以X86架构的手机好不好卖不是google决定的啊,是apk广大开发者决定的啊,没软件用,谁屌你,所以X86架构一般只能在几百块的水平打酱油,Windows Phone也是没软件用的例子。话说到这跑题了,我们继续库的类型。lib目录下一般包含着几种类型:armeabi,armeabi-v7a,arm64-v8a,mips,x86。不过我们只关注arm的。
armeabi是普通老的32位arm,armeabi-v7a是带浮点运算及一些高级指令的32位arm,arm64-v8a是近几年A53 A57架构的64位arm,具体渊源见如下文章
armeabi-v7a armeabi arm64-v8a》         http://blog.csdn.net/mao520741111/article/details/50328669
现在市面上的基本都是32位apk,所以你基本看不到arm64-v8a库,但是从android L版本后确实是64位虚拟机了,当然是兼容32位的啦,在64位虚拟机运行当然会默认去运行lib64类型的库,而我们内置带so(内置一般都是32位的so)的apk就是要强制让apk去用现有的32位so,方法如下:

4.2、带so库apk例子

1)解包拿出lib下的armeabi或者armeabi-v7a下的xxx.so(最终内置到系统相对于apk目录的lib/arm下)
2)Android.mk既要内置apk还要copy库
注意预置模块,用到BUILD_PREBUILT

例子一

(apk,so和Android.mk在一个目录下):

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional eng
LOCAL_MODULE := LiangdusIME
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_CLASS  := APPS
LOCAL_MODULE_PATH := $(PRODUCT_OUT)/system/priv-app
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_SUFFIX := .apk
include $(BUILD_PREBUILT)

include $(CLEAR_VARS)
LOCAL_MODULE        := libLDWWIme
LOCAL_MODULE_CLASS  := SHARED_LIBRARIES
LOCAL_MODULE_SUFFIX := .so
LOCAL_MULTILIB		:= 32
LOCAL_MODULE_TAGS   := optional
LOCAL_SRC_FILES     := libLDWWIme.so
LOCAL_MODULE_PATH   := $(PRODUCT_OUT)/system/priv-app/LiangdusIME/lib/arm
include $(BUILD_PREBUILT)


例子一有点缺点,要在$(product).mk中进行如下添加PRODUCT_PACKAGES += LiangdusIME libLDWWIme,在库比较少的时候没问题,多的时候,就比较麻烦了。比如一个apk有10个库,那么Android.mk都要要写10个module,$(product).mk也要写上这10个module名,麻烦死了,按道理就是一个apk,就写一个LOCAL_MODULE,其他的库应该有某种简单办法依赖一起内置,是的确实有那就是BUILD_MULTI_PREBUILT.

例子二

目录结构为
---Android.mk
---SeuicService.apk
---armeabi-v7a
       |---libctp_jni.so
       |---libkeypad_jni.so
---arm64-v8a
       |---libctp_jni.so
       |---libkeypad_jni.so

<span style="font-size:14px;">LOCAL_PATH := $(call my-dir)

#====== so lab =====
include $(CLEAR_VARS)
LOCAL_PREBUILT_LIBS := armeabi-v7a/libctp_jni.so
LOCAL_MULTILIB := 32
LOCAL_MODULE_TAGS := optional
include $(BUILD_MULTI_PREBUILT)

include $(CLEAR_VARS)
LOCAL_PREBUILT_LIBS := armeabi-v7a/libkeypad_jni.so
LOCAL_MULTILIB := 32
LOCAL_MODULE_TAGS := optional
include $(BUILD_MULTI_PREBUILT)

include $(CLEAR_VARS)
LOCAL_PREBUILT_LIBS := arm64-v8a/libctp_jni.so
LOCAL_MULTILIB := 64
LOCAL_MODULE_TAGS := optional
include $(BUILD_MULTI_PREBUILT)

include $(CLEAR_VARS)
LOCAL_PREBUILT_LIBS := arm64-v8a/libkeypad_jni.so
LOCAL_MULTILIB := 64
LOCAL_MODULE_TAGS := optional
include $(BUILD_MULTI_PREBUILT)

# ====  app ========================
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := SeuicService
LOCAL_MODULE_CLASS := APPS
LOCAL_CERTIFICATE := PRESIGNED     #预签名过了,不需在签名了
LOCAL_MODULE_PATH := $(TARGET_OUT)/app
LOCAL_REQUIRED_MODULES := \        #本模块加入系统,我需要的依赖模块也必须加入
		libkeypad_jni \
	        libctp_jni
LOCAL_SRC_FILES := SeuicService.apk

include $(BUILD_PREBUILT)</span>

其中LOCAL_REQUIRED_MODULES表示预置SeuicService这个模块时,会先去执行依赖的模块,就是内置so库。这样执行的结果是SeuicService.apk放到了android系统的system/app下,armeabi-v7a的2个so库内置到了android系统的system/lib/下,arm64-v8a的2个so放到了system/lib64/下,apk不管运行在32还是64位虚拟机都能正常。
例子二的区别是so库谁都能用,例子一的so库只有自己能用。

例子三

目录结构为
---Android.mk
---Chrome.apk
---lib
     |---armeabi-v7a
            |---crazy.libchrome.so
            |---libchrome.1847.114.so
|---crazy.libchrome.align
|---libchrome.1916.122.so
|---libchrome.1916.138.so
|---libchrome.1916.141.so
|---libchrome.1985.122.so
|---libchrome.1985.128.so
|---libchrome.1985.131.so
|---libchrome.1985.135.so
|---libchrome.2062.117.so
|---libchrome.2125.102.so
|---libchrome.2125.114.so
|---libchrome.2171.37.so
|---libchrome.2171.59.so
|---libchromium_android_linker.so
|---libchromeview.so

LOCAL_PATH := $(call my-dir)

# ====  app ========================
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := Chrome
LOCAL_MODULE_CLASS := APPS
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_MODULE_PATH := $(TARGET_OUT)/app
LOCAL_POST_PROCESS_COMMAND := $(shell mkdir -p $(LOCAL_MODULE_PATH)/Chrome/lib/arm) 
LOCAL_POST_PROCESS_COMMAND := $(shell cp -r $(LOCAL_PATH)/lib/armeabi-v7a/* $(LOCAL_MODULE_PATH)/Chrome/lib/arm) 
LOCAL_SRC_FILES := Chrome.apk

include $(BUILD_PREBUILT)

这是自由度最大的的方法,这个LOCAL_POST_PROCESS_COMMAND太厉害了简直为所欲为,因为它所引起的shell命令在make初期搜索Android.mk就执行了(我试过了可以多次加入执行)那个时候连system目录等都还没建立呢,这就是为什么我先mkdir再去cp,不然cp在目录没生成的情况下是失败的,而且注意了不报错。所以这种使用方式,需要你预先知道你要干什么,你得能预料结果,一般我就用mmm编译然后到out目录去验证。

5、源码集成带第三方so,jar的apk

这个是网上找到了
LOCAL_PATH:= $(call my-dir)  

include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional  
LOCAL_STATIC_JAVA_LIBRARIES := libbaidumapapi  
LOCAL_SRC_FILES := $(call all-subdir-java-files)  
LOCAL_PACKAGE_NAME := MyMaps  
include $(BUILD_PACKAGE)  

##################################################  

include $(CLEAR_VARS)  

LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES :=libbaidumapapi:libs/baidumapapi.jar  
LOCAL_PREBUILT_LIBS :=libBMapApiEngine_v1_3_1:libs/armeabi/libBMapApiEngine_v1_3_1.so  
LOCAL_MODULE_TAGS := optional  

include $(BUILD_MULTI_PREBUILT)  

6、其他预编译

其他如各种配置文件的拷贝基本都是通过include $(BUILD_PREBUILT)或者include $(BUILD_MULTI_PREBUILT)来完成,比如

include $(CLEAR_VARS)
LOCAL_MODULE := com.seuic.misc.xml
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)

基本就是修改LOCAL_MODULE名字,或者修改LOCAL_MODULE_CLASS(类型检查,临时文件存放等),以及修改LOCAL_MODULE_PATH放到哪。
其中LOCAL_MODULE_CLASS值有:APP,ETC,STATIC_LIBRARIES,EXECUTABLES,JAVA_LIBRARIES,SHARED_LIBRARIES等



另外,Android.mk它也是makefile,不要以为它就是特殊的文件,什么都强制照搬,思维会定在如何去用LOCAL_XXX上面,如果你足够熟悉,你可以更灵活点,当然可能不好看。


顺便提下
1)上面看到LOCAL_MODULE_TAGS := optional都是要在$(PRODUCT).mk中将模块名加入到PRODUCT_PACKAGES中的,否则make不编译别找我。
2)将so提升为系统库,就是对应so放到android系统的system/lib或者system/lib64下,这样系统中任何java文件就有机会通过System.loadLibrary("yyy"); 加载libyyy.so,但是,但但是,这个so不是普通c用的so,是jni格式的so。
3)将jar提升为系统包,就是将jar放到android系统的system/framework下,且添加权限,添加权限有2中方法(假设jar文件名为scanner.jar其java包名为com.seuic.scanner,模块名为scan)。
方法一:在$(PRODUCT).mk中添加PRODUCT_BOOT_JARS+=scan
方法二:在对应的*.rc中追加scanner.jar,用冒号分割,export BOOTCLASSPATH /system/framework/core.jar:scanner.jar,这个基本被方法一替代,不要再使用了,编译会自动展开PRODUCT_BOOT_JARS,在系统的root目录下生成init.environ.rc(内容全是export导出的环境变量)
方法三:这个方法独立性比较好,建议用这个方法。
<?xml version="1.0" encoding="UTF-8"?>
<permissions>
	<library
		name="com.seuic.scanner"
		file="/system/framework/scanner.jar"
	 />
</permissions>
4)Android如何调用第三方SO库
http://zwz94.blog.163.com/blog/static/3206039520131111101412959/
这篇文章问的还是有点意义的,看看能加深理解