关于Service的跨进程回调-AIDL

Posted by Chris on September 21, 2016
  • callback作为函数参数进行回调
  • RemoteCallbackList注册回调

//实现了Parcelable接口的类

package com.example.xiaopeng.androiddemo.Service;

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

/**
 * Created by xiaopeng on 13/01/2017.
 */

public class Book implements Parcelable {
    String name;
    int price;

    public  Book(){}

    protected Book(Parcel in) {
        name = in.readString();
        price = in.readInt();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(price);
    }

    /**
     * 参数是一个Parcel,用它来存储与传输数据
     * @param dest
     */
    public void readFromParcel(Parcel dest) {
        //注意,此处的读值顺序应当是和writeToParcel()方法中一致的
        name = dest.readString();
        price = dest.readInt();
    }

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

    //方便打印数据
    @Override
    public String toString() {
        return "name : " + name + " , price : " + price;
    }

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

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

// Book.aidl

//第一类AIDL文件
//这个文件的作用是引入了一个序列化对象 Book 供其他的AIDL文件使用
//注意:Book.aidl与Book.java的包名应当是一样的
package com.example.xiaopeng.androiddemo.Service;

parcelable Book;

// BookManager.aidl

//第二类AIDL文件
//作用是定义方法接口

package com.example.xiaopeng.androiddemo.Service;

//导入所需要使用的非默认支持数据类型的包
import com.example.xiaopeng.androiddemo.Service.Book;
import com.example.xiaopeng.androiddemo.Service.ISimpleCallback;

interface BookManager {

    //所有的返回值前都不需要加任何东西,不管是什么数据类型
    List<Book> getBooks();

    //传参时除了Java基本类型以及String,CharSequence之外的类型
    //都需要在前面加上定向tag,具体加什么量需而定
    void addBook(in Book book);

    //演示简单的回调
    void startCount(int to, ISimpleCallback callback);

    //可以脱离函数,注册多个回调
    void registerCallback(int id, ISimpleCallback callback);
    void unregisterCallback(int id, ISimpleCallback callback);
}

// ISimpleCallback.aidl

//第三类AIDL文件
//作用是定义回调接口
package com.example.xiaopeng.androiddemo.Service;

// Declare any non-default types here with import statements

interface ISimpleCallback {

    void handleCount(int count);
}

// Service

package com.example.xiaopeng.androiddemo.Service;

import android.app.Service;
import android.content.Intent;
import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;

import android.support.annotation.Nullable;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;


/**
 * Created by xiaopeng on 13/01/2017.
 */

public class AIDLService extends Service {

    private  final String TAG = AIDLService.class.getSimpleName();

    private List<Book> mBooks = new ArrayList<>();

    private RemoteCallbackList<ISimpleCallback> mCallbacks = new RemoteCallbackList<>();

    private BookManager.Stub bookManager = new BookManager.Stub() {

        @Override
        public List<Book> getBooks() throws RemoteException {
            return mBooks;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            // TODO
        }

        @Override
        public void startCount(final int to, final ISimpleCallback callback) throws RemoteException {
            Thread t = new Thread()
            {
                /*睡! */
                public void preciseSleep(long millis)
                {
                    long endTime = System.currentTimeMillis() + millis;

                    do {
                        try
                        {
                            Thread.sleep(endTime - System.currentTimeMillis());
                        }
                        catch (InterruptedException e)
                        {}
                    } while (System.currentTimeMillis() < endTime);
                }

                public void run()
                {
                    for (int i = 1; i <= to; i++)
                    {
                        preciseSleep(1000);

                        //simpleCallbackToClient(callback, i);

                        callbackToClient(i);
                    }
                }
            };

            t.start();
        }

        private void simpleCallbackToClient(ISimpleCallback callback, int counter){
            try
            {
                // simple callback
                callback.handleCount(counter);
            }
            // AIDL中唯一可能抛出的异常
            catch (DeadObjectException e)
            {
                Log.d(TAG, "Dead peer, aborting...", e);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        private void callbackToClient(int counter){
            int count = mCallbacks.beginBroadcast();
            try {
                for (int i = 0; i < count; i++) {
                    Log.d(TAG, "callbackToClient...");
                    mCallbacks.getBroadcastItem(i).handleCount(counter);
                }
            } catch (RemoteException e) {
                Log.e(TAG, "E/onUpdateMetaDatas", e);
            } finally {
                mCallbacks.finishBroadcast();
            }
        }

        @Override
        public void registerCallback(int id, ISimpleCallback callback) throws RemoteException {
            mCallbacks.register(callback);
        }

        @Override
        public void unregisterCallback(int id, ISimpleCallback callback) throws RemoteException {
            mCallbacks.unregister(callback);
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        Book book = new Book();
        book.setName("Android开发艺术探索");
        book.setPrice(28);
        mBooks.add(book);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 取消掉所有的回调
        mCallbacks.kill();
    }

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

// Client

package com.exstudio.testing;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.DeadObjectException;
import android.os.Handler;
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 android.widget.TextView;

import com.example.xiaopeng.androiddemo.Service.Book;
import com.example.xiaopeng.androiddemo.Service.BookManager;
import com.example.xiaopeng.androiddemo.Service.ISimpleCallback;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private final String TAG = MainActivity.class.getSimpleName();

    Button get_books_btn;
    Button service_simle_callback_btn;

    //由AIDL文件生成的Java类
    private BookManager mBookManager = null;

    //标志当前与服务端连接状况的布尔值,false为未连接,true为连接中
    private boolean mBound = false;

    //包含Book对象的list
    private List<Book> mBooks;

    private Handler mHandler = new Handler();
    private boolean mCounting = false;
    private TextView mCounterText;

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

        get_books_btn = (Button) this.findViewById(R.id.get_books_btn);
        service_simle_callback_btn = (Button) this.findViewById(R.id.service_simle_callback);
        mCounterText  = (TextView) this.findViewById(R.id.counter_text);


        get_books_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(mBound == true){
                    for (Book book : mBooks) {
                        Log.i(getLocalClassName(), book.getName());
                    }
                }
            }
        });

        service_simle_callback_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mBookManager == null)
                {
                    Log.d(TAG, "Nothing to do!");
                    return;
                }

                if (mCounting)
                {
                    Log.d(TAG, "mCounter is implemented globally and cannot be reused while counting is in progress.");
                    return;
                }

                try
                {
                    mCounting = true;
                    mBookManager.startCount(10, serviceCallback);
                    Log.d(TAG, "Counting has begun...");
                }
                catch (DeadObjectException e)
                {
                    mCounting = false;
                    Log.d(TAG, Log.getStackTraceString(e));
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (!mBound) {
            attemptToBindService();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mBound) {
            unbindService(mServiceConnection);
            mBound = false;
        }
    }

    /**
     * 尝试与服务端建立连接
     */
    private void attemptToBindService() {
        Intent intent = new Intent();
        intent.setAction("com.example.xiaopeng.androiddemo.Service.AIDLService");
        intent.setPackage("com.example.xiaopeng.androiddemo");
        boolean ret = bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
        if(ret == false){
            Log.d("main activity", "failed to binding remote service !");
        }
    }


    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(getLocalClassName(), "service connected");
            mBookManager = BookManager.Stub.asInterface(service);
            mBound = true;

            if (mBookManager != null) {
                try {
                    mBookManager.registerCallback(0, serviceCallback);

                    mBooks = mBookManager.getBooks();
                    Log.e(getLocalClassName(), mBooks.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            try {
                mBookManager.unregisterCallback(0, serviceCallback);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            Log.e(getLocalClassName(), "service disconnected");
            mBound = false;
        }
    };


    //mService.startCount(10, mCounter)中的参数,回调之后用来刷新界面
    // 此处用来setText, 该stub 也是IAsyncServiceCounter里的内部类。
    private ISimpleCallback.Stub serviceCallback = new ISimpleCallback.Stub()
    {
        public void handleCount(final int n)
        {
            Log.d(TAG, "handleCount(" + n + ")");

            mHandler.post(new Runnable()
            {
                public void run()
                {
                    // 不要这样写立即数
                    if (n == 10)
                    {
                        mCounterText.setText("Done!");
                        mCounting = false;
                    }
                    else
                        mCounterText.setText(String.valueOf(n));
                }
            });
        }
    };

}