Android深入源代碼分析理解Aidl總體調用流程(雷驚風)

? ? ? ? 2017年開始上班的第一天。老不想工作了,假期感覺還沒開始就已經結束了,唉,時間就是這樣,新的一年開始了,盡管非常不想干正事,沒辦法,必須干起來。由于后邊的路還非常長,距離六十歲還非常遠。

剛上班也沒什么事,復習一下之前的東西,看了一下Aidl相關的知識。細致瞅了瞅Aidl的調用流程,這里寫篇文章整理一下。幫助大家的同一時候。自己也加深一下印象。對Aidl不太了解的童鞋能夠先看一下我之前的一篇文章,

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

案例下載鏈接:http://download.csdn.net/detail/liuyonglei1314/9734165?

? ?在上篇文章中我們已經說過了AndroidAidl的簡單應用及對象的傳遞方式,還包括了在AS中進行Aidl開發會遇到的一些問題及決解方法,本篇文章針對用法我們不在多說。我們將以傳遞對象為例深入的剖析Aidl的具體調用流程,繼續以上文中傳遞Person對象為例展開,通過本片文章的學習你會學到下面相關知識:1.Aidl工作調用流程;2.Aidl傳遞對象時修飾符inoutinout的深入理解。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>() {@Overridepublic Person createFromParcel(Parcel in) {return new Person(in);}@Overridepublic 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;}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(name);dest.writeInt(age);}public void readFromParcel(Parcel dest) {name = dest.readString();age = dest.readInt();}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}

Person類實現了parcelable接口。有兩個構造方法,有nameage兩個成員變量。編譯器能夠幫我們生成一個staticfinalCREATORwriteToParcel()方法,我們自己寫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中配置。具體信息就不介紹了。上篇文章中進行了具體解說。在這里看到了修飾符inoutinout。后文會做具體解說。

我們繼續看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) {
//...}@Overridepublic android.os.IBinder asBinder() {return this;}@Overridepublic 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;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic java.lang.String inPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {//...                
return _result;}@Overridepublic java.lang.String outPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {//...return _result;}@Overridepublic 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(最外層的類)兩個內部類(StubProxy)。先看interface。它跟我們定義的.aidl文件名稱字同樣,繼承了IInterface,它的內容分為兩部分:一個內部類Stub及我們定義的Person抽象操作方法。每個都拋出RemoteException。接下來看他的內部類Stub,它是一個抽象類。繼承了Binder類,實現了外層的Interface,他比較重要的是一個asInterface()方法和onTransact()方法。它另一個static的內部類Proxy。它也實現了最外層的Interface,另外有一個須要傳遞Ibinder的構造函數,還有就是與我們在Aidl類中定義的方法名稱同樣的方法。這就是我們生成的.java文件的一個大致的架構。

由上邊可知最外層InterfaceIInterface的子類,而StubProxy是最外層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() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mService = IMyAidlInterface.Stub.asInterface(service);}@Overridepublic 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 {@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.i("Log_LYL", "Onstart");return super.onStartCommand(intent, flags, startId);}@Overridepublic IBinder onBind(Intent intent) {return stub;}IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() {@Overridepublic String inPerson(Person p) throws RemoteException {...        
}@Overridepublic String outPerson(Person p) throws RemoteException {...}@Overridepublic String inOutPerson(Person p) throws RemoteException {...}};
}

通過以上服務端代碼能夠知道我們返回的是一個stub 事例。而它是我們的.aidl文件自己主動生成的.java文件里Stub的一個實例對象。如今我們知道IMyAidlInterface.Stub.asInterface(service)中的service是編譯器為我們生成的.java文件的一個實例。我們接著看IMyAidlInterfaceStub.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類繼承了BinderBinder實現了Ibinder接口,所以傳遞Stub的實例在這里沒問題。上述代碼首先推斷了obj是否為空,不為空往下走,第二個if推斷是在推斷通過obj獲取的iin是否屬于當前程序執行的進程。假設是,直接返回,這里也就是說我們要調用的服務與我們當前程序在同一進程。不須要遠程通信,直接調用即可。假設不是能夠看見系統新生成了一個Proxy對象實例,并把我們的stub實例對象傳遞進去。看一下Proxy的構造函數,代碼例如以下:

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

能夠看到通過構造函數將Stub實例重新傳到了Proxy中并賦值給mRemotemRemote也變成了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();}
}

我們首先分析第一個inPersonp1);這種方法我們在定義時的修飾符為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>() {@Overridepublic Person createFromParcel(Parcel in) {return new Person(in);}@Overridepublic 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修飾后沒有對傳入的pnull推斷,也沒有取值保存到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有一個新的了解。對inoutinout修飾也明確了,對實現Parcelable接口有了新的認識,假設是這樣,那我這篇文章就沒白寫。謝謝大家。



轉載于:https://www.cnblogs.com/blfbuaa/p/7400678.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/454880.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/454880.shtml
英文地址,請注明出處:http://en.pswp.cn/news/454880.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

(轉載)UI接口分層自動化測試框架設計思想

閱讀本小節&#xff0c;需要讀者具備如下前提條件&#xff1a; 1. 掌握一種編程語言基礎&#xff0c;如java、python等。 2. 掌握一種單元測試框架&#xff0c;如java語言的testng框架、python的unittest框架。 3. 掌握目前主流的UI測試框架&#xff0c;移動端APP測試框架Ap…

如何提高閱讀源代碼的效率 .

如何提高閱讀源代碼的效率 記得在開源流行之前&#xff0c;我看過的代碼緊限于所參與的項目&#xff0c;能有個幾萬行就不錯哩。后來很多優秀開源項目都相繼蹦出來了&#xff0c;閱讀的代碼量那叫一個大呀&#xff01;不得不看。我現在掉到android這個大坑里&#xff0c;每天都…

170821-關于SpringMVC的知識點

1.SpringMVC 概述以及優勢 SpringMVC和Spring的關系&#xff1a; 軟件開發的三層架構&#xff1a; web層【表示層、表現層】---->Service層---->Dao[DataBase Access Object]---->數據庫&#xff01; SpringMVC實際上是Spring的一個子模塊&#xff0c;我們用Spring…

pojo類中list存儲其他字段_List集合流處理類型小結

本文為博主原創&#xff0c;未經允許不得轉載對應實體類importlombok.Getter;importlombok.Setter;GetterSetterpublic classStudent {privateString name;private intage;privateString className;privateString birthday;}1.根據字段取出某一個字段屬性的集合List studentLis…

Hash表的擴容(轉載)

Hash表&#xff08;Hash Table&#xff09;hash表實際上由size個的桶組成一個桶數組table[0...size-1] 。 當一個對象經過哈希之后。得到一個對應的value , 于是我們把這個對象放到桶table[ value ]中。當一個桶中有多個對象時。我們把桶中的對象組織成為一個鏈表。 這在沖突處…

寫在前面的一些話:《Learning OpenCV》中文版 .

2009-09-17 15:51 7578人閱讀 評論(4) 收藏 舉報 <!-- /* Font Definitions */ font-face {font-family:Helvetica; panose-1:2 11 5 4 2 2 2 2 2 4; mso-font-charset:0; mso-generic-font-family:swiss; mso-font-format:other; mso-font-pitch:variable; mso-font-sign…

獨家 | 一文讀懂自然語言處理NLP(附學習資料)

前言 自然語言處理是文本挖掘的研究領域之一&#xff0c;是人工智能和語言學領域的分支學科。在此領域中探討如何處理及運用自然語言。 對于自然語言處理的發展歷程&#xff0c;可以從哲學中的經驗主義和理性主義說起。基于統計的自然語言處理是哲學中的經驗主義&#xff0c;基…

python mock測試_使用mock測試python中的函數

對于測試覆蓋&#xff0c;我想測試文件signalC中該函數的異常塊&#xff1a;class SignalC:def readSignal(self, a):try:with open(os.path.join(self.newSubFolder, "my file" .csv), a) as csvfile:writer csv.writer(csvfile, delimiter,, quotechar|,quotingc…

如何更好閱讀源代碼 .

寫在前面的話&#xff1a;    自從我在linuxaid.com.cn上發表一些文章開始&#xff0c;就不斷的有網友發來電子郵件&#xff0c;或者是就其中某些問題進行探討&#xff0c;或者是查詢其他文章的地址&#xff08;往往這些網友看的是其他網站轉載的我的文章&#xff09;&#x…

wins系統flask綁定mysql_flask如何連接mssql,網上大多是sqlite和mysql教程?

這個居然也冒出來&#xff0c;刨墳了。我們不喜歡寫原生SQL語句&#xff0c;那個寫著費勁&#xff0c;日常開發時候&#xff0c;我們怎么CRUD數據庫呢&#xff1f;一般使用ORM&#xff0c;對象關系映射(英語&#xff1a;Object Relational Mapping&#xff0c;簡稱ORM)。主力使…

hdu 6086 -- Rikka with String(AC自動機 + 狀壓DP)

題目鏈接 Problem DescriptionAs we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Rikka some math tasks to practice. There is one of them:Yuta has n 01 strings si, and he wants to know the number of 01 antisymmetric strings …

課堂動手動腦問題

對于隨機數&#xff0c;java通過Math.random&#xff08;&#xff09;來實現&#xff0c;比如要得到一個隨機數我們可以int a&#xff1b; a&#xff08;int&#xff09;Math.random();但對于隨機數&#xff0c;它是從0到1之間的數&#xff0c;所以必須通過int把它轉為整數&…

GNU/Linux下有多少是GNU的?

導讀&#xff1a;一個葡萄牙的學生寫了一篇文章 《How much GNU is there in GNU/Linux?》由酷殼網的陳皓整理編譯為《GNU/Linux下有多少是GNU的》。這篇文章主要分布了今年4月份的Ubuntu Natty的Linux分發包。其主要是用代碼行來做的分析&#xff0c;用兩個餅圖對比分析。 內…

便攜式三星mysql_JDBC鏈接mysql - 三星藍

package chp07;importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.ResultSet;importjava.sql.SQLException;importjava.sql.Statement;public classJDBC_Test {//創建靜態全局變量staticConnection conn;staticStatement st;public static voidmain(Stri…

C++ 類、對象、class

一、對象初始化 1.不能在類聲明中對數據成員初始化&#xff0c;因為類只是一個抽象類型&#xff0c;不占存儲空間&#xff0c;無處容納數據。 2.若某類的數據成員都是public&#xff0c;則可以像結構體一樣初始化&#xff0c;如 Time t{12,21,04}&#xff1b; 若數據成員有priv…

Unity 富文本

參考鏈接&#xff1a;http://www.ceeger.com/Manual/StyledText.html 首先要說的是不僅僅ugui的text組件支持富文本&#xff0c;Debug.Log也是支持的 Debug.Log("<color#ffff00ff><b>愛生活</b></color> <color#00ffffff><b> 愛海瀾&…

Web項目替換jar包中的文件的方法

經常遇到這樣的問題&#xff0c;需要修改jar包中的方法。應該如何做&#xff1f; 1、有些很人性化的框架jar包&#xff0c;比如SpringSecurity&#xff0c;可以修改配置文件指定一個新建的類&#xff0c;讓類實現Jar包中的對應的接口就好了。 2、大部分的jar包都不會有這么方便…

程序員技術練級攻略

導讀&#xff1a;本文是由陳皓和他的一位朋友Mailper合作完成&#xff0c;原名叫《Build Your Programming Technical Skills》&#xff0c;本文分享了Mailper和作者個人的學習經歷。每個程序員都希望自己能順利的升級到高的層次&#xff0c;您不妨按照下面的方法去做。 前言 你…

Linux shell 之 提取文件名和目錄名的一些方法

很多時候在使用Linux的shell時&#xff0c;我們都需要對文件名或目錄名進行處理&#xff0c;通常的操作是由路徑中提取出文件名&#xff0c;從路徑中提取出目錄名&#xff0c;提取文件后綴名等等。例如&#xff0c;從路徑/dir1/dir2/file.txt中提取也文件名file.txt&#xff0c…

bzoj 2752: [HAOI2012]高速公路(road)

Description Y901高速公路是一條重要的交通紐帶&#xff0c;政府部門建設初期的投入以及使用期間的養護費用都不低&#xff0c;因此政府在這條高速公路上設立了許多收費站。Y901高速公路是一條由N-1段路以及N個收費站組成的東西向的鏈&#xff0c;我們按照由西向東的順序將收費…