在 IntentService 中启动一个单独的线程

时间:2021-06-01 12:20:30

标签: java android android-intent broadcastreceiver android-broadcast

设置:

组件:

  • 应用 A 具有:

    1. web server 在自己的线程中运行(在服务中启动)。服务器将意图发送到应用 B

    2. 广播侦听器,用于侦听来自应用 B

      的广播事件
  • 应用 B 具有:

    1. 广播侦听器,用于侦听来自应用 A 的意图并将意图发送回它(带有额外的 (JSON) 负载)

执行流程:

  • app A 启动,它在 MainActivity 的一个单独线程中启动一个服务
  • 该服务然后启动一个在其自己的线程中运行的网络服务器,并且(该服务)继续在后台运行
  • 在收到请求时,网络服务器向应用 B 发送 Intent 并等待一段时间
  • 应用 B 发送响应
  • app A 中的广播接收器将 Intent Extras 写入文件
  • 网络服务器现在检查文件是否存在并相应地将响应发送回客户端

网络服务器在“常规”服务(WebService

    public class WebService extends Service {

    private static final String TAG = WebService.class.getSimpleName();

    private Looper serviceLooper;
    private ServiceHandler serviceHandler;

    private static final String extensionId = "";
    private static final String receiverServiceAppId = "com.cam.receiverservice";

    private static final String intentChannelsJsonKey = "Channels";

    // Handler that receives messages from the thread
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
            Log.d(TAG, "Created service handler");
        }

        @Override
        public void handleMessage(Message msg) {
            Log.d(TAG, "Received message");
        }
    }

    @Override
    public void onCreate() {
        Log.d(TAG, "Creating service");
        // Start up the thread running the service. Note that we create a
        // separate thread because the service normally runs in the process's
        // main thread, which we don't want to block. We also make it
        // background priority so CPU-intensive work doesn't disrupt our UI.
        HandlerThread thread = new HandlerThread("ServiceStartArguments",
                Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();

        // Get the HandlerThread's Looper and use it for our Handler
        serviceLooper = thread.getLooper();
        serviceHandler = new ServiceHandler(serviceLooper);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "Starting web server");
        WebServer webServer;
        int portNumber = 8080;
        try {
            // WEBSERVER STARTED HERE
            webServer = new WebServer(getApplicationContext(), portNumber);
        } catch (IOException e) {
            Log.d(TAG, "\nCouldn't start web server on port " + portNumber + "\n");
            e.printStackTrace();
        }
        Log.d(TAG, "Started web server");

        // For each start request, send a message to start a job and deliver the
        // start ID so we know which request we're stopping when we finish the job
        Message msg = serviceHandler.obtainMessage();
        msg.arg1 = startId;
        serviceHandler.sendMessage(msg);

        // If we get killed, after returning from here, restart
        return START_STICKY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "Binding service");
        // We don't provide binding, so return null
        return null;
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "Destroying service");
        Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
    }
    }

).

应用程序 A 中的广播接收器将从应用程序 B 接收的意图的额外内容写入文件:

public class AppABroadcastReceiver extends BroadcastReceiver {
    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public void onReceive(Context context, Intent intent) {
        // writes contents of intent to file
        handleReceiver(Constants.RECEIVER_TYPE.RESPONSE_RECEIVER, context, intent);
    }
}

服务启动的网络服务器:

public class WebServer extends NanoHTTPD {

    private static final String TAG = WebServer.class.getName();
    private static final int PORT_NO = 8080;
    private int portNumber = PORT_NO;
    private Context context;
    private String cwd;

    public WebServer(Context context) throws IOException {
        super(PORT_NO);
        this.context = context;
        start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
        cwd =  context.getApplicationInfo().dataDir;
        Log.d(TAG, "\nWeb server started at http://localhost:" + PORT_NO + "/ \n");
    }

    public WebServer(Context context, int portNumber) throws IOException {
        super(portNumber);
        this.context = context;
        start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
        cwd =  context.getApplicationInfo().dataDir;
        Log.d(TAG, "\nWeb server started at http://localhost:" + portNumber + "/ \n");
    }

    public int getPortNumber() {
        return portNumber;
    }

    private void sendIntent(String data) {
        Intent intent = new Intent();

        Log.d(TAG, "Creating intent to request data from app B");
        intent.setAction(APP_B_ACTION_NAME);
        intent.putExtra(REQ_KEY_NAME, data);
        context.sendBroadcast(intent);
        Log.d(TAG, "GET Data req intent sent");     
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public Response serve(IHTTPSession session) {  
        Map<String, String> params = session.getParms();

        if (params.get(REQ_KEY_NAME) != null) {
            // send intent to req data to app B
            sendIntent(Constants.REQ_TYPE.GET_USER_DATA, params.get(REQ_KEY_NAME));
            // wait for response
            sleepThread(3);
            // check file system for response
            String responseData = checkResponse(cwd);
            // check for possible timeouts/issues
            if (responseData == null || responseData.isEmpty()) {
                return newFixedLengthResponse(requestTimedOutJSON);
            } else {
                return newFixedLengthResponse(responseData);
            }
        }

        return newFixedLengthResponse(invalidRequestTypeJSON);
    }
}

网络服务器中的所有逻辑都发生在 server() 中。查看 ServiceIntentService 之间的区别,

Service vs IntentService in the Android platform

IntentService 将意图排队并按顺序处理它们:

https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/app/IntentService.java

<块引用>

是的,我评论得太早了。我正在做 onStartCommand 的工作 onHandleIntent-- 看起来 onStartCommand 是在 UI 线程上运行的, 但是会为 onHandleIntent 执行生成一个单独的线程

IntentService 调用它自己,在 onHandleIntent() 返回后,如果 没有更多的工作要做。

问题是:

如果将 Service 替换为 IntentService,网络服务器是否可以在 startService() 中启动,然后,它会检查到达 {{1} 的意图}} 来自广播接收器(这都在 app A 中)?

意图服务有一个服务器对象,它在 onHandleIntent() 中初始化并启动它。然后,当它在 startService() 中接收意图时,它会定期调用网络服务器对象上的 setter 方法。

换句话说,启动服务器是否算作一个单独的工作单元,必须由意图服务完成才能继续另一个工作单元(例如从广播接收器接收的意图)?

0 个答案:

没有答案