公司需求: 参考悟空遥控器做下手机控制机顶盒的功能,手机端apk界面参考遥控器,机顶盒apk在设置里面加个开关,打开的时候运行个后台server,手机端发送数据机顶盒的server接收到后处理相关动作。
需求出来了怎么实现呢?首先应用程序的通信无非就是Socket与Http,其中Socket又可以用TCP和UDP,HTTP的话就衍生出很多方式,基础的HTTP GET和POST请求,然后就是WebService的SOAP。
在这些方式中,Socket当然是最基础的。因此先从Socket开始。
首先是服务器端:打开服务器,等待客户端链接并接收消息即KeyCode,响应相应事件;
然后是客户端:模拟遥控器的UI界面,搭好界面,为每个控件添加点击事件并赋值相应KeyCode,连接服务器,将KeyCode信息传递给服务器端。
完成之后需要在资源配置文件中添加网络权限:
<uses-permission android:name="android.permission.INTERNET"/>
所有完成之后,测试时发现响应按键时只能在当前应用内使用,当离开此应用界面时,会报无权限异常,这时需要在Manifest.xml中添加
最高权限:
android:sharedUserId="android.uid.system"
这时需要在SDK下使用mk文件编译:
Android.mk
ifneq ($(BOARD_USE_DEFAULT_APPINSTALL),false)
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := RemoteTV
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
endif
整个思路就是这样,下面上代码:
ClientActivity,这里用到了StrictMode(严苛模式),不懂的可以百度了解了解,在这里我只做了数字键和上下左右键的模拟,有需要的可以自己添加:
public class ClientActivity extends Activity implements OnClickListener {
private EditText mServerIp;
private EditText mServerPort;
private EventSender mEventSender;
@Override
protected void onCreate(Bundle savedInstanceState) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.penaltyLog()
.penaltyDeath()
.build());
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
mServerIp = (EditText) findViewById(R.id.cli_adr_edi);
mServerPort = (EditText) findViewById(R.id.cli_port_edi);
findViewById(R.id.cli_connect_btn).setOnClickListener(this);
findViewById(R.id.num_1).setOnClickListener(this);
findViewById(R.id.num_2).setOnClickListener(this);
findViewById(R.id.num_3).setOnClickListener(this);
findViewById(R.id.num_4).setOnClickListener(this);
findViewById(R.id.num_5).setOnClickListener(this);
findViewById(R.id.num_6).setOnClickListener(this);
findViewById(R.id.num_7).setOnClickListener(this);
findViewById(R.id.num_8).setOnClickListener(this);
findViewById(R.id.num_9).setOnClickListener(this);
findViewById(R.id.btn_up).setOnClickListener(this);
findViewById(R.id.btn_left).setOnClickListener(this);
findViewById(R.id.btn_right).setOnClickListener(this);
findViewById(R.id.btn_down).setOnClickListener(this);
}
@Override
public void onClick(View v) {
int code = -1;
switch (v.getId()) {
case R.id.cli_connect_btn:
if(mEventSender != null) mEventSender.close();
mEventSender = new EventSender(mServerIp.getText().toString(), Integer.valueOf(mServerPort.getText().toString()));
mEventSender.connect();
return;
case R.id.num_1:
code = KeyEvent.KEYCODE_1;
break;
case R.id.num_2:
code = KeyEvent.KEYCODE_2;
break;
case R.id.num_3:
code = KeyEvent.KEYCODE_3;
break;
case R.id.num_4:
code = KeyEvent.KEYCODE_4;
break;
case R.id.num_5:
code = KeyEvent.KEYCODE_5;
break;
case R.id.num_6:
code = KeyEvent.KEYCODE_6;
break;
case R.id.num_7:
code = KeyEvent.KEYCODE_7;
break;
case R.id.num_8:
code = KeyEvent.KEYCODE_8;
break;
case R.id.num_9:
code = KeyEvent.KEYCODE_9;
break;
case R.id.btn_up:
code=KeyEvent.KEYCODE_DPAD_UP;
break;
case R.id.btn_left:
code=KeyEvent.KEYCODE_DPAD_LEFT;
break;
case R.id.btn_down:
code=KeyEvent.KEYCODE_DPAD_DOWN;
break;
case R.id.btn_right:
code=KeyEvent.KEYCODE_DPAD_RIGHT;
break;
}
if(mEventSender == null) return;
mEventSender.println(createKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, code)));
}
private String createKeyEvent(KeyEvent event) {
JSONObject json = new JSONObject();
try {
json.put("Event", event.getKeyCode());
} catch (JSONException e) {
e.printStackTrace();
}
return json.toString();
}
public class EventSender {
private static final String TAG = "EventSender";
private Socket mSocket;
private String mDstAddress;
private int mDstPort;
private PrintWriter mPrintWriter;
private EventSender(String dstAddress, int dstPort){
Log.d(TAG, "EventSender");
mDstAddress = dstAddress;
mDstPort = dstPort;
mSocket = new Socket();
}
public void println(String str){
mPrintWriter.println(str);
}
public boolean close(){
Log.d(TAG, "close()");
if(mSocket != null){
try {
mSocket.close();
mSocket = null;
return true;
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
public boolean connect(){
Log.d(TAG, "connect()");
try {
if (mSocket != null && !mSocket.isConnected()) {
Log.d(TAG, "new InetSocketAddress()");
mSocket.connect(new InetSocketAddress(mDstAddress, mDstPort));
mPrintWriter = new PrintWriter(mSocket.getOutputStream(), true);
mSocket.setKeepAlive(true);
}
return true;
}catch (IOException e) {
e.printStackTrace();
}
return false;
}
}
}
服务器端:
public class ServerActivity extends Activity implements OnClickListener{
private static final String TAG = "ServerActivity";
private RemoteServer mRemoteServer;
private EditText mServerPort;
private TextView mServerRecEvent;
private int keycode;
private Instrumentation instrumentation;
@Override
protected void onCreate(Bundle savedInstanceState) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.penaltyLog()
.penaltyDeath()
.build());
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_server);
findViewById(R.id.ser_port_btn).setOnClickListener(this);
mServerPort = (EditText) findViewById(R.id.ser_port_edi);
mServerRecEvent = (TextView) findViewById(R.id.ser_recev_event);
}
@Override
public void onClick(View v) {
openServer(Integer.valueOf(mServerPort.getText().toString()));
}
public void openServer(int dstPort){
Log.d(TAG, "openServer() - dstPort:" + dstPort);
mServerRecEvent.setText("");
if (mRemoteServer == null) {
mRemoteServer = new RemoteServer(dstPort);
if(mRemoteServer.openServer()) {
mServerRecEvent.setText("服务启动成功!!!!/r/n");
}
Log.d(TAG, "openServer() - mRemoteServer:" + mRemoteServer);
}
}
private void onEvent(String line) {
Log.d(TAG, "line: " + line);
try {
JSONObject object=new JSONObject(line);
keycode = (int) object.get("Event");
Log.d(TAG, "keycode: " + keycode);
if (instrumentation==null){
instrumentation=new Instrumentation();
}
//通过KeyCode响应相应操作
instrumentation.sendKeyDownUpSync(keycode);
Log.d(TAG, "event key : " + keycode);
} catch (JSONException e) {
e.printStackTrace();
}
Message msg = Message.obtain();
msg.what = 0;
msg.obj = line;
mHandler.sendMessage(msg);
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
String text = mServerRecEvent.getText().toString();
mServerRecEvent.setText(text + String.valueOf(msg.obj));
}
};
class RemoteServer implements Runnable{
private static final String TAG = "RemoteServer";
private int mPort;
private ServerSocket mServerSocket;
private boolean mIsClose = false;
public boolean isClose(){
return mIsClose;
}
public boolean close(){
try {
mServerSocket.close();
mServerSocket = null;
mIsClose = true;
} catch (IOException e) {
e.printStackTrace();
}
return mIsClose;
}
public RemoteServer(int port){
Log.d(TAG, "RemoteServer()");
mPort = port;
}
public boolean openServer(){
try {
mServerSocket = new ServerSocket(mPort);
new Thread(this).start();
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
public void run() {
try {
while (!mIsClose) {
Socket socket = mServerSocket.accept();
new ServerThread(socket);
}
} catch (IOException e) {
}
}
private class ServerThread extends Thread {
private Socket client;
private BufferedReader in;
public ServerThread(Socket s) throws IOException {
client = s;
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
start();
}
public void run() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
try {
String line = in.readLine();
while (!mIsClose) {
if(line != null){
onEvent(line);
}
line = in.readLine();
}
Log.d(TAG, "--- See you, bye! ---");
client.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
MainActivity
public class MainActivity extends Activity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_client).setOnClickListener(this);
findViewById(R.id.btn_server).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_client:
startActivity(new Intent(this, ClientActivity.class));
break;
case R.id.btn_server:
startActivity(new Intent(this, ServerActivity.class));
break;
}
}
}
布局就不放了,有需要的可以看下实现过程,主要就是Socket通信,下面上界面,界面时测试界面,很丑,凑合着看吧:
首先在客户端和盒子上安装编译好的APP,在盒子端进入服务器端,输入端口号:
在手机端进入客户端,输入服务器端的IP地址和端口号:
注意这里,连接成功时没有做判断,点击连接服务后,如果没反应即连接成功了。
还有:盒子和手机一定要在同一个局域网下!!!客户端输入的端口号和服务器输入的端口号一定要一致。
代码下载地址:http://download.csdn.net/download/json_jerry/9955125
本来想0积分提供,不知道为什么最低需要1积分。