当我们要使用android的系统服务时,一般都是使用Context.getSystemService方法。例如我们要获取AudioManager,我们可以:
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
获取的服务,其实是在ServiceManager中注册的Binder服务,然后进行封装后,提供给用户。
可以看ContextImpl.java中的实现:
static { ...... // 将AudioManager加入SYSTEM_SERVICE_MAP中,调用getSystemService时, // 就会从SYSTEM_SERVICE_MAP得到AudioManager registerService(AUDIO_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return new AudioManager(ctx); }}); ...... }
AudioManager是对IAudioService的封装,实际操作都是使用IAudioService进行的,看AudioManager中的代码:
private static IAudioService getService() { if (sService != null) { return sService; } // 从ServiceManager中获取Binder IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); // 将Binder转化成IAudioService,方便调用 sService = IAudioService.Stub.asInterface(b); return sService; }
上面是android系统的使用方式。如果我们添加自己的服务,要如何做呢?
我们在eclipse中建3个测试工程:
1)MyServiceLib:这是个lib工程,需要在eclipse中勾选Is Library。后面的两个工程,都需要将MyServiceLib添加到Library中。
2) MyService: 用于在android开机时注册自定义服务进ServiceManager。因为ServiceManager被@hide隐藏了,所以要使用它需要自己手动添加sdk包,添加方式可参考http://my.oschina.net/u/262208/blog/379548。另外,添加服务,需要System用户,所以manifest文件中需要加上android:sharedUserId="android.uid.system", 并且要使用platform签名签名apk。
3)MyServiceTest:用于测试上面两个工程。
下面我们就来编码。
先在MyServiceLib工程中创建一个aidl文件,android编译工具会帮我们生成相应的java类,aidl文件如下
package com.test.lib; interface IMyService { void setValue(int val); int getValue(); }
定义了两个接口用于测试,setValue和getValue。
android编译工具会帮我们在gen目录下生成一个IMyService的java类。
2. 在MyService工程中创建MyService类, 这个类继承自IMyService.Stub,实现了setValue和getValue接口,这就是一个Service。
package com.test.myservice; import android.os.RemoteException; import com.test.lib.IMyService; public class MyService extends IMyService.Stub { private int value; @Override public void setValue(int val) throws RemoteException { this.value = val; } @Override public int getValue() throws RemoteException { return value; } }
下面我们将把它加入至ServiceManager中。
3. 在MyService工程中创建MyServiceApplication类
package com.test.myservice; import android.app.Application; import android.os.ServiceManager; public class MyServiceApplication extends Application{ @Override public void onCreate() { super.onCreate(); ServiceManager.addService("MYSERVICE", new MyService()); } }
这是一个Application,我们希望android系统启动时,就创建这个Application,在onCreate方法中,创建MyService类,并加入到ServiceManager中。因此,我需要修改下manifest文件
<application android:name=".MyServiceApplication" //指定Application为我们创建的MyServiceApplication android:allowBackup="true" android:icon="@drawable/ic_launcher" android:persistent="true" // 加上persistent=ture,ActivityManager创建的时候,就会创建该应用的进程,并调用MyServiceApplication的onCreate方法 android:label="@string/app_name" android:theme="@style/AppTheme" >
注意,这个应用需要system用户,并签名才可运行。
这样,服务端就好了,并且开机时,我们的服务就已经在ServiceManager中了。
4. 下面我们提供一个Manager类方便客户端使用。在MyServiceLib中创建MyManager类:
package com.test.lib; import android.os.RemoteException; import android.os.ServiceManager; public class MyManager { private static MyManager instance; private IMyService myservice; public static MyManager getInstance() { if (instance == null) { instance = new MyManager(); } return instance; } private MyManager() { // 从ServiceManager中获取服务 myservice = IMyService.Stub.asInterface(ServiceManager.getService("MYSERVICE")); } public void setValue(int value) throws RemoteException { myservice.setValue(value); } public int getValue() throws RemoteException { return myservice.getValue(); } }
5. 在MyServiceTest工程中进行测试
通过MyManager.getInstance()可以很方便的获取服务的Manager,对远程服务进行调用。我们创建一个Activity来使用MyManager
package com.test.client; import java.util.Random; import android.app.Activity; import android.content.Context; import android.media.AudioManager; import android.os.Bundle; import android.os.RemoteException; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.test.binder.client.R; import com.test.lib.MyManager; public class MainActivity extends Activity implements OnClickListener { MyManager myManager; Button btnSetValue; Button btnGetValue; TextView tvValue; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); setContentView(R.layout.activity_main); btnSetValue = (Button) findViewById(R.id.btn_set_value); btnGetValue = (Button) findViewById(R.id.btn_get_value); tvValue = (TextView) findViewById(R.id.tv_value); // 获取MyManager myManager = MyManager.getInstance(); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.btn_set_value: int value = new Random().nextInt(); try { myManager.setValue(value); Toast.makeText(this, "set value to "+value+ " success!", 0).show(); } catch (RemoteException e) { e.printStackTrace(); Toast.makeText(this, "set value fail!", 0).show(); } break; case R.id.btn_get_value: try { tvValue.setText("value:"+myManager.getValue()); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } break; default: break; } } }