service的进一步学习和总结(aidl binder机制和进程间通讯)
一、服务的一些分类:
android service主要分系统服务和应用程序服务,系统服务,framwork提供的,而应用程序服务是用户自己定义的继承Service的服务。
应用程序服务又分为2类:本地服务 和 远程服务,他们的区别在于:创建的服务的客户端与所创建的服务是否在同一个进程中
本地服务:利用startService或者bindService创建出的服务,若该服务与创建服务的Activity在同一个进程中则称为本地服务。本地服务只在创建该服务的进程内部使用,该应用程序终止服务也跟着终止。
远程服务:远程服务与创建者不在同一个进程中,因此主程序终止后服务仍然可以运行。

二、本地服务的介绍:
用做个音乐播放器为例,为了让音乐在后台播放,我们一般创建一个播放音乐的服务,在activity里调用服务即可。
主要步骤:1、创建service,在里面实现音乐的播放、暂停等功能。
2、实现onbind返回的类,它继承binder类,主要是向activity抛出服务对象,供activity使用。
3、在activity里面实现ServiceConnection接口类,然后bindservice(intent,new MusicServiceConnection(), Context.BIND_AUTO_CREATE) 注意第三个参数是自动生成本地服务的标记,当待绑定的服务不存在时使用。当待绑定的服务还没运行起来,绑定前先由framwork生成服务,再进行绑定。
4、在onServiceConnected 初始化实现onbind返回的类,利用该对象可以利用getService获取绑定的服务对象来控制音乐的播放。
5、利用广播来更新点击通知栏的播放按钮后activity的控件的状态。
下面给出相应的demo:是音乐播放器的实现,在通知栏中点击播放暂停音乐与在activity中点击时候它们的控件状态都是一致的。
public class MyService extends Service {
public static MediaPlayer mediaPlayer;
public String path = "/data/data/com.example.user.myservicedemo/files/she-say.mp3";
private Notification notification;
private MediaSession mSession;
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.'
init();
return new MyBinder();
}
public static final int STATE_STOP = 0;
public static final int STATE_PAUSE = 1;
public static final int STATE_PLAY = 2;
public static int musicPlayerState = STATE_STOP;
public void init()
{
startNotification();
rigisterNotificationListener();
}
public void startMusic()
{
if(musicPlayerState == STATE_STOP) {
mediaPlayer = new MediaPlayer();
Uri myUri = Uri.parse(path);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
mediaPlayer.start();
musicPlayerState = STATE_PLAY;
startNotification();
}
public void pauseMusic()
{
if(mediaPlayer!=null&&mediaPlayer.isPlaying())
mediaPlayer.pause();
musicPlayerState = STATE_PAUSE;
startNotification();
}
class MyBinder extends Binder
{
MyService getService()
{
return MyService.this;
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void startNotification()
{
mSession = new MediaSession(getApplicationContext(), this.getClass().getName());
int play = musicPlayerState == STATE_PLAY?R.drawable.pause:R.drawable.play;
notification = new Notification.Builder(this)
.setContentTitle("Music")
.setContentText("她说")
.setSmallIcon(R.drawable.ab)
.addAction(play, "",PendingIntent.getBroadcast(this, 0,
new Intent().setAction("android.intent.action.paly"),0))
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.a))
.setStyle(new Notification.MediaStyle()
.setShowActionsInCompactView(0)
.setMediaSession(mSession.getSessionToken()))
.build();
startForeground(101,notification);
}
public void rigisterNotificationListener()
{
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction())
{
case "android.intent.action.paly":
if(musicPlayerState == STATE_PLAY) {
pauseMusic();
startNotification();
}
else
{
startMusic();
startNotification();
}
break;
default:
break;
}
}
};
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.intent.action.paly");
registerReceiver(broadcastReceiver ,intentFilter);
}
}
public class MainActivity extends AppCompatActivity {
private Button button;
private MyService myService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
public void init()
{
Intent intent = new Intent(this ,MyService.class);
bindService(intent,new MusicServiceConnection(), Context.BIND_AUTO_CREATE);
button = (Button) findViewById(R.id.button);
rigistMusicListner();
}
class MusicServiceConnection implements ServiceConnection
{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myService = ((MyService.MyBinder) service).getService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
public void start(View view) {
//当然这里的MyService.musicPlayerState 可以利用bind来抛出
if (MyService.musicPlayerState == MyService.STATE_STOP||MyService.musicPlayerState == MyService.STATE_PAUSE)
{
myService.startMusic();
}
else
{
myService.pauseMusic();
}
updateButton(button);
}
public void updateButton(View view)
{
if(view.getId() == R.id.button)
{
//button.getText() 是为play,但是下面用button.getText()=="play" ,第一次会返回false,==是判断2个字符串是否为同一对象,第一次的play是button初始化的时候就有这个对象,而第二次setText
//是在字符串常量区。
if (button.getText().equals("play"))
button.setText("pause");
else
button.setText("play");
}
}
//点击通知栏时候收到的广播在activity里更新控件状态
public void rigistMusicListner()
{
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction())
{
case "android.intent.action.paly":
updateButton(button);
break;
default:
break;
}
}
};
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.intent.action.paly");
registerReceiver(receiver,intentFilter);
}
}
三、远程服务的介绍:
调用远程服务,我和原来坐过的一样:
1、写远程服务并在mainfest内配置,并在该服务的进程包里添加一个aidl文件RemoteService,申明一些服务抛出的方法。(android studio 下点击project ,在main目录下)。
2、clean(或者 rebuild)下工程后(会在目录下生成相应的RemoteService.java文件(可以在服务里写Mybinder extends Stub 就会让你导包,然后选择本项目目录,然后在打开文件的地方点击邮件可找到生的java文件了),里面是java的接口RemoteService和子类Stub,通过Stub子类的asasInterface(binder)可以获取RemoteService的实例对象),在服务里写如onbind方法返回的类,该类继承RemoteServce.Stub。
3、将aidl文件拷贝到调用服务的activity的对应的project - main 目录下,aidl下的包名与远程服务所在的包名得一致。
4、在bind时候回调函数中利用asasInterface(service)获得实现接口的实例对象,利用该对象就可以调用服务里的方法对服务进行操作了。
其中遇到了个问题,在bindService时候i启动服务总是失败,经查资料发现android 5.0 后不能隐式的启动服务了,即在清单里定义intentfilter 里添加action,再利用Intent intent = new intent("") 这种形式绑定服务后就会曝出:
Service Intent must be explicit: Intent { act=com.example.user.myservicedemo.IMyAidlInterface } 的异常。
5、android 6.0中提到了新的方法,来实现进程间通讯,这个以后有时间也总结一下。
解决办法就是将Intent转化为显示的:
方法一:相对笔记麻烦,但是里面的一些方法值得学习下
在activity里面定义一个函数:
public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager(); //获得所有的应用
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0); //所有应用中查询服务,存入resolveInfo
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName; //获取包
String className = serviceInfo.serviceInfo.name; //获取类名(全路径)
ComponentName component = new ComponentName(packageName, className);
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
然后绑定服务的时候:
final Intent intent = new Intent();
intent.setAction("com.example.user.firstapp.FIRST_SERVICE");
final Intent eintent = new Intent(createExplicitFromImplicitIntent(this,intent));
bindService(eintent,conn, Service.BIND_AUTO_CREATE);
分析了下,其实不用那么麻烦三行代码就搞定了:
方法二:
Intent intent = new Intent("android.intent.action.remote");
ComponentName componentName =new ComponentName("com.example.user.myservicedemo" , "com.example.user.myservicedemo.RemoteService");
intent.setComponent(componentName);
这里需要注意的是componentName的构造函数的2个参数一个是包名、一个是类名。必须是全路径的。
浙公网安备 33010602011771号