****
Androidバインドサービスの説明とメッセンジャーのクロスプロセス通信
**
Androidサービスはバックグラウンドで実行されるプログラムです。率直に言って、インターフェースはありません。ここで、バックグラウンドで実行することは、メインスレッド以外で実行することを意味しないことを強調したいと思います。IntentServiceを除いて、新しいサービス、通常のサービススレッドを開始しないでください。デフォルトでは、メインスレッドで実行されます。
サービスを開始するには、バインドと開始の2つの方法があります。2つの起動方法にはかなりの違いがあります。バインドがサービスをバインドするとき、バインドからサービスへの最後のプログラムがバインド解除を呼び出すまで、それ以外の場合、サービスは実行され続けることに注意してください。startserviceのstartメソッドについては、一度開始したら、自分でstopServiceを呼び出すか、サービス内でstopSelfを呼び出す必要があります。そうしないと、サービスが閉じられません。
また、サービスのライフサイクルにも注意を払う必要があります。この添付の写真は完全に理解できます。
onCreateメソッドは、サービスの開始時に1回だけ呼び出され、サービスを開始すると、onStartCommand()またはonBind()に直接移動することに注意してください。
さて、ナンセンスな話をやめましょう、デモを取りましょう:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private boolean hasBound;
private Button intent_Service;
private Button start_Service;
private Button bind_Service;
private Button messenger_Service;
//下面的handler和Messenger使用来进行跨进程通信的
private Handler handler=new Handler()
{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what==322)
{
Toast.makeText(getApplicationContext(),"receive message from server",Toast.LENGTH_SHORT).show();
}
}
};
private Messenger clientMessenger=new Messenger(handler);
private Messenger serverMessenger;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView()
{
intent_Service= (Button) findViewById(R.id.intent_service);
intent_Service.setOnClickListener(this);
start_Service= (Button) findViewById(R.id.start_service);
start_Service.setOnClickListener(this);
bind_Service=(Button)findViewById(R.id.bind_service);
bind_Service.setOnClickListener(this);
messenger_Service=(Button)findViewById(R.id.messenger_service);
messenger_Service.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch(v.getId())
{
case R.id.intent_service:
//启动IntentService
Intent intentService=new Intent(this,IntentTestService.class);
startService(intentService);
break;
case R.id.start_service:
//start调用普通Sservice
Intent startService=new Intent(this,NormalService.class);
startService(startService);
break;
case R.id.bind_service:
//bind调用service
Intent bindService=new Intent(this,NormalService.class);
if(bindService.resolveActivity(getPackageManager())!=null)
bindService(bindService,connection,BIND_AUTO_CREATE);
break;
//利用Messenger进行跨进程通信
case R.id.messenger_service:
if(!hasBound) {
Intent intent = new Intent("com.skateboard.serverservice.service.BIND");
intent.setPackage("com.skateboard.serverservice");
bindService(intent, messengerConnection, Context.BIND_AUTO_CREATE);
}
else
{
sendMessageToServier();
}
break;
}
}
private ServiceConnection messengerConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
hasBound=true;
serverMessenger=new Messenger(service);
Message message=new Message();
message.what=233;
message.replyTo=clientMessenger;
try {
serverMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
hasBound=false;
}
};
private ServiceConnection connection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
NormalService.NormalBinder binder= (NormalService.NormalBinder) service;
binder.bindMethod();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private void sendMessageToServier()
{
Message message=new Message();
message.what=233;
message.replyTo=clientMessenger;
try {
serverMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
IntentTestServiceは子IntentServiceを継承し、その関数は非常に単純です。「intentservicestart」の行を出力することです。さらに特別なのは、前述のように、このサービスが非メインスレッドで実行されることです。
public class IntentTestService extends IntentService {
public IntentTestService()
{
super("IntentTestService");
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public IntentTestService(String name) {
super(name);
}
@Override
protected void onHandleIntent(Intent intent) {
System.out.println("intent service start");
}
}
NormalServiceはstartService()で開始でき、bindServiceで開始することもできます。ここでは主にbindServiceのstartメソッドについて説明します。サービスにバインドするときに、onBindメソッドをコールバックしてIBinderクラスを返します。このクラス個人的には、サービスのメソッドを呼び出すプロキシモードのように感じます。bindserviceの場合、パラメータServiceConnectionを渡す必要があります。このオブジェクトには、2つのコールバックメソッドがあります。1つはublic void onServiceConnected(ComponentName name、IBinder service)1つはpublic void onServiceDisconnected(ComponentName name)で、onServiceConnectedのパラメーターserviceはonBindメソッドで返すIBinderであり、それを変換することで、対応するサービスのメソッドを呼び出すことができます。そこで、ここで内部クラスNormalBinderを作成し、それを使用して "" bindメソッド "を出力し、onBindメソッドで返します。これにより、MainActivityでこのNormalBinderを取得し、その内部メソッドを呼び出すことができます。
public class NormalService extends Service {
public NormalService() {
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("service start");
stopSelf();
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
NormalBinder normalBinder=new NormalBinder();
return normalBinder;
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("stop service");
}
public class NormalBinder extends Binder
{
public void bindMethod()
{
System.out.println("bind method");
}
}
}
このプロセスでは、回路図を描きたかったのですが、怠惰すぎたので、テキストのままにしておくことにしました。
プロセス間通信には2つの方法があります。1つはAIDLで、もう1つはMessengerを使用する方法です。これら2つの方法の違いは、AIDLがマルチスレッドであるのに対し、Messengerはシングルスレッドであるため、Messengerを使用してクロススレッドプロセス通信では、メッセージキューに一度に1つの要求しかありません。サーバーがクライアントにデータを送り返す必要がある場合は、ハンドラーのpublic void handleMessage(Message msg)メソッドでクライアントのメッセンジャーを取得する必要があることに注意してください。このメッセンジャーはメッセンジャーclientMessenger = msg.replyToです。これはクライアント上にあります。サーバーにメッセージを送信するときにメッセージに渡されるパラメーター。
public class ServerService extends Service {
private Handler handler=new Handler()
{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what==233)
{
Messenger clientMessenger=msg.replyTo;
Message message=new Message();
message.what=322;
try {
clientMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
};
private Messenger messenger=new Messenger(handler);
public ServerService()
{
}
@Override
public void onCreate() {
super.onCreate();
System.out.println("service create");
}
@Override
public IBinder onBind(Intent intent) {
System.out.println("bind service");
return messenger.getBinder();
}
}
次に、MainAcitivytがbindServiceリクエストを開始します(5.0以降、サービスを開くインテントは表示されたインテントである必要があるため、インテントには別のプログラムのパッケージ名に関する情報が含まれている必要があります)。onServiceConnectedinServiceConnectionメソッド内、返されたIBinderを介して対応するメッセンジャーを取得します
private ServiceConnection messengerConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
hasBound=true;
serverMessenger=new Messenger(service);
Message message=new Message();
message.what=233;
message.replyTo=clientMessenger;
try {
serverMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
hasBound=false;
}
};