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]);
        }
    }
}

你可能感兴趣的:(Android,Service)