ptz 发布的文章

去除标题栏有两种方法:

  1. 直接在 MainActivity 中的 onCreate 里面增加

    ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.hide();
        }
  2. AndroidManifest.xml 里面 android:theme 指向的 theme 里面 确保 windowNoTitle = true 并且 android:windowNoTitle = true 就可以无标题。
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <item name="windowActionBar">true</item>

参考: https://blog.csdn.net/vtming/article/details/52296090

activity

基本用法

  • AndroidManifest.xml文件中,android:label决定了 app 的名称
  • menu

intent

  • 隐式 intent 注意 category 和 action 的名称都要匹配, category 在 xml 文件中可以是多个。
  • 隐式 intent 还能调用系统提供的功能,比如浏览器 view,拨号 dial 等等
  • intent 通过 putExtra 和 get*Extra 来传递数据
  • startActivityResult 可以获得返回值的打开新 activity

life cycle

  • onCreate onDestory, onStart onStop, onResume onPause, onRestart
  • 暂停状态 一般只有在对话框的情况下出现,对话框处于栈顶,原来的 activity 不处于栈顶,但是仍然可见。所以只会调用 onPause 而不会进入 onStop
  • 为了防止 activity 被回收后数据丢失,所以使用 onSaveInstanceState 来储存临时数据。

start mode

  • standard 每次 startActivity 都会创建一个新的。
  • singleTop 每次非栈顶的情况就会创建新的。
  • singleTask 每次栈里面没有才会创建新的。
  • singleInstance 所有标记为这个的都会被放到一个单独的栈里面,这个单独的栈里面的是可以被多个程序进行调用的。

优化

  • BaseActivity 继承 AppCompatActivity,每次 onCreate 的时候, logd 打印当前是哪个 activity
  • AcitvityCollector 里面使用类静态列表,储存 activity,每次创建和删除都在列表中进行操作,额外再用一个 finishAll 来一次性关闭所有 activity 并清空列表,这样可以避免需要多次后退才能退出的问题。
  • 在被调用的 activity 里面增加 actionStart(Content content, String data1, String data2) 这样的函数,是的调用者可以很容易看出需要传入哪些数据,用来启动 activity.

UI

common

  • android:textAllCaps 这个默认是 true 会让 button 上面的文字默认全部大写。
  • editText 属性 android:hint 用来提示文字,android:maxLines 用来设置最多显示几行,多了就滚动显示。
  • imageView 大小属性都选择 wrap_content 比较好,android:srcsetImageResource 分别是 xml 和 java 中设置图片的方法。
  • 旋转等待图标一般是使用 visiable invisiable gone 这三种来控制显示
  • 水平进度条在 xml 中设置 styleandroid:max,然后在 java 中通过 getProgress setProgress 来控制进度条。
  • AlertDialog 用于警告对话框.ProgressDialog 用于长时间加载对话框,如果 setCancelable 设置为 false,那么 后面需要使用 dimiss() 来关闭对话框。

layout

  • layout_gravity 决定是布局时候的位置,当水平布局时,只能选择 top center_vertical bottom,当垂直布局的时候,只能选择 left center_horizontal end. gravity 决定的时内部文字的位置。
  • 如果使用 layout_weight, 那么相应的 layout_width 或者 layout_height 应该设置为 "0dp"。可以部分空间使用 wrap_content 另外一部分使用 layout_weight 来分空间。
  • RelativeLayout 中可以使用 alignParentTop 这样的布置到父界面的边角处的,也可以用 layout_above layout_toLeftOf 这样的布置到其他空间的相对位置的,也可以用 layout_alignTop 这样的和其他空间边缘对其的。
  • FrameLayout 使用场景很少,一般也就用 layout_gravity = "left" 之类的。
  • 可以通过引入 percent 来扩展 FrameLayoutRelativeLayout

custom

  • 可以把复用的部分写入一个 xml 布局文件里面,比如说标题栏。然后再其他布局里面通过 <include layout="@layout/title" 这样的形式引入这个复用的部分。
  • 原来的标题栏可以通过 getSupportActionBarhide 来进行隐藏.
  • 如果复用的还需要响应用户操作,并且在多个 activity 中响应动作时一样的,那么就需要自定义控件,新建类继承自 LinearLayout,然后重写他的构造函数,主要是 LayoutInflater.from(context).inflate(R.layout.title, this)用来动态加载 title.xml。 修改调用的 xml 代码, <com.example.uicustomviews.TitleLayout ...。 然后在子类里面写上响应的代码即可。

ListView

  • 简单的字符串列表,直接使用 ArrayAdapter android.R.layout.simple_list_item_1 即可。
  • 定制创建新的类,包含 list 中 item 需要的内容。然后需要继承 ArrayAdapter写一个相应的 adapter 类专门处理这个 item。还要创建一个新的 xml 专门描述这个 item 内容。最后就可以把这些合起来,使用自定义的 ListView 了。
  • 优化提升效率,使用 convertView 避免每次都要 LayoutInflater,新建一个类保存 findViewById 返回的内容,然后把这个类使用 setTag 保存到 convertView 中。
  • 点击事件,直接使用 setOnItemClickListener 即可。

RecyclerView

  • build.gradledependencies 里面可以不增加 implementation 'androidx.recyclerview:recyclerview:1.1.0'
  • 如果出现一行占据一个屏幕的现象,那就检查 fruit_item.xml 看看里面的 root 节点下的 layout_height 是不是 match_parent。 奇怪的是,这样的设置在 ListView 中就没有问题。
  • 想要横向滚动,只要修改 xml 文件,并且使用 setOrientation(LinearLayoutManager.HORIZONTAL) 即可。
  • 想要瀑布,只要使用 StaggerredGridLayoutManager 即可。
  • 点击事件,针对需要的使用 setOnClickListener 即可。

参考: https://www.cnblogs.com/rustfisher/p/12254732.html

paractise

  • 在 androidstudio 中,右键 png 图片,选择 create 9-patch file,即可创建9图片,然后在需要拉伸的地方用鼠标在边框上描黑,保存即可。然后删除原来的 png 文件就行了。
  • 使用 RecyclerView 时,有新的数据,可以使用 notifyItemInserted 来通知列表插入新数据,使用 scrollToPosition 来让列表滚动到需要的位置。

fragment

usage

  • fragment 直接使用 xml,然后新建类继承 Fragment 即可。
  • 动态加载,那么 xml 里面需要使用 FrameLayout来预先占据 fragment 的位置。使用 getSupportFragmentManager beginTransaction replace commit 来把 fragment 动态加载到这个 frame layout 里面去。
  • 按后退键能够类似 activity 一样,可以使用 addToBackStack 这个方法。
  • activity 调用 fragment 中的方法,首先使用 getSupportFragmentManager().findFragmentById(R.id.right_fragment 这样的方法获取到 fragment 实例,然后就可以调用方法了。
  • fragment 获取 activity 方法,可以使用 getActivity 来获取到 activity 实例,然后就可以调用方法了。

life

  • activity 进入停止状态,fragment 也会自动停止。或者使用 FragmentTranscation 的 remove, replace 方法移除 fragment。
  • fragment 专门的回调函数,onAttach onDetach 是 fragment 和 activity 建立解除关联时调用。 onCreateView onDestoryView 是创建视图加载布局或者相反时调用。 onActivityCreated 和 fragment 相关联的 activity 一定已经创建完毕时调用。
  • 添加 fragment -> onAttach -> onCreate -> onCreateView -> onActivityCreated -> onStart -> onResume + onPause -> onStop -> onDestoryView -> onDestory -> onDetach -> 销毁。
  • onDestoryView -> onCreateView.
  • 如果使用了 addToBackStack ,那么切换 fragment 的时候,可以让当前的 fragment 只执行 onDestoryView 而不执行 onDestory,使用返回键的时候跳过 onCreate 直接进入 onCreateView
  • onSaveInstanceState 可以用来保存临时数据,防止被系统因为内存问题直接回收了。

trick

  • 可以新建文件夹 layout-large,当系统认为当前设备是 large 的时候,就会调用这个文件夹下面的布局文件,否则调用 layout 文件夹下面的布局文件。 具体可以看 P158
  • 还可以手动指定最小宽度,新建 layout-sw600dp,当屏幕宽度大于等于 600dp 的时候,就会加载这个文件夹下面的布局文件。 具体可以看 P159

practice

  • android:ellipsize 文本超过控件宽度,文本从尾部进行缩略。
  • Configurations for activity_main.xml must agree on the root element's ID. 这个报错是因为在多个 layoutactivity_main.xml 中 root 节点的 android:id 必须一致。

转自: https://blog.csdn.net/qq_28193019/article/details/91050915

生成TAG

logt+Tab键:

private static final String TAG = "Extract";

生成Log.d()

logd+Tab键:

Log.d(TAG, "onCreate: ");

生成Log.e()

loge+Tab键:

Log.e(TAG, "onCreate: ",new Throwable());

生成Log.i/w类似

Log.i(TAG, "onCreate: ");
Log.w(TAG, "onCreate: ", );

logm+Tab:打印参数

Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");

logr+Tab:打印返回值

Log.d(TAG, "onCreate() returned: " + );

查看日志输出

在Android Studio Logcat选项卡,中可以选择过滤的级别,来过滤日志输出,比如Verbose、Debug、Error等,上面的选项(如Debug)会包含下面选项(如Error)的日志

当系统中缺少某个特定版本的 libc 的时候,可以在编译的时候,添加 -nostdlib 选项,并在后面附上指定的库即可。类似于

arm-linux-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -nostdlib /work/android-5.0.2/prebuilts/ndk/9/platform/android-19/arch-arm/usr/lib/libc.so

当c文件编译的时候,找不到 log 方面的头文件和库的时候,可以手动添加,类似下面这样

arm-linux-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -nostdlib /work/android-5.0.2/prebuilts/ndk/9/platform/android-19/arch-arm/usr/lib/libc.so -I /work/android-5.0.2/prebuilts/ndk/9/platform/android-19/arch-arm/usr/include /work/android-5.0.2/prebuilts/ndk/9/platform/android-19/arch-arm/usr/lib/liblog.so

硬件服务中,xxx_service.cpp, hal_xxx.c 的关系

com_android_server_LedService.cpp 这样的文件是 jni 源码,主要做的是向系统注册的事情。hal_led.c 这样的是实际的对硬件进行操作的源码,比如说 read, write, ioctrl 等等。 之所以分开来主要考虑两个原因。

  • com_android_server_LedService.cpp 这样的文件如果有变更,那么需要系统重新编译。hal_led.c这样的正常时编译为 so 库,所以可以不用系统重新编译。
  • 有些厂商为了保密的需要,不提供 hal_led.c 这样的源码,只是提供一个 so 的库文件。
  • onload.cpp 调用 com_android_server_LedService.cpp 中实现的函数
  • SystemServer.java 需要 new LedService 然后再 addService
  • LedService.java 里面调用 native 方法
  • ILedService.java 给 app 来使用。

ILedService.java

  1. 仿照其他的 aidl 文件,比如说 vibr 的 aidl 文件,复制为ILedService.aidl,然后修改一下接口。
  2. 查看 vibr aidl 文件所在的目录,放到类似的目录
  3. 从当前目录开始查看有没有 Android.mk,如果没有,就向上一层继续查找,知道找到为止。
  4. Android.mk 中仿照 vibr aidl,把ILedService.aidl加入进去。
  5. 使用 mmm . 进行编译。
  6. 到 out 目录下找找有没有 ILedService.java 生成
  7. 可以估计 app 中对 led 的用法是
    ILedService iLedService;
    iLedService = ILedService.stub.asInterface(ServiceManager.getService("led"));
    iLedService.ledCtrl(0, 1);

参考 VibratorService.java 来写 LedService.java.

  1. LedService 继承了 ILedService.Stub, 到 ILedService.javaStub 申明实现的接口 android.os.ILedService , android.os.ILedService 这个里面自动生成的需要实现的只有 ledCtrl 这个函数。
  2. LedService.java 中实现 ledCtrl,这个函数中直接调用 native 方法即可。别忘了申明 native 方法
  3. LedService.java 中的构造函数中也需要调用 open 的 native 方法。别忘了申明 native 方法
  4. SystemServer.java 中,找到 vibr 使用 ServiceManager.addService 的地方,仿照写个 led 的添加服务的调用。

com_android_server_LedService.cpp

参考 com_android_server_VibratorService.cpp 来写。

Onload.cpp

找到 register_android_server_VibratorService(env) 这个注册的地方,复制一份,改为 register_android_server_LedService(env). 别忘了添加这个函数相应的申明。

保存 mmm 编译时的指令

mmm frameworks/base show commands >log.txt 2>&1

在 app 中直接 import android.os.ILedService 会找不到

  1. 可以参考: https://stackoverflow.com/questions/7888191/how-do-i-build-the-android-sdk-with-hidden-and-internal-apis-available ,把 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar 这个复制出来,并改个名字。
  2. 可以参考 https://stackoverflow.com/questions/16608135/android-studio-add-jar-as-library 中的在 android studio 中添加 jar 文件。

led_hal.c

参考 hardware/libhardware/modules/vibrator/vibrator.chardware.h 来写 led_hal.c

编译

上面这些修改好了之后,可以使用命令进行编译。

mmm frameworks/base/services
mmm hardware/libhardware/modules/led
make snod

logcat

logcat 在命令行中可以使用类似 logcat LedHal:I *:S 这样的方法来进行过滤。

转自: https://www.cnblogs.com/wangshuo1/p/5697746.html

常常在网上看到有人询问:如何把 java 程序编译成 .exe 文件。通常回答只有两种,一种是制作一个可执行的 JAR 文件包,然后就可以像.chm 文档一样双击运行了;而另一种是使用 JET 来进行 编译。但是 JET 是要用钱买的,而且据说 JET 也不是能把所有的 Java 程序都编译成执行文件,性能也要打些折扣。所以,使用制作可执行 JAR 文件包的方法就是最佳选择了,何况它还能保持Java 的跨平台特性。

下面就来看看什么是 JAR 文件包吧:

1. JAR 文件包

JAR 文件就是 Java Archive File,顾名思意,它的应用是与 Java 息息相关的,是 Java 的一种文档格式。JAR 文件非常类似 ZIP 文件--准确的说,它就是 ZIP 文件,所以叫它文件包。JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中,包含了一个 META-INF/MANIFEST.MF 文件,这个文件是在生成 JAR 文件的时候自动创建的。举个例子,如果我们具有如下目录结构的一些文件:

==

`-- test

  `-- Test.class

把它压缩成 ZIP 文件 test.zip,则这个 ZIP 文件的内部目录结构为:

test.zip

`-- test

  `-- Test.class

如果我们使用 JDK 的 jar 命令把它打成 JAR 文件包 test.jar,则这个 JAR 文件的内部目录结构为:

test.jar

|-- META-INF

|  `-- MANIFEST.MF

`-- test

`--Test.class

2. 创建可执行的 JAR 文件包

制作一个可执行的 JAR 文件包来发布你的程序是 JAR 文件包最典型的用法。

Java 程序是由若干个 .class 文件组成的。这些 .class 文件必须根据它们所属的包不同而分级分目录存放;运行前需要把所有用到的包的根目录指定给 CLASSPATH 环境变量或者 java 命令的 -cp 参数;运行时还要到控制台下去使用 java 命令来运行,如果需要直接双击运行必须写 Windows 的批处理文件 (.bat) 或者 Linux 的 Shell 程序。因此,许多人说,Java 是一种方便开发者苦了用户的程序设计语言。

其实不然,如果开发者能够制作一个可执行的 JAR 文件包交给用户,那么用户使用起来就方便了。在 Windows 下安装 JRE (Java Runtime Environment) 的时候,安装文件会将 .jar 文件映射给 javaw.exe 打开。那么,对于一个可执行的 JAR 文件包,用户只需要双击它就可以运行程序了,和阅读 .chm 文档一样方便 (.chm 文档默认是由 hh.exe 打开的)。那么,现在的关键,就是如何来创建这个可执行的 JAR 文件包。

创建可执行的 JAR 文件包,需要使用带 cvfm 参数的 jar 命令,同样以上述 test 目录为例,命令如下:

jar cvfm test.jar manifest.mf test

这里 test.jar 和 manifest.mf 两个文件,分别是对应的参数 f 和 m,其重头戏在 manifest.mf。因为要创建可执行的 JAR 文件包,光靠指定一个 manifest.mf 文件是不够的,因为 MANIFEST 是 JAR 文件包的特征,可执行的 JAR 文件包和不可执行的 JAR 文件包都包含 MANIFEST。关键在于可执行 JAR 文件包的 MANIFEST,其内容包含了 Main-Class 一项。这在 MANIFEST 中书写格式如下:

Main-Class: 可执行主类全名(包含包名)

例如,假设上例中的 Test.class 是属于 test 包的,而且是可执行的类 (定义了 public static void main(String[]) 方法),那么这个 manifest.mf 可以编辑如下:

Main-Class: test.Test <回车>

这个 manifest.mf 可以放在任何位置,也可以是其它的文件名,只需要有 Main-Class: test.Test 一行,且该行以一个回车符结束即可。创建了 manifest.mf 文件之后,我们的目录结构变为:

==

|-- test

|  `-- Test.class

`-- manifest.mf

这时候,需要到 test 目录的上级目录中去使用 jar 命令来创建 JAR 文件包。也就是在目录树中使用“==”表示的那个目录中,使用如下命令:

jar cvfm test.jar manifest.mf test

之后在“==”目录中创建了 test.jar,这个 test.jar 就是执行的 JAR 文件包。运行时只需要使用 java -jar test.jar 命令即可。

需要注意的是,创建的 JAR 文件包中需要包含完整的、与 Java 程序的包结构对应的目录结构,就像上例一样。而 Main-Class 指定的类,也必须是完整的、包含包路径的类名,如上例的 test.Test;而且在没有打成 JAR 文件包之前可以使用 java <类名> 来运行这个类,即在上例中 java test.Test 是可以正确运行的 (当然要在 CLASSPATH 正确的情况下)。

3. jar 命令详解

jar 是随 JDK 安装的,在 JDK 安装目录下的 bin 目录中,Windows 下文件名为 jar.exe,Linux 下文件名为 jar。它的运行需要用到 JDK 安装目录下 lib 目录中的 tools.jar 文件。不过我们除了安装 JDK 什么也不需要做,因为 SUN 已经帮我们做好了。我们甚至不需要将 tools.jar 放到 CLASSPATH 中。

使用不带任何的 jar 命令我们可以看到 jar 命令的用法如下:

jar {ctxu}[vfm0M] [jar-文件] [manifest-文件] [-C 目录] 文件名 ...

其中 {ctxu} 是 jar 命令的子命令,每次 jar 命令只能包含 ctxu 中的一个,它们分别表示:

 -c 创建新的 JAR 文件包

 -t 列出 JAR 文件包的内容列表

 -x 展开 JAR 文件包的指定文件或者所有文件

 -u 更新已存在的 JAR 文件包 (添加文件到 JAR 文件包中)

 [vfm0M] 中的选项可以任选,也可以不选,它们是 jar 命令的选项参数

 -v 生成详细报告并打印到标准输出

 -f 指定 JAR 文件名,通常这个参数是必须的

 -m 指定需要包含的 MANIFEST 清单文件

 -0 只存储,不压缩,这样产生的 JAR 文件包会比不用该参数产生的体积大,但速度更快

 -M 不产生所有项的清单(MANIFEST〕文件,此参数会忽略 -m 参数

 [jar-文件] 即需要生成、查看、更新或者解开的 JAR 文件包,它是 -f 参数的附属参数

 [manifest-文件] 即 MANIFEST 清单文件,它是 -m 参数的附属参数

 [-C 目录] 表示转到指定目录下去执行这个 jar 命令的操作。它相当于先使用 cd 命令转该目录下再执行不带 -C 参数的 jar 命令,它只能在创建和更新 JAR 文件包的时候可用。

文件名 ... 指定一个文件/目录列表,这些文件/目录就是要添加到 JAR 文件包中的文件/目录。如果指定了目录,那么 jar 命令打包的时候会自动把该目录中的所有文件和子目录打入包中。

下面举一些例子来说明 jar 命令的用法:

  1. jar cf test.jar test

该命令没有执行过程的显示,执行结果是在当前目录生成了 test.jar 文件。如果当前目录已经存在 test.jar,那么该文件将被覆盖。

  1. jar cvf test.jar test

该命令与上例中的结果相同,但是由于 v 参数的作用,显示出了打包过程,如下:

标明清单(manifest)

增加:test/(读入= 0) (写出= 0)(存储了 0%)

增加:test/Test.class(读入= 7) (写出= 6)(压缩了 14%)

  1. jar cvfM test.jar test

该命令与 2) 结果类似,但在生成的 test.jar 中没有包含 META-INF/MANIFEST 文件,打包过程的信息也略有差别:

增加:test/(读入= 0) (写出= 0)(存储了 0%)

增加:test/Test.class(读入= 7) (写出= 6)(压缩了 14%)

  1. jar cvfm test.jar manifest.mf test

运行结果与 2) 相似,显示信息也相同,只是生成 JAR 包中的 META-INF/MANIFEST 内容不同,是包含了 manifest.mf 的内容

  1. jar tf test.jar

在 test.jar 已经存在的情况下,可以查看 test.jar 中的内容,如对于 2) 和 3) 生成的 test.jar 分别应该此命令,结果如下;

对于 2)

META-INF/

META-INF/MANIFEST.MF

test/

test/Test.class

对于 3)

test/

test/Test.class
  1. jar tvf test.jar

除显示 5) 中显示的内容外,还包括包内文件的详细信息,如:

0 Wed Jun 19 15:39:06 GMT 2002 META-INF/

86 Wed Jun 19 15:39:06 GMT 2002 META-INF/MANIFEST.MF

0 Wed Jun 19 15:33:04 GMT 2002 test/

7 Wed Jun 19 15:33:04 GMT 2002 test/Test.class
  1. jar xf test.jar

解开 test.jar 到当前目录,不显示任何信息,对于 2) 生成的 test.jar,解开后的目录结构如下:

==

|-- META-INF

|  `-- MANIFEST

`-- test

`--Test.class

jar xvf test.jar

运行结果与 7) 相同,对于解压过程有详细信息显示,如:

创建:META-INF/

展开:META-INF/MANIFEST.MF

创建:test/

展开:test/Test.class

  1. jar uf test.jar manifest.mf

在 test.jar 中添加了文件 manifest.mf,此使用 jar tf 来查看 test.jar 可以发现 test.jar 中比原来多了一个 manifest。这里顺便提一下,如果使用 -m 参数并指定 manifest.mf 文件,那么 manifest.mf 是作为清单文件 MANIFEST 来使用的,它的内容会被添加到 MANIFEST 中;但是,如果作为一般文件添加到 JAR 文件包中,它跟一般文件无异。

  1. jar uvf test.jar manifest.mf

与 9) 结果相同,同时有详细信息显示,如:

增加:manifest.mf(读入= 17) (写出= 19)(压缩了 -11%)

4. 关于 JAR 文件包的一些技巧

  1. 使用 unzip 来解压 JAR 文件

在介绍 JAR 文件的时候就已经说过了,JAR 文件实际上就是 ZIP 文件,所以可以使用常见的一些解压 ZIP 文件的工具来解压 JAR 文件,如 Windows 下的 WinZip、WinRAR 等和 Linux 下的 unzip 等。使用 WinZip 和 WinRAR 等来解压是因为它们解压比较直观,方便。而使用 unzip,则是因为它解压时可以使用 -d 参数指定目标目录。

在解压一个 JAR 文件的时候是不能使用 jar 的 -C 参数来指定解压的目标的,因为 -C 参数只在创建或者更新包的时候可用。那么需要将文件解压到某个指定目录下的时候就需要先将这具 JAR 文件拷贝到目标目录下,再进行解压,比较麻烦。如果使用 unzip,就不需要这么麻烦了,只需要指定一个 -d 参数即可。如:

unzip test.jar -d dest/
  1. 使用 WinZip 或者 WinRAR 等工具创建 JAR 文件

上面提到 JAR 文件就是包含了 META-INF/MANIFEST 的 ZIP 文件,所以,只需要使用 WinZip、WinRAR 等工具创建所需要 ZIP 压缩包,再往这个 ZIP 压缩包中添加一个包含 MANIFEST 文件的 META-INF 目录即可。对于使用 jar 命令的 -m 参数指定清单文件的情况,只需要将这个 MANIFEST 按需要修改即可。

  1. 使用 jar 命令创建 ZIP 文件

有些 Linux 下提供了 unzip 命令,但没有 zip 命令,所以需要可以对 ZIP 文件进行解压,即不能创建 ZIP 文件。如要创建一个 ZIP 文件,使用带 -M 参数的 jar 命令即可,因为 -M 参数表示制作 JAR 包的时候不添加 MANIFEST 清单,那么只需要在指定目标 JAR 文件的地方将 .jar 扩展名改为 .zip 扩展名,创建的就是一个不折不扣的 ZIP 文件了,如将上一节的第 3) 个例子略作改动:

jar cvfM test.zip test

  使用jar命令解决解压缩后,文件乱码问题。

使用JAR解压,命令行下执行 jar -xvf 压缩包.zip

转自: http://gityuan.com/2016/03/19/android-build/

工欲善其事,必先利其器,对于想要深入学习Android源码,必须先掌握Android编译命令.

一、引言

关于Android Build系统,这个话题很早就打算整理下,迟迟没有下笔,决定跟大家分享下。先看下面几条指令,相信编译过Android源码的人都再熟悉不过的。

source setenv.sh
lunch
make -j12

记得最初刚接触Android时,同事告诉我用上面的指令就可以编译Android源码,指令虽短但过几天就记不全或者忘记顺序,每次编译时还需要看看自己的云笔记,冰冷的指令总是难以让我记忆。后来我决定认真研究下这个指令的含义。知其然还需知其所以然,这样能更深层次的理解并记忆,才能与自身的知识体系建立强连接,或许还有意外收获,果然如此,接下来跟大家分享一下在研究上述几条指令含义的过程中,深入了解到的Android Build(编译)系统。

二、编译命令

准备好编译环境后,编译Android源码的第一步是 source build/envsetup.sh,其中source命令就是用于运行shell脚本命令,功能等价于”.”,因此该命令也等价于. build/envsetup.sh。在文件envsetup.sh声明了当前会话终端可用的命令,这里需要注意的是当前会话终端,也就意味着每次新打开一个终端都必须再一次执行这些指令。起初并不理解为什么新开的终端不能直接执行make指令,到这里总算明白了。

接下来,解释一下本文开头的引用的命令:

source setenv.sh  //初始化编译环境,包括后面的lunch和make指令
lunch  //指定此次编译的目标设备以及编译类型
make  -j12 //开始编译,默认为编译整个系统,其中-j12代表的是编译的job数量为12。

所有的编译命令都在envsetup.sh文件能找到相对应的function,比如上述的命令lunch,make,在文件一定能找到

function lunch(){
    ...
}

function make(){
    ...
}

source envsetup.sh,需要cd到setenv.sh文件所在路径执行,路径可能在build/envsetup.sh,或者integrate/envsetup.sh,再或者不排除有些厂商会封装自己的.sh脚本,但核心思路是一致的。

具体实现这里就不展开说明,下面精炼地总结了一下各个指令用法和功效。

2.1 代码编译

编译指令 解释
m 在源码树的根目录执行编译
mm 编译当前路径下所有模块,但不包含依赖
mmm [module_path] 编译指定路径下所有模块,但不包含依赖
mma 编译当前路径下所有模块,且包含依赖
mmma [module_path] 编译指定路径下所有模块,且包含依赖
make [module_name] 无参数,则表示编译整个Android代码

下面列举部分模块的编译指令:

模块 make命令 mmm命令
init make init mmm system/core/init
zygote make app_process mmm frameworks/base/cmds/app_process
system_server make services mmm frameworks/base/services
java framework make framework mmm frameworks/base
framework资源 make framework-res mmm frameworks/base/core/res
jni framework make libandroid_runtime mmm frameworks/base/core/jni
binder make libbinder mmm frameworks/native/libs/binder

上述mmm命令同样适用于mm/mma/mmma,编译系统采用的是增量编译,只会编译发生变化的目标文件。当需要重新编译所有的相关模块,则需要编译命令后增加参数-B,比如make -B [module_name],或者 mm -B [module_path]

Tips:

  • 对于m、mm、mmm、mma、mmma这些命令的实现都是通过make方式来完成的。
  • mmm/mm编译的效率很高,而make/mma/mmma编译较缓慢;
  • make/mma/mmma编译时会把所有的依赖模块一同编译,但mmm/mm不会;
  • 建议:首次编译时采用make/mma/mmma编译;当依赖模块已经编译过的情况,则使用mmm/mm编译。

2.2 代码搜索

搜索指令 解释
cgrep 所有C/C++文件执行搜索操作
jgrep 所有Java文件执行搜索操作
ggrep 所有Gradle文件执行搜索操作
mangrep [keyword] 所有AndroidManifest.xml文件执行搜索操作
mgrep [keyword] 所有Android.mk文件执行搜索操作
sepgrep [keyword] 所有sepolicy文件执行搜索操作
resgrep [keyword] 所有本地res/*.xml文件执行搜索操作
sgrep [keyword] 所有资源文件执行搜索操作

上述指令用法最终实现方式都是基于grep指令,各个指令用法格式:

xgrep [keyword]  //x代表的是上表的搜索指令

例如,搜索所有AndroidManifest.xml文件中的launcher关键字所在文件的具体位置,指令

mangrep launcher

再如,搜索所有Java代码中包含zygote所在文件

jgrep zygote

又如,搜索所有system_app的selinux权限信息

sepgrep system_app

Tips: Android源码非常庞大,直接采用grep来搜索代码,不仅方法笨拙、浪费时间,而且搜索出很多无意义的混淆结果。根据具体需求,来选择合适的代码搜索指令,能节省代码搜索时间,提高搜索结果的精准度,方便定位目标代码。

2.3 导航指令

导航指令 解释
croot 切换至Android根目录
cproj 切换至工程的根目录
godir [filename] 跳转到包含某个文件的目录

Tips: 当每次修改完某个文件后需要编译时,执行cproj后会跳转到当前模块的根目录,也就是Android.mk文件所在目录,然后再执行mm指令,即可编译目标模块;当进入源码层级很深后,需要返回到根目录,使用croot一条指令完成;另外cd - 指令可用于快速切换至上次目录。

2.4 信息查询

查询指令 解释
hmm 查询所有的指令help信息
findmakefile 查询当前目录所在工程的Android.mk文件路径
print_lunch_menu 查询lunch可选的product
printconfig 查询各项编译变量值
gettop 查询Android源码的根目录
gettargetarch 获取TARGET_ARCH值

2.5 其他指令

上述只是列举比较常用的指令,还有其他指令,而且不同的build编译系统,支持的指令可能会存在一些差异,当忘记这些编译指令,可以通过执行hmm,查询指令的帮助信息。

最后再列举两个比较常用的指令:

  • make clean:执行清理操作,等价于 rm -rf out/
  • make update-api:更新API,在framework API改动后需执行该指令,Api记录在目录frameworks/base/api

三、编译系统

Android 编译系统是Android源码的一部分,用于编译Android系统,Android SDK以及相关文档。该编译系统是由Make文件、Shell以及Python脚本共同组成,其中最为重要的便是Make文件。关于编译系统可参考 理解 Android Build 系统。 www.ibm.com/developerworks/cn/opensource/os-cn-android-build/

3.1 Makefile分类

整个Build系统的Make文件分为三大类:

  • 系统核心的Make文件:定义了Build系统的框架,文件全部位于路径/build/core,其他Make文件都是基于该框架编写的;
  • 针对产品的Make文件:定义了具体某个型号手机的Make文件,文件路径位于/device,该目录下往往又以公司名和产品名划分两个子级目录,比如/device/qcom/msm8916
  • 针对模块的Make文件:整个系统分为各个独立的模块,每个模块都一个专门的Make文件,名称统一为”Android.mk”,该文件定义了当前模块的编译方式。Build系统会扫描整个源码树中名为”Android.mk”的问题,并执行相应模块的编译工作。

3.2 编译产物

经过make编译后的产物,都位于/out目录,该目录下主要关注下面几个目录:

  • /out/host:Android开发工具的产物,包含SDK各种工具,比如adb,dex2oat,aapt等。
  • /out/target/common:通用的一些编译产物,包含Java应用代码和Java库;
  • /out/target/product/[product_name]:针对特定设备的编译产物以及平台相关C/C++代码和二进制文件;

在/out/target/product/[product_name]目录下,有几个重量级的镜像文件:

  • system.img:挂载为根分区,主要包含Android OS的系统文件;
  • ramdisk.img:主要包含init.rc文件和配置文件等;
  • userdata.img:被挂载在/data,主要包含用户以及应用程序相关的数据;

当然还有boot.img,reocovery.img等镜像文件,这里就不介绍了。

3.3 Android.mk解析

在源码树中每一个模块的所有文件通常都相应有一个自己的文件夹,在该模块的根目录下有一个名称为“Android.mk” 的文件。编译系统正是以模块为单位进行编译,每个模块都有唯一的模块名,一个模块可以有依赖多个其他模块,模块间的依赖关系就是通过模块名来引用的。也就是说当模块需要依赖一个jar包或者apk时,必须先将jar包或apk定义为一个模块,然后再依赖相应的模块。

对于Android.mk文件,通常都是以下面两行

LOCAL_PATH := $(call my-dir)  //设置当编译路径为当前文件夹所在路径
include $(CLEAR_VARS)  //清空编译环境的变量(由其他模块设置过的变量)

为方便模块编译,编译系统设置了很多的编译环境变量,如下:

  • LOCAL_SRC_FILES:当前模块包含的所有源码文件;
  • LOCAL_MODULE:当前模块的名称(具有唯一性);
  • LOCAL_PACKAGE_NAME:当前APK应用的名称(具有唯一性);
  • LOCAL_C_INCLUDES:C/C++所需的头文件路径;
  • LOCAL_STATIC_LIBRARIES:当前模块在静态链接时需要的库名;
  • LOCAL_SHARED_LIBRARIES:当前模块在运行时依赖的动态库名;
  • LOCAL_STATIC_JAVA_LIBRARIES:当前模块依赖的Java静态库;
  • LOCAL_JAVA_LIBRARIES:当前模块依赖的Java共享库;
  • LOCAL_CERTIFICATE:签署当前应用的证书名称,比如platform。
  • LOCAL_MODULE_TAGS:当前模块所包含的标签,可以包含多标签,可能值为debgu,eng,user,development或optional(默认值)

针对这些环境变量,编译系统还定义了一些便捷函数,如下:

  • $(call my-dir):获取当前文件夹路径;
  • $(call all-java-files-under, ):获取指定目录下的所有Java文件;
  • $(call all-c-files-under, ):获取指定目录下的所有C文件;
  • $(call all-Iaidl-files-under, ) :获取指定目录下的所有AIDL文件;
  • $(call all-makefiles-under, ):获取指定目录下的所有Make文件;

示例:

  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)

  # 获取所有子目录中的Java文件
  LOCAL_SRC_FILES := $(call all-subdir-java-files)

  # 当前模块依赖的动态Java库名称
  LOCAL_JAVA_LIBRARIES := com.gityuan.lib

  # 当前模块的名称
  LOCAL_MODULE := demo

  # 将当前模块编译成一个静态的Java库
  include $(BUILD_STATIC_JAVA_LIBRARY)

转自: https://segmentfault.com/a/1190000021742841

基于 Android 9.0。
许多命令时间一长就记不清了,记录一下。

Android 环境引入的命令

在shell中执行"source build/envsetup.sh"后,Android将增加一些命令到当前环境中。

  • lunch:选择编译的产品类型,并存储在当前环境中。
        lunch <product_name>-<build_variant>
  • tapas:与 'lunch' 类似,可以指定编译的应用。
        tapas [<App1> <App2> ...] [arm|x86|mips|arm64|x86_64|mips64] [eng|userdebug|user]
  • printconfig:打印当前配置。
  • croot:切换到顶层目录
  • m:从顶层目录开始编译。
  • mm:编译当前目录中的所有模块,但不包含依赖的模块。

  • mmm:编译指定目录的所有模块,但不包含依赖的模块。可以使用下述方式限制编译的模块。
        mmm dir/:target1,target2.
  • mma:编译当前目录中的所有模块,包含依赖的模块。
  • mmma:编译指定目录的所有模块,包含依赖的模块
  • provision:对设备进行固件升级,选项将被传递到 fastboot 中。
  • cgrep:在当前目录的所有 C/C++ 文件中查找。
  • ggrep:在当前目录的所有 Gradle 文件中查找。
  • jgrep:在当前目录的所有 Java 文件中查找。
  • resgrep:在当前目录的所有 res/*.xml 文件中查找。
  • mangrep:在当前目录的所有 AndroidManifest.xml 文件中查找。
  • mgrep:在当前目录的所有 Makefiles 文件中查找。
  • sepgrep:在当前目录的所有 sepolicy 文件中查找。
  • sgrep:在当前目录的所有文件中查找。
  • godir:切换到包含指定文件的目录。

make相关命令

make 或 m 后面可以跟随不同的参数来指定编译目标。

  • droid:默认的 make 编译目标。
  • all:编译所有内容,包括不含droid标记的内容。编译服务器会运行此命令,以确保项目中包含Android.mk文件的所有元素都会编译。
  • checkbuild:编译所有的模块。
  • showcommands:显示编译步骤中实际的编译指令。
  • PRODUCT-xxx-yyy:编译指定的产品。
  • dump-products:显示产品的编译信息。

  • LOCAL_MODULE:编译指定的模块。例如,
        make runtime
  • nothing:不编译任何事情,只是解析和验证编译框架。
  • java:编译项目中的所有java代码。
  • native:编译项目中的所有native代码。
  • host:编译host端代码。
  • target:编译target端代码。
  • (java|native)-(host|target):按组合编译限定代码。
  • (host|target)-(java|native):按组合编译限定代码。
  • snod:快速重新编译system镜像。
  • vnod:快速重新编译vendor镜像。
  • pnod:快速重新编译product镜像。
  • update-api:更新SDK API。
  • otapackage:生成OTA升级包。
  • updatepackage:生成zip升级包。
  • docs:编译文档。
  • ndk-docs:生成ndk文档。
  • sdk:编译生成SDK。
  • ndk:编译生成NDK。
  • ramdisk:编译生成ramdisk。
  • systemtarball:生成system的tar包。
  • boottarball:生成boot的tar包。
  • userdatatarball:生成data的tar包。
  • systemimage:生成system.img镜像。
  • bootimage:生成boot.img镜像。
  • vbmetaimage:生成vbmeta.img镜像。
  • userdateimage:生成userdata.img镜像。
  • cacheimage:生成cache.img镜像。
  • bptimage:生成partition-table.img镜像。
  • vendorimage:生成vendor.img镜像。
  • productimage:生成product.img镜像。
  • apps_only:编译不包含 user,userdebug,eng 标签的应用程序。
  • dist:拷贝输出到dist目录。
  • dist_files:仅仅拷贝库文件到dist目录。
  • libandroid_runtime:编译JNI相关内容。
  • framework:编译java framework相关内容。
  • services:编译系统服务相关内容。
  • cts:编译CTS测试套件。
  • vts:编译VTS测试套件。

make clean 相关命令

  • clean:清除当前配置编译的所有文件,等同于rm -rf out//。
  • **clean-$(LOCAL_MODULE):清理指定模块的编译结果。
  • clean-$(LOCAL_PACKAGE_NAME):清理指定包的编译结果。
  • clobber:清除所有输出,等同于rm -rf out/。
  • dataclean:删除当前配置上data目录的所有文件。
  • clean-sdk:清理SDK编译输出。
  • installclean:清除与编译类型相关的文件。在切换编译目标是使用该命令可以加快编译。