希望对您有所帮助,我刚刚在 github 上找到了您的解决方案。
有关详细信息和官方链接,请check this out,我认为这对您有帮助。
如果不是答案,请忽略这个答案。
我们使用 AsyncTask 来避免网络访问的 StrictMode 致命错误(查看参考资料)。 StrictMode 策略只是禁止我们影响 UI 线程。
/* AsyncTask class which manages connection with server app and is sending shutdown command.
*/
public class ShutdownAsyncTask extends AsyncTask<String, String, TCPClient> {
private static final String COMMAND = "shutdown -s" ;
private TCPClient tcpClient ;
private Handler mHandler ;
private static final String TAG = "ShutdownAsyncTask";
/**
* ShutdownAsyncTask constructor with handler passed as argument. The UI is updated via handler.
* In doInBackground(...) method, the handler is passed to TCPClient object.
* @param mHandler Handler object that is retrieved from MainActivity class and passed to TCPClient
* class for sending messages and updating UI.
*/
public ShutdownAsyncTask(Handler mHandler){
this.mHandler = mHandler;
}
/**
* Overriden method from AsyncTask class. There the TCPClient object is created.
* @param params From MainActivity class empty string is passed.
* @return TCPClient object for closing it in onPostExecute method.
*/
@Override
protected TCPClient doInBackground(String... params) {
Log.d(TAG, "In do in background");
try{
tcpClient = new TCPClient(mHandler,
COMMAND,
"192.168.1.1",
new TCPClient.MessageCallback() {
@Override
public void callbackMessageReceiver(String message) {
publishProgress(message);
}
});
}catch (NullPointerException e){
Log.d(TAG, "Caught null pointer exception");
e.printStackTrace();
}
tcpClient.run();
return null;
}
在这个 AsyncTask 中,我们正在创建 TCPClient 对象(解释如下)。在 TCPClient 构造函数中,我们传递了用于更改 UI 的 Handler 对象,COMMAND - 带有“shutdown -s”命令的字符串,用于关闭计算机,IP 号 - 服务器 IP 号;回调对象 - 当我们获得服务器响应时,回调方法 'messageCallbackReceiver' 正在启动 'publishProgress' 方法,它正在将进度发布到 'onProgressUpdate' AsyncTask 的方法。
/**
* Overriden method from AsyncTask class. Here we're checking if server answered properly.
* @param values If "restart" message came, the client is stopped and computer should be restarted.
* Otherwise "wrong" message is sent and 'Error' message is shown in UI.
*/
@Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
Log.d(TAG, "In progress update, values: " + values.toString());
if(values[0].equals("shutdown")){
tcpClient.sendMessage(COMMAND);
tcpClient.stopClient();
mHandler.sendEmptyMessageDelayed(MainActivity.SHUTDOWN, 2000);
}else{
tcpClient.sendMessage("wrong");
mHandler.sendEmptyMessageDelayed(MainActivity.ERROR, 2000);
tcpClient.stopClient();
}
}
收到正确的消息后,我们正在发送命令,或者如果我们收到错误的消息,我们正在发送消息“错误”并停止客户端。在此之后,我们将被转移到 'onPostExecute' 方法:
@Override
protected void onPostExecute(TCPClient result){
super.onPostExecute(result);
Log.d(TAG, "In on post execute");
if(result != null && result.isRunning()){
result.stopClient();
}
mHandler.sendEmptyMessageDelayed(MainActivity.SENT, 4000);
}
}
所以一步一步来:
->AsyncTask 正在创建 TCPClient 对象。
->在 TCPClient 构造函数中,我们传递 Handler、Command、IP 号和回调对象。
->当 TCPClient 开始连接时,它会向服务器发送消息“shutdown”。
->当我们从服务器接收消息时,回调将其传递给'onProgressUpdate'。
->如果收到的消息(来自服务器的响应)等于“关闭”,我们正在向服务器发送命令。
->发送后我们停止客户端,将我们转移到'onPostExecute'方法。
->同时,处理程序正在接收带有在 MainActivity 中定义的“msg.what”整数的空消息,它们负责更新 GUI。
小部件 UI 如何更新的示例:
mHandler = new Handler(){
public void handleMessage(Message msg) {
switch(msg.what){
case SHUTDOWN:
Log.d(mTag, "In Handler's shutdown");
views = new RemoteViews(context.getPackageName(), R.layout.activity_main);
widget = new ComponentName(context, MainActivity.class);
awManager = AppWidgetManager.getInstance(context);
views.setTextViewText(R.id.state, "Shutting PC...");
awManager.updateAppWidget(widget,views);
break;
TCP客户端
这个类负责维护连接。我会一步一步解释:
在第一步中,我们可以看到从 ShutdownAsyncTask 和其他传递的对象。此外,我们还可以看到 sendMessage 和 stopClient 方法。
public class TCPClient {
private static final String TAG = "TCPClient" ;
private final Handler mHandler ;
private String ipNumber, incomingMessage, command;
BufferedReader in ;
PrintWriter out ;
private MessageCallback listener = null ;
private boolean mRun = false ;
/**
* TCPClient class constructor, which is created in AsyncTasks after the button click.
* @param mHandler Handler passed as an argument for updating the UI with sent messages
* @param command Command passed as an argument, e.g. "shutdown -r" for restarting computer
* @param ipNumber String retrieved from IpGetter class that is looking for ip number.
* @param listener Callback interface object
*/
public TCPClient(Handler mHandler, String command, String ipNumber, MessageCallback listener) {
this.listener = listener;
this.ipNumber = ipNumber;
this.command = command ;
this.mHandler = mHandler;
}
/**
* Public method for sending the message via OutputStream object.
* @param message Message passed as an argument and sent via OutputStream object.
*/
public void sendMessage(String message){
if (out != null && !out.checkError()) {
out.println(message);
out.flush();
mHandler.sendEmptyMessageDelayed(MainActivity.SENDING, 1000);
Log.d(TAG, "Sent Message: " + message);
}
}
/**
* Public method for stopping the TCPClient object ( and finalizing it after that ) from AsyncTask
*/
public void stopClient(){
Log.d(TAG, "Client stopped!");
mRun = false;
}
神奇的地方发生在 'run()' 方法中。在这里,我们使用“try-catch”工具来处理异常(服务器未启用,ip 不正确等)。正如你在下面看到的,我们有无限的 while() 循环来监听传入的消息。我们可以简单地停止它并使用 'stopClient()' 方法完成(用于 ShutdownAsyncTask 的 'onProgressUpdate' 方法)
public void run() {
mRun = true;
try {
// Creating InetAddress object from ipNumber passed via constructor from IpGetter class.
InetAddress serverAddress = InetAddress.getByName(ipNumber);
Log.d(TAG, "Connecting...");
/**
* Sending empty message with static int value from MainActivity
* to update UI ( 'Connecting...' ).
*
* @see com.example.turnmeoff.MainActivity.CONNECTING
*/
mHandler.sendEmptyMessageDelayed(MainActivity.CONNECTING,1000);
/**
* Here the socket is created with hardcoded port.
* Also the port is given in IpGetter class.
*
* @see com.example.turnmeoff.IpGetter
*/
Socket socket = new Socket(serverAddress, 4444);
try {
// Create PrintWriter object for sending messages to server.
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
//Create BufferedReader object for receiving messages from server.
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
Log.d(TAG, "In/Out created");
//Sending message with command specified by AsyncTask
this.sendMessage(command);
//
mHandler.sendEmptyMessageDelayed(MainActivity.SENDING,2000);
//Listen for the incoming messages while mRun = true
while (mRun) {
incomingMessage = in.readLine();
if (incomingMessage != null && listener != null) {
/**
* Incoming message is passed to MessageCallback object.
* Next it is retrieved by AsyncTask and passed to onPublishProgress method.
*
*/
listener.callbackMessageReceiver(incomingMessage);
}
incomingMessage = null;
}
Log.d(TAG, "Received Message: " +incomingMessage);
} catch (Exception e) {
Log.d(TAG, "Error", e);
mHandler.sendEmptyMessageDelayed(MainActivity.ERROR, 2000);
} finally {
out.flush();
out.close();
in.close();
socket.close();
mHandler.sendEmptyMessageDelayed(MainActivity.SENT, 3000);
Log.d(TAG, "Socket Closed");
}
} catch (Exception e) {
Log.d(TAG, "Error", e);
mHandler.sendEmptyMessageDelayed(MainActivity.ERROR, 2000);
}
}
客户端中的最后一件事是回调接口。我们把它放在最后的 TCPClient 类中:
/**
* Callback Interface for sending received messages to 'onPublishProgress' method in AsyncTask.
*
*/
public interface MessageCallback {
/**
* Method overriden in AsyncTask 'doInBackground' method while creating the TCPClient object.
* @param message Received message from server app.
*/
public void callbackMessageReceiver(String message);
}