2017年開始上班的第一天。老不想工作了,假期感觉还没開始就已经结束了,唉,时间就是这样,新的一年開始了,尽管非常不想干正事,没办法,必须干起来。由于后边的路还非常长,距离六十岁还非常远。

刚上班也没什么事,复习一下之前的东西,看了一下Aidl相关的知识。细致瞅了瞅Aidl的调用流程,这里写篇文章整理一下。帮助大家的同一时候。自己也加深一下印象。对Aidl不太了解的童鞋能够先看一下我之前的一篇文章,

链接例如以下:http://blog.csdn.net/liuyonglei1314/article/details/54317902

案例下载链接:http://download.csdn.net/detail/liuyonglei1314/9734165 。

在上篇文章中我们已经说过了Android中Aidl的简单应用及对象的传递方式,还包括了在AS中进行Aidl开发会遇到的一些问题及决解方法,本篇文章针对用法我们不在多说。我们将以传递对象为例深入的剖析Aidl的具体调用流程,继续以上文中传递Person对象为例展开,通过本片文章的学习你会学到下面相关知识:1.Aidl工作调用流程;2.Aidl传递对象时修饰符in、out、inout的深入理解。3.对实现Parcelable接口对象中须要实现方法的深入理解。

首先看一下我们要传递对象的代码:

public class Person implements Parcelable {
private String name;
private int age; public Person() {
} protected Person(Parcel in) {
name = in.readString();
age = in.readInt();
} public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
} @Override
public Person[] newArray(int size) {
return new Person[size];
}
}; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public int describeContents() {
return 0;
} @Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
public void readFromParcel(Parcel dest) {
name = dest.readString();
age = dest.readInt();
} @Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

Person类实现了parcelable接口。有两个构造方法,有name、age两个成员变量。编译器能够帮我们生成一个static、final的CREATOR跟writeToParcel()方法,我们自己写readFromParcel()方法,这里切记顺序要与writeToParcel()方法一直。

下边再看一下我们的.aidl文件代码:

import com.jason.aidl.aidldemo.Person;
interface IMyAidlInterface {
String inPerson(in Person p);
String outPerson(out Person p);
String inOutPerson(inout Person p);
}

这里须要注意我们的Person也须要写相应的Person.aidl文件,并在build.gradle中配置。具体信息就不介绍了。上篇文章中进行了具体解说。在这里看到了修饰符in、out、inout。后文会做具体解说。

我们继续看Aidl文件生成相应的.java文件的一个总体架构。缩略代码例如以下:

public interface IMyAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.jason.aidl.aidldemo.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.jason.aidl.aidldemo.IMyAidlInterface"; /**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
} /**
* Cast an IBinder object into an com.jason.aidl.aidldemo.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.jason.aidl.aidldemo.IMyAidlInterface asInterface(android.os.IBinder obj) {
//...
} @Override
public android.os.IBinder asBinder() {
return this;
} @Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
//...
return super.onTransact(code, data, reply, flags);
} private static class Proxy implements com.jason.aidl.aidldemo.IMyAidlInterface {
private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) {
mRemote = remote;
} @Override
public android.os.IBinder asBinder() {
return mRemote;
} public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
} @Override
public java.lang.String inPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
//...
return _result;
} @Override
public java.lang.String outPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
//...
return _result;
} @Override
public java.lang.String inOutPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
//...
return _result;
}
} static final int TRANSACTION_inPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_outPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_inOutPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
} public java.lang.String inPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException; public java.lang.String outPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException; public java.lang.String inOutPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException;
}

大致分为三部分。一个interface(最外层的类)两个内部类(Stub、Proxy)。先看interface。它跟我们定义的.aidl文件名称字同样,继承了IInterface,它的内容分为两部分:一个内部类Stub及我们定义的Person抽象操作方法。每个都抛出RemoteException。接下来看他的内部类Stub,它是一个抽象类。继承了Binder类,实现了外层的Interface,他比較重要的是一个asInterface()方法和onTransact()方法。它另一个static的内部类Proxy。它也实现了最外层的Interface,另外有一个须要传递Ibinder的构造函数,还有就是与我们在Aidl类中定义的方法名称同样的方法。这就是我们生成的.java文件的一个大致的架构。

由上边可知最外层Interface是IInterface的子类,而Stub与Proxy是最外层Interface的实现类。Stub继承了Binder,他们之间存在这种关系。以下我们就对代码的调用流程进行具体解说。

首先我们在应用Aidl时通过client绑定的方式来获取我们的IMyAidlInterface,并通过它来调用服务端的方法进行通信,例如以下代码:

private IMyAidlInterface mService;

Intent intent = new Intent();
intent.setAction("com.lyl.aidl");
Intent intent1 = new Intent(createExplicitFromImplicitIntent(this, intent));//兼容5.0以后版本号
bindService(intent1, mServiceC, Context.BIND_AUTO_CREATE); ServiceConnection mServiceC = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IMyAidlInterface.Stub.asInterface(service);
} @Override
public void onServiceDisconnected(ComponentName name) { }
};
public void do(View v) {
try {
Person p1 = new Person();
p1.setName("刘大");
p1.setAge(3);
Person p2 = new Person();
p2.setName("赵二");
p2.setAge(3);
Person p3 = new Person();
p3.setName("张三");
p3.setAge(3); tv.setText("" + mService.inPerson(p1) + "\n" + mService.outPerson(p2) + "\n" + mService.inOutPerson(p3)); } catch (RemoteException e) {
e.printStackTrace();
}
}

上边代码通过IMyAidlInterface.Stub.asInterface(service)获取了我们的IMyAidlInterface,do()方法中通过它才调用了我们服务端的代码,那我们先看一下这个asInterface方法中的參数是什么,我们知道当通过绑定的方式获取的binder是我们在服务中的onBind()方法中返回的,看一下我们服务端的代码:

public class MyAidlService extends Service {

    @Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("Log_LYL", "Onstart");
return super.onStartCommand(intent, flags, startId); } @Override
public IBinder onBind(Intent intent) {
return stub;
} IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() { @Override
public String inPerson(Person p) throws RemoteException {
...
} @Override
public String outPerson(Person p) throws RemoteException {
...
} @Override
public String inOutPerson(Person p) throws RemoteException {
...
}
};
}

通过以上服务端代码能够知道我们返回的是一个stub
事例。而它是我们的.aidl文件自己主动生成的.java文件里Stub的一个实例对象。如今我们知道IMyAidlInterface.Stub.asInterface(service)中的service是编译器为我们生成的.java文件的一个实例。我们接着看IMyAidlInterface中Stub.asInterface(service)方法,代码例如以下:

public static com.jason.aidl.aidldemo.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.jason.aidl.aidldemo.IMyAidlInterface))) {
return ((com.jason.aidl.aidldemo.IMyAidlInterface) iin);
}
return new com.jason.aidl.aidldemo.IMyAidlInterface.Stub.Proxy(obj);
}

由于我们的Stub类继承了Binder。Binder实现了Ibinder接口,所以传递Stub的实例在这里没问题。上述代码首先推断了obj是否为空,不为空往下走,第二个if推断是在推断通过obj获取的iin是否属于当前程序执行的进程。假设是,直接返回,这里也就是说我们要调用的服务与我们当前程序在同一进程。不须要远程通信,直接调用即可。假设不是能够看见系统新生成了一个Proxy对象实例,并把我们的stub实例对象传递进去。看一下Proxy的构造函数,代码例如以下:

private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {
mRemote = remote;
}

能够看到通过构造函数将Stub实例重新传到了Proxy中并赋值给mRemote,mRemote也变成了Stub实例。

到这里绑定服务获取ImyAidlInterface对象就介绍完了,做一下总结:通过绑定的方式获取到了在服务端的系统生成的.aidl文件相应的.java文件里子类Stub的一个实例,调用Stub类中的asInterface()方法推断Stub实例所在进程与当前进程是否为同一个。是直接能够操作。不是生成一个Proxy实例并将Stub传入。

以下我们看一下调用代码实现:

public void do(View v) {
try {
Person p1 = new Person();
p1.setName("刘大");
p1.setAge(3);
Person p2 = new Person();
p2.setName("赵二");
p2.setAge(3);
Person p3 = new Person();
p3.setName("张三");
p3.setAge(3); tv.setText("" + mService.inPerson(p1) + "\n" + mService.outPerson(p2) + "\n" + mService.inOutPerson(p3)); } catch (RemoteException e) {
e.printStackTrace();
}
}

我们首先分析第一个inPerson(p1);这种方法我们在定义时的修饰符为in,及String
inPerson(in Person p);下边我们看一下它的实现过程,通过上边的介绍能够知道mService假设不是在同一个进程中是返回的Proxy对象实例,那么我们就进入Proxy中查找相应的方法,代码例如以下:

@Override
public java.lang.String inPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((p != null)) {
_data.writeInt(1);
p.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_inPerson, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

能够看到系统生成了_data、_reply两个Parcel类型的对象,然后操作_data。写入Token,推断假设传入的Person对象不为空写入了一个1,接下来调用了person类的writeToParcel()方法,传入了_data,咦,注意这里。我们就知道了实现Parcelable接口时的writeToParcel()方法在这里会用到。好的,我们跟进看一下,代码例如以下:

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}

这一步看来就是给_data赋值,将我们从開始调用inPerson(com.jason.aidl.aidldemo.Person p)  p实例中的值以writeString、writeInt的方式保存到_data中,假设传入的p为null则写入一个0。继续往下走。我标红的那句是重点,也是这句进行了binder通信,关于binder通讯机制有非常多非常多东西。因为本人能力有限在这里就不多讲了,感兴趣的能够去了解一下。通过前边能够知道mRemote为asInterface方法传入的service,及Stub实例,而Stub中根本没有transact()方法,而它是Binder的子类,好,我们去Binder中找,还真有:

public final boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this); if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}

能够看到。在这里调用了onTransact(code, data, reply, flags);我们知道在Stub类中我们实现了这种方法,我们看一下Stub类中的这种方法:

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_inPerson: {
data.enforceInterface(DESCRIPTOR);
com.jason.aidl.aidldemo.Person _arg0;
if ((0 != data.readInt())) {
_arg0 = com.jason.aidl.aidldemo.Person.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
java.lang.String _result = this.inPerson(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
} }
return super.onTransact(code, data, reply, flags);
}

在这里我们仅仅摘取了inPerson()方法相关内容。先了解一下传入的四个參数:1.code 每个方法相应一个code,我们定义了多少方法就会有多少code,通过它来做不同处理;2.data、reply为在proxy相应方法中生成的两个Parcel对象。3.flags 一般为1。这个不用管先。
首先通过code找到相应方法的操作,推断data中写入的int值是否为0,不为0说明之前的p不为null。而且已经存入到data中。调用Person中的CREATOR的createFromParcel()方法,咦。这里又用到了Parcelable的东西。看一下这种方法:

public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
protected Person(Parcel in) {
name = in.readString();
age = in.readInt();
}

能够看到,系统在这里创建了一个Person实例。传入了我们之前的data(保存有person信息),并将data中的信息赋值给新的person对象。

回到Stub类的onTransace()方法中继续往下看:

java.lang.String _result = this.inPerson(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;

接着系统调用了this.inPerson(_arg0)方法。_arg0为上边通过createFromParcel()方法新生成的person(包括传递过来的person值)对象,我们知道Stub为抽象类。他实现了我们外层的Interface类。可是没有实现inPerson()方法。那么我们就须要它的子类实现,而我们的mRemote.transace()中的mRemote就是服务端返回的Stub的一个实现子类,也就是说这里调用的this.inPerson(_arg0)方法实际是调用了服务端的那个Stub实现类里边的inPerson()方法,看一下我们之前服务端的inPerson()方法的详细实现:

@Override
public String inPerson(Person p) throws RemoteException {
String old = "name:" + p.getName() + " age:" + p.getAge();
Log.d("Log_LYL:iPerson_", old);
p.setName("李四");
p.setAge(13);
return "name:" + p.getName() + " age:" + p.getAge();
}

通过以上分析,大家就知道了这里的 Log.d("Log_LYL:iPerson_", old); 是为什么有值了,由于我们一開始就将传入的person值附到了Parcel类型的data中了,然后又通过createFromParcel()方法将data中的值付给了新的person对象。

然后回到Stub类的onTransace()方法,会看到调用了reply.writeNoException();reply.writeString(_result);两个方法,将服务端处理完后返回的信息写入了reply中。最后return。

再回到Proxy类中的inPerson()方法中:

mRemote.transact(Stub.TRANSACTION_inPerson, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;

将服务端返回的信息读出来并付给_result。最后返回,这样我们在activity中就能够得到服务端处理后的结果了。到如今为止。我们就应该明确了,通过in修饰符传递person后服务端生成了新的person对象。并对新person对象进行处理,这种话不管服务端怎样改动person对象我们client的person对象是不会受不论什么影响的,为什么,由于不是一个对象。这就是in操作符。

以下我们分析一下out操作符,直接看代码:

String outPerson(out Person p);//.aidl文件里定义;
mService.outPerson(p2)//client调用;

我们在client直接通过绑定时返回的Stub实例调用outPerson()方法。通过上边对inPerson()方法的介绍我们知道不是同一个进程中的会终于调用到Proxy中对应的方法中,那我们直接看Proxy中代码:

@Override
public java.lang.String outPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_outPerson, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
if ((0 != _reply.readInt())) {
p.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

有了上边对inPerson()方法的了解这个相对就简单多了,相同生成两个Parcel对象。作用相同,不同的是用out修饰后没有对传入的p做null推断,也没有取值保存到data中。而是直接调用了mRemote.transact(Stub.TRANSACTION_outPerson,
_data, _reply, 0)方法,那好,通过前边我们直接去Stub中找onTransact()方法吧:

case TRANSACTION_outPerson: {
data.enforceInterface(DESCRIPTOR);
com.jason.aidl.aidldemo.Person _arg0;
_arg0 = new com.jason.aidl.aidldemo.Person();
java.lang.String _result = this.outPerson(_arg0);
reply.writeNoException();
reply.writeString(_result);
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}

我们仅仅要out方法相关信息,这里系统通过new Person()的方式创建了一个person对象实例赋值个_arg0,这个对象与我们调用时传进来的对象没有一点关系,然后直接调用了this.outPerson()方法,通过inPerson()的介绍我们就知道这里直接调用了服务端outPerson()实现:

@Override
public String outPerson(Person p) throws RemoteException {
//这里的p是空的,由于在IMyAidlInterface的Stub类中onTransact()方法中没有写入;
// _arg0 = new com.jason.aidl.aidldemo.Person();
//java.lang.String _result = this.outPerson(_arg0);
String old = "name:" + p.getName() + " age:" + p.getAge();
Log.d("Log_LYL:outPerson_", old);
p.setName("周六");
p.setAge(20);
return "name:" + p.getName() + " age:" + p.getAge();
}

这里能够知道old是没有赋值的,以下赋值return,回到Stub中:

java.lang.String _result = this.outPerson(_arg0);
reply.writeNoException();
reply.writeString(_result);
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;

将处理结果写入reply中。推断_arg0是否为null,不是,写入1,这里重新用到了writeToParcel()方法,通过上边inPerson()方法介绍可知这里是将_arg0的值付给reply。假设为null,reply中赋值0。回到Proxy中:

try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_outPerson, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
if ((0 != _reply.readInt())) {
p.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;

运行完transact()方法后从_reply中读出服务端操作返回结果,推断写入的int值,假设不为0,说明_reply中写入了服务端产生的person信息,则将通过readFromParcel()方法将新的_reply中保存的服务端的person信息写入到client传入的p中,这样client就收到了服务端的person信息。

总结一下out修饰符。即使我们在服务端将p的值写好。服务端也不会接收我们的client信息。能够说服务端根本不关系client传入的信息,服务端通过new
Person()方式产生新对象,并返回给client,client的p通过readFromParcel()方法获得服务端值,并没有做对象的交换。

下边看一下inout修饰符代码:

String inOutPerson(inout Person p);//.aidl文件里;
mService.inOutPerson(p3)。//client调用;

相同看一下Proxy中对应方法:

@Override
public java.lang.String inOutPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((p != null)) {
_data.writeInt(1);
p.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_inOutPerson, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
if ((0 != _reply.readInt())) {
p.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
case TRANSACTION_inOutPerson: {
data.enforceInterface(DESCRIPTOR);
com.jason.aidl.aidldemo.Person _arg0;
if ((0 != data.readInt())) {
_arg0 = com.jason.aidl.aidldemo.Person.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
java.lang.String _result = this.inOutPerson(_arg0);
reply.writeNoException();
reply.writeString(_result);
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}

能够看到在inout修饰符时推断了传入的p对象,也就是说服务端接收了保存了client的p对象,操作类似与in修饰符,也是通过Person.CREATOR.createFromParcel(data)创建新对象,在Proxy中相同推断了_reply中写入的int值,也就是说,client也能收到服务端对对象的改动,也就是说。inout操作符是int、out操作符的合体。到这里相信大家已经明确in、out、inout操作符的差别了。而且连实现都明确了。

到如今今天要跟大家分享的东西就写完了,相信您看完这篇文章会对Android中的Aidl有一个新的了解。对in、out、inout修饰也明确了,对实现Parcelable接口有了新的认识,假设是这样,那我这篇文章就没白写。谢谢大家。

Android深入源代码分析理解Aidl总体调用流程(雷惊风)的更多相关文章

  1. android开发源代码分析--多个activity调用多个jni库的方法

    android开发源代码分析--多个activity调用多个jni库的方法 有时候,我们在开发android项目时会遇到须要调用多个native c/jni库文件,下面是本人以前实现过的方法,假设有知 ...

  2. Android HttpURLConnection源代码分析

    Android HttpURLConnection源代码分析 之前写过HttpURLConnection与HttpClient的差别及选择.后来又分析了Volley的源代码. 近期又遇到了问题,想在V ...

  3. Android 消息处理源代码分析(1)

    Android 消息处理源代码分析(1) 在Android中,通常被使用的消息队列的代码在文件夹\sources\android-22\android\os下,涉及到下面几个类文件 Handler.j ...

  4. Android HandlerThread 源代码分析

    HandlerThread 简单介绍: 我们知道Thread线程是一次性消费品,当Thread线程运行完一个耗时的任务之后.线程就会被自己主动销毁了.假设此时我又有一 个耗时任务须要运行,我们不得不又 ...

  5. Android KLog源代码分析

    Android KLog源代码分析 Android KLog源代码分析 代码结构 详细分析 BaseLog FileLog JsonLog XmlLog 核心文件KLogjava分析 遇到的问题 一直 ...

  6. Android 消息处理源代码分析(2)

    Android 消息处理源代码分析(1)点击打开链接 继续接着分析剩下的类文件 Looper.java public final class Looper { final MessageQueue m ...

  7. Appium Android Bootstrap源代码分析之启动执行

    通过前面的两篇文章<Appium Android Bootstrap源代码分析之控件AndroidElement>和<Appium Android Bootstrap源代码分析之命令 ...

  8. Appium Android Bootstrap源代码分析之简单介绍

    在上一个系列中我们分析了UiAutomator的核心源代码,对UiAutomator是怎么执行的原理有了根本的了解.今天我们会開始另外一个在安卓平台上基于UiAutomator的新起之秀--Appiu ...

  9. Android AsyncTask 源代码分析

    AsyncTask源代码分析 public abstract class AsyncTask<Params, Progress, Result> { //日志TAG private sta ...

随机推荐

  1. [jzoj]3777.最短路(shortest)

    Link https://jzoj.net/senior/#main/show/3777 Description 小Y最近学得了最短路算法,一直想找个机会好好练习一下.话虽这么说,OJ上最短路的题目都 ...

  2. java.lang.IllegalArgumentException: Attribute 'items' is required and must be a Collection, an Array or a Map

    很有可能是涉及items的时候写成了item导致此错

  3. goland 中国 caisy qq Czx123456

    goland 中国 caisy  qq  Czx123456

  4. HttpServletResponse常见应用——设置响应头控制浏览器的行为

    1.设置http响应头控制浏览器禁止缓存当前文档内容 1 response.setDateHeader("expries", -1); 2 response.setHeader(& ...

  5. JS的document.links函数使用示例

    ? <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title&g ...

  6. poj2376 Cleaning Shifts(区间贪心,理解题意)

    https://vjudge.net/problem/POJ-2376 题意理解错了!!真是要仔细看题啊!! 看了poj的discuss才发现,如果前一头牛截止到3,那么下一头牛可以从4开始!!! # ...

  7. 为什么样本方差分母是n-1

    https://blog.csdn.net/qq_39521554/article/details/79633207 为什么样本方差的分母是n-1?为什么它又叫做无偏估计? 至于为什么是n-1,可以看 ...

  8. jvm理论-字节码指令案例

    案例1 public class Demo { public int calc(){ int a=100; int b=200; int c=300; return(a+b)*c; } public ...

  9. 【iOS】ARC-MRC下的单例及其应用

    单例的应用十分普遍,单例模式使一个类仅仅有一个实例. *易于供外界訪问. *方便控制实例个数,节约系统资源. *OC中的常见单例: 如:UIApplication,  NSNotificationCe ...

  10. 20.1翻译系列:EF 6中自动数据迁移技术【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/automated-migration-in-code-first.aspx EF 6 ...