Android(安卓)Service——在子线程中更新UI
Android中的UI是线程不安全的,也就是说,如果要更新应用程序里的UI 元素,则必须在主线程中进行,否则就会出现异常。在这里介绍两个方法来解决这个问题
解析异步处理机制
Android中的异步消息处理主要分为四个部分,Message、Handler、MessageQueue、Looper。
1.Message 是在线程之间传递的消息,它可以在内部携带少量的消息,用于在不同线程之间交换数据。
2.Handler 顾名思义就是处理者的意思,它主要是用来发送和处理消息的。
3.MessageQueue 是消息队列的意思,它主要是用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程只有一个消息队列对象。
4.Looper 是每个线程中消息队列的管家,调用其loop()方法后,就会进入到一个无限循环中,然后每发现消息队列中有一条消息,就会将它取出,并传递到Handler的handleMessage()方法中,每个线程也只会有一个Looper对象。
定义一个点击事件,使得点击时,服务可以改变UI中的内容
package com.example.administrator.myhandlerapplication;import android.app.Activity;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity { public static final int UPDATE_TIME=1; private int count=60; private Button mButton; private Button mButtonMainsend; private TextView mTextView; //定义一个内部类Handle private Handler handler=new Handler(){ public void handleMessage(Message msg){ switch (msg.what){ case UPDATE_TIME //在这里进行UI操作 String time= (String) msg.obj; mButton.setText(time); if (count>0){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //这里还可以简单的使用,对UI的操作在这里写,点击事件只需要发送一个空消息,然后在这里类似递归调用发送空消息,然后在这里对UI一次又一次的操作。即把count--放在handler中。 //handler.sendEmptyMessage(UPDATE_TIME); } break; default: break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButton= (Button) findViewById(R.id.button_time); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new MyThread().start(); new Thread(new Runnable() { @Override public void run() { while (count>0){ count--; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Message message=handler.obtainMessage(); //new Message(); message.obj=count+"秒"; message.what=UPDATE_TIME; //发送Message对象 handler.sendMessage(message); } } }).start(); } }); }}
先定义一个整型常量用来表示更新UI 中的内容的一个操作,然后新建一个Handler对象,并重写父类的handleMessage方法,然后对Message进行处理
这里是由子线程发送消息,由主线程来作相应处理,还可以通过主线程发送消息,然后子线程做相应处理,但是不常用。这里只是举例说明。
package com.example.administrator.myhandlerapplication;import android.app.Activity;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity { public static final int UPDATE_TIME=1; private int count=60; private Button mButton; private Button mButtonMainsend; private TextView mTextView; //private Handler handler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButton= (Button) findViewById(R.id.button_time); mButtonMainsend= (Button) findViewById(R.id.button_mainsend); mButtonMainsend.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //这个点击事件用来表示主线程发送消息,只是发送的是空消息,表示不进行任何操作 handler.sendEmptyMessage(UPDATE_TIME); } }); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) {//这个按钮点击事件用来启动子线程。 new MyThread().start(); } //这是子线程,等待接受主线程发送的消息,使用log打印来显示。 class MyThread extends Thread{ @Override public void run() { //super.run(); Looper.prepare(); handler=new Handler(){ @Override public void handleMessage(Message msg) { Log.d("11111111111111", "接收到主线程发来的消息"); } }; Looper.loop(); } }}
在主线程中已经将Looper全部封装好所以不需要定义。而在子线程中需要将接收消息写在Looper的prepare()方法和loop()之间。
使用AsyncTask
Android还提供了AsyncTask抽象类来实现子类对UI进行操作。其实这个类实现原理也是基于消息处理机制,只是做了封装而已,通过几个方法来实现。
AsyncTask是一个抽象类,所以使用时需要创建一个子类去继承,继承时需要制定三个泛性值参数。
第一个参数Params ,在执行AsyncTask时需要传入的参数,可用于在后台任务中使用
第二个参数Progress ,在后台执行任务时,如果需要在界面上显示当前的进度,使用这里制定的泛型作为进度单位
第三个参数Result ,当任务执行完毕后如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
继承自AsyncTask的类还需要重写其方法,其中经常需要重写的有四个方法
1.onPreExecute() 在后台任务开始执行之前调用,用于进行一些界面上的初始化操作。
2.doInBackground这个方法中的代码都会在子线程中运行,需要在这里处理所有的耗时操作任务。任务完后之后通过return语句来讲任务的执行结果返回,如果AsyncTask的第三个泛型制定的是Void,可以不返回任务执行结果。需要注意的是,在这个方法中是不可以进行UI操作的,如果需要更新UI 元素,可以调用publishProgress()方法来完成
3.onProgressUpdate(Integer… values)当后台任务调用了publishProgress()方法后,这个方法就会很快被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
4.onPostExecute(String s)当后台任务执行完毕并通过return语句进行返回时调用这个方法,返回的数据作为参数传递到这个方法中,可以利用返回的数据进行UI操作。
首先,定义一个按钮和一个进度条,通过按钮的点击事件启动后台服务,后台服务将信息发送回来显示到进度条上
"@+id/progressBar" android:layout_width="match_parent" android:layout_height="wrap_content" style="@style/Widget.AppCompat.ProgressBar.Horizontal"/>
在MainActivity中声明一个类继承自AsyncTask并重写其方法,然后在按钮的点击事件中得到该类的一个对象,并可传参数给后台服务。
package com.example.administrator.masynctask;import android.app.Activity;import android.os.AsyncTask;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.Button;import android.widget.ProgressBar;public class MainActivity extends Activity { private int count=0; private Button mButtonStart; private ProgressBar mProgressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButtonStart= (Button) findViewById(R.id.button_sync); mProgressBar= (ProgressBar) findViewById(R.id.progressBar); mButtonStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //得到一个AsyncTask的对象,这里需要传参数,所以为空 MyAsyncTask myAsyncTask=new MyAsyncTask(); myAsyncTask.execute(); } }); } class MyAsyncTask extends AsyncTask{ @Override protected String doInBackground(Void... params) { //进行后台操作 while (count<101){ count++; publishProgress(count); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } return "下载完成"; } @Override protected void onPostExecute(String s) { super.onPostExecute(s); //利用doInBackground(Void... params)完成之后返回的值对UI 进行操作 mButtonStart.setText(s); } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); //利用doInBackground(Void... params)中publishProgress(count)传过来的值对UI中的进度条进行操作 mProgressBar.setProgress(values[0]); } }}
更多相关文章
- Qt on Android:添加分享功能
- android 面试题经典
- Android(安卓)Framework 框架系列之 PhoneWindowManager
- 教你如何在 Android(安卓)使用多线程下载文件
- Android(安卓)Handler 消息传递机制
- Android(安卓)多线程编程:IntentService & HandlerThread
- Android(安卓)ANR (转)
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用