37.Android Service 及 AIDL

37.Android Service 及 AIDL


Android Service介绍

├── Context
│   ├── ContextWrapper (Service)
│   │   ├── ContextThemeWrapper (Activity)
  • Service只是一个没有界面(Theme)的Activity,它继承的是ContextWrapper。

  • Activity继承的是ContextThemeWrapper,所以有主题,有界面。

Service的等级和Activity差不多。由于Service没有界面,用户不可见的。所以,Service一般作为运行在后台的服务,做一个后台的工作(监听什么,记录什么,刷新什么)。

说这么多很多东西可能被概念混淆:Service虽然是不可以见的,运行的后台的,说到底也只是一个没有主题的Activity,但是还是运行在主线程中的。即使是Activity,不开子线程也是运行在主线程中的,所以Service也一样,即使用了Service,要做一些耗时的操作,也必须在子线程中,或者异步任务中

要实现IPC(跨进程调用)时,Service上只能用AIDL的方式调用bindService;其次,还有使用BroadcastReceiver。


Android Service类型

  • Local Service运行在主进程中,因为在主进程中,所以不需要IPC,那就更不需要写一AIDL,这里的话用bindService比较方便,绑定Service的Context销毁的时候,再unbindService,就可以停止服务了。

  • Remote Service :在AndroidMainfest.xml的< service >标签中指定android:process=":remote"即可。此时,Service启动后将作为一个独立的进程。启动Service的Context所在的进程被回收后,Service一样可以运行。这样的话,有助于不同App之间的通信。


Android Service bind 和 start

startService流程图:

Created with Raphaël 2.1.0startService()onCreate()onStartCommand()Service was running...stopService() or stopSelf()onDestroy()service was destroyed

bindService流程图:

Created with Raphaël 2.1.0bindService()onCreate()onBind()Service was running and it was boundunbindService()onUnbind()onDestroy()service was destroyed
  • startService

    • 就是简单的开启一个Service,然后服务运行,不能进行通信,除非调用stopService。

    • startService 方法被调用N次,onCreate方法只会调用一次,onStartCommand将会被调用多次(和startService的次数一致)。

  • bindService

    • 绑定Service需要一个ServiceConnection,通过这个ServiceConnection可以拿到IBinder对象,就可以和这个Service进行通信。如果要停止服务,则要使用unbindService相当于把Service的生命周期交给绑定它的Context去管理(对应的onCreate去bind,对用的onDestory去unbind)

    • 调用 bindService 调用N次,onCreate方法都只会调用一次,同时onStartCommand方法始终不会被调用

  • startService + bindService

    • 调用unbindService已经不能停止Service,必须调用stopService 或 Service自身的stopSelf。

    • 启动又绑定Service,Service后一直会在后台运行,当然onCreate也只会调用一次,后面的onStartCommand会被调用N次。


Local Service 实现

要做成什么样子呢?就做一个绑定Service,然后进行通信。然后,下载图片,再将图片渲染在Activity的ImageView上。

还引用了一个AsyncTask模板

先写一个Activity的回调接口 IBinderView

public interface IBinderView {
    /**
     * 开始下载
     */
    void downloadStart();

    /**
     * 下载成功
     *
     * @param imageFilePath
     */
    void downloadSuccess(String imageFilePath);

    /**
     * 下载失败
     */
    void downloadFailure();
}

DownloadService

public class DownloadService extends Service {

    private static final String TAG = "DownloadService";
    private IBinder binder;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return this.binder;
    }

    /**
     * Called by the system when the service is first created.  Do not call this method directly.
     */
    @Override
    public void onCreate() {
        super.onCreate();
        this.binder = new DownloadServiceBinder();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * Called by the system to notify a Service that it is no longer used and is being removed.  The
     * service should clean up any resources it holds (threads, registered
     * receivers, etc) at this point.  Upon return, there will be no more calls
     * in to this Service object and it is effectively dead.  Do not call this method directly.
     */
    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    /**
     * Service Binder
     */
    public class DownloadServiceBinder extends Binder {
        public IBinderView iBinderView;

        public DownloadService getService() {
            return DownloadService.this;
        }
    }

    public void startDownload(String imageUrl) {
        ((DownloadServiceBinder) DownloadService.this.binder).iBinderView.downloadStart();
        new DownloadImageAsyncTask(this).execute(imageUrl);
    }

    /**
     * 下载图片异步任务
     */
    public class DownloadImageAsyncTask extends AsyncTask<String, Integer, String> {

        private Service service;
        private String localFilePath;

        public DownloadImageAsyncTask(Service service) {
            super();
            this.service = service;
        }

        /**
         * 对应AsyncTask第一个参数
         * 异步操作,不在主UI线程中,不能对控件进行修改
         * 可以调用publishProgress方法中转到onProgressUpdate(这里完成了一个handler.sendMessage(...)的过程)
         *
         * @param params The parameters of the task.
         * @return A result, defined by the subclass of this task.
         * @see #onPreExecute()
         * @see #onPostExecute
         * @see #publishProgress
         */
        @Override
        protected String doInBackground(String... params) {
            URL fileUrl = null;
            try {
                fileUrl = new URL(params[0]);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
            if (fileUrl == null) return null;
            try {
                HttpURLConnection connection = (HttpURLConnection) fileUrl.openConnection();
                connection.setRequestMethod("GET");
                connection.setDoInput(true);
                connection.connect();

                //计算文件长度
                int lengthOfFile = connection.getContentLength();
                /**
                 * 不存在SD卡,就放到缓存文件夹内
                 */
                File cacheDir = this.service.getCacheDir();
                File downloadFile = new File(cacheDir, UUID.randomUUID().toString() + ".jpg");
                this.localFilePath = downloadFile.getPath();
                if (!downloadFile.exists()) {
                    File parent = downloadFile.getParentFile();
                    if (parent != null) parent.mkdirs();
                }
                FileOutputStream output = new FileOutputStream(downloadFile);
                InputStream input = connection.getInputStream();
                InputStream bitmapInput = connection.getInputStream();
                //下载
                byte[] buffer = new byte[1024];
                int len;
                long total = 0;
                // 计算进度
                while ((len = input.read(buffer)) > 0) {
                    total += len;
                    this.publishProgress((int) ((total * 100) / lengthOfFile));
                    output.write(buffer, 0, len);
                }
                output.close();
                input.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

        /**
         * 对应AsyncTask第三个参数 (接受doInBackground的返回值)
         * 在doInBackground方法执行结束之后在运行,此时已经回来主UI线程当中 能对UI控件进行修改
         *
         * @param string The result of the operation computed by {@link #doInBackground}.
         * @see #onPreExecute
         * @see #doInBackground
         * @see #onCancelled(Object)
         */
        @Override
        protected void onPostExecute(String string) {
            super.onPostExecute(string);
            ((DownloadServiceBinder) DownloadService.this.binder).iBinderView.downloadSuccess(this.localFilePath);
        }

        /**
         * 对应AsyncTask第二个参数
         * 在doInBackground方法当中,每次调用publishProgress方法都会中转(handler.sendMessage(...))到onProgressUpdate
         * 在主UI线程中,可以对控件进行修改
         *
         * @param values The values indicating progress.
         * @see #publishProgress
         * @see #doInBackground
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
        }

        /**
         * 运行在主UI线程中,此时是预执行状态,下一步是doInBackground
         *
         * @see #onPostExecute
         * @see #doInBackground
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        /**
         * <p>Applications should preferably override {@link #onCancelled(Object)}.
         * This method is invoked by the default implementation of
         * {@link #onCancelled(Object)}.</p>
         * <p/>
         * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
         * {@link #doInBackground(Object[])} has finished.</p>
         *
         * @see #onCancelled(Object)
         * @see #cancel(boolean)
         * @see #isCancelled()
         */
        @Override
        protected void onCancelled() {
            super.onCancelled();
            ((DownloadServiceBinder) DownloadService.this.binder).iBinderView.downloadFailure();
        }

    }

}

DownloadServiceActivity

public class DownloadServiceActivity extends AppCompatActivity implements View.OnClickListener, IBinderView {

    private static final String OBJECT_IMAGE_URL = "https://img-blog.csdn.net/20150913233900119";

    private Button startBT;
    private ImageView imageIV;
    private DownloadService service;

    private ServiceConnection connection;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.activity_download_service);
        this.initViews();
        this.initData();
        this.initListeners();
    }

    private void initViews() {
        TextView imageTV = (TextView) this.findViewById(R.id.image_tv);
        imageTV.setText(OBJECT_IMAGE_URL);

        this.startBT = (Button) this.findViewById(R.id.start_service_bt);
        this.imageIV = (ImageView) this.findViewById(R.id.image_iv);
    }

    private void initData() {
        this.connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                DownloadService.DownloadServiceBinder binder = (DownloadService.DownloadServiceBinder) service;
                binder.iBinderView = DownloadServiceActivity.this;
                DownloadServiceActivity.this.service = binder.getService();
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                DownloadServiceActivity.this.service = null;
            }
        };

        DownloadServiceActivity.this.bindService(
                new Intent(DownloadServiceActivity.this, DownloadService.class),
                DownloadServiceActivity.this.connection,
                Context.BIND_AUTO_CREATE
        );
    }

    private void initListeners() {
        this.startBT.setOnClickListener(this);
    }

    /**
     * Called when a view has been clicked.
     *
     * @param v The view that was clicked.
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start_service_bt:
                this.service.startDownload(OBJECT_IMAGE_URL);
                break;
        }
    }

    /**
     * 开始下载
     */
    @Override
    public void downloadStart() {
        this.startBT.setEnabled(false);
    }

    /**
     * 下载成功
     *
     * @param imageFilePath
     */
    @Override
    public void downloadSuccess(String imageFilePath) {
        /**
         * 设置按钮可用,并隐藏Dialog
         */
        this.startBT.setEnabled(true);
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        int screenWidth = metrics.widthPixels;
        int screenHeight = metrics.heightPixels;
        /**
         * ImageUtil.decodeScaleImage 解析图片
         */
        Bitmap bitmap = ImageUtil.decodeScaleImage(imageFilePath, screenWidth, screenHeight);
        DownloadServiceActivity.this.imageIV.setImageBitmap(bitmap);
    }

    /**
     * 下载失败
     */
    @Override
    public void downloadFailure() {
        this.startBT.setEnabled(true);
    }

}

AndroidManifest.xml

<service android:name="com.camnter.newlife.service.DownloadService" />

Remote Service 实现

这里就需要写一个AIDL文件,提供给Activity与Remote Service通讯。

IPushMessage.aidl

interface IPushMessage {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    String onMessage();

}

写完AIDL文件后,此时,会自动生成一个 IPushMessage 接口的代码。

IPushMessage

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/CaMnter/GitHub/AndroidLife/app/src/main/aidl/com/camnter/newlife/aidl/IPushMessage.aidl
 */
package com.camnter.newlife.aidl;
// Declare any non-default types here with import statements

public interface IPushMessage extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.camnter.newlife.aidl.IPushMessage {
        private static final java.lang.String DESCRIPTOR = "com.camnter.newlife.aidl.IPushMessage";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.camnter.newlife.aidl.IPushMessage interface,
         * generating a proxy if needed.
         */
        public static com.camnter.newlife.aidl.IPushMessage asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.camnter.newlife.aidl.IPushMessage))) {
                return ((com.camnter.newlife.aidl.IPushMessage) iin);
            }
            return new com.camnter.newlife.aidl.IPushMessage.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_basicTypes: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0 != data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_onMessage: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.onMessage();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.camnter.newlife.aidl.IPushMessage {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean) ? (1) : (0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public java.lang.String onMessage() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_onMessage, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_onMessage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;

    public java.lang.String onMessage() throws android.os.RemoteException;
}

可以看到:

  • 1.生成了一个静态抽象类 Stub ,用于提供给Service实现 Service 的 IBinder,然后返回给ServiceConnection。

  • 2.生成了抽象类 Stub 的一个静态内部类 Proxy ,作为AIDL代理服务类,进行远程通信。

  • 3.生成了AIDL文件中自定义的方法(basicTypes、onMessage)。

注意!!!!!!,在Service的实现中,要实现自动生成的 IPushMessage 接口中的静态抽象类 Stub ,以此作为 Service 的 Binder 对象,进行通信。

PushMessageService

public class PushMessageService extends Service {

    private static final String TAG = "MessageService";

    private IPushMessageImpl binder;

    /**
     * AIDL implement
     * 实现AIDL生成静态抽象类 IPushMessage.Stub
     */
    private class IPushMessageImpl extends IPushMessage.Stub {
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         *
         * @param anInt
         * @param aLong
         * @param aBoolean
         * @param aFloat
         * @param aDouble
         * @param aString
         */
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public String onMessage() throws RemoteException {
            return UUID.randomUUID().toString();
        }

    }

    /**
     * Called by the system when the service is first created.  Do not call this method directly.
     */
    @Override
    public void onCreate() {
        super.onCreate();
        this.binder = new IPushMessageImpl();
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return this.binder;
    }

}

当使用ServiceConnection桥接好Service 与 Activity成功后,我们能拿到IBinder service 对象。注意!!!!!!此时,的IBinder对象只是BinderProxy对象,不能直接转换为Binder对象,我们只能再调用AIDL生成类IPushMessage中的内部静态抽象类 Stub 的静态方法 public static com.camnter.newlife.aidl.IPushMessage asInterface(android.os.IBinder obj) 将其转为IPushMessage类型,以此来跟Service通信

AIDLActivity

public class AIDLActivity extends AppCompatActivity {

    private static final String TAG = "AIDLActivity";

    private TextView aidlTV;

    private String pushMessage;
    private IPushMessage iPushMessage;
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            /*
             * 这里的 service 不是 Binder对象
             * 而是 BinderProxy对象
             * 不能 直接转为Binder( (Binder)service ),是错误的。
             */
            AIDLActivity.this.iPushMessage = IPushMessage.Stub.asInterface(service);
            try {
                AIDLActivity.this.pushMessage = AIDLActivity.this.iPushMessage.onMessage();
                AIDLActivity.this.aidlTV.setText(AIDLActivity.this.pushMessage);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            AIDLActivity.this.iPushMessage = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.activity_aidl);
        this.aidlTV = (TextView) this.findViewById(R.id.aidl_tv);
        Intent intent = new Intent(this, PushMessageService.class);
        this.startService(intent);
        this.bindService(intent, this.connection, Context.BIND_AUTO_CREATE);
    }

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

}

AndroidManifest.xml

<!-- action 写上 aidl生成类的所在包 -->
<service
    android:name=".aidl.PushMessageService"
    android:process=":remote">
    <intent-filter>
        <action android:name="com.camnter.newlife.aidl.IPushMessage" />
    </intent-filter>
</service>

然后,可以看到Service作为单独进程存在了:

aidl_process

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章