Android AIDL的基本用法

AIDL 是 Android 中实现跨进程通信的一种方式。下面是一个简单的实现案例,通过 AIDL 实现客户端调用服务端的接口, 实现跨进程通信。<!--more-->

Server

首先,创建一个服务端工程 AIDLServer。main下面创建aidl文件夹,然后在aidl文件夹下创建包和aidl文件,如下:

IMyAidlInterface.aidl 文件内容如下:

1
2
3
4
5
6
7
8
9
// IMyAidlInterface.aidl
package cn.qiracle.aidlserver;

// Declare any non-default types here with import statements

interface IMyAidlInterface {
void print();
int add(int a, int b);
}

这里定义了AIDL接口IMyAidlInterface,接口里定义了两个待实现的方法。然后 重新构建下工程,这样在build目录下就会生成IMyAidlInterface.java 文件。

接着定义一个Service,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package cn.qiracle.aidlserver;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class MyService extends Service {
public MyService() {
}

@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}

class MyBinder extends IMyAidlInterface.Stub {

@Override
public void print() throws RemoteException {
Log.i("qiracle", "my aidl test");
}

@Override
public int add(int a, int b) throws RemoteException {
Log.i("qiracle", "a + b:" + (a + b));
return a + b;
}
}
}

在这个Service里 ,内部类MyBinder继承IMyAidlInterface.Stub ,然后实现接口里待实现的方法。这两个方法就是提供给客户端进行调用的。

最后,清单文件里记得注册下服务

1
2
3
4
5
6
7
8
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="cn.qiracle.aidlservice" />
</intent-filter>
</service>

然后启动服务

1
2
3
4
Intent intent = new Intent();
intent.setAction("cn.qiracle.aidlservice");
intent.setPackage("cn.qiracle.aidlserver");
startService(intent);

以上就是AIDL的服务端简单实现。主要就是定义一个AIDL接口,然后在服务里实现这个接口,提供给客户端调用。下面介绍客户端部分。

Client

先建立一个客户端的工程,跟服务端工程一样,需要在main下建立aidl目录,然后在aidl目录下新建包和aidl文件,这里的包名和aidl文件名必须和服务端保持一致。如下:

IMyAidlInterface.aidl 的内容也和 服务端的 IMyAidlInterface.aidl 保持一致

然后,在Activity里绑定服务,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Intent intent = new Intent();
intent.setAction("cn.qiracle.aidlservice");
intent.setPackage("cn.qiracle.aidlserver");

bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {

}
},BIND_AUTO_CREATE);

可以通过按钮的点击事件,调用服务端的方法。

1
2
3
4
5
6
7
8
9
public void click(View view){
Toast.makeText(getApplicationContext(),"click",Toast.LENGTH_SHORT).show();
try {
iMyAidlInterface.print();
iMyAidlInterface.add(1,2);
} catch (RemoteException e) {
e.printStackTrace();
}
}

以上就是客户端实现。

开始运行,注意需要先运行服务端,开启Service,然后运行客户端,绑定服务,通过点击按钮,可以看到服务端控制台有日志打印。

以上,我们就通过AIDL实现了跨进程的调用,方法的实现在服务端,方法的调用在客户端。

Android 知识点总结(一)

Handler

  1. Handler 的回调方法是在 Looper.loop()所调用的线程进行的;

  2. Handler 的创建需要先调用 Looper.prepare() ,然后再手动调用 loop()方法开启循环;

  3. App 启动时会在ActivityThread.main()方法中创建主线程的 Looper ,并开启循环,所以主线程使用 Handler 不用调用第2点的逻辑;<!--more -->

  4. 延时消息并不会阻塞消息队列;

  5. 异步消息不会马上执行,插入队列的方式跟同步消息一样,唯一的区别是当有消息屏障时,异步消息可以继续执行,同步消息则不行;

  6. Callback.handleMessage() 的优先级比 Handler.handleMessage()要高*

  7. Handler.post(Runnable)传递的 Runnale 对象并不会在新的线程执行;

  8. Message 的创建推荐使用 Message.obtain() 来获取,内部采用缓存消息池实现;

  9. 不要在 handleMessage()中对消息进行异步处理;

  10. 可以通过removeCallbacksAndMessages(null)或者静态类加弱引用的方式防止内存泄漏;

  11. Looper.loop()不会造成应用卡死,里面使用了 Linux 的 epoll 机制。

Context

源码中的注释是这么来解释Context的:Context提供了关于应用环境全局信息的接口。它是一个抽象类,它的执行被Android系统所提供。它允许获取以应用为特征的资源和类型,是一个统领一些资源(应用程序环境变量等)的上下文。就是说,它描述一个应用程序环境的信息(即上下文);是一个抽象类,Android提供了该抽象类的具体实现类;通过它我们可以获取应用程序的资源和类(包括应用级别操作,如启动Activity,发广播,接受Intent等)。既然上面Context是一个抽象类,那么肯定有他的实现类咯,我们在Context的源码中通过IDE可以查看到他的子类最终可以得到如下关系图:

Context类本身是一个纯abstract类,它有两个具体的实现子类:ContextImpl和ContextWrapper。其中ContextWrapper类,如其名所言,这只是一个包装而已,ContextWrapper构造函数中必须包含一个真正的Context引用,同时ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用ContextWrapper的方法都会被转向其所包含的真正的Context对象。ContextThemeWrapper类,如其名所言,其内部包含了与主题(Theme)相关的接口,这里所说的主题就是指在AndroidManifest.xml中通过android:theme为Application元素或者Activity元素指定的主题。当然,只有Activity才需要主题,Service是不需要主题的,因为Service是没有界面的后台场景,所以Service直接继承于ContextWrapper,Application同理。而ContextImpl类则真正实现了Context中的所以函数,应用程序中所调用的各种Context类的方法,其实现均来自于该类。一句话总结:Context的两个子类分工明确,其中ContextImpl是Context的具体实现类,ContextWrapper是Context的包装类。Activity,Application,Service虽都继承自ContextWrapper(Activity继承自ContextWrapper的子类ContextThemeWrapper),但它们初始化的过程中都会创建ContextImpl对象,由ContextImpl实现Context中的方法。

一个应用程序有几个Context?

实这个问题本身并没有什么意义,关键还是在于对Context的理解,从上面的关系图我们已经可以得出答案了,在应用程序中Context的具体实现子类就是:Activity,Service,Application。那么Context数量=Activity数量+Service数量+1。当然如果你足够细心,可能会有疑问:我们常说四大组件,这里怎么只有Activity,Service持有Context,那Broadcast Receiver,Content Provider呢?Broadcast Receiver,Content Provider并不是Context的子类,他们所持有的Context都是其他地方传过去的,所以并不计入Context总数。上面的关系图也从另外一个侧面告诉我们Context类在整个Android系统中的地位是多么的崇高,因为很显然Activity,Service,Application都是其子类,其地位和作用不言而喻。

视图

Activity

Activity并不负责视图控制,它只是控制生命周期和处理事件。真正控制视图的是Window。一个Activity包含了一个Window,Window才是真正代表一个窗口。Activity就像一个控制器,统筹视图的添加与显示,以及通过其他回调方法,来与Window、以及View进行交互。

Window

Window是视图的承载器,内部持有一个 DecorView,而这个DecorView才是 view 的根布局。Window是一个抽象类,实际在Activity中持有的是其子类PhoneWindow。PhoneWindow中有个内部类DecorView,通过创建DecorView来加载Activity中设置的布局R.layout.activity_main。Window 通过WindowManager将DecorView加载其中,并将DecorView交给ViewRoot,进行视图绘制以及其他交互。

DecorView

DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图。DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上下三个部分,上面是个ViewStub,延迟加载的视图(应该是设置ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。

ViewRoot

ViewRoot可能比较陌生,但是其作用非常重大。所有View的绘制以及事件分发等交互都是通过它来执行或传递的。

ViewRoot对应ViewRootImpl类,它是连接WindowManagerService和DecorView的纽带,View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRoot来完成。

ViewRoot并不属于View树的一份子。从源码实现上来看,它既非View的子类,也非View的父类,但是,它实现了ViewParent接口,这让它可以作为View的名义上的父视图。RootView继承了Handler类,可以接收事件并分发,Android的所有触屏事件、按键事件、界面刷新等事件都是通过ViewRoot进行分发的。

要知道,当用户点击屏幕产生一个触摸行为,这个触摸行为则是通过底层硬件来传递捕获,然后交给ViewRootImpl,接着将事件传递给DecorView,而DecorView再交给PhoneWindow,PhoneWindow再交给Activity,然后接下来就是我们常见的View事件分发了。

硬件 -> ViewRootImpl -> DecorView -> PhoneWindow -> Activity

通过以上了解可以知道,Activity就像个控制器,不负责视图部分。Window像个承载器,装着内部视图。DecorView就是个顶层视图,是所有View的最外层布局。ViewRoot像个连接器,负责沟通,通过硬件的感知来通知视图,进行用户之间的交互。

Android Boardcast 权限的使用

1. 广播的接收权限

设置广播接收权限的目的在于避免自己应用发送的广播被其他恶意应用接收到。简单来说就是定义谁能接收我的广播,用法如下:

首先发送方的应用在清单文件里自定义一个权限:

1
2
<permission android:name="cn.qiracle.RECEIVER"
android:protectionLevel="signature" />

这里 protectionLevel 选择 signature 或者 signatureOrSystem 更加安全。<!--more-->

然后发送广播时调用如下 sendBoradcast 方法:

1
2
3
4
private static final String PERMISSION_RECEIVER= "cn.qiracle.RECEIVER";
...
...
sendBroadcast(intent,PERMISSION_RECEIVER);

此时接收方app若是想接收到这个广播,需要在接收方应用的清单文件里添加如下权限:

1
<uses-permission android:name="cn.qiracle.RECEIVER" />

2.广播的发送权限

设置广播的发送权限目的在于避免自己的应用里的 receiver 被其他恶意应用发送的带有同样 action 的广播所骚扰。简单来说就是定义谁能给我发送广播。用法如下:

首先在接收方应用的清单文件里自定义一个权限:

1
2
<permission android:name="cn.qiracle.SEND"
android:protectionLevel="signature" />

然后注册广播时采用如下regiserReceiver方法:

1
2
3
4
private static final String PERMISSION_SEND = "cn.qiracle.SEND";
...
...
registerReceiver(new MyReceiver(),intentFilter,PERMISSION_SEND,null);

上面是动态注册,静态注册广播方式如下:

1
2
3
4
5
6
<receiver android:name=".receiver.MyReceiver"
android:permission="cn.qiracle.SEND">

<intent-filter>
<action android:name="cn.qiracle.MYRECEIVER"/>
</intent-filter>
</receiver>

推荐使用动态注册的方式,因为笔者尝试发现自定义权限时,静态注册的方式在 **Android8.0 **及以上会不起作用。

此时广播的发送方 app 要想给这个应用发送广播,必须在清单文件里添加如下权限:

1
<uses-permission android:name="cn.qiracle.SEND" />

读《深入理解Java虚拟机》总结<二.java内存模型与线程>

主内存与工作内存

java内存模型规定了所有变量都存储在主内存中。此处的变量是指实例字段,静态字段和构成数组对象的元素。但不包括局部变量与方法参数。因为后者是线程私有的,不会被共享。除主内存之外,每个线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取,赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同线程之间也无法之间访问对方工作内存中的变量。线程间变量值得传递均需要通过主内存来完成。 <!--more-->

一个变量如何从主内存拷贝到工作内存,如何从工作内存同步回主内存之类的实现细节,java内存模型定义了以下八种操作来完成:

  • lock(锁定):作用于主内存的变量,把一个变量标记为一条线程独占状态
  • unlock(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
  • read(读取):作用于主内存的变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
  • load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中
  • use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎
  • assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量
  • store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作
  • write(写入):作用于工作内存的变量,它把store操作从工作内存中的一个变量的值传送到主内存的变量中

java内存模型

volatile 关键字

用于修饰变量。主要作用有两个:

1.保证修改的可见性

2.禁止指令重排序

volatile不保证操作的原子性

读《深入理解Java虚拟机》总结<一自动内存管理机制>

这一周来比较空闲,读了《深入理解java虚拟机一书》以提高自己对java底层的认知,还没看完,只是挑选了书中自己比较感兴趣的两个章节来看,写下此篇博客一是为了总结,二是为了方便今后回顾。下面是第一部分自动内存管理机制 <!--more-->

运行时数据区域

java内存

程序计数器

程序计数器时一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。为了线程切换后能够恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程直接的计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

java虚拟机栈

与程序计数器一样,java虚拟机栈也是线程私有的,它的生命周期与线程相同。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackoverflowError异常;如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。

本地方法栈

本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行java方法服务,而本地方法栈则是为虚拟机使用到的Native方法服务。。本地方法栈也会抛出StackoverflowError和OutOfMemoryError异常。

java堆

对于大多数应用来说,java堆是java虚拟机所管理的内存中最大的一块。java堆是所以线程共享的一块内存区域,在虚拟机启动时创建。java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。如果在堆中没有内存完成实例分配,并且堆无法再扩展时,将会抛出OutOfMemoryError异常。

方法区

方法区与java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。垃圾回收行为在这个区域是比较少出现的,这个区域内存回收目标主要是针对常量池的回收和对类型的卸载。。当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

运行时常量池

运行时常量池时方法区的一部分。Class文件除了有类的版本,字段,方法,接口等描述信息外,还有一项信息就是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区运行时常量池中。运行期间也可能将新的常量放入池中,如String类的intern()方法。

确定对象是否存活的算法

垃圾回收器在对堆进行回收前,第一件事情就是要确定这些对象有哪些还存活着,哪些已经死去。

引用计数算法

给对象中添加一个引用计算器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器都为0的对象就是不可能再被使用的。。

java语言没有选用引用计数法来管理内存,其中最主要的原因是它很难解决对象之间的相互循环引用的问题。如下:

1
2
3
4
5
6
ReferenceCountGC objA = new ReferenceCountGC();
ReferenceCountGC objB = new ReferenceCountGC();
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;

根搜索算法

在主流的商用程序语言中,都是使用根搜索算法判断对象是否存活的。基本思路是通过一系列的名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为引用链,当一个对象待GC Roots没有任何引用链相连,则证明此对象是不可用的。

Java语言里,可作为GC Roots对象包括下面几种:

  • 虚拟机栈中引用的对象
  • 方法区中的类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI的引用对象

java中的四种引用

**强引用:**代码至中普遍存在。类似 Object obj = new Object()。主要强引用还在,垃圾回收器永远不会回收掉被引用的对象。

**软引用:**当内存不够时,即系统将要发生内存溢出异常之前,将会把这些对象列进回收范围并进行二次回收。java中提供SoftReference类实现软引用。

**弱引用:**被弱引用关联的对象只能生存到下次垃圾收集发生之前,当垃圾收集器工作时,无论当前内存是否足够,都会回收掉被弱引用关联的对象。java中提供WeakReference类实现软引用。

虚引用:一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是希望这个对象在被收集器回收时收到一个系统通知。java中提供PhantomReference类实现软引用。

对象死亡过程

在跟搜索算法中不可达的对象也并非是非死不可的。这些不可达的对象先会被判断是否有必要执行finalize()方法,当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机会将这两种情况都视为“没有必要执行”。

finalize()方法是对象逃脱死亡命运的最后一次机会,如果对象想要在finalize()方法中成功拯救自己,只要重新与引用链上的任何一个对象建立关联即可。譬如把自己(this)赋值给某个类变量或者某个对象的成员变量。如下:

1
2
3
4
5
6
7
8
9
10
public class FinalizeEscapeGc{
public static FinalizeEscapeGc Save_HooK = null;

@Override
protected void finalized() throws Throwable{
super.finalized();
FinalizeEscapeGc.Save_HooK = this;
}

}

如何一个对象的finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()方法不会再次被执行。

垃圾回收算法

标记-清除算法

首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。它的主要缺点有两个:

一是效率问题,标记和清楚过程效率都不高

二是空间问题,标记清除后会产生大量不连续的内存碎片。

标记-清除

复制算法

它将可用内存按容量划分为大小相同的两块,每次只使用其中的一块,当一块内存用完了,就将还存活的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。这种算法的代价是将内存缩小为原来的一半未免太高了一些。

复制

现在的商用虚拟机都采用这种收集算法来回收新生代,IBM的专门研究表明,新生代中的对象98%是朝生夕死的,所以并不需要按照1:1的比例来划分空间。而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活的对象一次性拷贝到另一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例为8:1。当Survivor空间不够时,需要依赖其他内存(老年代)进行分配担保。

标记-整理算法

复制算法在对象存活率较高时需要执行较多的复制操作,更关键是如果不想浪费50%空间,就需要额外的空间进行担保,以应对内存中所有对象都100%存活的极端情况,所以老年代一般不能直接选用这种算法。

标记-整理算法的标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存。

标记-整理

分代收集算法

根据对象的存活周期不同将内存划分为几块,一般是把Java堆分为新生代和老年代。在新生代中,每次垃圾收集时发现大批对象死去,只有少量存活,那就选用复制算法。老年代中因为对象存活率高,没有额外空间对它进行分配担保,就必须使用“标记-清理”,“标记-整理”算法来进行回收。

垃圾收集器

垃圾收集器

如果两个收集器之间存在连线,就说明他们可以搭配使用。下面就只说下Serial收集器:

Serial收集器

Serial收集器是最基本,历史最悠久的收集器,这是一个单线程的收集器。它在进行垃圾收集时,必须暂停其他所有工作线程指到它收集结束。

CentOS下编译Hadoop

系统及软件版本准备

CentOS-6.7-64bit

Hadoop-2.8.3-src

java-1.8.0_141

apache-maven-3.3.9

protobuf-2.5.0

findbugs-1.3.9 <!-- more -->

相关软件下载地址: 链接:https://pan.baidu.com/s/1FfRRPiosrhWpRIPYtcsEgg 提取码:l84n

软件安装

1.java-1.8.0_141

下载 jdk-8u141-linux-x64.tar.gz, 解压到 /root/apps/jdk1.8.0_141。解压命令:

tar -xf jdk-8u141-linux-x64.tar.gz

配置环境变量:

vi /etc/profile

在文件最下面加上:

export JAVA_HOME=/root/apps/jdk1.8.0_141 export PATH=$PATH:$JAVA_HOME/bin export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

添加完记得用下面的命令让其立即生效:

source /etc/profile

注意:下面每次配置完环境变量都需要使用这个命令

检查是否安装成功:

java -version

如果输出下面信息,则表示安装成功:

java version "1.8.0_141" Java(TM) SE Runtime Environment (build 1.8.0_141-b15) Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode)

2.apache-maven-3.3.9

下载apache-maven-3.3.9-bin.zip,并使用 下面命令解压:

unzip apache-maven-3.3.9-bin.zip

我将其解压到了 /opt/software/a:pache-maven-3.3.9. 同样,配置环境变量,在/etc/profile中添加下面信息:

export MAVEN_HOME=/opt/software/apache-maven-3.3.9 export MAVEN_OPTS="-Xms256m -Xmx512m" export PATH=$MAVEN_HOME/bin:$JAVA_HOME/bin:$PATH

验证是否安装成功:

mvn -version

出现下列信息表明安装成功:

Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T00:41:47+08:00) Maven home: /opt/software/apache-maven-3.3.9 Java version: 1.8.0_141, vendor: Oracle Corporation Java home: /root/apps/jdk1.8.0_141/jre Default locale: en_US, platform encoding: UTF-8 OS name: "linux", version: "2.6.32-573.el6.x86_64", arch: "amd64", family: "unix"

3.protobuf-2.5.0

下载 protobuf-2.5.0.tar.gz ,解压到/opt/software/protobuf-2.5.0,接下来:

cd protobuf-2.5.0

yum install -y gcc gcc-c++ make cmake

./configure --prefix=/usr/local/protobuf

make && make install

同样导入环境变量:

export PROTOC_HOME=/usr/local/protobuf export PATH=$PROTOC_HOME/bin:$FINDBUGS_HOME/bin:$MAVEN_HOME/bin:$JAVA_HOME/bin:$PATH

验证是否安装成功:

protoc --version

出现下面信息表明安装成功:

libprotoc 2.5.0

4. findbugs-1.3.9

下载findbugs-1.3.9.zip ,通过unzip findbugs-1.3.9.zip 解压到/opt/software/findbugs-1.3.9。

同样需要配置环境变量

export FINDBUGS_HOME=/opt/software/findbugs-1.3.9 export PATH=$FINDBUGS_HOME/bin:$MAVEN_HOME/bin:$JAVA_HOME/bin:$PATH

验证是否安装成功:

findbugs -version

出现下面信息表示安装成功:

1.3.9

5.其他依赖的安装

yum install -y openssl openssl-devel svn ncurses-devel zlib-devel libtool yum install -y snappy snappy-devel bzip2 bzip2-devel lzo lzo-devel lzop autoconf automake

6.编译

下载hadoop-2.8.3-src.tar.gz并解压 tar -xf hadoop-2.8.3-src.tar.gz 到 /root/hadoop-2.8.3-src。运行下面命令:

cd hadoop-2.8.3-src

mvn clean package -Pdist,native -DskipTests -Dtar

编译过程中需要下载很多东西,需要很长时间,同时因为网络原因可能会一直卡在某个地方,这时可以ctrl+c中止然后重新运行上面命令。最后如果出现 BUILD SUCCESS 字样则表明编译成功.

编译好的tar包在 /root/hadoop-2.8.3-src/hadoop-dist/target/hadoop-2.8.1.tar.gz下

java IO流总结

java IO流简介

java IO流可 分为输入流 和 输出流。这里的输入和输出是相对于程序而言的,外部数据输入到程序中时,我们用输入流,程序中的数据输出到外部时,我们用输出流。

输入流又可分为字节输入流和字符输入流。字节输入流的基类为InputStream,我们常用到它的子类FileInputStream, 字符输入流的基类为Reader,我们常用到它的子类FileReader。

输出流又可分为字节输出流和字符输出流。字节输出流的基类为OutputStream,我们常用到它的子类FileOutputStream, 字符输出流的基类为Writer,我们常用到它的子类FileWriter。

<!--more-->

那什么时候该用字节流,什么时候该用字符流呢?

一般来说,在输入输出一些二进制对象的时候比如图片,音乐,视频文件,我们用字节流。 在输入输出一些文本文件的时候比如文字,我们用字符流。字符其实可以理解为是字节+对应的编码表(utf-8,gbk等)构成的,相同的一段字节,用不同的编码格式,最后得到的字符是不一样的(生活常见的乱码就是这样产生的)

另外,java在字节流和字符流中间提供了一层吧转换流,,可以将字节流转化为字符流。输入的转换流为InputStreamReader(InputStream is),输出的转换流为OutputStreamReader(OutputStream os )。

解释了这么多,不如用一图来总结一下:

<img src="http://94.191.72.22/blog/image/20181128/io.bmp" alt="IO流" />

IO 流在代码中的实际应用

java提供了操作IO流的API,在代码中如何使用呢? 接下来,直接附上几个比较实用的demo,可参考运用到自己的代码里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public class CopyMp3Demo {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
method1("e:\\music.mp3", "copy1.mp3");
method2("e:\\music.mp3", "copy2.mp3");
method3("e:\\music.mp3", "copy3.mp3");
method4("e:\\music.mp3", "copy4.mp3");

long end = System.currentTimeMillis();
System.out.println(" 总共耗时:" + (end - start) + "毫秒");

}

// 基本字节流一次读写一个字节
public static void method1(String src, String dest) throws Exception {

FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest);

int by = 0;
while ((by = fis.read()) != -1) {
fos.write(by);
}
fis.close();
fos.close();

}

// 基本字节流一次读写一个字节数组
public static void method2(String src, String dest) throws Exception {

FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest);

int len = 0;
byte[] bys = new byte[1024];
while ((len = fis.read(bys)) != -1) {
fos.write(bys, 0, len);
}
fis.close();
fos.close();

}

// 高效字节流一次读写一个字节:

public static void method3(String src, String dest) throws Exception {

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest));

int by = 0;
while ((by = bis.read()) != -1) {
bos.write(by);
}
bis.close();
bos.close();

}

// 高效字节流一次读写一个字节数组:

public static void method4(String src, String dest) throws Exception {

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest));

int len = 0;
byte[] bys = new byte[1024];
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
bis.close();
bos.close();

}

}

这里我们的目的是利用IO流将E盘根目录下的music.mp3文件拷贝到项目工程目录下,前面说了,拷贝音乐文件用字节流,这里提供了四种拷贝方式,可以比较拷贝所需时间,其中第一种方式(基本字节流一次读写一个字节 )最慢,第四种(高效字节流一次读写一个字节数组)最快,中间两种差不多。

说一下这里的BufferedInputStream是输入流(对应的输出流BufferedOutputStream)的缓冲流,又称装饰流,一般我们建议在基本流外面包上装饰流,因为这样可极大提升效率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//字符流读写数据的五种方式
public class CopyTxtDemo {
public static void main(String[] args) throws IOException {
String SrcString = "a.txt";
String DestString = "b.txt";
// method1(SrcString,DestString);
// method3(SrcString,DestString);
// method4(SrcString,DestString);
method5(SrcString, DestString);

}

// 基本字符流一次读写一个字符
private static void method1(String srcString, String destString) throws IOException {
FileReader fr = new FileReader(srcString);
FileWriter fw = new FileWriter(destString);

int len = 0;
while ((len = fr.read()) != -1) {
fw.write(len);
}
fr.close();
fw.close();

}

// 基本字符流一次读写一个字符数组
private static void method2(String srcString, String destString) throws IOException {
FileReader fr = new FileReader(srcString);
FileWriter fw = new FileWriter(destString);

char[] chs = new char[1024];
int len = 0;
while ((len = fr.read(chs)) != -1) {
fw.write(chs, 0, len);
}
fr.close();
fw.close();

}

// 高效字符流一次读写一个字符
private static void method3(String srcString, String destString) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(srcString));
BufferedWriter bw = new BufferedWriter(new FileWriter(destString));

int len = 0;
while ((len = br.read()) != -1) {
bw.write(len);
}
br.close();
bw.close();

}

// 高效字符流一次读写一个字符数组
private static void method4(String srcString, String destString) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(srcString));
BufferedWriter bw = new BufferedWriter(new FileWriter(destString));

char[] chs = new char[1024];
int len = 0;
while ((len = br.read(chs)) != -1) {
bw.write(chs, 0, len);
}
br.close();
bw.close();

}

// 高效字符流一次读写一行
private static void method5(String srcString, String destString) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(srcString));
BufferedWriter bw = new BufferedWriter(new FileWriter(destString));

String len = null;
while ((len = br.readLine()) != null) {
bw.write(len);
bw.newLine();
bw.flush();
}
br.close();
bw.close();

}
}

上面代码的目的是将项目工程下的“a.txt”文件拷贝到b.txt里。前面说了拷贝文本文件用字符流。前四种拷贝方式就不说了,跟字节流差不多,第五种比较特殊,一次读写一行。

注意:上述代码最好加上try--catch,并在finally里关闭流。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

public static void method3(String src, String dest) throws Exception {

BufferedInputStream bis =null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(src));
bos = new BufferedOutputStream(new FileOutputStream(dest));

int by = 0;
while ((by = bis.read()) != -1) {
bos.write(by);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
bos.close();
bis.close();
}

}

Android 中的jar包,aar包以及module

Android Studio中引入第三方资源一般有三种方式:jar包,aar包,module.

1.jar包的导入

第一种: 一般用于比较流行的第三方库上,直接将你在GitHub上看到的jar包compile放入build.gradle的dependencies里面,然后sync同步一下就可以了。例如:导入v7包。 <!--more-->

1
2
3
4
dependencies{
......
compile ‘com.android.support:appcompat-v7:24.2.1’
}

第二种: 我们已经从网上下好了(或者别人给你的)jar,需要将他导入到AS工程里,首先将jar包放到libs里面,然后还是在build.gradle的dependencies里面添加compile路径,最后sync一下。例如:导入volley。

1
2
3
4
dependencies{
.....
compile files(‘libs/volley.jar’)
}

2.aar包导入 aar包是Android特有的一种引用包,Android Archive,它跟jar包的区别在于: jar包:只包含class文件和清单文件,不包含资源文件,比如图片等所有的 res下的资源文件; aar包:class以及res下的所有的资源文件全部包含。 aar包的引用也很简单,以上是一个正常的module(工程应用,而非作为第三方library使用的)引用aar文件为例,首先也是将拿到的aar包放进libs下。然后按如下配置,最后sync一下就可以了,列如,导入一个叫abc.aar的包:

1
2
3
4
5
repositories {  
flatDir {
dirs 'libs'
}
} // 此在android标签下添加

1
2
3
4
dependencies {
.....
compile(name: 'abc.aar, ext: 'aar')
}

注意,以上是一个正常的module(工程应用,而非作为第三方library使用的)引用aar文件需要配置的步骤。

下面说一下当一个library类型的module需要引用aar文件时: a.在该library中按照上面的代码,在library的module下的bulid.gradle中配置。

b.任何依赖此library的module必须声明在它的build.gradle声明此 aar 的 lib 所在的位置,这个位置根据文件路径所确定

1
2
3
4
5
6
7
android{    //android节点下配置
repositories {
flatDir {
dirs 'libs', '../../../../library_module/libs'
}
}
}

c. 配置完b步骤后,还要在project的build.gradle文件中配置,如下:

1
2
3
4
5
6
7
8
allprojects {
repositories {
jcenter()
flatDir {
dirs '../../../library_module/libs'
}
}
}

梳理一下这里的依赖关系: aar->library module->依赖此library的module->project

3.module的导入 module的导入在上一篇博客中Android Studio中module的导入 已经讲过。这里主要说一下module是可以打包成aar包的,通过gradle的assembeRelease或assembleDebug可以将module打包成aar包(分别对应release版本和debug版本)。 另外,笔者曾经遇到一个大坑,那就是当aar包引用第三方module的时候,编译不会报错,但是运行时会一直报类找不到的错误,因为在打包aar包时不会将第三方module打包进aar包里面。这里如果将第三方module先打包成aar包,给工程调用就不会出现这样的问题了。

Android Studio 导入 module

  1. 在当前工程里,点击 File->New->import module. 在弹出的对话框里选择需要导入的 module 的路径。这里将 module 名字改成了 :mylib . <!--more-->

这一步之后会在主工程的build.gradle生成如下代码:

这里原本是 compile project(path: ':mylib')。 但是我这里的gradle是3之后的版本,所以需要将compile替换成implementation。

2.如果导入的 mylib 之前是一个工程应用,则需要更改 mylib 里的 build.gradle。具体修改如下:

需要将图中的 com.android.application 改成 com.android.library 以及删除applicationId这一行。

改完后记得sync一下。

3.修改主工程下的 setting.gradle. 添加如下:

同样改完之后sync一下。

这样,module的导入就完成了,我们可以试试调用module里的方法。

Android 混淆机制

什么是混淆

按照我的个人理解,混淆就是将代码里的包名,类名,方法名,变量名等用无意义的a,b,c等字母代替。这样做的目的是为了防止他人获取你的apk后可以轻松的反编译并读取你的源码,而加入混淆之后,反编译出的源码是很难被读懂的。详细解释可参考Android混淆是什么 。<!--more-->

如何打开混淆

在Android Studio工程下,找到app目录下的build.gradle. 然后将minifyEnabled设置为true即可。如下:

1
2
3
4
release{
minifyEnabled true//是否启动混淆 ture:打开 false:关闭
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}

这里的proguard-android.txt是混淆规则,里面定义的是哪些东西不能被混淆。proguard-android.txt存在于app目录下。在做混淆时我们只需把我们定义的混淆规则写在里面即可。

混淆模板

下面是一个混淆模板,网上类似的模板有很多:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#
#-------------------------------------------基本不用动区域----------------------------------------------
#
#
# -----------------------------基本 -----------------------------
#

# 指定代码的压缩级别 0 - 7(指定代码进行迭代优化的次数,在Android里面默认是5,这条指令也只有在可以优化时起作用。)
-optimizationpasses 5
# 混淆时不会产生形形色色的类名(混淆时不使用大小写混合类名)
-dontusemixedcaseclassnames
# 指定不去忽略非公共的库类(不跳过library中的非public的类)
-dontskipnonpubliclibraryclasses
# 指定不去忽略包可见的库类的成员
-dontskipnonpubliclibraryclassmembers
#不进行优化,建议使用此选项,
-dontoptimize
# 不进行预校验,Android不需要,可加快混淆速度。
-dontpreverify
# 屏蔽警告
-ignorewarnings
# 指定混淆是采用的算法,后面的参数是一个过滤器
# 这个过滤器是谷歌推荐的算法,一般不做更改
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
# 保护代码中的Annotation不被混淆
-keepattributes *Annotation*
# 避免混淆泛型, 这在JSON实体映射时非常重要
-keepattributes Signature
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
#优化时允许访问并修改有修饰符的类和类的成员,这可以提高优化步骤的结果。
# 比如,当内联一个公共的getter方法时,这也可能需要外地公共访问。
# 虽然java二进制规范不需要这个,要不然有的虚拟机处理这些代码会有问题。当有优化和使用-repackageclasses时才适用。
#指示语:不能用这个指令处理库中的代码,因为有的类和类成员没有设计成public ,而在api中可能变成public
-allowaccessmodification
#当有优化和使用-repackageclasses时才适用。
-repackageclasses ''
# 混淆时记录日志(打印混淆的详细信息)
# 这句话能够使我们的项目混淆后产生映射文件
# 包含有类名->混淆后类名的映射关系
-verbose

#
# ----------------------------- 默认保留 -----------------------------
#
#----------------------------------------------------
# 保持哪些类不被混淆
#继承activity,application,service,broadcastReceiver,contentprovider....不进行混淆
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.support.multidex.MultiDexApplication
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep class android.support.** {*;}## 保留support下的所有类及其内部类

-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
#表示不混淆上面声明的类,最后这两个类我们基本也用不上,是接入Google原生的一些服务时使用的。
#----------------------------------------------------

# 保留继承的
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**


#表示不混淆任何包含native方法的类的类名以及native方法名,这个和我们刚才验证的结果是一致
-keepclasseswithmembernames class * {
native <methods>;
}


#这个主要是在layout 中写的onclick方法android:onclick="onClick",不进行混淆
#表示不混淆Activity中参数是View的方法,因为有这样一种用法,在XML中配置android:onClick=”buttonClick”属性,
#当用户点击该按钮时就会调用Activity中的buttonClick(View view)方法,如果这个方法被混淆的话就找不到了
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}

#表示不混淆枚举中的values()和valueOf()方法,枚举我用的非常少,这个就不评论了
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}

#表示不混淆任何一个View中的setXxx()和getXxx()方法,
#因为属性动画需要有相应的setter和getter的方法实现,混淆了就无法工作了。
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}

#表示不混淆Parcelable实现类中的CREATOR字段,
#毫无疑问,CREATOR字段是绝对不能改变的,包括大小写都不能变,不然整个Parcelable工作机制都会失败。
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# 这指定了继承Serizalizable的类的如下成员不被移除混淆
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# 保留R下面的资源
#-keep class **.R$* {
# *;
#}
#不混淆资源类下static的
-keepclassmembers class **.R$* {
public static <fields>;
}

# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
-keepclassmembers class * {
void *(**On*Event);
void *(**On*Listener);
}

# 保留我们自定义控件(继承自View)不被混淆
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}

#
#----------------------------- WebView(项目中没有可以忽略) -----------------------------
#
#webView需要进行特殊处理
-keepclassmembers class fqcn.of.javascript.interface.for.Webview {
public *;
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, jav.lang.String);
}
#在app中与HTML5的JavaScript的交互进行特殊处理
#我们需要确保这些js要调用的原生方法不能够被混淆,于是我们需要做如下处理:
-keepclassmembers class com.ljd.example.JSInterface {
<methods>;
}

#
#---------------------------------实体类---------------------------------
#--------(实体Model不能混淆,否则找不到对应的属性获取不到值)-----
#
-dontwarn com.suchengkeji.android.confusiondemo.md.**
#对含有反射类的处理
-keep class com.suchengkeji.android.confusiondemo.md.** { *; }
#
# ----------------------------- 其他的 -----------------------------
#
# 删除代码中Log相关的代码
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
}

# 保持测试相关的代码
-dontnote junit.framework.**
-dontnote junit.runner.**
-dontwarn android.test.**
-dontwarn android.support.test.**
-dontwarn org.junit.**


#
# ----------------------------- 第三方 -----------------------------
#
-dontwarn com.orhanobut.logger.**
-keep class com.orhanobut.logger.**{*;}
-keep interface com.orhanobut.logger.**{*;}

-dontwarn com.google.gson.**
-keep class com.google.gson.**{*;}
-keep interface com.google.gson.**{*;}

Proguard关键字及作用

dontwarn dontwarn是一个和keep可以说是形影不离,尤其是处理引入的library时. keep 保留类和类中的成员,防止被混淆或移除 keepnames 保留类和类中的成员,防止被混淆,成员没有被引用会被移除 keepclassmembers 只保留类中的成员,防止被混淆或移除 keepclassmembernames 只保留类中的成员,防止被混淆,成员没有引用会被移除 keepclasseswithmembers 保留类和类中的成员,防止被混淆或移除,保留指明的成员 keepclasseswithmembernames 保留类和类中的成员,防止被混淆,保留指明的成员,成员没有引用会被移除

常见用法

(1)保留某个包下面的类以及子包下面的类.

1
-keep public class cn.qiracle.**

注意两个号表示当前包下及该包的所有子包下的类都不被混淆。一个只表示当前包下的类不被混淆。

(2)保留所有类中使用otto的public方法

1
2
3
4
5
# Otto
-keepclassmembers class ** {
@com.squareup.otto.Subscribe public *;
@com.squareup.otto.Produce public *;
}

(3)保留Contants类的BOOK_NAME属性

1
2
3
-keepclassmembers class com.example.admin.proguardsample.Constants {
public static java.lang.String BOOK_NAME;
}

(4)dontwarn: 引入的library可能存在一些无法找到的引用和其他问题,在build时可能会发出警告,如果我们不进行处理,通常会导致build中止.因此为了保证build继续,我们需要使用dontwarn处理这些我们无法解决的library的警告. 比如关闭Twitter sdk的警告,我们可以这样做

1
-dontwarn com.twitter.sdk.**

(5)保留一个类及其子类不被混淆

1
-keep public class * extends android.app.Activity

哪些类不能被混淆

使用了自定义控件那么要保证它们不参与混淆 使用了枚举要保证枚举不被混淆 对第三方库中的类不进行混淆 运用了反射的类也不进行混淆 使用了 Gson 之类的工具要使 JavaBean 类即实体类不被混淆 在引用第三方库的时候,一般会标明库的混淆规则的,建议在使用的时候就把混淆规则添加上去,免得到最后才去找 有用到 WebView 的 JS 调用也需要保证写的接口方法不混淆,原因和第一条一样 Parcelable 的子类和 Creator 静态成员变量不混淆,否则会产生 Android.os.BadParcelableException 异常 使用的四大组件,自定义的Application* 实体类 JNI中调用的类 Layout布局使用的View构造函数(自定义控件)、android:onClick等。

参考文章: https://www.jianshu.com/p/b5b2a5dfaaf4

|