1. 程式人生 > >Android AIDL -通過一個比較完整的Demo快速運用

Android AIDL -通過一個比較完整的Demo快速運用

前端時間專案運用到AIDL,關於AIDL客戶端以及AIDL服務端網路上沒有一個比較完備的Demo.

而參考Demo無疑是一個比較快速的學習方法.因此,我寫了一個Demo.

供大家參考,也非常歡迎大家對其中寫的不好的地方進行指正.

好了,首先簡述下基本功能:

在AIDL 客戶端三個EditText中輸入三個值,點選提交按鈕,將這三個值傳入到AIDL服務端進行處理.

服務端處理後會執行客戶端的回撥函式:在AIDL客戶端介面進行重新整理,並顯示一個toast.

接下來看看程式碼結構:

需要注意的是,兩個工程中com.harlan.demo.aidl包內部的檔案必須保持一致.

(1)HarlanInfo.java:這是包中唯一的一個java檔案,是一個數據結構,該類實現了Parcelable介面

package com.harlan.demo.aidl;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * 
 * <一句話功能簡述>
 * Parcelable是Android特有的功能,效率比實現Serializable介面高
 * 
 * @author  Administrator
 * @version  [版本號, 2012-12-10]
 * @see  [相關類/方法]
 * @since  [產品/模組版本]
 */
public class HarlanInfo implements Parcelable
{
    private String name;
    
    private int age;
    
    private String place;

    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;
    }

    public String getPlace()
    {
        return place;
    }

    public void setPlace(String place)
    {
        this.place = place;
    }
    
    /**
     * <預設建構函式>
     */
    public HarlanInfo()
    {
        
    }
    
    /**
     * <預設建構函式>
     */
    public HarlanInfo(Parcel in)
    {
        //注意順序
         name = in.readString();
        age = in.readInt();
        place = in.readString();
    }
    
    /**
     * seems meaningless
     * return 0;
     */
    @Override
    public int describeContents()
    {
        return 0;
    }

    /**
     * 將物件序列化為一個Parcel物件
     *  可以將Parcel看成是一個流,通過writeToParcel把物件寫到流裡面,
     *  再通過createFromParcel從流裡讀取物件
     *  注意:寫的順序和讀的順序必須一致。 
     */
    @Override
    public void writeToParcel(Parcel dest, int flags)
    {
        dest.writeString(name);
        dest.writeInt(age);
        dest.writeString(place);
    }
    
    /**
     * 例項化靜態內部物件CREATOR實現介面Parcelable.Creator
     * public static final一個都不能少,內部物件CREATOR的名稱也不能改變,必須全部大寫
     */
    public static final Parcelable.Creator<HarlanInfo> CREATOR = new Creator<HarlanInfo>(){

      //將Parcel物件反序列化為HarlanInfo   
        @Override
        public HarlanInfo createFromParcel(Parcel source)
        {
            HarlanInfo hlInfo = new HarlanInfo(source);
            return hlInfo;
        }

        @Override
        public HarlanInfo[] newArray(int size)
        {
            return new HarlanInfo[size];
        }
        
    };
    
}

(2)HarlanInfo.aidl :協同HarlanInfo.java檔案"作戰",缺一不可.

package com.harlan.demo.aidl;

parcelable HarlanInfo;

(3)ICallBack.aidl:
這是客戶端回撥方法的介面,在客戶端實現其具體方法,在服務端呼叫執行.

package com.harlan.demo.aidl;
interface ICallBack{
	/**
	*callback of AIDLClient
	*handle by server
	**/
	void handleByServer(String param);
}

(4)ExecuteService.aidl:

這是從服務端獲取資料方法的介面,在服務端實現其具體方法,在客戶端呼叫執行.

引數info是由使用者輸入的資料構成的,同時傳遞的還要客戶端回撥方法的控制代碼,從而服務端可以呼叫客戶端的回撥方法.

該方法返回一個HarlanInfo的資料型別,客戶端獲得此資料,在介面上進行相應的顯示.

package com.harlan.demo.aidl;
import com.harlan.demo.aidl.HarlanInfo;
import com.harlan.demo.aidl.ICallBack;
interface ExecuteServiceAIDL
{
	/**
	 *get info from server and 
	 *Transfer a callback methods handle;
	 *if occur error ,will be return null
	 *對於非基本資料型別和String和CharSequence型別,要加上方向指示
	 *包括in、out和inout,in表示由客戶端設定,out表示由服務端設定,inout是兩者均可設定。
     */
	HarlanInfo getServerHarlanInfo(in HarlanInfo info,ICallBack icallback);
}


好了,現在對com.harlan.demo.aidl包已經大致瞭解,build一下project,發現gen資料夾下面多出來一個包:


 包中檔案可以隨便看看,不看也沒事.因為你只要在客戶端服務端相應的位置實現對應的介面就可以了.

先來看看服務端,因為服務端相對簡單些,不需要介面什麼的,只是一個Service.

上程式碼:

package com.harlan.demo.service;

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

import com.harlan.demo.aidl.ExecuteServiceAIDL;
import com.harlan.demo.aidl.HarlanInfo;
import com.harlan.demo.aidl.ICallBack;

public class AIDLService extends Service
{
    public static final String TAG = "AIDLService";
    
    private ICallBack mCallBack;
    
    /**
     * 繫結服務
     */
    @Override
    public IBinder onBind(Intent intent)
    {
        // TODO Auto-generated method stub
        return mBinder;
    }
    
    /**
     * 建立服務
     */
    @Override
    public void onCreate()
    {
        super.onCreate();
    }

    /**
     * 銷燬服務
     */
    @Override
    public void onDestroy()
    {
        super.onDestroy();
    }

    /**
     * 啟動服務
     */
    @Override
    public void onStart(Intent intent, int startId)
    {
        super.onStart(intent, startId);
    }

    /**
     * 解綁服務
     */
    @Override
    public boolean onUnbind(Intent intent)
    {
        mCallBack = null;
        return super.onUnbind(intent);
    }
    
    ExecuteServiceAIDL.Stub mBinder = new ExecuteServiceAIDL.Stub()
    {
       //這裡實現了getServiceHarlanInfo介面
        @Override
        public HarlanInfo getServerHarlanInfo(HarlanInfo info, ICallBack icallback)
            throws RemoteException
        {
            Log.d(TAG,"getServerHarlanInfo");
            mCallBack = icallback;
            mCallBack.handleByServer("The msg is from server");
            HarlanInfo newInfo = new HarlanInfo();
            newInfo.setName(info.getName().toLowerCase());
            newInfo.setAge(info.getAge() + 10);
            newInfo.setPlace("Home");
            return newInfo;
        }
    };
    
}

一目瞭然,服務端主要的功能就是實現了aidl中的getServerHarlanInfo(HarlanInfo info, ICallBack icallback)介面.返回了一個mBinder供客戶端呼叫.

寫好了服務,還得在Manifest檔案裡面配置一下:

 <service 
            android:name=".AIDLService">
            <intent-filter>
                <action android:name="com.harlan.demo.aidl.service"/>
            </intent-filter>
</service>

服務端寫好了,就來客戶端的了.客戶端主要是一個activity,介面相對簡單,如圖所示:

介面佈局相對簡單,就不貼程式碼了.

下面貼ClientActivity的程式碼:

package com.harlan.demo.activity;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.harlan.demo.aidl.ExecuteServiceAIDL;
import com.harlan.demo.aidl.HarlanInfo;
import com.harlan.demo.aidl.ICallBack;

public class ClientActivity extends Activity
{
    public static final String TAG = "ClientActivity";
    
    private static final String BIND_ACTION = "com.harlan.demo.aidl.service";
    
    private EditText mEditTextName;
    
    private EditText mEditTextAge;
    
    private EditText mEditTextPlace;
    
    private Button mButtonCommit;
    
    private ExecuteServiceAIDL executeService;
    
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);
        getView();
	//使用者點選提交按鈕,將資料傳至服務端進行處理  
     	 mButtonCommit.setOnClickListener(new View.OnClickListener()
        {
            
            @Override
            public void onClick(View v)
            {
	    //繫結服務
                getServiceConnect();             
            }
        });
    }
        
    private void getView()
    {
        mEditTextName = (EditText)findViewById(R.id.editText_name);
        mEditTextAge = (EditText)findViewById(R.id.editText_age);
        mEditTextPlace = (EditText)findViewById(R.id.editText_place);
        mButtonCommit = (Button)findViewById(R.id.button_commit);
    }
    
    private void getServiceConnect()
    {
        Intent it = new Intent();
        it.setAction(BIND_ACTION);
        startService(it);
        bindService(it, mserviceConnection, BIND_AUTO_CREATE);
    }
    
    ServiceConnection mserviceConnection = new ServiceConnection()
    {
        
        @Override
        public void onServiceDisconnected(ComponentName name)
        {
            Log.d(TAG, "onServiceDisconnected");           
        }
        
        @Override
        public void onServiceConnected(ComponentName name, IBinder service)
        {
            Log.d(TAG, "onServiceConnected");
            //獲取服務端傳過來的IBinder物件,通過該物件呼叫服務端的方法
              executeService = ExecuteServiceAIDL.Stub.asInterface(service);
            if (executeService != null)
            {
                handlerInfo();
            }
        }
    };
    
    private void handlerInfo()
    {
        String mName;
        int mAge;
        String mPlace;
        if (mEditTextName.getText().toString().equals(""))
        {
            mEditTextName.setText("Harlan");
            mName = "Harlan";
        }
        else
        {
            mName = mEditTextName.getText().toString();
        }
        if (mEditTextAge.getText().toString().equals(""))
        {
            mAge = 22;
        }
        else
        {
            mAge = Integer.parseInt(mEditTextAge.getText().toString());
        }
        if (mEditTextPlace.getText().toString().equals(""))
        {
            mPlace = "Nanjing";
        }
        else
        {
            mPlace = mEditTextPlace.getText().toString();
        }
        HarlanInfo mInfo = new HarlanInfo();
        mInfo.setName(mName);
        mInfo.setAge(mAge);
        mInfo.setPlace(mPlace);
        try
        {
            HarlanInfo serverInfo = new HarlanInfo();
	   //呼叫服務端的方法
              serverInfo = executeService.getServerHarlanInfo(mInfo, mCallBack);
            //更新介面
              mEditTextName.setText(serverInfo.getName());
            mEditTextAge.setText(String.valueOf(serverInfo.getAge()));
            mEditTextPlace.setText(serverInfo.getPlace());
            unbindService(mserviceConnection);
        }
        catch (RemoteException e)
        {
            e.printStackTrace();
        }
    }
    ICallBack.Stub mCallBack = new ICallBack.Stub()
    {
       //客戶端回撥方法的具體實現
        @Override
        public void handleByServer(String param)
            throws RemoteException
        {
            Toast.makeText(getApplicationContext(), param, Toast.LENGTH_LONG).show();          
        }
    };
   
}

服務端呼叫客戶端回撥方法,在介面上顯示一個toast.客戶端根據服務端傳回來的資料,重新整理介面. 

最後執行結果如圖所示: