Socket 編程在Android手機(jī)調(diào)試過程詳解
昨天正式開始 Android 編程學(xué)習(xí)與實(shí)踐,由于 Android 模擬器在 WinXP 下一直未安裝成功,所在將閑置很久的 Android 手機(jī): 聯(lián)想 A750 找到用于調(diào)試。
A750 是 Android 版本是: 2.3.6,在手機(jī) 上打開 USB 調(diào)試功能后,就可以通過 USB 線與 PC 連接進(jìn)行調(diào)試了。
調(diào)試的主要功能是 Socket 通訊,手機(jī)做為服務(wù)器端。先用 PC 做為客戶端。后期的客戶端是車機(jī),車機(jī)的系統(tǒng)可能是 WinCE 或 Android。
開始之前,先了解了一下 Android 編程的基本知識(后附個人學(xué)習(xí)記錄的知識點(diǎn)),然后學(xué)習(xí)了關(guān)于 Socket 編程的知識。
其中 一個關(guān)鍵的知識點(diǎn)是線程與主進(jìn)程之間的消息傳遞機(jī)制,主要是線程中的消息傳遞到主進(jìn)程。例如:
mHandler.sendMessage(msg);
手機(jī)端代碼如下(XML就不提供了,很簡單,大家看圖就知識的):
/*
?* 通過 ?WIFI 網(wǎng)絡(luò)進(jìn)行 Socket 通訊成功, 手機(jī)的 IP: 172.25.103.4(隨個人網(wǎng)絡(luò)環(huán)境變化)
?* 測試使用 360 隨身 WIFI, PC 機(jī)的 IP: 172.25.103.1
?* */
package?com.jia.leozhengfirstapp;
import?java.io.IOException;
import?java.io.InputStream;
import?java.io.UnsupportedEncodingException;
import?java.net.ServerSocket;
import?java.net.Socket;
import?android.support.v7.app.ActionBarActivity;
import?android.annotation.SuppressLint;
import?android.os.Bundle;
import?android.os.Handler;
import?android.os.Message;
import?android.util.Log;
import?android.view.Menu;
import?android.view.MenuItem;
import?android.widget.TextView;
import?android.widget.Toast;
public?class?MainActivity?extends?ActionBarActivity?{
private?Socket?clientSocket?=?null;
private?ServerSocket?mServerSocket?=?null;
private?Handler?mHandler?=?null;
private?AcceptThread?mAcceptThread?=?null;
private?ReceiveThread?mReceiveThread?=?null;
private?boolean?stop?=?true;
private?TextView?ipText;
private?TextView?rcvText;
private?TextView?ConnectStatusText;
????@SuppressLint("HandlerLeak")
@Override
????protected?void?onCreate(Bundle?savedInstanceState)?{
????????super.onCreate(savedInstanceState);
????????setContentView(R.layout.activity_main);
????????
????????ipText?=?(TextView)findViewById(R.id.textView1);
????????rcvText?=?(TextView)findViewById(R.id.textView3);
????????ConnectStatusText?=?(TextView)findViewById(R.id.textView4);
????????
????????ConnectStatusText.setText("連接狀態(tài):?未連接");
????????
????????//?消息處理
????????mHandler?=?new?Handler()
????????{
????????????@Override
????????????public?void?handleMessage(Message?msg)
????????????{
????????????????switch(msg.what)
????????????????{
????????????????????case?0:
????????????????????{
???????????????????? String?strAddress?=?(msg.obj).toString();
????????????????????????ipText.setText("IP地址:?"?+?strAddress);
????????????????????????Log.v("Leo:?IP:?",?strAddress);
????????????????????????ConnectStatusText.setText("連接狀態(tài):?已連接");
????????????????????????//?顯示客戶端IP
????????????????????????//?ipTextView.setText((msg.obj).toString());
????????????????????????//?使能發(fā)送按鈕
????????????????????????//?sendButton.setEnabled(true);
????????????????????????break;
????????????????????}
????????????????????case?1:
????????????????????{
???????????????????? String?strRcv?=?(msg.obj).toString();
????????????????????????rcvText.setText("接收到數(shù)據(jù):?"?+?strRcv);
????????????????????????Log.v("Leo:?Rcv:?",?strRcv);
????????????????????????//?顯示接收到的數(shù)據(jù)
????????????????????????//?mTextView.setText((msg.obj).toString());
????????????????????????break;
????????????????????}?????????????????
????????????????}???????????????????????????????????????????
????????????????
????????????}
????????};
????????
????????mAcceptThread?=?new?AcceptThread();
????????//?開啟監(jiān)聽線程
????????mAcceptThread.start();
????}
????@Override
????public?boolean?onCreateOptionsMenu(Menu?menu)?{
????????//?Inflate?the?menu;?this?adds?items?to?the?action?bar?if?it?is?present.
????????getMenuInflater().inflate(R.menu.main,?menu);
????????return?true;
????}
????@Override
????public?boolean?onOptionsItemSelected(MenuItem?item)?{
????????//?Handle?action?bar?item?clicks?here.?The?action?bar?will
????????//?automatically?handle?clicks?on?the?Home/Up?button,?so?long
????????//?as?you?specify?a?parent?activity?in?AndroidManifest.xml.
????????int?id?=?item.getItemId();
????????if?(id?==?R.id.action_settings)?{
????????????return?true;
????????}
????????return?super.onOptionsItemSelected(item);
????}
????
????//?顯示Toast函數(shù)
????private?void?displayToast(String?s)
????{
????????Toast.makeText(this,?s,?Toast.LENGTH_SHORT).show();
????}
????
????private?class?AcceptThread?extends?Thread
????{
????????@Override
????????public?void?run()
????????{
????????????try?{
????????????????//?實(shí)例化ServerSocket對象并設(shè)置端口號為?12589
????????????????mServerSocket?=?new?ServerSocket(12589);
????????????}?catch?(IOException?e)?{
????????????????//?TODO?Auto-generated?catch?block
????????????????e.printStackTrace();
????????????}
????????????
????????????try?{
????????????????//?等待客戶端的連接(阻塞)
????????????????clientSocket?=?mServerSocket.accept();
????????????}?catch?(IOException?e)?{
????????????????//?TODO?Auto-generated?catch?block
????????????????e.printStackTrace();
????????????}
????????????
????????????mReceiveThread?=?new?ReceiveThread(clientSocket);
????????????stop?=?false;
????????????//?開啟接收線程
????????????mReceiveThread.start();
????????????
????????????Message?msg?=?new?Message();
????????????msg.what?=?0;
????????????//?獲取客戶端IP
????????????msg.obj?=?clientSocket.getInetAddress().getHostAddress();
????????????//?發(fā)送消息
????????????mHandler.sendMessage(msg);
????????}
????}
????private?class?ReceiveThread?extends?Thread
????{
????????private?InputStream?mInputStream?=?null;
????????private?byte[]?buf;??
????????private?String?str?=?null;
????????
????????ReceiveThread(Socket?s)
????????{
????????????try?{
????????????????//?獲得輸入流
????????????????this.mInputStream?=?s.getInputStream();
????????????}?catch?(IOException?e)?{
????????????????//?TODO?Auto-generated?catch?block
????????????????e.printStackTrace();
????????????}
????????}
????????
????????@Override
????????public?void?run()
????????{
????????????while(!stop)
????????????{
????????????????this.buf?=?new?byte[512];
????????????????
????????????????//?讀取輸入的數(shù)據(jù)(阻塞讀)
????????????????try?{
????????????????????this.mInputStream.read(buf);
????????????????}?catch?(IOException?e1)?{
????????????????????//?TODO?Auto-generated?catch?block
????????????????????e1.printStackTrace();
????????????????}
????????????????
????????????????//?字符編碼轉(zhuǎn)換
????????????????try?{
????????????????????this.str?=?new?String(this.buf,?"GB2312").trim();
????????????????}?catch?(UnsupportedEncodingException?e)?{
????????????????????//?TODO?Auto-generated?catch?block
????????????????????e.printStackTrace();
????????????????}
????????????????
????????????????Message?msg?=?new?Message();
????????????????msg.what?=?1;????????
????????????????msg.obj?=?this.str;
????????????????//?發(fā)送消息
????????????????mHandler.sendMessage(msg);
????????????}
????????}
????}
}
1) 先通過 WIFI 進(jìn)行 Socket 通訊
運(yùn)行后,在手機(jī)上顯示的未連接界面如下圖:
在 PC 上運(yùn)行類似于 SocketTool.exe 的工具,SocketTool.exe 的顯示界面如下:
Socket 連接后,手機(jī)上顯示的界面如下圖:
2) 通過 USB 實(shí)現(xiàn) PC 與手機(jī)的通訊
在 PC 上實(shí)現(xiàn)一個 Java 小應(yīng)用,應(yīng)用的主要代碼如下:
try
{
????Runtime.getRuntime().exec("adb?forward?tcp:12581?tcp:12589");
}
catch?(IOException?e)
{
????e.printStackTrace();
}
Socket?socket?=?null;
try
{
????InetAddress?serverAddr?=?null;
????OutputStream?outStream?=?null;
????byte[]?msgBuffer?=?null;
????
????serverAddr?=?InetAddress.getByName("127.0.0.1");
????System.out.println("TCP?1"?+?"C:?Connecting...");
????socket?=?new?Socket(serverAddr,?12581); //?12581?是?PC?的端口,已重定向到?Device?的?12589?端口
????
????String?message?=?"PC?ADB,send?message";
????msgBuffer?=?message.getBytes("GB2312");
????
????outStream?=?socket.getOutputStream();
????outStream.write(msgBuffer);
????Thread.sleep(10000);
}
catch?(UnknownHostException?e1)
{
????System.out.println("TCP?2"?+?"ERROR:?"?+?e1.toString());
}
catch?(IOException?e2)
{
????System.out.println("TCP?3"?+?"ERROR:?"?+?e2.toString());
}?catch?(InterruptedException?e3)
{
//?Thread.sleep(1000);?增加的異常處理
e3.printStackTrace();
}
finally
{
????try
????{
????????if?(socket?!=?null)
????????{
????????????socket.close(); //?關(guān)閉時(shí)會導(dǎo)致接收到的數(shù)據(jù)被清空,所以延時(shí)?10?秒顯示
????????}
????}
????catch?(IOException?e)
????{
????????System.out.println("TCP?4"?+?"ERROR:?"?+?e.toString());
????}
}
運(yùn)行后,在手機(jī)上顯示的界面如下圖:
以下是學(xué)習(xí)中記錄的一些個人認(rèn)為需要掌握的知識點(diǎn),由于偶是從零開始學(xué)習(xí)的,所以有經(jīng)難的朋友們可以直接忽略此部分:
Intent 用法:
Uri?myUri?=?Uri.parse?("http://www.flashwing.net");
Intent?openBrowserIntent?=?new?Intent(Intent.ACTION_VIEW?,myUri);
startActivity(openBrowserIntent);
Intent?openWelcomeActivityIntent=?new?Intent();
openWelcomeActivityIntent.setClass(AndroidStudy_TWO.this,Welcome.class);
startActivity(openWelcomeActivityIntent);
從源 Activity 中傳遞數(shù)據(jù)
//?數(shù)據(jù)寫入?Intent
Intent?openWelcomeActivityIntent=?new?Intent();
Bundle?myBundelForName=?new?Bundle();
myBundelForName.putString("Key_Name",inName.getText().toString());
myBundelForName.putString("Key_Age",inAge.getText().toString());
openWelcomeActivityIntent.putExtras(myBundelForName);
openWelcomeActivityIntent.setClass(AndroidBundel.this,Welcome.class);
startActivity(openWelcomeActivityIntent);
// 從 Intent 中獲取數(shù)據(jù)
Bundle?myBundelForGetName=?this.getIntent().getExtras();
String?name=myBundelForGetName.getString("Key_Name");
從源請求 Activity 中通過一個 Intent 把一個服務(wù)請求傳到目標(biāo) Activity 中
private?Intent?toNextIntent;?//Intent?成員聲明 toNextIntent?=?new?Intent();?//Intent?定義 toNextIntent.setClass(TwoActivityME3.this,SecondActivity3.class); //?設(shè)定開啟的下一個?Activity startActivityForResult(toNextIntent,REQUEST_ASK); //?開啟?Intent?時(shí)候?,把請求碼同時(shí)傳遞
在源請求 Activity 中等待 Intent 返回應(yīng)答結(jié)果,通過重載 onActivityResult() 方法
@Override
protected?void?onActivityResult(int?requestCode,int?resultCode,?Intent?data)
{
??//?TODO?Auto-generated?method?stub
??super.onActivityResult(requestCode,?resultCode,?data);
??if?(requestCode?==?REQUEST_ASK)
??{
????if?(resultCode?==?RESULT_CANCELED)
????{
??????setTitle(?"Cancel****"?);
????}
????else?if?(resultCode?==?RESULT_OK)
????{
??????showBundle?=?data.getExtras();?//?從返回的?Intent?中獲得?Bundle
??????Name?=?showBundle.getString("myName");?//?從?bundle?中獲得相應(yīng)數(shù)據(jù)
??????text.setText("the?name?get?from?the?second?layout:n"?+?Name);
????}
??}
}
* 第一個參數(shù)是你開啟請求 Intent 時(shí)的對應(yīng)請求碼,可以自己定義。
* 第二個參數(shù)是目標(biāo) Activity 返回的驗(yàn)證結(jié)果碼
* 第三個參數(shù)是目標(biāo) Activity 返回的 Intent
目標(biāo) Activity 中發(fā)送請求結(jié)果代碼,連同源 Activity 請求的數(shù)據(jù)一同綁定到 Bundle 中通過 Intent 傳回源請求 Activity 中
backIntent?=?new?Intent();
stringBundle?=?new?Bundle();
stringBundle.putString("myName",Name);
backIntent.putExtras(stringBundle);
setResult(RESULT_OK,backIntent);?//?返回?Activity?結(jié)果碼
finish();
使用服務(wù)進(jìn)行音樂播放
Manifest.xml中的 Service 定義
Service 子類中的 Player
public?void?onStart(Intent?intent,?int?startId)?{
??super.onStart(intent,?startId);
??player?=?MediaPlayer.create(this,?R.raw.seven_days);
??player.start();
}
public?void?onDestroy()?{
??super.onDestroy();
??player.stop();
}
Activity 中定義 的 Intent開啟相應(yīng)的
startService(new?Intent("com.test.service.START_AUDIO_SERVICE"));
stopService(new?Intent("com.test.service.START_AUDIO_SERVICE"));DisplayMetrics?displaysMetrics=?new?DisplayMetrics(); //DisplayMetrics?一個描述普通顯示信息的結(jié)構(gòu),例如顯示大小、密度、字體尺寸 getWindowManager().getDefaultDisplay().getMetrics(displaysMetrics); //getManager()?獲取顯示定制窗口的管理器。 //?獲取默認(rèn)顯示?Display?對象 //?通過?Display?對象的數(shù)據(jù)來初始化一個?DisplayMetrics?對象 標(biāo)題欄/狀態(tài)欄隱藏?(?全屏?) //?隱藏標(biāo)題 requestWindowFeature(Window.FEATURE_NO_TITLE); //?定義全屏參數(shù) int?flag?=?WindowManager.LayoutParams.FLAG_FULLSCREEN?; //?獲得窗口對象 Window?myWindow?=?this.getWindow(); //?設(shè)置?Flag?標(biāo)識 myWindow.setFlags(flag,flag);
Button 按下的處理:
press?=(Button)findViewById(R.id.Click_Button);
(1)
press.setOnClickListener(new?Button.OnClickListener()
{
??@Override
??public?void?onClick(View?v)
??{
????//?TODO?Auto-generated?method?stub
??}
});
(2)
press.setOnClickListener(this);
@Override
public?void?onClick(View?v)
{
??switch(v.getId())?/*?根據(jù)?ID?判斷按鈕事件?*/
??{
????case?R.id.Click_Button:
????{
????}
????break;
????default:
????break;
??}
}
Toast--Android 專屬浮動小提示
(1) 顯示文本: Toast.makeText
(2) 顯示圖像:?
/*?創(chuàng)建新?Toast?對象?*/ Toast?showImageToast=?new?Toast(this); /*?創(chuàng)建新?ImageView?對象?*/ ImageView?imageView=?new?ImageView(this); /*?從資源中獲取圖片?*/ imageView.setImageResource(R.drawable.?argon?); /*?設(shè)置?Toast?上的?View--(ImageView)*/ showImageToast.setView(imageView); /*?設(shè)置?Toast?顯示時(shí)間?*/ showImageToast.setDuration(Toast.?LENGTH_LONG?); /*?顯示?Toast*/ showImageToast.show();
模擬器調(diào)試:
socket?=?new?Socket("10.0.2.2",?12589); //?如果用?localhost?不能成功連接




