Android AIDL: 服务到活动的通信

4
我尝试实现一种Android AIDL通信策略。我有一个Activity和一个Service,我的Activity可以成功地与我的Service进行通信,但反过来好像不起作用。
简而言之,由于Activity和Service运行在不同的进程中,它们无法通过IBinder接口共享任何数据。因此,onServiceConnected()方法接收到一个AIDL接口,这个接口是在Service端实现的,旨在被Activity端使用(调用)。我使用这个接口去注册另一个AIDL,这个新的AIDL是在Activity端实现的,并通过AIDL接口从Service端调用,它充当监听器。不幸的是,这个新AIDL的方法似乎没有被调用。
Service在AndroidManifest.xml中运行在自己的进程中,这得益于以下行:

AndroidManifest.xml

<service android:name=".DemoService" android:process=":DemoServiceProcess" />

我有两个AIDL文件,其中一个知道另一个。

IAidlActivity.aidl

package app.test.aidldemo;

interface IAidlActivity {
    void publish(int count);
}

IAidlService.aidl

package app.test.aidldemo;

import app.test.aidldemo.IAidlActivity;
interface IAidlService {
    void startCounter();
    void register(IAidlActivity activity);
}

这个服务实现了 onBind() 方法并运行一个负责增加计数器的 handler

DemoService.java

package app.test.aidldemo;
import [...]
public class DemoService extends Service
{
    protected IAidlActivity aidlActivity;

    @Override
    public IBinder onBind(Intent intent)
    {
        return new IAidlService.Stub() {
            @Override
            public void startCounter() {
                DemoService.this.startJob();
            }
            @Override
            public void register(IAidlActivity activity) {
                DemoService.this.aidlActivity = activity;
            }
        };
    }

    public void startJob() {
        final Handler handler = new Handler();
        handler.post(new Runnable() {
            protected int count = 0;
            @Override
            public void run() {
                if (count < 500) {
                    count++;  // increment counter
                    try {  // then publish it to view
                        DemoService.this.aidlActivity.publish(count);  // interface, implemented activity-side
                    } catch (RemoteException e) {}
                    handler.postDelayed(this, 2000);  // 2sec.
                }
            }
        });
    }
}

Activity 只包含一个 TextView。 它与 Service 开始绑定,并定时更新视图。 当调用 publish() 时,它也应该更新视图。 但这并没有发生。

MainActivity.java

package app.test.aidldemo;
import [...]
public class MainActivity extends Activity {
    protected TextView view;
    protected ServiceConnection connection;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        view = new TextView(this);
        setContentView(view);
        appendToView("Let's go!");

        connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                IAidlService aidlService = IAidlService.Stub.asInterface(service);
                appendToView("IAidlService accessed");
                IAidlActivity.Stub aidlActivity = new IAidlActivity.Stub() {
                    @Override
                    public void publish(int count) {
                        appendToView("*** Hey, new count is: " + count + "!! ***");
                    }
                };
                appendToView("IAidlActivity created");
                try {
                    aidlService.register(aidlActivity);
                    aidlService.startCounter();  // interface, implemented service-side
                }
                catch (RemoteException e) { appendToView(e.toString()); }
            }
            @Override
            public void onServiceDisconnected(ComponentName name) {}
        };

        Intent intent = new Intent(MainActivity.this, DemoService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    @Override
    public void onDestroy() {
        unbindService(connection);
        super.onDestroy();
    }

    public void appendToView(String text) {
        view.append(text + "\n");
    }
}

我也尝试了一些变化,比如:

  1. appendToView("*** Hey... 放进 runOnUiThread()
  2. 使用另一个 handler + postDelayed() 来延迟 bindService()

如果以上方法都不行,我的备选方案就是只使用 IAidlService 并在 Activity 端设置一个“观察者”来不断检查计数器的值。但我更想知道为什么它不能正常工作,以及正确使用 AIDL 的方法是什么。


请在 register() 方法内部调用 publish() 方法进行测试。 - pskink
@pskink 好的,没错。这个可以运行。调用是通过 Activity 进程 (通过 register()) 触发的,所以我并不感到意外。似乎当 Service 进程启动它时会出现问题。 - gabriel
startJob函数内添加一些Log.d语句并进行跟踪。 - pskink
好的,花了我一些时间。看起来在这个演示中run()没有运行。现在我正在使用new Handler(DemoService.this.getMainLooper())创建处理程序。所以run()现在可以运行了。但是当应用程序在前台时,视图仍然没有更新。我需要保持返回按钮并重新选择应用程序才能看到所有新条目!我尝试使用View.invalidate()但无济于事。 - gabriel
最终看起来我掌握了它。我将view.append()(从appendToView)包装在view.post(new Runnable() {...})中。如果它能够很好地与我的初始应用程序配合使用,我将关闭问题 - gabriel
很好,它起作用了。view.post(new Runnable() { view.append() })解决了问题。感谢@pskink帮助我坚持下去。 - gabriel
1个回答

1

摘要

需要更改2个语句。

DemoService.java

final Handler handler = new Handler(DemoService.this.getMainLooper());

MainActivity.java

public void appendToView(String text) {
   view.post(new Runnable() {
       @Override
       public void run() {
           view.append(text + "\n");
       }
    });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接