以前写了一篇文章是关于同一个应用中有一个Activity 和 一个 Service,然后在AndroidManifest.xml文件中 将service的
android:process设置为":remote" , 这里要强调一下带“:”和不带“:”时的属性, android:process=":remote",代表在应用
程序里,当需要该service时,会自动创建新的进程,意思就是说你配的service会在另外一个进程中运行,而如果是
android:process="remote",没有“:”分号的,则创建全局进程,不同的应用程序共享该进程。
上次虽然同一个应用中实现了Activity和Service的夸进程通信,但是感觉不爽,毕竟是在同一应用中,这次实现一个两个应用中的跨进程使用aidl通信。
1 首先在创建一个应用工程MyApplication4,作用就是创建一个继承自Service的一个服务类,然后再创建一个aidl 文件。
Myservice.java
package com.example.lsw.myapplication;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
Log.d(MainActivity.TAG,"onBind");
// TODO: Return the communication channel to the service.
return new MyBinder();
}
@Override
public void onCreate() {
Log.d(MainActivity.TAG,"onCreate");
super.onCreate();
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(MainActivity.TAG,"onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.d(MainActivity.TAG,"onDestroy");
super.onDestroy();
}
public class MyBinder extends IMyAidlInterface.Stub{
@Override
public String getMusicName() throws RemoteException {
return "明天会更好";
}
}
}
这里也强调一个知识点,启动Service一共有两种方法,一个是StartService,这种启动方式一般启动的Service和当前启动它的组件就没有任何关系了,和这个组件处于不同的进程了。 另一种启动方式就是bindService这种方法,这种启动方式Service和绑定它的组件共存亡。(具体情况大家可以去查看service的启动方式)。
IMyAidlInterface.aidl
// IMyAidlInterface.aidl
package com.example.lsw.myapplication;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
String getMusicName();
}
这里就是定义一个接口,在MyService中定义一个class 去继承这个接口然后,实现这个接口的方法,之后再通过onBind()方法将这个继承接口类的对象返回给绑定的应用中,实现可以访问到MyService内部方法的机制。
AndroidManifest.xml
这里强调一下属性 android:exported="true"表示这个进程中的MyService服务允许外接访问。
可以看到这了的aidl包名为 com/example/lsw/myapplication
2 Client 端
(1) 重新创建一个应用工程MyApplication5,主要任务就是在Mainactivity.java中点击button1去绑定MyApplication4中的MyService,然后点击button2调用 aidl中的getMusicName()方法。
Mainctivity.java
package com.example.lisiwei.myapplication;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.example.lsw.myapplication.IMyAidlInterface;
public class MainActivity extends AppCompatActivity {
private Button m_startButton = null;
private Button m_getMusicButton = null;
private ServiceConnection m_serviceConnection = null;
private IMyAidlInterface m_iMyAidlInterface = null;
private Context m_context = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
m_context = this;
m_startButton = findViewById(R.id.startButton);
m_getMusicButton = findViewById(R.id.getMusicButton);
m_serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("lisiwei","onServiceConnected");
if (null == m_iMyAidlInterface) {
m_iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
m_startButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("lisiwei","m_startButton setOnClickListener");
Intent intent = new Intent();
// 这里注意这两个参数第一个是Myservice的包名也就是application4的包名,第二个是包名+类型,
intent.setComponent(new ComponentName("com.example.lsw.myapplication","com.example.lsw.myapplication.MyService"));
bindService(intent,m_serviceConnection,Context.BIND_AUTO_CREATE);
}
});
m_getMusicButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("lisiwei","m_getMusicButton setOnClickListener + " + getMusicName());
Toast.makeText(m_context,getMusicName(),Toast.LENGTH_LONG).show();
}
});
}
private String getMusicName() {
String name = null;
if ( null != m_iMyAidlInterface) {
try {
name = m_iMyAidlInterface.getMusicName();
} catch (RemoteException e) {
e.printStackTrace();
}
}
return name;
}
}
这里需要强调4个点:
1 因为我们需要去绑定application4中的service就涉及到了Intent启动service,自从Android 5.0之后就不允许隐式启动intent 启动service了,只能显示intent启动service。
2 就是比较坑的一个地方,这里感谢同事的指导,就是要将application4中的aidl文件copy到application5中,而且两个应用中的aidl要处于同一个目录下。
3 这个又是一个坑逼的地方,我要通过application5(client)去绑定application4中service,结果必须要先启动application4的工程,注意这里只是启动了工程,并没有启动application4中的service,service还是由application5中点击button来绑定。你可以看log,是点击button之后application4中的service还是第一次走onCreate方法,那就证明application4只是启动了它自身的工程,并没有启动服务,服务还是由application5绑定之后启动的。
4 为什么不在application5中启动Myservice的时候又通过m_iMyAidlInterface去调用getMusicName()方法那?因为绑定是一个异步执行的过程,如果绑定之后立马去使用m_iMyAidlInterface这个时候m_iMyAidlInterface还可能是null。
这里这个项目的路径名和application4不一样,但是aidl的路径名一样 都为com/example/lsw/myapplication
具体代码路径: https://download.csdn.net/download/lisiwei1994/10946447