– layout: post title: “android AIDL” description: category: android tags: [android, app] mathjax: chart: comments: false —

###1. AIDL

AIDL(Android Interface Definition Language)是一种接口定义语言,编译器通过*.aidl文件的描述信息生成符合通信协议的Java代码, AIDL可以处理多线程、多客户端并发访问

###2. 定义AIDL文件

package com.android.example;

Interface IRemoteService {
    
    String hello(String someone);

}

AIDL 会被翻译成对应的 java 文件, 例如, 在 Android Studio 中, 上述的 AIDL 文件会被翻译成 “app/build/generated/source/aidl/debug/com/android/example/IRemoteService.java”

###3. 实现service端

package com.example.service;
 
import com.example.android.IRemoteService;
 
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;  
import android.util.Log;  
   
public class DDService extends Service {

     private static final String TAG = "AIDLService";

    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("DDService onCreate........" + "Thread: " + Thread.currentThread().getName());
    }
    
    @Override
    public IBinder onBind(Intent arg0) {
        Log.i(TAG, "onBind() called");  
        return mStub;
    }


    @Override  
    public boolean onUnbind(Intent intent) {  
        Log.i(TAG, "onUnbind() called");  
        return true;  
    }  
 
    private final IRemoteService.Stub mStub = new IRemoteService.Stub() {
    
        public String hello(String someone) throws RemoteException {  
            Log.i(TAG, "hello() called");  
            return "hello, " + someone;  
        }  
    }; 
}    

然后在AndroidManifest 中添加如下配置

<service android:name=".AIDLService"
    android:exported="true">  
    <intent-filter>  
        <action android:name="android.intent.action.AIDLService" />  
             <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</service>  

AndroidManaifest.xml 里 Service 元素的常见选项

  • android:name service name
  • android:label 显示的服务名, 若不设置则显示为类名
  • android:icon service 的图标
  • android:permission 申明service的权限, 只有获取了该权限的应用才能链接此服务
  • android:process 表示该服务是否运行在另外一个进程
  • android:enabled 如果设置为true, 该service会默认被系统启动, 默认值为false
  • android:exported 表示该服务是否能被外部的应用或者组件连接, 默认为false, 则只能被同一应用程序/组件或者具有同一UID的应用程序才能连接该应用

###4. 实现client端

package com.example.client;  
  
import android.app.Activity;  
import android.content.ComponentName;  
import android.content.Context;  
import android.content.Intent;  
import android.content.ServiceConnection;  
import android.os.Bundle;  
import android.os.IBinder;  
import android.os.RemoteException;  
import android.util.Log;  
import android.view.View;  
import android.widget.Button;  
import android.widget.Toast;  
  
import com.example.android.IRemoteService;  
  
public class MainActivity extends Activity {  
  
    private Button bindBtn;  
    private Button helloBtn;  
    private Button unbindBtn;  
  
    private IRemoteService remoteService;  
    private ServiceConnection conn = new ServiceConnection() {  
  
        @Override  
        public void onServiceConnected(ComponentName name, IBinder service) {  
            Log.i("ServiceConnection", "onServiceConnected() called");  
            remoteService = IRemoteService.Stub.asInterface(service);  
        }  
  
        @Override  
        public void onServiceDisconnected(ComponentName name) {
            Log.i("ServiceConnection", "onServiceDisconnected() called");  
        }  
    };  
    
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
  
        bindBtn = (Button) findViewById(R.id.bindBtn);  
        bindBtn.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                Intent intent = new Intent("android.intent.action.AIDLService");
                intent.setPackage("com.example.service");
                bindService(intent, conn, Context.BIND_AUTO_CREATE);  
                  
                bindBtn.setEnabled(false);  
                greetBtn.setEnabled(true);  
                unbindBtn.setEnabled(true);  
            }  
        });  
  
        helloBtn = (Button) findViewById(R.id.helloBtn);  
        helloBtn.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                try {  
                    String retVal = remoteService.hello("AIDL");  
                    Toast.makeText(MainActivity.this, retVal, Toast.LENGTH_SHORT).show();  
                } catch (RemoteException e) {  
                    Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show();  
                }  
            }  
        });  
  
        unbindBtn = (Button) findViewById(R.id.unbindBtn);  
        unbindBtn.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                unbindService(conn);  
                  
                bindBtn.setEnabled(true);  
                greetBtn.setEnabled(false);  
                unbindBtn.setEnabled(false);  
            }  
        });  
}

###5. bindService() 与 startService()

service通过Context.startService()方法开始,通过Context.stopService()方法停止;也可以通过Service.stopSelf()方法或者Service.stopSelfResult()方法来停止自己。只要调用一次stopService()方法便可以停止服务,无论之前它被调用了多少次的启动服务方法

客户端建立一个与Service的连接,并使用此连接与Service进行通话,通过Context.bindService()方法来绑定服务,Context.unbindService()方法来关闭服务, 这个时候调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind->onDestroy相应退出, 多个客户端可以绑定同一个服务,如果Service还未被启动,bindService()方法可以启动服务, 但是只有通过startService()方法启动service服务时才会调用onStart()方法

如果一个service允许别人绑定,那么需要实现以下额外的方法:

  IBinder onBind(Intent intent)
  boolean onUnbind(Intent intent)
  void onRebind(Intent intent)

bindService() 与 startService()可以同时使用, 可以绑定到一个通过startService()启动的服务, 如一个intent想要播放音乐,通过startService() 方法启动后台播放音乐的service, 然后,也许用户想要操作播放器或者获取当前正在播放的乐曲的信息,一个activity就会通过bindService()建立一个到此service的连接. 这种情况下 stopService() 在全部的连接关闭后才会真正停止service