這個系列的文章就是爲了防止以後自己開發的時候忘了怎麼回事,提醒自己用的……由於自己Android開發非常非常菜,所以貼上來的代碼很有可能像一坨shi一樣,如果有新手朋友看到,希望謹慎地作爲參考,同時如果有神牛看到的話,非常希望能批評指正。
到現在爲止做了兩個安卓的app,都跟網絡通訊有關,通過WIFI連接電腦上的服務器,然後他們之間互相交流一點數據。目前我會的最簡單的實現方法是利用socket,它就像一個文件一樣,連接好以後寫/讀它就可以了。
要注意的是安卓凡是涉及網絡的事情,都不允許在主線程中完成,都必須單開一個線程做,否則會拋出異常,名字的意思就是“主線程中有網絡操作”。
另外需要注意的是要開啓網絡權限,第二個app我調了半天收不到數據,最後猛然想起是沒有開網絡權限導致的…… 這個下面說。
一般我們都是用一個線程的run函數來做我們希望做的網絡操作,就像下面這個登錄Activity一樣:
代碼大部分其實沒什麼用,看幾個關鍵點就可以了……
有一點這個代碼裏沒有的,就是socket的定義,直接
Socket socket = null;
就可以了。
public class LoginActivity extends Activity
{
ImageButton btn;
EditText editText;
GetThread getThread; //這個是網絡線程
Handler handler; //網絡線程要是想更新UI,需要一個handler來轉消息
THUClient the_app; //這個東西是爲了使用全局變量用的,不用看,THUClient是個類名
class GetThread implements Runnable //一般線程用內部類寫,然後implements Runnable
{
public void getMsg()
{
try
{
Scanner in = new Scanner(the_app.socket.getInputStream());//輸入流包裝一下
String gotmsg = in.nextLine();//就像讀控制檯和讀文件一樣讀就可以了
Message msg = new Message();
msg.obj = gotmsg;//用msg的obj域搭載消息
LoginActivity.this.handler.sendMessage(msg);//這句話就能把msg扔給handler讓他去處理了
} catch (UnknownHostException e)
{
Message msg = new Message();
msg.obj = "net error";
LoginActivity.this.handler.sendMessage(msg);
} catch (IOException e)
{
Message msg = new Message();
msg.obj = "net error";
LoginActivity.this.handler.sendMessage(msg);
}
}
@Override
public void run() //啓動線程時,啓動這個函數
{
try
{
the_app.socket = new Socket(); //實例化socket
the_app.socket.connect(new InetSocketAddress(the_app.ip, the_app.port) , 5000);//connect函數,爲什麼要這麼寫呢?因爲這樣可以設置超時時間,比如這裏就是5000毫秒 = 5秒
} catch (UnknownHostException e)
{
Message msg = new Message();
msg.obj = "did not login";
LoginActivity.this.handler.sendMessage(msg);
} catch (IOException e) {
Message msg = new Message();
msg.obj = "did not login";
LoginActivity.this.handler.sendMessage(msg);
}//凡是這種try-catch語句塊,都要處理好必要的異常,否則程序很容易崩出去(比如UnknownHostException不處理的話如果服務器沒開就崩出去了),我的處理方法是遇到網絡異常的時候,通知主線程彈一個Toast,告訴一下用戶
if( the_app.socket != null)
getMsg();
else
{
Message msg = new Message();
msg.obj = "did not login";
LoginActivity.this.handler.sendMessage(msg);
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_login);
the_app = (THUClient) getApplicationContext();
btn = (ImageButton)findViewById(R.id.buttonId);
editText = (EditText) findViewById(R.id.editTextId);
Context ctx = LoginActivity.this;//這些是幹別的事用的不用看
SharedPreferences sp = ctx.getSharedPreferences("SP", MODE_PRIVATE);
final String lastIP = sp.getString("IP", "none");
if(! lastIP.equals("none") )
{
the_app.ip = lastIP;
editText.setText(lastIP);
}
final Editor editor = sp.edit();
handler = new Handler()//handler是這麼用的
{
public void handleMessage(Message msg)
{
String words = (String)msg.obj;
if( words.equals("ok") == true )
{
Toast.makeText(LoginActivity.this, "來自服務器的問候:登陸成功!!歡迎使用~",Toast.LENGTH_LONG).show();
finish();
}
else if(words.equals("did not login") )
{
Toast.makeText(LoginActivity.this,"ip輸錯了或者是主機沒有開", Toast.LENGTH_SHORT).show();
}
}
};
btn.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0)
{
the_app.ip = editText.getText().toString();
if(! lastIP.equals(the_app.ip))
{
editor.putString("IP",the_app.ip);
editor.commit();
}
getThread = new GetThread();//點擊按鈕,發起線程
new Thread(getThread).start();
}
});
}
}
總的來看,過程就是這樣的:
1)用戶點擊按鈕,發起網絡線程;
2)run函數進入,socket連接;
3)連接正常的話,收數據,
4)告訴handler更新UI。
有時候我們可能希望和服務器進行交互,下面是一個例子:
public void run()
{
try
{
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))
, true); // 用PrintWriter包裝一下輸出流
out.println("1 "+ nowFrom +" "+ nowTo);//這樣就可以向socket輸出
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));//剛纔用Scanner,用BufferedReader也行
String ans = in.readLine();
if(ans != null)//有時候會讀進來null,我也不知道怎麼回事,總之這個判斷可以幫你的程序變得更健壯
{
<span style="white-space:pre"> </span>Message msg = new Message();
msg.obj = ans;
AskInputActivity.this.handler.sendMessage(msg);
}
} catch (IOException e) {
Message msg = new Message();
msg.obj = "net error";
AskInputActivity.this.handler.sendMessage(msg);
}
}
}
服務器端就是典型的java/c++網絡編程,跟這篇文章要討論的就沒關係了。
一點經驗是網絡線程能不一直跑着就不要一直跑着,如果是嚴格的“請求-響應”工作模式的話,最好每次要發起連接的時候發起一個網絡線程,發完-收完數據就結束,不然想結束一個線程不是那麼輕鬆的事情。
最後,想正常利用網絡,你需要在manifest裏面申請網絡權限:
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.INTERNET" /> //這一句
<application
android:name="com.zero.client.THUClient"
android:allowBackup="true"
android:icon="@drawable/icon_mod"
android:label="@string/app_name"
android:theme="@style/AppTheme" > …………
一定要有,不然上面的寫了等於白寫,而且可惡的是他不告訴你……
暫且先記錄這麼多吧……