一、Remote
Remote(远程的)是Android系统提供的一种跨进程通信(IPC)机制,允许一个应用程序(或进程)调用另一个进程中的服务或UI元素。通过Remote机制,应用程序可以像访问本地服务或UI元素一样访问远程服务或UI元素,这为Android应用程序开发提供了更多的灵活性。
二、Remotely
Android提供了两种方式实现对Remote的远程访问,一种是通过AIDL(Android接口定义语言)生成Java接口的方式,另一种则是通过RemoteView实现。
三、Remote Control
RemoteView(远程视图)是一种特殊的View,使用它可以在一个进程中创建一个View,然后将该View在另一个进程中显示。RemoteView在设计上基本和普通View一致,能够支持大部分的View功能和属性,但是有一定的限制,例如:
- 不支持自定义的绘制代码 - 只支持一部分的View属性,如:layout_width、layout_height、background、padding等属性 - 只能访问远程进程中已经注册的布局文件或View ID - 只能更新布局中已存在的View(即不能动态增加Views)
四、Remote软件下载
在实际应用中,如果需要在一个应用程序中访问另一个应用程序中的RemoteView,我们需要在另一个应用程序中注册一个RemoteView布局文件或者View ID,并在需要显示该View的时候,通过RemoteViews类构造远程视图,然后通过AppWidgetManager来更新该远程视图。下面是一个注册RemoteView布局文件并展示的示例代码:
// 在另一个进程中的RemoteService中注册RemoteView布局 public class RemoteService extends Service { private static final int LAYOUT_ID = R.layout.remote_view_layout; @Override public IBinder onBind(Intent intent) { return new RemoteBinder(); } private class RemoteBinder extends IRemoteBinder.Stub() { @Override public RemoteViews getRemoteView() throws RemoteException { RemoteViews remoteViews = new RemoteViews(getApplicationContext().getPackageName(), LAYOUT_ID); // 设置点击事件 Intent intent = new Intent(getApplicationContext(), RemoteActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0); remoteViews.setOnClickPendingIntent(R.id.button, pendingIntent); return remoteViews; } } } // 在本地应用程序中通过RemoteViews获取远程视图并展示 public class LocalActivity extends Activity { private static final String PACKAGE_NAME = "com.example.remote"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); AppWidgetManager manager = AppWidgetManager.getInstance(this); RemoteViews remoteViews = new RemoteViews(PACKAGE_NAME, R.layout.remote_view_layout); ComponentName componentName = new ComponentName(PACKAGE_NAME, RemoteService.class.getName()); // 获取另一个进程中RemoteService返回的RemoteViews remoteViews = manager.getAppWidgetOptions(componentName).getBoolean(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, false) ? manager.createWidget(this, R.xml.widget_gadget) : remoteViews; // 更新本地View ViewGroup viewGroup = findViewById(R.id.container); viewGroup.addView(remoteViews.apply(this, viewGroup)); } }
五、Remove
除了RemoteView,在使用AIDL调用Remote服务时,我们也可以在Binder中返回RemoteView,这样就能在服务端创建一个View,然后在客户端中展示。
// 服务端实现 public class RemoteService extends Service { @Override public IBinder onBind(Intent intent) { return new RemoteBinder(); } private class RemoteBinder extends IRemoteBinder.Stub() { @Override public RemoteViews getRemoteView() throws RemoteException { // 创建一个RemoteView并返回 RemoteViews remoteViews = new RemoteViews(getApplicationContext().getPackageName(), R.layout.remote_view_layout); return remoteViews; } @Override public IBinder getRemoteBinder() throws RemoteException { // 返回带有远程View的Binder return new RemoteViewBinder(new RemoteViews(getApplicationContext().getPackageName(), R.layout.remote_view_layout)); } } // 返回带有远程View的Binder private class RemoteViewBinder extends IRemoteBinder.Stub() { RemoteViews remoteViews; public RemoteViewBinder(RemoteViews remoteViews) { this.remoteViews = remoteViews; } @Override public RemoteViews getRemoteView() throws RemoteException { return remoteViews; } } } // 客户端实现 public class LocalActivity extends Activity { IRemoteBinder remoteService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 绑定RemoteService服务 Intent intent = new Intent(this, RemoteService.class); bindService(intent, conn, Context.BIND_AUTO_CREATE); } private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { remoteService = IRemoteBinder.Stub.asInterface(service); try { // 获取带有远程View的Binder IRemoteBinder remoteBinder = remoteService.getRemoteBinder(); // 展示远程View RemoteViews remoteViews = remoteBinder.getRemoteView(); ViewGroup viewGroup = findViewById(R.id.container); viewGroup.addView(remoteViews.apply(getApplicationContext(), viewGroup)); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; }