Android四大组件之 Service

71 阅读4分钟

    }

    @Override    public void onDestroy() {        super.onDestroy();        this.threadDisable= true;

        Log.v("CountService","on destroy");

    }    public int getCount() {        return count;

    }

}

 需要将该服务注册到配置文件AndroidManifest.xml中,否则无法找到:

<manifest xmlns:android="schemas.android.com/apk/res/and…"    package="com.easymorse" android:versionCode="1" android:versionName="1.0">    <applicationandroid:icon="@drawable/icon" android:label="@string/app_name">        <activityandroid:name=".LocalServiceDemoActivity"            android:label="@string/app_name">            <intent-filter>                actionandroid:name\="android.intent.action.MAIN"/                categoryandroid:name\="android.intent.category.LAUNCHER"/            </intent-filter>        </activity>        serviceandroid:name\="CountService"/    </application>    uses-sdkandroid:minSdkVersion\="3"/

</manifest/>

在Activity中启动和关闭本地服务。

package com.easymorse;import android.app.Activity;import android.content.Intent;import android.os.Bundle;public class LocalServiceDemoActivityextends Activity {    /** Called when the activity is first created.*/    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);        this.startService(new Intent(this, CountService.class));

    }

    @Override    protectedvoid onDestroy() {        super.onDestroy();        this.stopService(new Intent(this, CountService.class));

    }

}

可通过日志查看到后台线程打印的计数内容。

编写本地服务和Activity交互的示例

上面的示例是通过startService和stopService启动关闭服务的。适用于服务和activity之间没有调用交互的情况。如果之间需要传递参数或者方法调用。需要使用bind和unbind方法。

具体做法是,服务类需要增加接口,比如ICountService,另外,服务类需要有一个内部类,这样可以方便访问外部类的封装数据,这个内部类需要继承Binder类并实现ICountService接口。还有,就是要实现Service的onBind方法,不能只传回一个null了。

这是新建立的接口代码:

package com.easymorse;public interface ICountService {    public abstract int getCount();

}

修改后的CountService代码:

package com.easymorse;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.util.Log;public class CountServiceextends Serviceimplements ICountService {    privateboolean threadDisable;    privateint count;    private ServiceBinder serviceBinder=new ServiceBinder();    public class ServiceBinderextends Binderimplements ICountService{

        @Override        publicint getCount() {            return count;

        }

    }

    @Override

    public IBinder onBind(Intent intent) {        return serviceBinder;

    }

    @Override    public void onCreate() {        super.onCreate();        new Thread(new Runnable() {

            @Override            publicvoid run() {                while (!threadDisable) {                    try {

                        Thread.sleep(1000);

                    } catch (InterruptedException e) {

                    }

                    count++;

                    Log.v("CountService","Count is " + count);

                }

            }

        }).start();

    }

    @Override    public void onDestroy() {        super.onDestroy();        this.threadDisable= true;

        Log.v("CountService","on destroy");

    }    /* (non-Javadoc)

     * @see com.easymorse.ICountService#getCount()     */    public int getCount() {        return count;

    }

}

服务的注册也要做改动,AndroidManifest.xml文件:

<manifest xmlns:android="schemas.android.com/apk/res/and…"    package="com.easymorse" android:versionCode="1" android:versionName="1.0">    <applicationandroid:icon="@drawable/icon" android:label="@string/app_name">        <activityandroid:name=".LocalServiceDemoActivity"            android:label="@string/app_name">            <intent-filter>                actionandroid:name\="android.intent.action.MAIN"/                categoryandroid:name\="android.intent.category.LAUNCHER"/            </intent-filter>        </activity>        serviceandroid:name\="CountService"\            <intent-filter>                actionandroid:name\="com.easymorse.CountService"/            </intent-filter>        </service>    </application>    uses-sdkandroid:minSdkVersion\="3"/

</manifest>

Acitity代码不再通过startSerivce和stopService启动关闭服务,另外,需要通过ServiceConnection的内部类实现来连接Service和Activity。

package com.easymorse;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.util.Log;public class LocalServiceDemoActivity extends Activity {    private ServiceConnection serviceConnection= new ServiceConnection() {

        @Override        publicvoid onServiceConnected(ComponentName name, IBinder service) {

            countService = (ICountService) service;

            Log.v("CountService","on serivce connected, count is"                    + countService.getCount());

        }

        @Override        publicvoid onServiceDisconnected(ComponentName name) {

            countService =null;

        }

    };    private ICountService countService;    /** Called when the activity is first created.*/    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);        this.bindService(new Intent("com.easymorse.CountService"),                this.serviceConnection, BIND_AUTO_CREATE);

    }

    @Override    protectedvoid onDestroy() {

          this.unbindService(serviceConnection);       

          super.onDestroy();      //注意先后    }

}

编写传递基本型数据的远程服务

上面的示例,可以扩展为,让其他应用程序复用该服务。这样的服务叫远程(remote)服务,实际上是进程间通信(RPC)。

这时需要使用android接口描述语言(AIDL)来定义远程服务的接口,而不是上述那样简单的java接口。扩展名为aidl而不是java。可用上面的ICountService改动而成ICountSerivde.aidl,eclipse会自动生成相关的java文件。

package com.easymorse;

interface ICountService {    int getCount();

}

编写服务(Service)类,稍有差别,主要在binder是通过远程获得的,需要通过桩(Stub)来获取。桩对象是远程对象的本地代理。

package com.easymorse;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;import android.util.Log;public class CountService extends Service {    privateboolean threadDisable;    privateint count;    private ICountService.Stub serviceBinder= new ICountService.Stub() {

        @Override        publicint getCount()throws RemoteException {            return count;

        }

    };

    @Override    public IBinder onBind(Intent intent) {        return serviceBinder;

    }

    @Override    public void onCreate() {        super.onCreate();        new Thread(new Runnable() {

            @Override            publicvoid run() {                while (!threadDisable) {                    try {

                        Thread.sleep(1000);

                    } catch (InterruptedException e) {

                    }

                    count++;

                    Log.v("CountService","Count is " + count);

                }

            }

        }).start();

    }

    @Override    public void onDestroy() {        super.onDestroy();        this.threadDisable= true;

        Log.v("CountService","on destroy");

    }

}

配置文件AndroidManifest.xml和上面的类似,没有区别。

在Activity中使用服务的差别不大,只需要对ServiceConnection中的调用远程服务的方法时,要捕获异常。

private ServiceConnection serviceConnection= new ServiceConnection() {

    @Override    public void onServiceConnected(ComponentName name, IBinder service) {

        countService = (ICountService) service;        try {

            Log.v("CountService","on serivce connected, count is"                    + countService.getCount());

        } catch (RemoteException e) {            thrownew RuntimeException(e);

        }

    }

    @Override    public void onServiceDisconnected(ComponentName name) {

        countService =null;

    }

};

这样就可以在同一个应用程序中使用远程服务的方式和自己定义的服务交互了。

如果是另外的应用程序使用远程服务,需要做的是复制上面的aidl文件和相应的包构到应用程序中,其他调用等都一样。

编写传递复杂数据类型的远程服务

远程服务往往不只是传递java基本数据类型。这时需要注意android的一些限制和规定:

  1. android支持String和CharSequence

  2. 如果需要在aidl中使用其他aidl接口类型,需要import,即使是在相同包结构下;

  3. android允许传递实现Parcelable接口的类,需要import;

  4. android支持集合接口类型List和Map,但是有一些限制,元素必须是基本型或者上述三种情况,不需要import集合接口类,但是需要对元素涉及到的类型import;

  5. 非基本数据类型,也不是String和CharSequence类型的,需要有方向指示,包括in、out和inout,in表示由客户端设置,out表示由服务端设置,inout是两者均可设置。

这里将前面的例子中返回的int数据改为复杂数据类型:

package com.easymorse;import android.os.Parcel;import android.os.Parcelable;public class CountBean implements Parcelable {    public static final Parcelable.Creator<CountBean> CREATOR = new Creator<CountBean>() {

        @Override        public CountBean createFromParcel(Parcel source) {

            CountBean bean =new CountBean();

            bean.count = source.readInt();            return bean;

        }

        @Override        public CountBean[] newArray(int size) {            returnnew CountBean[size];

        }

    };    public int count;

    @Override    public void writeToParcel(Parcel dest,int flags) {

        dest.writeInt(this.count);

    }

    @Override    public int describeContents() {        return0;

    }

}

然后,需要在相同包下建一个同名的aidl文件,用于android生成相应的辅助文件:

package com.easymorse;

parcelable CountBean;

这一步是android 1.5后的变化,无法通过adt生成aidl,也不能用一个比如全局的project.aidl文件,具体见:

www.anddev.org/viewtopic.p…

然后,需要在服务的aidl文件中修改如下:

package com.easymorse;import com.easymorse.CountBean;interface ICountService {

    CountBean getCount();

}