主线程访问网络
㈠ android做客户端socket如何让点击按钮向服务器发送信息
使用基于TCP协议的Socket
一个客户端要发起一次通信,首先必须知道运行服务器端的主机IP地址。然后由网络基础设施利用目标地址,将客户端发送的信息传递到正确的主机上,在java中,地址可以由一个字符串来定义,这个字符串可以使数字型的地址(比如192.168.1.1),也可以是主机名(example.com)。
而在android 4.0 之后系统以后就禁止在主线程中进行网络访问了,原因是:
主线程是负责UI的响应,如果在主线程进行网络访问,超过5秒的话就会引发强制关闭,所以这种耗时的操作不能放在主线程里。放在子线程里,而子线程里是不能对主线程的UI进行改变的,因此就引出了Handler,主线程里定义Handler,子线程里使用。
以下是一个android socket客户端的例子:
---------------------------------Java代码---------------------------------------
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class TCPSocketActivity extends Activity {
public static final String TAG = TCPSocketActivity.class.getSimpleName();
/* 服务器地址 */
private String host_ip = null;
/* 服务器端口 */
private int host_port = 0;
private Button btnConnect;
private Button btnSend;
private EditText editSend;
private EditText hostIP;
private EditText hostPort;
private Socket socket;
private PrintStream output;
private String buffer = "";
private Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_socket_test);
context = this;
initView();
btnConnect.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
host_ip = hostIP.getText().toString();
host_port = Integer.parseInt(hostPort.getText().toString());
new Thread(new ConnectThread()).start();
}
});
btnSend.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new SendThread(editSend.getText().toString())).start();
}
});
}
private void toastText(String message) {
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
public void handleException(Exception e, String prefix) {
e.printStackTrace();
toastText(prefix + e.toString());
}
public void initView() {
btnConnect = (Button) findViewById(R.id.btnConnect);
btnSend = (Button) findViewById(R.id.btnSend);
editSend = (EditText) findViewById(R.id.sendMsg);
hostIP = (EditText) findViewById(R.id.hostIP);
hostPort = (EditText) findViewById(R.id.hostPort);
}
private void closeSocket() {
try {
output.close();
socket.close();
} catch (IOException e) {
handleException(e, "close exception: ");
}
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (0x123 == msg.what) {
toastText("连接成功!");
}
}
};
/* 连接socket线程 */
public class ConnectThread implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
Message msg = Message.obtain();
try {
if (null == socket || socket.isClosed()) {
socket = new Socket();
socket.connect(new InetSocketAddress(host_ip,host_port),5000);
output = new PrintStream(socket.getOutputStream(), true,
"utf-8");
}
msg.what = 0x123;
handler.sendMessage(msg);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*发送信息线程*/
public class SendThread implements Runnable {
String msg;
public SendThread(String msg) {
super();
this.msg = msg;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
output.print(msg);
} catch (Exception e) {
e.printStackTrace();
}
closeSocket();
}
}
public class SocketThread implements Runnable {
public String txt1;
public SocketThread(String txt1) {
super();
this.txt1 = txt1;
}
@Override
public void run() {
// TODO Auto-generated method stub
Message msg = Message.obtain();
try {
/* 连接服务器 并设置连接超时为5秒 */
if (socket.isClosed() || null == socket) {
socket = new Socket();
socket.connect(new InetSocketAddress(host_ip,host_port),5000);
}
// 获取输入输出流
PrintStream ou = new PrintStream(socket.getOutputStream(),
true, "UTF-8");
BufferedReader bff = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
// 读取发来服务器信息
String line = null;
buffer = "";
while ((line = bff.readLine()) != null) {
buffer = line + buffer;
}
// 向服务器发送信息
ou.print(txt1);
ou.flush();
// 关闭各种输入输出流
bff.close();
ou.close();
socket.close();
msg.what = 0x123;
handler.sendMessage(msg);
} catch (UnknownHostException e) {
} catch (IOException e) {
}
}
}
}
-----------------------------布局文件activity_socket_test.xml--------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/white"
android:orientation="vertical" >
<EditText
android:id="@+id/hostIP"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:hint="服务器ip"
android:singleLine="true"
android:inputType="text" />
<EditText
android:id="@+id/hostPort"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:hint="端口"
android:singleLine="true"
android:inputType="number" />
<Button
android:id="@+id/btnConnect"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:background="@drawable/style_btn_shape"
android:layout_margin="5dip"
android:text="@string/connect"
android:textColor="@color/white" />
<EditText
android:id="@+id/sendMsg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:hint="需要发送的内容"
android:inputType="text" />
<Button
android:id="@+id/btnSend"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:background="@drawable/style_btn_shape"
android:layout_gravity="center_vertical|center_horizontal"
android:text="@string/send"
android:textColor="@color/white" />
</LinearLayout>
-------------------------样式文件style_btn_shape.xml----------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- 填充的颜色 -->
<solid android:color="#0465b2" />
<!-- 设置按钮的四个角为弧形 -->
<!-- android:radius 弧形的半径 -->
<corners android:radius="15dip" />
<!-- padding:Button里面的文字与Button边界的间隔 -->
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp"
/>
</shape>
------------------------------END---------------------------------------
㈡ android访问网络状态是否可在主线程中执行
是的,android2.2版本以下是可以在主线程中执行网络访问的,2.2以上就得重起一个线程了。
㈢ android 主线程和子线程有什么区别
本文较为深入的分析了android中UI主线程与子线程。分享给大家供大家参考。具体如下:
在一个Android 程序开始运行的时候,会单独启动一个Process。默认的情况下,所有这个程序中的Activity或者Service(Service和 Activity只是Android提供的Components中的两种,除此之外还有Content Provider和Broadcast Receiver)都会跑在这个Process。
一个Android 程序默认情况下也只有一个Process,但一个Process下却可以有许多个Thread。在这么多Thread当中,有一个Thread,我们称之为UI Thread。UI Thread在Android程序运行的时候就被创建,是一个Process当中的主线程Main Thread,主要是负责控制UI界面的显示、更新和控件交互。在Android程序创建之初,一个Process呈现的是单线程模型,所有的任务都在一个线程中运行。因此,我们认为,UI Thread所执行的每一个函数,所花费的时间都应该是越短越好。而其他比较费时的工作(访问网络,下载数据,查询数据库等),都应该交由子线程去执行,以免阻塞主线程。
那么,UI Thread如何和其他Thread一起工作呢?常用方法是:诞生一个主线程的Handler物件,当做Listener去让子线程能将讯息Push到主线程的Message Quene里,以便触发主线程的handlerMessage()函数,让主线程知道子线程的状态,并在主线程更新UI。
例如,在子线程的状态发生变化时,我们需要更新UI。如果在子线程中直接更新UI,通常会抛出下面的异常:
11-07 13:33:04.393: ERROR/JavaBinder(1029):android.view.ViewRoot$:Only the original thread that created a view hierarchy can touch its views.
意思是,无法在子线程中更新UI。为此,我们需要通过Handler物件,通知主线程Ui Thread来更新界面。
如下,首先创建一个Handler,来监听Message的事件:
private final int UPDATE_UI = 1;
private Handler mHandler = new MainHandler();
private class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_UI: {
Log.i("TTSDeamon", "UPDATE_UI");
showTextView.setText(editText.getText().toString());
ShowAnimation();
break;
}
default:
break;
}
}
}
或者:
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_UI: {
Log.i("TTSDeamon", "UPDATE_UI");
showTextView.setText(editText.getText().toString());
ShowAnimation();
break;
}
default:
break;
}
}
}
当子线程的状态发生变化,则在子线程中发出Message,通知更新UI。
mHandler.sendEmptyMessageDelayed(UPDATE_UI, 0);
在我们的程序中,很多Callback方法有时候并不是运行在主线程当中的,所以如果在Callback方法中更新UI失败,也可以采用上面的方法。
㈣ 如何解决空指针异常和 ThreadException 中的错误android
空指针异常: 可以直接在可能出现空指针的代码处 try catch一下,你也可以这样做:在用到可能出现空指针的 变量或者对象处,先增加一个判空处理(比如:if(textutil.isempty(str) { })
对于ThreadException:对于线程的异常,在android中,google禁止了在主线程中进行访问网络等一些耗时操作(否则就会出现这种异常)。可以开个子线程(workThread)在里面进行访问网络,用异步任务或者 handler来处理。