android异步加载图片显示,并且对图片进行缓存实例

分类:Android 493人阅读 评论(4) 收藏 举报

step1:新建项目DataAsyncLoad,如下图所示

android异步加载图片显示,并且对图片进行缓存实例_第1张图片

step2:设置应用的UI界面

a.应用的主界面 main.xml

[html] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent"
  6. >
  7. <ListView
  8. android:layout_width="fill_parent"
  9. android:layout_height="fill_parent"
  10. android:id="@+id/listView"
  11. />
  12. </LinearLayout>

b.每个ListView的界面 listview_item.xml

[html] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayoutxmlns: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. <ImageView
  7. android:layout_width="42dp"
  8. android:layout_height="42dp"
  9. android:id="@+id/imageView"
  10. />
  11. <TextView
  12. android:layout_width="match_parent"
  13. android:layout_height="wrap_content"
  14. android:textSize="18sp"
  15. android:textColor="#FFFFFF"
  16. android:id="@+id/textView"
  17. />
  18. </LinearLayout>

step3:写一些辅助类 cn.roco.data.utilsMD5.java

[java] view plain copy
  1. packagecn.roco.data.utils;
  2. importjava.security.MessageDigest;
  3. importjava.security.NoSuchAlgorithmException;
  4. publicclassMD5{
  5. publicstaticStringgetMD5(Stringcontent){
  6. try{
  7. MessageDigestdigest=MessageDigest.getInstance("MD5");
  8. digest.update(content.getBytes());
  9. returngetHashString(digest);
  10. }catch(NoSuchAlgorithmExceptione){
  11. e.printStackTrace();
  12. }
  13. returnnull;
  14. }
  15. privatestaticStringgetHashString(MessageDigestdigest){
  16. StringBuilderbuilder=newStringBuilder();
  17. for(byteb:digest.digest()){
  18. builder.append(Integer.toHexString((b>>4)&0xf));
  19. builder.append(Integer.toHexString(b&0xf));
  20. }
  21. returnbuilder.toString();
  22. }
  23. }


step4:写应用使用的JavaBean cn.roco.data.domain.Contact.java

[java] view plain copy
  1. packagecn.roco.data.domain;
  2. publicclassContact{
  3. privateintid;
  4. privateStringname;
  5. privateStringimage;
  6. publicintgetId(){
  7. returnid;
  8. }
  9. publicvoidsetId(intid){
  10. this.id=id;
  11. }
  12. publicStringgetName(){
  13. returnname;
  14. }
  15. publicvoidsetName(Stringname){
  16. this.name=name;
  17. }
  18. publicStringgetImage(){
  19. returnimage;
  20. }
  21. publicvoidsetImage(Stringimage){
  22. this.image=image;
  23. }
  24. publicContact(intid,Stringname,Stringimage){
  25. this.id=id;
  26. this.name=name;
  27. this.image=image;
  28. }
  29. publicContact(){
  30. }
  31. }

step5:写一个应用的service层,用于对javabean进行操作 cn.roco.data.service.ContactService.java

[java] view plain copy
  1. packagecn.roco.data.service;
  2. importjava.io.File;
  3. importjava.io.FileOutputStream;
  4. importjava.io.InputStream;
  5. importjava.net.HttpURLConnection;
  6. importjava.net.URL;
  7. importjava.util.ArrayList;
  8. importjava.util.List;
  9. importorg.xmlpull.v1.XmlPullParser;
  10. importandroid.net.Uri;
  11. importandroid.util.Xml;
  12. importcn.roco.data.domain.Contact;
  13. importcn.roco.data.utils.MD5;
  14. publicclassContactService{
  15. /**
  16. *获取联系人数据
  17. *
  18. *@return
  19. *@throwsException
  20. */
  21. publicstaticList<Contact>getContacts()throwsException{
  22. Stringpath="http://192.168.1.100:8080/Hello/contact.xml";
  23. HttpURLConnectionconnection=(HttpURLConnection)newURL(path)
  24. .openConnection();
  25. connection.setConnectTimeout(5000);
  26. connection.setRequestMethod("GET");
  27. if(connection.getResponseCode()==200){
  28. returnparseXML(connection.getInputStream());
  29. }
  30. returnnull;
  31. }
  32. /**转化XML获取数据
  33. *服务器端的xml文件如下。。。。。。
  34. *<?xmlversion="1.0"encoding="UTF-8"?>
  35. <contacts>
  36. <contactid="1">
  37. <name>Roco_1</name>
  38. <imagesrc="http://192.168.1.100:8080/Hello/images/1.png"/>
  39. </contact>
  40. .......
  41. </contacts>*/
  42. privatestaticList<Contact>parseXML(InputStreaminputStream)
  43. throwsException{
  44. List<Contact>contacts=newArrayList<Contact>();
  45. Contactcontact=null;
  46. XmlPullParserpullParser=Xml.newPullParser();
  47. pullParser.setInput(inputStream,"UTF-8");
  48. intevent=pullParser.getEventType();
  49. while(event!=XmlPullParser.END_DOCUMENT){
  50. switch(event){
  51. caseXmlPullParser.START_TAG:
  52. if("contact".equals(pullParser.getName())){
  53. contact=newContact();
  54. contact.setId(newInteger(pullParser.getAttributeValue(0)));
  55. }elseif("name".equals(pullParser.getName())){
  56. contact.setName(pullParser.nextText());
  57. }elseif("image".equals(pullParser.getName())){
  58. contact.setImage(pullParser.getAttributeValue(0));
  59. }
  60. break;
  61. caseXmlPullParser.END_TAG:
  62. if("contact".equals(pullParser.getName())){
  63. contacts.add(contact);
  64. contact=null;
  65. }
  66. }
  67. event=pullParser.next();
  68. }
  69. returncontacts;
  70. }
  71. /**
  72. *获取网络图片,如果图片存在于缓存中,就返回该图片,否则从网络中加载该图片并缓存起来
  73. *
  74. *@parampath
  75. *图片路径
  76. *@return
  77. */
  78. publicstaticUrigetImage(StringimagePath,FilecacheDir)
  79. throwsException{
  80. //缓存文件的文件名用MD5进行加密
  81. FilelocalFile=newFile(cacheDir,MD5.getMD5(imagePath)
  82. +imagePath.substring(imagePath.lastIndexOf(".")));
  83. if(localFile.exists()){
  84. returnUri.fromFile(localFile);
  85. }else{
  86. HttpURLConnectionconnection=(HttpURLConnection)newURL(
  87. imagePath).openConnection();
  88. connection.setConnectTimeout(5000);
  89. connection.setRequestMethod("GET");
  90. //将文件缓存起来
  91. if(connection.getResponseCode()==200){
  92. FileOutputStreamoutputStream=newFileOutputStream(localFile);
  93. InputStreaminputStream=connection.getInputStream();
  94. byte[]buffer=newbyte[1024];
  95. intlen=0;
  96. while((len=inputStream.read(buffer))!=-1){
  97. outputStream.write(buffer,0,len);
  98. }
  99. inputStream.close();
  100. outputStream.close();
  101. returnUri.fromFile(localFile);
  102. }
  103. }
  104. returnnull;
  105. }
  106. }


step6:写一个Adapter用于对ListView进行数据更新cn.roco.data.adapter.ContactAdapter.java

[java] view plain copy
  1. packagecn.roco.data.adapter;
  2. importjava.io.File;
  3. importjava.util.List;
  4. importcn.roco.data.R;
  5. importcn.roco.data.domain.Contact;
  6. importcn.roco.data.service.ContactService;
  7. importandroid.content.Context;
  8. importandroid.net.Uri;
  9. importandroid.os.AsyncTask;
  10. importandroid.os.Handler;
  11. importandroid.os.Message;
  12. importandroid.view.LayoutInflater;
  13. importandroid.view.View;
  14. importandroid.view.ViewGroup;
  15. importandroid.widget.BaseAdapter;
  16. importandroid.widget.ImageView;
  17. importandroid.widget.TextView;
  18. /**适配器,用于更新View*/
  19. publicclassContactAdapterextendsBaseAdapter{
  20. privateList<Contact>data;
  21. privateintlistviewItem;
  22. privateFilecache;
  23. /**
  24. *LayoutInflater的作用类似于findViewById(),不同点是LayoutInflater是用来找layout文件夹下的xml布局文件,并且实例化!
  25. *而findViewById()是找具体某一个xml下的具体widget控件(如:Button,TextView等)。
  26. */
  27. privateLayoutInflaterlayoutInflater;
  28. publicContactAdapter(Contextcontext,List<Contact>data,
  29. intlistviewItem,Filecache){
  30. this.data=data;
  31. this.listviewItem=listviewItem;
  32. this.cache=cache;
  33. this.layoutInflater=(LayoutInflater)context
  34. .getSystemService(Context.LAYOUT_INFLATER_SERVICE);//取得xml里定义的view
  35. /***
  36. *getSystemService()是Android很重要的一个API,它是Activity的一个方法,
  37. *根据传入的NAME来取得对应的Object,然后转换成相应的服务对象。以下介绍系统相应的服务。
  38. *传入的Name返回的对象说明
  39. WINDOW_SERVICEWindowManager管理打开的窗口程序
  40. LAYOUT_INFLATER_SERVICELayoutInflater取得xml里定义的view
  41. ACTIVITY_SERVICEActivityManager管理应用程序的系统状态
  42. POWER_SERVICEPowerManger电源的服务
  43. ALARM_SERVICEAlarmManager闹钟的服务
  44. NOTIFICATION_SERVICENotificationManager状态栏的服务
  45. KEYGUARD_SERVICEKeyguardManager键盘锁的服务
  46. LOCATION_SERVICELocationManager位置的服务,如GPS
  47. SEARCH_SERVICESearchManager搜索的服务
  48. VEBRATOR_SERVICEVebrator手机震动的服务
  49. CONNECTIVITY_SERVICEConnectivity网络连接的服务
  50. WIFI_SERVICEWifiManagerWi-Fi服务
  51. TELEPHONY_SERVICETeleponyManager电话服务
  52. */
  53. }
  54. /**得到数据的总数*/
  55. @Override
  56. publicintgetCount(){
  57. returndata.size();
  58. }
  59. /**根据数据索引,得到集合中所对应的数据*/
  60. @Override
  61. publicObjectgetItem(intposition){
  62. returndata.get(position);
  63. }
  64. @Override
  65. publiclonggetItemId(intposition){
  66. returnposition;
  67. }
  68. @Override
  69. publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
  70. ImageViewimageView=null;
  71. TextViewtextView=null;
  72. if(convertView==null){
  73. convertView=layoutInflater.inflate(listviewItem,null);
  74. imageView=(ImageView)convertView.findViewById(R.id.imageView);
  75. textView=(TextView)convertView.findViewById(R.id.textView);
  76. convertView.setTag(newDataWrapper(imageView,textView));//将内容包装起来以备以后使用
  77. }else{
  78. DataWrapperdataWrapper=(DataWrapper)convertView.getTag();//将包装类取出来
  79. //从包装类中取数据
  80. imageView=dataWrapper.getImageView();
  81. textView=dataWrapper.getTextView();
  82. }
  83. Contactcontact=data.get(position);
  84. textView.setText(contact.getName());
  85. /**异步加载图片文件*/
  86. asynchImageLoad(imageView,contact.getImage());
  87. returnconvertView;
  88. }
  89. /*
  90. //该方法会创建很多的线程,也会很耗资源
  91. privatevoidasynchImageLoad(finalImageViewimageView,finalStringimagePath){
  92. finalHandlerhandler=newHandler(){
  93. @Override
  94. publicvoidhandleMessage(Messagemsg){//运行在主线程中
  95. Uriuri=(Uri)msg.obj;
  96. if(uri!=null&&imageView!=null){
  97. imageView.setImageURI(uri);
  98. }
  99. }
  100. };
  101. Runnablerunnable=newRunnable(){
  102. @Override
  103. publicvoidrun(){
  104. try{
  105. Uriuri=ContactService.getImage(imagePath,cache);
  106. handler.sendMessage(handler.obtainMessage(10,uri));
  107. }catch(Exceptione){
  108. e.printStackTrace();
  109. }
  110. }
  111. };
  112. newThread(runnable).start();
  113. }
  114. */
  115. /**异步加载图片文件*/
  116. privatevoidasynchImageLoad(ImageViewimageView,StringimagePath){
  117. AsycImageTaskasycImageTask=newAsycImageTask(imageView);
  118. asycImageTask.execute(imagePath);
  119. }
  120. /**
  121. *使用AsyncTask提高性能
  122. *可选方法:
  123. 1,onprogressupdate(progress…)可以使用进度条增加用户体验度。此方法在主线程执行,用户显示任务执行的进度。
  124. 2,onpreExecute()这里是最新用户调用excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
  125. 3,onCancelled()用户调用取消时,要做的操作。
  126. AsyncTask<Params,Progress,Result>
  127. AsyscTask定义了三种泛型类型params,progress和result.
  128. 1,params启动任务执行的输入参数,比如http请求的URL
  129. 2,progress后台任务执行的百分比
  130. 3,result后台执行任务最终返回的结果,比如String,比如我需要得到的list。
  131. 使用AsyncTask类,遵守的准则:1,Task的实例必须在UIthread中创建;2,Execute方法必须在UIthread中调用
  132. 3,不要手动的调用onPfreexecute(),onPostExecute(result)Doinbackground(params…),onProgressupdate(progress…)这几个方法;
  133. 4,该task只能被执行一次,否则多次调用时将会出现异常;
  134. AsyncTask的整个调用过程都是从execute方法开始的,一旦在主线程中调用execute方法,就可以通过onpreExecute方法,
  135. 这是一个预处理方法,比如可以在这里开始一个进度框,同样也可以通过onprogressupdate方法给用户一个进度条的显示,增加用户体验;
  136. 最后通过onpostexecute方法,相当于handler处理UI的方式,在这里可以使用在doinbackground得到的结果处理操作UI。
  137. 此方法在主线程执行,任务执行的结果作为此方法的参数返回
  138. */
  139. privatefinalclassAsycImageTaskextendsAsyncTask<String,Integer,Uri>{
  140. privateImageViewimageView;
  141. publicAsycImageTask(ImageViewimageView){
  142. this.imageView=imageView;
  143. }
  144. /**
  145. *后台执行,比较耗时的操作都可以放在这里。
  146. 注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作
  147. ,通常需要较长的时间。在执行过程中可以调用
  148. Publicprogress(progress…)来更新任务的进度。
  149. */
  150. @Override
  151. protectedUridoInBackground(String...params){//子线程中执行
  152. try{
  153. returnContactService.getImage(params[0],cache);
  154. }catch(Exceptione){
  155. e.printStackTrace();
  156. }
  157. returnnull;
  158. }
  159. /**
  160. *相当于handler处理UI的方式,在这里可以使用在doinbackground得到的结果
  161. *处理操作UI。此方法在主线程执行,任务执行的结果作为此方法的参数返回。
  162. */
  163. @Override
  164. protectedvoidonPostExecute(Uriresult){//运行在主线程
  165. if(result!=null&&imageView!=null){
  166. imageView.setImageURI(result);
  167. }
  168. }
  169. }
  170. /**数据包装类*/
  171. privatefinalclassDataWrapper{
  172. privateImageViewimageView;
  173. privateTextViewtextView;
  174. publicImageViewgetImageView(){
  175. returnimageView;
  176. }
  177. publicTextViewgetTextView(){
  178. returntextView;
  179. }
  180. publicDataWrapper(ImageViewimageView,TextViewtextView){
  181. this.imageView=imageView;
  182. this.textView=textView;
  183. }
  184. }
  185. }


step7:应用的主程序 cn.roco.data.MainActivity.java

[java] view plain copy
  1. packagecn.roco.data;
  2. importjava.io.File;
  3. importjava.util.List;
  4. importcn.roco.data.adapter.ContactAdapter;
  5. importcn.roco.data.domain.Contact;
  6. importcn.roco.data.service.ContactService;
  7. importandroid.app.Activity;
  8. importandroid.os.Bundle;
  9. importandroid.os.Environment;
  10. importandroid.os.Handler;
  11. importandroid.widget.ListView;
  12. publicclassMainActivityextendsActivity{
  13. privateListViewlistView;
  14. /**缓存文件*/
  15. privateFilecache;
  16. /**接受消息,处理消息,此Handler会与当前主线程一块运行
  17. *使用匿名内部类来复写Handler当中的handlerMessage()方法*/
  18. Handlerhandler=newHandler(){
  19. //接受数据
  20. publicvoidhandleMessage(android.os.Messagemsg){
  21. //设置适配器,将获取的数据使用适配器更新View
  22. listView.setAdapter(newContactAdapter(MainActivity.this,
  23. (List<Contact>)msg.obj,R.layout.listview_item,cache));
  24. };
  25. };
  26. @Override
  27. publicvoidonCreate(BundlesavedInstanceState){
  28. super.onCreate(savedInstanceState);
  29. setContentView(R.layout.main);
  30. listView=(ListView)this.findViewById(R.id.listView);
  31. /**在SD卡中生成缓存目录*/
  32. cache=newFile(Environment.getExternalStorageDirectory(),"cache");
  33. /**如果目录不存在就新建一个*/
  34. if(!cache.exists())cache.mkdir();
  35. newThread(newRunnable(){
  36. @Override
  37. publicvoidrun(){
  38. try{
  39. //获取联系人数据
  40. List<Contact>data=ContactService.getContacts();
  41. //向Handler发送消息,更新UI
  42. handler.sendMessage(handler.obtainMessage(22,data));
  43. }catch(Exceptione){
  44. e.printStackTrace();
  45. }
  46. }
  47. }).start();
  48. }
  49. @Override
  50. protectedvoidonDestroy(){
  51. /**清除缓存文件*/
  52. for(Filefile:cache.listFiles()){
  53. file.delete();
  54. }
  55. cache.delete();
  56. super.onDestroy();
  57. }
  58. }

step8:AndroidManifest.xml

[html] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <manifestxmlns:android="http://schemas.android.com/apk/res/android"
  3. package="cn.roco.data"android:versionCode="1"android:versionName="1.0">
  4. <uses-sdkandroid:minSdkVersion="8"/>
  5. <!--访问Internet权限-->
  6. <uses-permissionandroid:name="android.permission.INTERNET"/>
  7. <!--在SD卡中创建和删除文件权限-->
  8. <uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
  9. <!--往SD卡中写入数据权限-->
  10. <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  11. <applicationandroid:icon="@drawable/icon"android:label="@string/app_name">
  12. <activityandroid:name=".MainActivity"android:label="@string/app_name">
  13. <intent-filter>
  14. <actionandroid:name="android.intent.action.MAIN"/>
  15. <categoryandroid:name="android.intent.category.LAUNCHER"/>
  16. </intent-filter>
  17. </activity>
  18. </application>
  19. </manifest>

step9:编写服务器端的代码,主要是一个contact.xml文件

[html] view plain copy
  1. <?xmlversion="1.0"encoding="UTF-8"?>
  2. <contacts>
  3. <contactid="1">
  4. <name>Roco_1</name>
  5. <imagesrc="http://192.168.1.100:8080/Hello/images/1.png"/>
  6. </contact>
  7. <contactid="2">
  8. <name>Roco_2</name>
  9. <imagesrc="http://192.168.1.100:8080/Hello/images/2.png"/>
  10. </contact>
  11. <contactid="3">
  12. <name>Roco_3</name>
  13. <imagesrc="http://192.168.1.100:8080/Hello/images/3.png"/>
  14. </contact>
  15. <contactid="4">
  16. <name>Roco_4</name>
  17. <imagesrc="http://192.168.1.100:8080/Hello/images/4.png"/>
  18. </contact>
  19. <contactid="5">
  20. <name>Roco_5</name>
  21. <imagesrc="http://192.168.1.100:8080/Hello/images/5.png"/>
  22. </contact>
  23. <contactid="6">
  24. <name>Roco_6</name>
  25. <imagesrc="http://192.168.1.100:8080/Hello/images/6.png"/>
  26. </contact>
  27. <contactid="7">
  28. <name>Roco_7</name>
  29. <imagesrc="http://192.168.1.100:8080/Hello/images/7.png"/>
  30. </contact>
  31. <contactid="8">
  32. <name>Roco_8</name>
  33. <imagesrc="http://192.168.1.100:8080/Hello/images/8.png"/>
  34. </contact>
  35. <contactid="9">
  36. <name>Roco_9</name>
  37. <imagesrc="http://192.168.1.100:8080/Hello/images/9.png"/>
  38. </contact>
  39. <contactid="10">
  40. <name>Roco_10</name>
  41. <imagesrc="http://192.168.1.100:8080/Hello/images/10.png"/>
  42. </contact>
  43. <contactid="11">
  44. <name>Roco_11</name>
  45. <imagesrc="http://192.168.1.100:8080/Hello/images/11.png"/>
  46. </contact>
  47. <contactid="12">
  48. <name>Roco_12</name>
  49. <imagesrc="http://192.168.1.100:8080/Hello/images/12.png"/>
  50. </contact>
  51. <contactid="13">
  52. <name>Roco_13</name>
  53. <imagesrc="http://192.168.1.100:8080/Hello/images/13.png"/>
  54. </contact>
  55. <contactid="14">
  56. <name>Roco_14</name>
  57. <imagesrc="http://192.168.1.100:8080/Hello/images/14.png"/>
  58. </contact>
  59. <contactid="15">
  60. <name>Roco_15</name>
  61. <imagesrc="http://192.168.1.100:8080/Hello/images/15.png"/>
  62. </contact>
  63. <contactid="16">
  64. <name>Roco_16</name>
  65. <imagesrc="http://192.168.1.100:8080/Hello/images/16.png"/>
  66. </contact>
  67. <contactid="17">
  68. <name>Roco_17</name>
  69. <imagesrc="http://192.168.1.100:8080/Hello/images/17.png"/>
  70. </contact>
  71. <contactid="18">
  72. <name>Roco_18</name>
  73. <imagesrc="http://192.168.1.100:8080/Hello/images/18.png"/>
  74. </contact>
  75. <contactid="19">
  76. <name>Roco_19</name>
  77. <imagesrc="http://192.168.1.100:8080/Hello/images/19.png"/>
  78. </contact>
  79. <contactid="20">
  80. <name>Roco_20</name>
  81. <imagesrc="http://192.168.1.100:8080/Hello/images/20.png"/>
  82. </contact>
  83. </contacts>

以及在images目录下放置了一些图片

android异步加载图片显示,并且对图片进行缓存实例_第2张图片


step10:将项目部署到模拟器上运行效果如下图:

android异步加载图片显示,并且对图片进行缓存实例_第3张图片 android异步加载图片显示,并且对图片进行缓存实例_第4张图片

在SD卡中会生成缓存文件

android异步加载图片显示,并且对图片进行缓存实例_第5张图片

当应用退出的时候,会将缓存文件删除

android异步加载图片显示,并且对图片进行缓存实例_第6张图片


有了缓存文件,只要应用没有退出,即使联网不成功,也可以读取缓存中的图片文件

更多相关文章

  1. Android显示网络图片实例
  2. 今天开始写android的照片浏览器(一)至返回所有图片文件
  3. (2019年10月更新) Android 最全的底部导航栏实现方法
  4. Android工程内嵌资源文件的两种方法
  5. 解决国行安卓(Android)手机无谷歌(Google)服务的一个参考方法
  6. 3.NDK Android jni开发 C语言中打印log debug模式下 (相机图片美

随机推荐

  1. Android(安卓)Layout_weight 属性
  2. Android(安卓)自定义CheckBox 样式
  3. android tips:从资源文件中读取文件流显
  4. 2013.08.08——— android 中文简繁体转
  5. Android:如何显示网络图片
  6. android NDK/JNI-实例开发流程
  7. Android网络连接处理学习笔记
  8. 使用 EditText来调用软键盘的搜索功能
  9. android 闹钟提醒并且在锁屏下弹出Dialog
  10. Power Profiling: MQTT on Android(安卓)