編輯:關於Android編程
在android上有很多跨進程的通訊方法例如aidl,messenger,ContentProvider,BroadCast,Socket等等,安卓進程間通信(IPC)那肯定要談到AIDL。
你知道你需要進程間通信、需要AIDL(以及Binder),那麼可以默認你對這些概念已經有了一些了解,你(大致)知道它們是什麼,它們有什麼用,所以為了節約大家的眼力和時間。
AIDL:Android Interface Definition Language,即Android接口定義語言
Android系統中的進程之間不能共享內存,因此,需要提供一些機制在不同進程之間進行數據通信。為了使其他的應用程序也可以訪問本應用程序提供的服務,Android系統采用了遠程過程調用(Remote Procedure Call,RPC)方式來實現。與很多其他的基於RPC的解決方案一樣,Android使用一種接口定義語言(Interface Definition Language,IDL)來公開服務的接口。我們知道4個Android應用程序組件中的3個(Activity、BroadcastReceiver和ContentProvider)都可以進行跨進程訪問,另外一個Android應用程序組件Service同樣可以。因此,可以將這種可以跨進程訪問的服務稱為AIDL(Android Interface Definition Language)服務。我們知道建立AIDL服務的步驟有一下三個步驟:
建立一個擴展名為aidl的文件。該文件的語法類似於Java代碼,但會稍有不同
建立一個服務類(Service的子類)
實現由aidl文件生成的Java接口
其實AIDL這門語言非常的簡單,基本上它的語法和 Java 是一樣的,只是在一些細微處有些許差別——畢竟它只是被創造出來簡化Android程序員工作的,太復雜不好——所以在這裡我就著重的說一下它和 Java 不一樣的地方。主要有下面這些點:
文件類型:用AIDL書寫的文件的後綴是 .aidl,而不是 .java。
數據類型:AIDL默認支持一些數據類型,在使用這些數據類型的時候是不需要導包的,但是除了這些類型之外的數據類型,在使用之前必須導包, 就算目標文件與當前正在編寫的 .aidl 文件在同一個包下 ——在 Java 中,這種情況是不需要導包的。比如,現在我們編寫了兩個文件,一個叫做 Person.java ,另一個叫做 Person.aidl ,它們都在com.losileeya.aidlmaster包下 ,現在我們需要在 .aidl 文件裡使用 Peerson 對象,那麼我們就必須在 .aidl 文件裡面寫上 *import com.losileeya.aidlmaster.Person;哪怕 .java 文件和 .aidl 文件就在一個包下。
默認支持的數據類型包括:
Java中的八種基本數據類型,包括 byte,short,int,long,float,double,boolean,char。 String 類型。 CharSequence類型。 List類型:List中的所有元素必須是AIDL支持的類型之一,或者是一個其他AIDL生成的接口,或者是定義的parcelable(下文關於這個會有詳解)。List可以使用泛型。 Map類型:Map中的所有元素必須是AIDL支持的類型之一,或者是一個其他AIDL生成的接口,或者是定義的parcelable。Map是不支持泛型的。定向tag:這是一個極易被忽略的點——這裡的“被忽略”指的不是大家都不知道,而是很少人會正確的使用它。在我的理解裡,定向 tag 是這樣的: AIDL中的定向 tag 表示了在跨進程通信中數據的流向,其中 in 表示數據只能由客戶端流向服務端, out 表示數據只能由服務端流向客戶端,而 inout 則表示數據可在服務端與客戶端之間雙向流通。其中,數據流向是針對在客戶端中的那個傳入方法的對象而言的。in 為定向 tag 的話表現為服務端將會接收到一個那個對象的完整數據,但是客戶端的那個對象不會因為服務端對傳參的修改而發生變動;out 的話表現為服務端將會接收到那個對象的的空對象,但是在服務端對接收到的空對象有任何修改之後客戶端將會同步變動;inout 為定向 tag 的情況下,服務端將會接收到客戶端傳來對象的完整信息,並且客戶端將會同步服務端對該對象的任何變動。
另外,Java 中的基本類型和 String ,CharSequence 的定向 tag 默認且只能是 in 。還有,請注意, 請不要濫用定向 tag ,全都一上來就用 inout ,等工程大了系統的開銷就會大很多——因為排列整理參數的開銷是很昂貴的。
兩種AIDL文件:在我的理解裡,所有的AIDL文件大致可以分為兩類。一類是用來定義parcelable對象,以供其他AIDL文件使用AIDL中非默認支持的數據類型的。一類是用來定義方法接口,以供系統使用來完成跨進程通信的。可以看到,兩類文件都是在“定義”些什麼,而不涉及具體的實現,這就是為什麼它叫做“Android接口定義語言”。
注: 所有的非默認支持數據類型必須通過第一類AIDL文件定義才能被使用。
我的編譯環境為Android Studio2.1.3,SDK Version 24,JDK 1.8
鼠標移到module上面去,點擊右鍵,然後 new->AIDL->AIDL File,按下鼠標左鍵就會彈出一個框提示生成AIDL文件了。生成AIDL文件之後,項目的目錄會變成這樣的:
裡面的代碼按照你的意思來,必需以interface開頭,並且參數裡面使用了引用的parcelable對象,我們需要import,示例如下:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
// IRemoteAidl.aidl
package com.losileeya.aidlmaster;
import com.losileeya.aidlmaster.Person;
// Declare any non-default types here with import statements
//這個文件的作用是引入了一個序列化對象 Person 供其他的AIDL文件使用
//注意:Person.aidl與Person.java的包名應當是一樣的
parcelable Person;
interface IRemoteAidl {
int add(int num1,int num2);//計算2個數之和
//傳參時除了Java基本類型以及String,CharSequence之外的類型
//都需要在前面加上定向tag,具體加什麼量需而定
List
好了我們這裡提到了parcelable對象,所以我們需要1個Person.aidl與Person.java而且須保證包名應當是一樣的。
先寫Person.java,並且實現parcelable接口,android studio早就替我們提供了這樣的插件。
package com.losileeya.aidlmaster; import android.os.Parcel; import android.os.Parcelable; /** * User: Losileeya ([email protected]) * Date: 2016-07-10 * Time: 00:37 * 類描述:實現Parcelable序列化 * * @version : */ public class Person implements Parcelable { private String name; private int age; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.name); dest.writeInt(this.age); } public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } protected Person(Parcel in) { this.name = in.readString(); this.age = in.readInt(); } public static final Parcelable.CreatorCREATOR = new Parcelable.Creator () { @Override public Person createFromParcel(Parcel source) { return new Person(source); } @Override public Person[] newArray(int size) { return new Person[size]; } }; }
Person.aidl
package com.losileeya.aidlmaster; // Declare any non-default types here with import statements parcelable Person;
到這裡我們就已經將AIDL文件新建並且書寫完畢了,clean 一下項目,如果沒有報錯,這一塊就算是大功告成了。
在服務端實現AIDL中定義的方法接口的具體邏輯,然後在客戶端調用這些方法接口,從而達到跨進程通信的目的。
接下來我直接貼上我寫的服務端代碼:
package com.losileeya.aidlmaster; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; /** * User: Losileeya ([email protected]) * Date: 2016-07-09 * Time: 12:28 * 類描述:aidl實現功能的邏輯代碼 * * @version : */ public class IRemoteService extends Service{ private ArrayListpersons; @Nullable @Override public IBinder onBind(Intent intent) { persons=new ArrayList<>(); return mBinder; } private IBinder mBinder=new IRemoteAidl.Stub(){ @Override public int add(int num1, int num2) throws RemoteException { Log.d("TAG","服務已開啟"); return num1+num2; } @Override public List addPerson(Person p) throws RemoteException { persons.add(p); return persons; } }; }
整體的代碼結構很清晰,大致可以分為2塊:第一塊是 重寫 IRemoteAidl.Stub 中的方法 。在這裡面提供AIDL裡面定義的方法接口的具體實現邏輯。第三塊是 重寫 onBind() 方法 。在裡面返回寫好的 IRemoteAidl.Stub 。
到這裡我們的服務端代碼就編寫完畢了,真正是扯得多,代碼少。
首先就是拷貝AIDL文件夾到客戶端目錄下(與java目錄同級)
然後需要重新寫一個Person.java,也可以直接從服務端拷貝過來,必須保證Person.java和aidl的文件是相同的包名。
private ServiceConnection conn=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { iRemoteAidl= IRemoteAidl.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { iRemoteAidl=null; } };
private void bindService() { Intent intent=new Intent(); intent.setComponent(new ComponentName("com.losileeya.aidlmaster","com.losileeya.aidlmaster.IRemoteService")); bindService(intent,conn, Context.BIND_AUTO_CREATE); }
ComponentName裡面的絕對不能寫錯,前面一個是包名,後面是啟動的服務類,
這樣基本就寫好了,可以通信了。
## MainActivity package com.losileeya.aidlclient; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import com.losileeya.aidlmaster.IRemoteAidl; import com.losileeya.aidlmaster.Person; import java.util.ArrayList; import java.util.Iterator; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private IRemoteAidl iRemoteAidl=null; private EditText etNum1,etNum2,etResult; private Button btnCalculate; private ArrayListdatas; private TextView tv_desc; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); bindService(); } private void initView() { etNum1= (EditText) findViewById(R.id.et_num1); etNum2= (EditText) findViewById(R.id.et_num2); etResult= (EditText) findViewById(R.id.et_result); btnCalculate= (Button) findViewById(R.id.btn_calculate); tv_desc= (TextView) findViewById(R.id.tv_desc); btnCalculate.setOnClickListener(this); } private void bindService() { Intent intent=new Intent(); intent.setComponent(new ComponentName("com.losileeya.aidlmaster","com.losileeya.aidlmaster.IRemoteService")); bindService(intent,conn, Context.BIND_AUTO_CREATE); } private ServiceConnection conn=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { iRemoteAidl= IRemoteAidl.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { iRemoteAidl=null; } }; @Override protected void onDestroy() { super.onDestroy(); unbindService(conn); } @Override public void onClick(View v) { int num1=Integer.parseInt(etNum1.getText().toString().trim()); int num2=Integer.parseInt(etNum2.getText().toString().trim()); try { int result=iRemoteAidl.add(num1,num2); etResult.setText(result+""); datas= (ArrayList ) iRemoteAidl.addPerson(new Person("小a",21)); Iterator i=datas.iterator(); while (i.hasNext()){ tv_desc.setText(i.next().toString()); } } catch (RemoteException e) { e.printStackTrace(); etResult.setText("失敗"); } } }
效果如下:
擺脫XML布局文件相信每一個Android開發者,在接觸“Hello World”的時候,就形成了一個觀念:Android UI布局是通過layo
咱們在做Android APP開發的時候經常碰到有下拉刷新和上拉加載跟多的需求,這篇文章咱們先說說下來刷新,咱們就以google的原生的下拉刷新控件SwipeRefres
由於在項目開發時,有這樣的簡單需求,問谷歌,網絡上也有好多Utils工具類,但是比較冗余。自己就簡單的寫了一個簡單幫助類。/** * 下載文件,更新UI簡單幫助類 *
View的平滑滾動效果什麼是實現View的平滑滾動效果呢,舉個簡單的例子,一個View從在我們指定的時間內從一個位置滾動到另外一個位置,我們利用Scroller類可以實現