Android之线程处理
初入Android可谓是举步维艰,当涉及到使用后台的接口获取信息并更新UI,我便遇到了处理多线程的许多问题,经过两个月的实践以及精细的打磨,学到了线程处理的一些知识,接下来我将详细的阐述有关Android的线程处理方面的知识,如有理解错误的,敬请指正!
首先,我们要明确一点Android的耗时操作是绝对不允许在主线程(Main Tread)里面执行的!这些耗时操作,比如下载或上传图片、获取或上传大量的字符串、视频的传输等等。当一个控件的点击事件涉及到这些耗时操作时,那么意味着控件的响应将变得很慢,当然这只是理想状态的,而实际上,当出现这种情况时,程序是会出现ANR(程序不响应)的,那么程序会崩溃掉。Android的主线程基本上是用来响应控件的点击事件而更新UI的,所以我们的耗时操作是需要放到子线程执行的,这里我再添加一点,严格意义上讲,Anroid的子线程是不能够更新UI的,具体见(http://blog.csdn.net/zhao_csdn_8/article/details/64126195)。
接下来,我们借两张图来了解一下Handler机制:
Handler机制包括三个部分:Handler、Looper、MessageQueue,这三个部分构成一个处理循环,Handler即处理,Looper是一个死循环,MessageQueue是消息队列,如下:
这里边就有着许多步骤:
>>Handler回调用sendxxx方法将延时操作所获取的数据发送出去。
>>然后Handler再调用enqueueMessage(Message msg, long when)方法将消息入队列。
>>当looper轮询到消息时,Looper调用next() ,将消息移出队列。
>>然后Looper回调Handler的dispatchMessage方法将消息分发给Handler的实例去处理。
Handler有如下的处理方法……
方法一:new Handler().sendMessage()
……
//如下是一个实现异步加载图片的实例……
private Handler handler=new Handler(){//处理消息
@Override
public void handleMessage(Message msg) {
switch (msg.what)
{
case 0:
getc.setImageBitmap((Bitmap)msg.obj);
//设置图片显示
break;
default:
break;
}
super.handleMessage(msg);
}
};
public void getIdentifyingCode(){//显示验证码
new Thread(){
@Override
public void run() {
super.run();
Bitmap bitmap=LoadBitmap();//延时操作
Message msg=Message.obtain();
msg.what=0;
msg.obj=bitmap;
handler.sendMessage(msg);
}
}.start();
}
public Bitmap LoadBitmap(){//异步线程加载验证码的图片
Bitmap bitmap=null;
String urlString="获取图片的URL地址";
URLConnection con= null;
try {
con = new URL(urlString).openConnection();
InputStream is=new BufferedInputStream(con.getInputStream());
bitmap= BitmapFactory.decodeStream(is);
is.close();
} catch (IOException e) {
e.printStackTrace();}
if(bitmap==null) Toast.makeText(Login.this,"服务器连接错误,无法获取验证码",Toast.LENGTH_LONG); return bitmap;}
另外,我在用此方法实现账号登录供能的时候,我在OnClick()里实现一段如下:
public class MainActivity extends Activity {
protected String jsonData;
……
Handler handler=new Handler(){
@Override
public void handMessage(Object msg){
……
jsonData=msg.toString();
}
}
……
case R.id.login:
getLoginData();//此函数里面开启线程获取数据
If(用jsonData做出响应的判断)
执行相应的操作
break;
……
}
然后会在if判断处报出空指针错误,原因是:其实在getLogin()执行时,执行了thread.start(); 但是handler并不是马上执行(相对而言的),而是在执行完case R.id.login:……这一代码块,handler才会执行,即主线程是优先执行的,所以在使用全局变量jsonData作为条件判断时,这个字符串根本就没有被赋值,所以会报出空指针错误。
于是就只能将getLoginData()后面的操作放到handler里面执行。
方法二:使用Runable
//使用runOnUiThread
public class MainActivity extends Activity {
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);
new Thread(){
public void run() {
runOnUiThread(new Runnable() {
public void run() {
//此处可执行一般的耗时操作(小于5秒)
tv.setText("你好!!!");
}
});
};
}.start();
}
}
//使用handler.post(……)
public class MainActivity extends Activity {
private TextView tv;
private Handler handler=new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);
new Thread(){
public void run() {
Handler.post(new Runnable() {
public void run() {
//此处可执行一般的耗时操作(小于5秒)
tv.setText("你好!!!");
}
});
};
}.start();
}
}
//继承Runable接口(postDelayed方法)
public class MainActivity extends Activity {
private Imageview im;
Private int images[]={R.drawable.image1,R.drawable.image2,
R.drawable.image2,};
private int index;
private MyRunable myRunable=new MyRunable();
private Handler handler=new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
im = (ImageView) findViewById(R.id.im);
hander.postDelayed(myRunable,1000);//过一秒执行……
}
class MyRunable implements Runable{
@Override
public void run(){
index++;
Index=index%3//实现三张图片的轮流播放
im.setImageResource(images[index]);
hander.postDelayed(myRunable,1000);//采用递归 达到循环的效果
}
}
}
这三种方式的基本差不多,只是用到了不同的函数,不同的函数在底层调用的sendxxx方法和postxxx方法有所不同,本人还没学的那么深入,见谅。
对于异步处理,AsyncTask是一个比较好的选择,适合初学者使用,结构明朗易懂,只要是三个函数 onPre(),onPost()以及doback()等等(简略的写),下面为AsyncTask的基本结构:
class getDeptList extends AsyncTask<String,Void,JSONArray>{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected JSONArray doInBackground(String... params) {
String data=getDeptlData(params[0]);//延时操作
try {
JSONObject jsonObject=new JSONObject(data);
dataArray=new JSONArray(jsonObject.getString("data"));
return dataArray;
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(JSONArray jsonArray) {
super.onPostExecute(jsonArray);
if(jsonArray==null){
Toast.makeText(DeptList.this,"部门列表为空……",Toast.LENGTH_LONG).show();
}
else{
//适配
DeptListAdapter deptListAdapter=newDeptListAdapter
(DeptList.this,jsonArray);
mList.setAdapter(deptListAdapter);
}
}
}
onPre()用于前期的处理,doBack()用于开启异步线程,此
数可将所得结果返回出去并由onPost()接收进行UI的更新。AsyncTask<argc1,argc2,argc3>类有三个传入参数类型,argc1起始传入参数,argc2为中间量的返回参数类型,一般用于耗时操作进度的记录,argc3为doBack()的返回值类型,AsyncTask<String,Void,JSONArray>
表示传入一个String类型的数据,不反回中间值,doBack()返回一个json数组,用 new MyAsyncTask().excute(传入参数) 启动异步线程。