Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android:進程間通信交互

Android:進程間通信交互

編輯:關於Android編程

Intent 的 ComponentName 廣播-BroadcastReceiver ContentProvider AIDL

Intent 的 ComponentName

Intent作為我們最常用的數據傳輸渠道,特別是通過Intent打開一個Activity,想必每個人都不會陌生。通常我們用到的都是通過Intent打開同一個進程(App)內部的Activity,如果想實現跨進程通訊,就需要把Intent對象發送到另一個(App)中,並解析出來,這時就需要ComponentName來為我們做這件事情了。既然可以發送數據到另外的進程,也就可以實現不同進程間的交互了。
注意事項:如果A要打開另一個進程中的B中的Activity,那麼要在B項目中的AndroidManifest文件中,把要打開的Activity的exported設置為true ,否則將會報錯。

android:exported="true"

我們將要用到ComponentName的構造函數如下:

    public ComponentName(String pkg, String cls) {
        if (pkg == null) throw new NullPointerException("package name is null");
        if (cls == null) throw new NullPointerException("class name is null");
        mPackage = pkg;
        mClass = cls;
    }

它需要兩個參數,第一個參數是一個存在的pakage(包),第二個是這個包中你要打開的類的名字(注意是帶完整包名的),下面是使用例子:
進程AndroidAIDL(com.example.androidaidl)中的MainActivity

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //接收傳過來的Intent
        if (getIntent() != null){
            System.out.println("----------"+getIntent().getIntExtra("id", 0)+"----------");
        }
    }

另一個進程AndroidTest,在這裡的MainActivity中我們寫如下代碼:

/**指定包名和帶包名的Activity的名字*/
ComponentName componentName = new ComponentName("com.example.androidaidl", "com.example.androidaidl.MainActivity");
Intent intent = new Intent();
intent.putExtra("id", 1001);
intent.setComponent(componentName);
startActivity(intent);

運行上面代碼後,會看到LogCat中打印出 1001,表示接收AndroidTest進程傳來的Intent正常。

注意事項:上面代碼中我們傳遞的是基本的數據類型,對於基本數據類型,比如Int,String等接收時可以像上面那樣直接讀取,但是如果發送的是復雜的對象,該對象需要實現Serializable或者Parcelable接口。
比如我們定義一個 SendData 對象作為傳遞對象,它實現 Parcelable 接口:

public class SendData implements Parcelable{
    int id;
    String content;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {

        @Override
        public SendData createFromParcel(Parcel source) {
            // TODO Auto-generated method stub
            SendData data = new SendData();
            data.setId(source.readInt());
            data.setContent(source.readString());
            return data;
        }

        @Override
        public SendData[] newArray(int size) {
            // TODO Auto-generated method stub
            return new SendData[size];
        }

    };

    @Override
    public int describeContents() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // TODO Auto-generated method stub
        dest.writeInt(id);
        dest.writeString(content);
    }

}

發送代碼:

Intent intent = new Intent();
SendData data = new SendData();
data.setId(1001);
data.setContent("hellow world");
//發送序列化對象
intent.putExtra("data", data);
startActivity(intent);

接收代碼

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (getIntent() != null){
        if (getIntent().getParcelableExtra("data") == null)
            return;
        //讀取序列化對象,並轉化為SendData類型
        SendData data = (SendData)getIntent().getParcelableExtra("data");
        System.out.println("----------"+data.getContent()+"-----------");
    }
}

如果在同一個進程的話,上面的方法沒有任何問題,如果在不同進程中,就要注意,SendData 這個bean所在的包名,在各個項目中必須一樣,否則接收方,無法解析

廣播-BroadcastReceiver

Android的廣播是系統級的,只要傳遞的Action一樣(下面的例子中,都使用 Action_Test),就可以接收到其他進程廣播的消息,廣播中可以通過Intent傳遞數據。

發送方代碼:

Intent intent = new Intent("Action_Test");
SendData data = new SendData();
data.setId(1001);
data.setContent("hellow world");
intent.putExtra("data", data);
intent.putExtra("id", 1001);
getActivity().sendBroadcast(intent);

接收方代碼(動態注冊廣播):

innerReceiver = new InnerReceiver();
IntentFilter filter = new IntentFilter("Action_Test");
registerReceiver(innerReceiver, filter);

class InnerReceiver extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub
        if (intent.getAction().equals("Action_Test")){
            SendData data = (SendData)intent.getParcelableExtra("data");
            System.out.println("-----------"+data.getContent()+"------------");
        }
    }   
}

因為是通過Intent傳遞數據,所以對復雜對象的要求和第一種方式一樣,要求bean所在的包名一樣

ContentProvider

ContentProvider通常用來操作數據集,Android本身就提供了不少的ContentProvider訪問,比如聯系人、相冊等。
訪問ContentProvider,需要通過Uri,需要以“content://”開頭。下面看看使用方法:
在進程AndroidAIDL(com.example.androidaidl),我們定義一個繼承自ContentProvider的類,需要重載它的方法,這裡我們以query為例

public class ProviderTest extends ContentProvider {
    private static final UriMatcher MATCHER = new UriMatcher(  
            UriMatcher.NO_MATCH);  
    static{
    //添加訪問字符串,對應配置文件中的android:authorities屬性
        MATCHER.addURI("com.mh.getdata", "stock", 10001);
    }

    @Override
    public boolean onCreate() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    /**
     * @param uri URI 路徑
     * @param projection 要保護的列集合,如果傳 null,所有列都會被包含進去.
     * @param selection 用來過濾數據集的規則,null表示不篩選.
     * @param selectionArgs 類似字符串的格式化,selection參數中可以包含 ?s, 這個將被selectionArgs的內容替換
     * @param sortOrder 排序.
     */
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        // TODO Auto-generated method stub
        System.out.println("--------------------query----------------------");
        return null;
    }

    @Override
    public String getType(Uri uri) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }

}

定義了這個類後,需要在AndroidManifest對它進行聲明,android:exported=true,記得要寫,否則會提示權限錯誤

                    
        

上面的配置有兩個屬性
android:name就是類名成
android:authorities這個就是前面提到的Uri,外界需要通過這個來訪問Provider

下面看看調用者,在另一個進程中,我們有如下代碼:

ContentResolver resolver = getActivity().getContentResolver();
/**com.mh.getdata/stock這個要和Provider所在進程中添加的Uri一致*/
Uri uri = Uri.parse("content://com.mh.getdata/stock");
Cursor cursor = resolver.query(uri, null, null, null, null);

調用上面代碼後,LogCat中打印出 “query”字樣,表名Provider調用成功,我們通過resolver就可以和AndroidAIDL進程中的Provider對象進行交互了。

AIDL

一種接口定義語言,Android會自動生成通訊代碼,通過AIDL我們可以在一個進程中,調用另一個進程中的方法,據說一些大公司的殺不死的Service就是通過這個AIDL來保活的,可以好好研究下。個人認為AIDL更適合做插件化系統,或者有多個app共同組成一個系統,比如WebView因為內存洩露比較嚴重,所以一些公司就單獨把WebView封裝成一個app,單獨調用,這時,我們通過AIDL技術,就可以調用到這個app中的接口,來操作WebView的行為。
下面開始介紹使用方法:
在兩個項目中新建普通文件(Eclipse中是:new ->General->File),記得同時寫上後綴名(aidl),兩個項目中這個文件所在的包名要保持一致,內容也要一樣,如圖

這裡寫圖片描述
編譯之後, 會在gen目錄下,自動產生同名的,後綴為 java 的文件。裡面有我們要用到的 Stub類。

public static abstract class Stub extends android.os.Binder implements com.example.aidl.AidlFunctions

在接口文件AIDLFunctions.aidl中,我們定義一個方法 show

interface AidlFunctions{
    void show();
}

服務端:
AIDL的使用,需要一個Service配合,所以我們在服務端還要聲明一個Service

public class AIDLService extends Service {
//stub就是系統自動產生的
    AidlFunctions.Stub binder;

    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        binder = new AidlFunctions.Stub() {

            @Override
            //這裡是我們在接口中聲明的方法的實現
            public void show() throws RemoteException {
                // TODO Auto-generated method stub
                System.out.println("--------------------收到----------------------");
            }
        };
        return binder;
    }   
}

在AndroidManifest聲明Service

        
            
                
            
        

客戶端:

//綁定服務,要用到ServiceConnection 
private ServiceConnection serviceConnection;
//自定義的接口,和服務端一樣
private AidlFunctions aidlFunctions;

serviceConnection = new ServiceConnection() {

    @Override
    public void onServiceDisconnected(ComponentName name) {
        // TODO Auto-generated method stub
        System.out.println("--------------------ServiceDisconnected----------------------");
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // TODO Auto-generated method stub
        System.out.println("--------------------ServiceConnected----------------------");
        aidlFunctions = AidlFunctions.Stub.asInterface(service);
    }
};
Intent intent = new Intent("com.example.androidaidl.AIDLService");
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
//調用show方法
try {
    aidlFunctions.show();
} catch (RemoteException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved