Android跨進(jìn)程通信(IPC機(jī)制 一)

最近在復(fù)習(xí)主席的《Android開發(fā)藝術(shù)探索》這本書,將第二章IPC機(jī)制相關(guān)的部分簡單總結(jié)下俺附。

開啟多進(jìn)程的方式:給四大組件在AndroidMenifest文件中指定 android:process屬性噪服。

Android中的IPC方式主要有六種唾那,分別為:

  1. 使用Bundle
  2. 使用文件共享
  3. 使用Messenger
  4. 使用AIDL
  5. 使用ContentProvider
  6. 使用Socket

下面我們主要看下 3. 使用Messenger 和 4.使用AIDL 這兩種跨進(jìn)程通信的方式埠帕。其實Messenger的底層實現(xiàn)也是AIDL。

使用Messenger:

實現(xiàn)一個Messenger可以分為兩個步驟话侄,分別為服務(wù)端和客戶端亏推。

服務(wù)端進(jìn)程:首先,我們需要在服務(wù)端創(chuàng)建一個Service來處理客戶端的連接請求满葛,同時創(chuàng)建一個Handler,并通過它來創(chuàng)建一個Messenger對象罢屈,然后在Service 的 onBind 方法中返回這個Messenger對象底層的Binder即可嘀韧。

客戶端進(jìn)程:客戶端進(jìn)程中,首先要綁定服務(wù)端的Service缠捌,綁定成功后用服務(wù)端返回的IBinder對象創(chuàng)建一個Messenger锄贷,通過這個Messenger就可以向服務(wù)端發(fā)送消息了,消息類型為Message對象曼月。

下面我們新建一個Demo來測試下谊却,我們在客戶端進(jìn)程中向服務(wù)端發(fā)送一句話,在服務(wù)端進(jìn)程中打印出這句話:

1.新建項目MessengerTest哑芹,創(chuàng)建MessengerService炎辨,讓其運行在單獨的進(jìn)程中。

        <service
            android:name=".MessengerService"
            android:process=":remote"/>

2.服務(wù)端進(jìn)程代碼:

public class MessengerService extends Service{
    
    private static final String TAG = "MessengerService";
    
    private static class MessengerHandler extends Handler{
        
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MyConstants.MSG_FROM_CLIENT:
                    Log.e(TAG,"receive msg from client:  "+msg.getData().getString("msg"));
                    break;
                
                default:
                    break;
            }
        }
    }
    
    private final Messenger mMessenger = new Messenger(new MessengerHandler());
    
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}

3.客戶端進(jìn)程代碼:

public class MainActivity extends AppCompatActivity {

    private Button mBtnSend;
    
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mMessenger = new Messenger(service);
            Message msg = Message.obtain(null,MyConstants.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString("msg","hello world");
            msg.setData(data);
            try {
                mMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    private Messenger mMessenger;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnSend = (Button) findViewById(R.id.mBtnSend);
        
        mBtnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Intent intent = new Intent(MainActivity.this,MessengerService.class);
                bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
            }
        });
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}

測試結(jié)果如下:

08-19 23:13:42.646 27432-27432/? E/MessengerService: receive msg from client:  hello world

上述我們只是發(fā)送消息到服務(wù)端進(jìn)程聪姿,服務(wù)端進(jìn)程對msg進(jìn)行相應(yīng)的操作碴萧。也就是說我們只是實現(xiàn)了客戶端到服務(wù)端的單向通信,如果我們需要服務(wù)端處理完畢后回復(fù)客戶端進(jìn)程的話末购,要怎么操作呢破喻?修改后的代碼如下:

服務(wù)端進(jìn)程:

public class MessengerService extends Service{

    private static final String TAG = "MessengerService";

    private static class MessengerHandler extends Handler{

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MyConstants.MSG_FROM_CLIENT:
                    Log.e(TAG,"receive msg from client:  "+msg.getData().getString("msg"));

                    Messenger client = msg.replyTo;
                    Message replyMessage = Message.obtain(null,MyConstants.MSG_FROM_SERVICE);
                    Bundle bundle = new Bundle();
                    bundle.putString("reply","你好,世界");
                    replyMessage.setData(bundle);
                    try {
                        client.send(replyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;

                default:
                    break;
            }
        }
    }

    private final Messenger mMessenger = new Messenger(new MessengerHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}

客戶端進(jìn)程:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private Button mBtnSend;
    private Messenger mMessenger;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mMessenger = new Messenger(service);
            Message msg = Message.obtain(null,MyConstants.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString("msg","hello world");
            msg.setData(data);

            msg.replyTo = mGetReplyMessenger;
            try {
                mMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());

    private static class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MyConstants.MSG_FROM_SERVICE:
                    Log.e(TAG,"receive msg from service:  "+msg.getData().getString("reply"));

                    break;
                default:
                    break;
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnSend = (Button) findViewById(R.id.mBtnSend);

        mBtnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Intent intent = new Intent(MainActivity.this,MessengerService.class);
                bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
            }
        });
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}

測試結(jié)果如下:

08-19 23:34:04.236 4737-4737/com.example.administrator.messengertest:remote E/MessengerService: receive msg from client:  hello world
08-19 23:34:04.236 4386-4386/com.example.administrator.messengertest E/MainActivity: receive msg from service:  你好盟榴,世界

有關(guān)Messenger的操作方式到這里就介紹完畢了曹质,下面我們看下AIDL方式。使用AIDL來進(jìn)行進(jìn)程間通信擎场,同樣分為服務(wù)端和客戶端兩個方面羽德。

  1. 服務(wù)端
    服務(wù)端首先要創(chuàng)建一個Service用來監(jiān)聽客戶端的連接請求,然后創(chuàng)建一個AIDL文件迅办,將暴露給客戶端的接口在這個AIDL文件中聲明玩般,最后在Service中實現(xiàn)這個AIDL接口即可。

  2. 客戶端
    客戶端所要做的事情就稍微簡單一些礼饱,首先需要綁定服務(wù)端的Service坏为,綁定成功后究驴,將服務(wù)端返回的Binder對象轉(zhuǎn)成AIDL接口所屬的類型,接著就可以調(diào)用AIDL中的方法了匀伏。

下面我們看下代碼:

  1. 新建項目AIDLTest洒忧,創(chuàng)建BookManagerService,讓其運行在單獨的進(jìn)程中够颠。
        <service
            android:name=".BookManagerService"
            android:process=":remote"/>
  1. AIDL文件創(chuàng)建熙侍,為了方便AIDL的開發(fā),將所有和AIDL相關(guān)的類和文件全部放入同一個包中履磨,如下所示:
    截圖.PNG

Book.java:

package com.example.administrator.aidltest;

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

public class Book implements Parcelable{

    public int bookId;

    public String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.bookId);
        dest.writeString(this.bookName);
    }

    protected Book(Parcel in) {
        this.bookId = in.readInt();
        this.bookName = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

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

Book.aidl:

// Book.aidl
package com.example.administrator.aidltest;

parcelable Book;

IBookManager.aidl:

// IBookManager.aidl
package com.example.administrator.aidltest;

import com.example.administrator.aidltest.Book;

interface IBookManager {

     List<Book> getBookList();
     void addBook(in Book book);
}
  1. 服務(wù)端進(jìn)程代碼:
package com.example.administrator.aidltest;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class BookManagerService extends Service{

    private static final String TAG = "BMS";
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();

    private Binder mBinder = new IBookManager.Stub(){

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}
  1. 客戶端進(jìn)程代碼:
package com.example.administrator.aidltest;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private Button mBookAdd;
    private Button mBookGet;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bookManager = IBookManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    private IBookManager bookManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        initListener();
        serviceBind();
    }

    private void initView(){
        mBookAdd = (Button) findViewById(R.id.mBookAdd);
        mBookGet = (Button) findViewById(R.id.mBookGet);
    }

    private void serviceBind(){
        Intent intent = new Intent(MainActivity.this,BookManagerService.class);
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    }

    private void initListener(){
        mBookAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (bookManager != null){
                    try {
                        bookManager.addBook(new Book(001,"第一行代碼"));
                        bookManager.addBook(new Book(002,"Android藝術(shù)開發(fā)探索"));
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        mBookGet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (bookManager != null){
                    try {
                        List<Book> mBookList = bookManager.getBookList();
                        for (int i = 0; i<mBookList.size(); i++){
                            Log.e(TAG,"query book list: "+mBookList.get(i).bookName+"\n");
                        }
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}
  1. 編譯運行出錯蛉抓,提示找不到符號:
Error:(20, 34) 錯誤: 找不到符號
符號:   類 Book
位置: 類 BookManagerService

解決方式:在 app 目錄下的build.gradle文件中添加java.srcDirs路徑,如下所示:

    sourceSets{       //android節(jié)點下
        main{
            java.srcDirs = ['src/main/java','src/main/aidl']
        }
    }

測試結(jié)果如下:

08-21 00:01:18.899 30868-30868/com.example.administrator.aidltest E/MainActivity: query book list: 第一行代碼
08-21 00:01:18.899 30868-30868/com.example.administrator.aidltest E/MainActivity: query book list: Android藝術(shù)開發(fā)探索

現(xiàn)在我們考慮下剃诅,如果用戶不想時不時地去查詢圖書列表了巷送,太累了,他去問圖書館矛辕,“當(dāng)有新書的時候笑跛,能不能把新書的信息告訴我呢?”聊品。很顯然這是一種觀察者模式飞蹂,下面我們來實現(xiàn)下:

  1. 在上述代碼的基礎(chǔ)上,新增 IOnNewBookArrivedListener.aidl文件:
// IOnNewBookArrivedListener.aidl
package com.example.administrator.aidltest;

import com.example.administrator.aidltest.Book;

interface IOnNewBookArrivedListener {
    void onNewBookArrived(in Book newBook);
}
  1. 在原有 IBookManager 接口中增加兩個方法:
// IBookManager.aidl
package com.example.administrator.aidltest;

import com.example.administrator.aidltest.Book;
import com.example.administrator.aidltest.IOnNewBookArrivedListener;

interface IBookManager {

     List<Book> getBookList();
     void addBook(in Book book);
     void registerListener(IOnNewBookArrivedListener listener);
     void unregisterListener(IOnNewBookArrivedListener listener);
}
  1. 服務(wù)端代碼修改如下:
package com.example.administrator.aidltest;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

public class BookManagerService extends Service{

    private static final String TAG = "BMS";

    private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);

    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();

    private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList =
            new CopyOnWriteArrayList<IOnNewBookArrivedListener>();

    private Binder mBinder = new IBookManager.Stub(){

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }

        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            if (!mListenerList.contains(listener)){
                mListenerList.add(listener);
            }
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            if (mListenerList.contains(listener)){
                mListenerList.remove(listener);
            }
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        new Thread(new ServiceWorker()).start();
    }

    @Override
    public void onDestroy() {
        mIsServiceDestroyed.set(true);
        super.onDestroy();
    }

    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
        for (int i=0; i<mListenerList.size(); i++){
            IOnNewBookArrivedListener listener = mListenerList.get(i);
            listener.onNewBookArrived(book);
        }
    }

    private class ServiceWorker implements Runnable{

        @Override
        public void run() {
            while (!mIsServiceDestroyed.get()){
                try {
                    Thread.sleep(3000);
                }catch (Exception e){
                    e.printStackTrace();
                }

                int bookId = mBookList.size()+1;
                Book newBook = new Book(bookId,"new book#"+bookId);

                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  1. 客戶端代碼修改如下:
package com.example.administrator.aidltest;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
    
    private IBookManager mRemoteBookManager;

    private Button mBookAdd;
    private Button mBookGet;
    
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MESSAGE_NEW_BOOK_ARRIVED:
                    Log.e(TAG,"receive new book :  "+msg.obj);
                    break;
                
                default:
                    break;
            }
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bookManager = IBookManager.Stub.asInterface(service);
            try {
                mRemoteBookManager = bookManager;
                bookManager.registerListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteBookManager = null;
        }
    };
    
    private IOnNewBookArrivedListener mListener = new IOnNewBookArrivedListener.Stub(){
        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED,newBook).sendToTarget();
        }
    };
    
    private IBookManager bookManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        initListener();
        serviceBind();
    }

    private void initView(){
        mBookAdd = (Button) findViewById(R.id.mBookAdd);
        mBookGet = (Button) findViewById(R.id.mBookGet);
    }

    private void serviceBind(){
        Intent intent = new Intent(MainActivity.this,BookManagerService.class);
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    }

    private void initListener(){
        mBookAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (bookManager != null){
                    try {
                        bookManager.addBook(new Book(001,"第一行代碼"));
                        bookManager.addBook(new Book(002,"Android藝術(shù)開發(fā)探索"));
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        mBookGet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (bookManager != null){
                    try {
                        List<Book> mBookList = bookManager.getBookList();
                        for (int i = 0; i<mBookList.size(); i++){
                            Log.e(TAG,"query book list: "+mBookList.get(i).bookName+"\n");
                        }
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        if (mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()){
            try {
                mRemoteBookManager.unregisterListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mConnection);
        super.onDestroy();
    }
}

運行結(jié)果如下:

08-22 00:24:59.829 12598-12598/com.example.administrator.aidltest E/MainActivity: receive new book :  com.example.administrator.aidltest.Book@3010b98b
08-22 00:25:02.829 12598-12598/com.example.administrator.aidltest E/MainActivity: receive new book :  com.example.administrator.aidltest.Book@1c298a68
08-22 00:25:05.829 12598-12598/com.example.administrator.aidltest E/MainActivity: receive new book :  com.example.administrator.aidltest.Book@1a4bce81

我們接著分析翻屈,在MainActivity銷毀時陈哑,我們會解除已經(jīng)注冊到服務(wù)端的listener,結(jié)果服務(wù)端無法找到我們之前注冊的那個listener伸眶,解注冊宣布失敗芥颈。原因是:對象是不能跨進(jìn)程直接傳輸?shù)模瑢ο蟮目邕M(jìn)程傳輸本質(zhì)上都是反序列化的過程赚抡,Binder會把客戶端傳遞過來的對象重新轉(zhuǎn)化并生成一個新的對象爬坑。解決方式是采用RemoteCallbackList,代碼修改如下:

BookManagerService:

package com.example.administrator.aidltest;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

public class BookManagerService extends Service{

    private static final String TAG = "BMS";

    private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);

    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();

    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList =
            new RemoteCallbackList<IOnNewBookArrivedListener>();

    private Binder mBinder = new IBookManager.Stub(){

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }

        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.register(listener);
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.unregister(listener);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        new Thread(new ServiceWorker()).start();
    }

    @Override
    public void onDestroy() {
        mIsServiceDestroyed.set(true);
        super.onDestroy();
    }

    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
        final int N = mListenerList.beginBroadcast();
        for (int i = 0; i < N; i++){
            IOnNewBookArrivedListener l = mListenerList.getBroadcastItem(i);
            if (l != null){
                l.onNewBookArrived(book);
            }
        }
        mListenerList.finishBroadcast();
    }

    private class ServiceWorker implements Runnable{

        @Override
        public void run() {
            while (!mIsServiceDestroyed.get()){
                try {
                    Thread.sleep(3000);
                }catch (Exception e){
                    e.printStackTrace();
                }

                int bookId = mBookList.size()+1;
                Book newBook = new Book(bookId,"new book#"+bookId);

                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

這樣涂臣,跨進(jìn)程解注冊問題就解決了盾计,到這里AIDL的基本使用方法就介紹完畢了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赁遗,一起剝皮案震驚了整個濱河市署辉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌岩四,老刑警劉巖哭尝,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異剖煌,居然都是意外死亡材鹦,警方通過查閱死者的電腦和手機(jī)逝淹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來桶唐,“玉大人栅葡,你說我怎么就攤上這事∮仍螅” “怎么了欣簇?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坯约。 經(jīng)常有香客問我熊咽,道長,這世上最難降的妖魔是什么闹丐? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任横殴,我火速辦了婚禮,結(jié)果婚禮上妇智,老公的妹妹穿的比我還像新娘滥玷。我一直安慰自己氏身,他們只是感情好巍棱,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蛋欣,像睡著了一般航徙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上陷虎,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天到踏,我揣著相機(jī)與錄音,去河邊找鬼尚猿。 笑死窝稿,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的凿掂。 我是一名探鬼主播伴榔,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼庄萎!你這毒婦竟也來了踪少?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤糠涛,失蹤者是張志新(化名)和其女友劉穎援奢,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體忍捡,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡集漾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年切黔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帆竹。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡绕娘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出栽连,到底是詐尸還是另有隱情险领,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布秒紧,位于F島的核電站绢陌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏脐湾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一叙淌、第九天 我趴在偏房一處隱蔽的房頂上張望秤掌。 院中可真熱鬧,春花似錦鹰霍、人聲如沸闻鉴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽孟岛。三九已至,卻和暖如春督勺,著一層夾襖步出監(jiān)牢的瞬間渠羞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工智哀, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留次询,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓瓷叫,卻偏偏與公主長得像屯吊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子赞辩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容