设置:
组件:
应用 A 具有:
web server 在自己的线程中运行(在服务中启动)。服务器将意图发送到应用 B
广播侦听器,用于侦听来自应用 B
的广播事件应用 B 具有:
执行流程:
网络服务器在“常规”服务(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()
中。查看 Service
和 IntentService
之间的区别,
Service vs IntentService in the Android platform
IntentService
将意图排队并按顺序处理它们:
是的,我评论得太早了。我正在做 onStartCommand 的工作 onHandleIntent-- 看起来 onStartCommand 是在 UI 线程上运行的, 但是会为 onHandleIntent 执行生成一个单独的线程
IntentService 调用它自己,在 onHandleIntent() 返回后,如果 没有更多的工作要做。
问题是:
如果将 Service
替换为 IntentService
,网络服务器是否可以在 startService()
中启动,然后,它会检查到达 {{1} 的意图}} 来自广播接收器(这都在 app A 中)?
意图服务有一个服务器对象,它在 onHandleIntent()
中初始化并启动它。然后,当它在 startService()
中接收意图时,它会定期调用网络服务器对象上的 setter 方法。
换句话说,启动服务器是否算作一个单独的工作单元,必须由意图服务完成才能继续另一个工作单元(例如从广播接收器接收的意图)?