android——handler机制原理
在android版本4.0及之后的版本中多线程有明确的分工,子线程可以写所有耗时的代码(数据库、蓝牙、网络服务),但是绝对不能碰UI,想碰UI跟着主线程走,那么我们如何才能让主线程知道我们要对 UI进行操作呢?这时我们就可以利用用消息机制——handler去通知主线程(因为子线程本身不可以发消息)
下面是handler简单的工作原理图(此图为转载)
handler用来发消息和处理消息
下面用个案例来说明:
activity_main.xml:
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 > 10 11 <TextView 12 android:id="@+id/textView1" 13 android:layout_width="wrap_content" 14 android:layout_height="wrap_content" 15 android:text="@string/hello_world" /> 16 17 <Button 18 android:id="@+id/button1" 19 android:layout_width="wrap_content" 20 android:layout_height="wrap_content" 21 android:layout_alignParentRight="true" 22 android:layout_alignTop="@+id/textView1" 23 android:layout_marginRight="53dp" 24 android:text="Button" /> 25 26 <ListView 27 android:id="@+id/listView1" 28 android:layout_width="match_parent" 29 android:layout_height="wrap_content" 30 android:layout_below="@+id/button1" 31 android:layout_marginTop="84dp" > 32 </ListView> 33 34 <!-- <ProgressBar 35 android:id="@+id/progressBar1" 36 style="?android:attr/progressBarStyleHorizontal" 37 android:layout_width="wrap_content" 38 android:layout_height="wrap_content" 39 android:layout_alignLeft="@+id/listView1" 40 android:layout_alignRight="@+id/button1" 41 android:layout_below="@+id/button1" 42 android:layout_marginTop="28dp" /> --> 43 44 </RelativeLayout>
listview中的布局item.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="horizontal" > 6 <TextView 7 android:id="@+id/username" 8 android:layout_weight="1" 9 android:layout_width="match_parent" 10 android:layout_height="60dp" 11 android:textSize="45dp" 12 /> 13 14 <TextView 15 android:id="@+id/sex" 16 android:layout_width="match_parent" 17 android:layout_height="60dp" 18 android:layout_weight="1" 19 android:textSize="45dp" /> 20 21 </LinearLayout>
User.java(用于模拟数据)
1 public class User { 2 private String username; 3 private String sex; 4 public String getUsername() { 5 return username; 6 } 7 public void setUsername(String username) { 8 this.username = username; 9 } 10 public String getSex() { 11 return sex; 12 } 13 public void setSex(String sex) { 14 this.sex = sex; 15 } 16 17 }
mainActivity.java
1 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import android.app.Activity; 7 import android.os.Bundle; 8 import android.os.Handler; 9 import android.os.Message; 10 import android.util.Log; 11 import android.view.LayoutInflater; 12 import android.view.Menu; 13 import android.view.MenuItem; 14 import android.view.View; 15 import android.view.View.OnClickListener; 16 import android.view.ViewGroup; 17 import android.widget.BaseAdapter; 18 import android.widget.Button; 19 import android.widget.ListView; 20 import android.widget.TextView; 21 import android.widget.Toast; 22 23 24 public class MainActivity extends Activity { 25 26 private String fromDb_str1 = ""; 27 private Button btn; 28 private TextView tv; 29 private ListView lv; 30 private BaseAdapter adapter; 31 private List<User> userList = new ArrayList<User>(); 32 private Runnable doInBackground1; 33 private Runnable doInBackground2; 34 35 //1.跟着主线程走,可以碰UI 36 //2.能够接受子线程发送的消息(Message) 37 // 子线程类本身不可以发信息 38 private Handler handler; 39 40 @Override 41 protected void onCreate(Bundle savedInstanceState) { 42 super.onCreate(savedInstanceState); 43 setContentView(R.layout.activity_main); 44 45 Log.i("UI_MainThread","id:"+Thread.currentThread().getId()); 46 47 48 //模拟数据访问产生数据 49 for (int i = 0; i < 5; i++) { 50 User u = new User(); 51 u.setUsername("小明"+i); 52 u.setSex("女"+i); 53 userList.add(u); 54 } 55 56 tv =(TextView)findViewById(R.id.textView1); 57 btn =(Button)findViewById(R.id.button1); 58 btn.setOnClickListener(new OnClickListener() { 59 60 @Override 61 public void onClick(View v) { 62 //1.访问数据库或者互联网(但会卡的) 63 //2.更新界面 64 Thread t1 = new Thread(doInBackground1); 65 t1.start(); 66 67 Thread t2 = new Thread(doInBackground2); 68 t2.start(); 69 70 } 71 }); 72 adapter = new BaseAdapter(){ 73 74 @Override 75 public int getCount() { 76 // TODO Auto-generated method stub 77 return userList.size(); 78 } 79 80 @Override 81 public View getView(int position, View convertView, ViewGroup parent) { 82 LayoutInflater inflater = MainActivity.this.getLayoutInflater(); 83 View view; 84 if (convertView==null){ 85 view = inflater.inflate(R.layout.item, null); 86 } 87 else{ 88 view = convertView; 89 } 90 91 TextView tv_username = (TextView)view.findViewById(R.id.username); 92 TextView tv_sex = (TextView)view.findViewById(R.id.sex); 93 tv_username.setText(userList.get(position).getUsername()); 94 tv_sex.setText(userList.get(position).getSex()); 95 return view; 96 } 97 98 @Override 99 public Object getItem(int position) { 100 // TODO Auto-generated method stub 101 return null; 102 } 103 104 @Override 105 public long getItemId(int position) { 106 // TODO Auto-generated method stub 107 return 0; 108 } 109 }; 110 lv = (ListView)findViewById(R.id.listView1); 111 lv.setAdapter(adapter); 112 113 handler = new Handler(){ 114 115 //1.消息msg来自于子线程 116 //2.消息可以多个,采用msg.what识别 117 //3.处理消息,一般就会更新UI 118 //4.此方法可以参考onPostExecute 119 @Override 120 public void handleMessage(Message msg) { 121 122 super.handleMessage(msg); 123 int msgwhat = msg.what; 124 Log.i("handler","已经收到消息,消息what:"+msgwhat+",id:"+Thread.currentThread().getId()); 125 126 if (msgwhat==1){ 127 //更新helloworld 128 tv.setText("子线程让我更新"+msgwhat); 129 } 130 if (msgwhat==2){ 131 //更新ListView 132 adapter.notifyDataSetChanged(); 133 } 134 135 } 136 137 }; 138 139 //子线程代码1 140 doInBackground1 = new Runnable() { 141 142 @Override 143 public void run() { 144 Log.i("sub_Thread","子线程1启动,id:"+Thread.currentThread().getId()); 145 146 try { 147 Thread.sleep(3000); 148 } catch (InterruptedException e) { 149 // TODO Auto-generated catch block 150 e.printStackTrace(); 151 } 152 153 //1.访问数据库或者互联网,不在UI进程,所以不卡 154 Message msg = new Message(); 155 //对消息一个识别号,便于handler能够识别 156 msg.what = 1; 157 handler.sendMessage(msg); 158 Log.i("sub_Thread","子线程1已经发送消息给handler"); 159 } 160 }; 161 162 163 164 //子线程代码1 165 doInBackground2 = new Runnable() { 166 167 @Override 168 public void run() { 169 Log.i("sub_Thread","子线程2启动,id:"+Thread.currentThread().getId()); 170 171 try { 172 Thread.sleep(6000); 173 } catch (InterruptedException e) { 174 // TODO Auto-generated catch block 175 e.printStackTrace(); 176 } 177 178 Message msg = new Message(); 179 //对消息一个识别号,便于handler能够识别 180 msg.what = 2; 181 //handler.sendMessage(msg); 182 handler.sendMessageDelayed(msg, 500); 183 184 185 //访问互联网,下载最新的,更新data,但不碰界面 186 for (User user : userList) { 187 user.setSex("不男不女"); 188 } 189 190 Log.i("sub_Thread","子线程2已经发送消息给handler"); 191 } 192 }; 193 194 } 195 } 196 197
效果图:
点击button的时候如下图
上面就是一个小例子;但是上面的代码写的还有很多需要改进的地方,详情请参考(此链接为转载):
Android中使用Handler造成内存泄露的分析和解决http://www.linuxidc.com/Linux/2013-12/94065.htm