Android中远程Service和Activity交互
2017-04-19 22:01
1166 查看
Android四大组件之二: Activity和Service, 两者在很多情况下, 不是分裂的,那么,它们如何联系起来呢?通常使用IBinder两者建立关联, 方法如下:
为了描述两者的通讯方式, 我们需要建立两个App, 一个为server, 另一个为client。 一个App内包含server和client的这种本地service我们就不描述, 没有什么挑战。
1. 使用Android studio来建立一个名为AIDLServer的Project
2. 为AIDLServer增加一个AIDL文件。 里面的内容为:
// IMyAidlInterface.aidl
package com.zltz.www.aidlserver;
// 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.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
int getPid();
}
其实我们就是增加了一个获取pid的方法getPid()
3. 新建一个MyService.java的文件, 用于描述Service, 内容如下:
package com.zltz.www.aidlserver;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Process;
import android.util.Log;
public class MyService extends Service {
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return (IBinder)mBinder;
}
private IMyAidlInterface mBinder = new IMyAidlInterface.Stub()
{
public int getPid()
{
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString)
{}
};
}
里面创建了Binder, 实现了getPid()方法, 这个就是一般的写法。 到这里server部分的代码就写完了。
4. 然后我们需要配置manifest文件了:
这里因为我们是两个不同的进程, 所以使用了android:process来配置成远程进程, 注意:remote前面的冒号的描述, 这是一个知识点。
5. 到这里, 我们的Server端就完成了。 下面要开始client段的编写了。我们需要使用两个按钮和一个textview,一个按钮用于绑定远程的service, 一个用于执行远程service的方法, textview则是用于显示获取到的pid。 layout文件见下:
里面使用到来了两个字符串, 需要在values文件下的strings.xml文件中添加。 见下:
6. 要使用远程service, 那么得知道远程的aidl的定义呀。 所以需要定义一个和远程service的aidl一样的aidl, 你可以重新写一边或者直接从server中拷贝过来。 记得包名要和server中的一致, 否则会出现错误。 见下:
7. 现在, 就差client中的绑定service, 执行service方法的操作了。 见下:
这里需要主要bindService的操作, 我在这里耽误了一个多小时,原来是android5.0后修改了绑定的处理, 网上代码大部分是android4.4或以前的, 直接使用就会出现异常。
8. 至此,我们就完成了一个远程service和Activity的交互的例子了。
9. 简单总结一下:
新建一个AIDL描述文件, 一个Service,在Service中生产Binder, 等待绑定
在需要使用Service的App中新建一个和Service一样的AIDL描述文件。 然后在App中启动绑定, 获取到Binder后,就可以根据AIDL里面给出的方法来操作了。
主要点有几个, 一个是BindService的方法使用, 需要制定Package名, 这个是android5.0后的变化; 一个是Server和Client中的AIDL描述要一致;另外一个就是server中的service要使用android:process来指定为remote, 否则就为本地service(没有跨进程这个说法了)
为了描述两者的通讯方式, 我们需要建立两个App, 一个为server, 另一个为client。 一个App内包含server和client的这种本地service我们就不描述, 没有什么挑战。
1. 使用Android studio来建立一个名为AIDLServer的Project
2. 为AIDLServer增加一个AIDL文件。 里面的内容为:
// IMyAidlInterface.aidl
package com.zltz.www.aidlserver;
// 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.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
int getPid();
}
其实我们就是增加了一个获取pid的方法getPid()
3. 新建一个MyService.java的文件, 用于描述Service, 内容如下:
package com.zltz.www.aidlserver;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Process;
import android.util.Log;
public class MyService extends Service {
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return (IBinder)mBinder;
}
private IMyAidlInterface mBinder = new IMyAidlInterface.Stub()
{
public int getPid()
{
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString)
{}
};
}
里面创建了Binder, 实现了getPid()方法, 这个就是一般的写法。 到这里server部分的代码就写完了。
4. 然后我们需要配置manifest文件了:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.zltz.www.aidlserver"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <service android:name="com.zltz.www.aidlserver.MyService" android:enabled="true" android:exported="true" android:process=":remote" > <!-- 加:和不加:还是有区别。加:表示会创建一个私有的进程; 直接小写字母开头表示全局进程 --> <intent-filter> <action android:name="com.zltz.www.aidlserver.IMyAidlInterface" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service> </application> </manifest>
这里因为我们是两个不同的进程, 所以使用了android:process来配置成远程进程, 注意:remote前面的冒号的描述, 这是一个知识点。
5. 到这里, 我们的Server端就完成了。 下面要开始client段的编写了。我们需要使用两个按钮和一个textview,一个按钮用于绑定远程的service, 一个用于执行远程service的方法, textview则是用于显示获取到的pid。 layout文件见下:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.zltz.www.aidlclient.MainActivity"> <TextView android:id="@+id/tv_pid" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/bind_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/bind_name" android:onClick="BindService"/> <Button app:layout_constraintLeft_toRightOf="@+id/bind_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/btn_name" android:onClick="onGetPid"/> </android.support.constraint.ConstraintLayout>
里面使用到来了两个字符串, 需要在values文件下的strings.xml文件中添加。 见下:
<resources> <string name="app_name">AIDLClient</string> <string name="bind_name">Bind Service</string> <string name="btn_name">Get Pid</string> </resources>
6. 要使用远程service, 那么得知道远程的aidl的定义呀。 所以需要定义一个和远程service的aidl一样的aidl, 你可以重新写一边或者直接从server中拷贝过来。 记得包名要和server中的一致, 否则会出现错误。 见下:
// IMyAidlInterface.aidl package com.zltz.www.aidlserver; // 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. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); int getPid(); }
7. 现在, 就差client中的绑定service, 执行service方法的操作了。 见下:
package com.zltz.www.aidlclient; 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.Process; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.TextView; import com.zltz.www.aidlserver.IMyAidlInterface; public class MainActivity extends AppCompatActivity { private IMyAidlInterface mBinder; private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView)findViewById(R.id.tv_pid); } ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { mBinder = IMyAidlInterface.Stub.asInterface(iBinder); } @Override public void onServiceDisconnected(ComponentName componentName) { } }; public void BindService(View view) { /* 如果为android 5.0前的代码, 那么使用 Intent intent = new Intent("com.zltz.www.aidlserver.IMyAidlInterface"); 即可, 但是5.0及后面的代码, 就需要修改了, 对intent设置Package即可 // 来自 http://blog.csdn.net/guolin_blog/article/details/9797169的评论部分 Google从Android 5.0之后就不再允许使用隐式的方式去启动和绑定一个Service了,如果采用隐式的方式就会抛出安全异常。 如果你看5.0之前的4.4的源码你就会发现validateServiceIntent这个方法里面有几句代码是注释掉的,注释掉的这部分代码 在5.0的时候被放开了,这部分代码就是禁止隐式启动Service的。可以看出其实Google早就想这么干了。 private void validateServiceIntent(Intent service) { if (service.getComponent() == null && service.getPackage() == null) { if (true || getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.KITKAT) { Log.w(TAG, "Implicit intents with startService are not safe: " + service + " " + Debug.getCallers(2, 3)); //IllegalArgumentException ex = new IllegalArgumentException( // "Service Intent must be explicit: " + service); //Log.e(TAG, "This will become an error", ex); //throw ex; } } } */ Intent intent = new Intent("com.zltz.www.aidlserver.IMyAidlInterface"); intent.setPackage("com.zltz.www.aidlserver"); //Intent intent = new Intent(IMyAidlInterface.class.getName()); bindService(intent, conn, Context.BIND_AUTO_CREATE); } public void onGetPid(View view) { try { tv.setText(String.valueOf("Remote Pid:" + mBinder.getPid()) + ", Cur Pid:" + Process.myPid()); }catch(RemoteException ex) { Log.e("Err", ex.getMessage()); } } }
这里需要主要bindService的操作, 我在这里耽误了一个多小时,原来是android5.0后修改了绑定的处理, 网上代码大部分是android4.4或以前的, 直接使用就会出现异常。
8. 至此,我们就完成了一个远程service和Activity的交互的例子了。
9. 简单总结一下:
新建一个AIDL描述文件, 一个Service,在Service中生产Binder, 等待绑定
在需要使用Service的App中新建一个和Service一样的AIDL描述文件。 然后在App中启动绑定, 获取到Binder后,就可以根据AIDL里面给出的方法来操作了。
主要点有几个, 一个是BindService的方法使用, 需要制定Package名, 这个是android5.0后的变化; 一个是Server和Client中的AIDL描述要一致;另外一个就是server中的service要使用android:process来指定为remote, 否则就为本地service(没有跨进程这个说法了)
相关文章推荐
- Android Service学习之AIDL, Parcelable和远程服务
- Android Service学习之AIDL, Parcelable和远程服务
- Android Service学习之AIDL, Parcelable和远程服务
- 一个简单的demo学习Android远程Service(AIDL的使用)
- 一个简单的demo学习Android远程Service(AIDL的使用)
- Android Service学习之AIDL, Parcelable和远程服务
- Android Service学习之AIDL, Parcelable和远程服务
- Android Service学习之AIDL, Parcelable和远程服务
- Android Service学习之AIDL, Parcelable和远程服务
- Android Service AIDL 远程调用服务之简单音乐播放实例
- Android Service学习之AIDL, Parcelable和远程服务
- Android Service学习之AIDL, Parcelable和远程服务
- Android Service学习之AIDL, Parcelable和远程服务
- Android Service学习之AIDL, Parcelable和远程服务
- Android Service学习之AIDL, Parcelable和远程服务
- Android开发学习笔记:Service的远程调用
- Android远程service aidl的用法
- Android Service学习之AIDL, Parcelable和远程服务
- Android Service学习之AIDL, Parcelable和远程服务