Android技术分享

异步消息处理机制

2019-11-07  本文已影响0人  瑟闻风倾

说明:异步消息处理框架 HandlerAsyncTaskHandlerThreadIntentService 本质上都是对线程和线程池的封装。

1. Handler

1.1 what is Handle

handler:handler是一个消息分发对象,进行发送和处理消息。handle通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue。让耗时操作放在子线程,更新UI放在主线程。

1.2 handle机制原理

handle机制原理.png
四者关系.png

1.3 handler异步消息处理的两种方式

(1) 通过handler.post(runnable)的方式来进行消息传递

(2) 通过handler.sendMessage(message)的方式来进行消息传递

备注:post(runnable)只是对sendMessage(message)进行了封装,底层最终调用的还是sendMessage(message)。
注意:UI线程(主线程)才能更新UI,所以创建Handler只能在主线程而不能在内部类,从而使Handler的HandlerhandleMessage(msg)方法执行在UI线程,这样才能保证UI线程是线程安全的。

1.4 handler异步消息处理示例

(1) 常见的子线程向主线程发送消息更新UI

package comi.example.liy.mytestdemo;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

/**
 * Created by liy on 2019-11-06 15:36
 */
public class HandlerWithRunnableActivity extends AppCompatActivity {

    private HandlerWithRunnableActivity activity;

    //(1)在成员变量中创建Handler对象:创建后Handler就会绑定到UI线程(主线程)
    private Handler myHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        activity = this;
    }

    @Override
    protected void onResume() {
        super.onResume();
        new MyThread().start();
    }

    private class MyThread extends Thread{
        @Override
        public void run() {
            super.run();
            try{
                System.out.println("开始耗时操作");
                sleep(5000);
                System.out.println("耗时操作完成");
                //(2)创建runnable对象并重写run方法
                Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        /*Toast.makeText(HandlerWithMessageActivity.this,"更新UI",Toast.LENGTH_SHORT).show();*/
                        Toast.makeText(activity,"更新UI",Toast.LENGTH_SHORT).show();
                    }
                };
                myHandler.post(runnable);//(3)完成耗时操作后将runnable对象通过post方法传到handler中:主线程会在合适的时候执行runnable对象run方法中的代码从而更新UI
            }catch (Exception e){
                e.printStackTrace();
            }

        }
    }
}

package comi.example.liy.mytestdemo;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

/**
 * Created by liy on 2019-11-06 15:35
 */
public class HandlerWithMessageActivity extends AppCompatActivity {

    private static final int MESSAGE_TEST = 1;//消息标识

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    //(1)在成变量中创建Handler对象并重写handleMessage方法
    private Handler myHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){//(3)根据消息标识处理消息
                case MESSAGE_TEST:
                    Toast.makeText(HandlerWithMessageActivity.this,"更新UI",Toast.LENGTH_SHORT).show();
            }
        }
    };


    @Override
    protected void onResume() {
        super.onResume();
        new MyThread().start();
    }

    private class MyThread extends Thread{
        @Override
        public void run() {
            super.run();
            try{
                sleep(5000);
            }catch (Exception e){
                e.printStackTrace();
            }
            //(2)创建Message对象添加消息标识后发送消息
            Message message = new Message();// Message messsag = myHandler.obtainMessage();
            message.what = MESSAGE_TEST;
            myHandler.sendMessage(message);
        }
    }

}

Bundle传参示例

Message message = new Message();
message.what = MESSAGE_TEST;
Bundle bundle = new Bundle();
bundle.putString("number",number);
bundle.putString("name",name);
message.setData(bundle);
myHandler.sendMessage(message);

(2) 不常见的的主线程向子线程发送消息
拓展:我们平时开发时,经常是子线程向主线程发消息让主线程更新UI,但根据具体的项目需求也可能会要求主线程向子线程发消息。

package comi.example.liy.mytestdemo;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Created by liy on 2019-11-07 14:40
 * 主线程向子线程发送消息
 */
public class HandlerTestActivity extends AppCompatActivity {

    private Handler handler;
    private  HandlerTestActivity activity;

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        activity = this;

        textView = findViewById(R.id.text);
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                handler.sendEmptyMessage(2);//主线程发送消息
            }
        });

    }

    @Override
    protected void onResume() {
        super.onResume();
        new MyThread().start();
    }

    //子线程接收消息
    private class MyThread extends Thread{
        @Override
        public void run() {
            super.run();

            //(1)子线程创建Looper对象开启消息循环(默认情况下Android中除主线程外新建的子线程都没有开启消息循环,主线程系统会自动为其创建Looper对象并开启消息循环)
            Looper.prepare();
            //(2)在子线程中创建Handler
            handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    System.out.println("threadName:" + Thread.currentThread().getName() + ",messageWhat:" + msg.what);
                    Toast.makeText(activity,"threadName:" + Thread.currentThread().getName() + ",messageWhat:" + msg.what,Toast.LENGTH_SHORT).show();
                }
            };

            try{
                sleep(3000);
            }catch (Exception e){
                e.printStackTrace();
            }
            //(3)取出消息对象开始消息循环
            //注意:写在Looper.loop()之后的代码不会被执行,这个函数内部是一个死循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。
            Looper.loop();//开始消息循环:Looper从消息队列取出消息并交给Handler处理
        }
    }

}

拓展开启子线程的三种方式

1.5 handler引起的内存泄漏及解决方法

(1) handler引起内存泄漏原因:非静态内部类持有外部类的匿名引用导致外部activity无法释放(java相关)。创建的Handler若不是静态内部类,则会隐秘地持有activity的引用,当activity被回收时,若handler内部仍在做耗时操作则handler没有被释放,所有handler所持有的activity的引用也不能被释放,导致activity无法被回收从而导致内存泄漏。
(2) 解决方法:Android常见异常与性能优化

2. AsyncTask框架

2.1 what is AsyncTask

AsyncTask:AsyncTask本质上是封装了线程池和Handler的异步框架,主要是来执行异步任务的(适用于耗时短的操作,耗时长的任务使用线程池比较好),由于内部集成了Handler,所以能够方便地在UI线程(主线程)和工作线程(子线程)灵活切换。

2.2 AsyncTask内部原理

内部原理:AsyncTask框架内部封装了线程池,通过Handler发送消息在UI线程和主线程中传递

2.3 AsyncTask的使用方法(掌握3个参数和5个方法)

3个参数和5个方法.png

(1) AsyncTaskTestActivity.java

package comi.example.liy.mytestdemo;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * Created by liy on 2019-11-07 17:10
 */
public class AsyncTaskTestActivity extends AppCompatActivity {

    private Button button;
    private ProgressBar progressBar;
    private TextView textView;
    
    private AsyncTaskUpdateInfo asyncTask;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_asynctask);

        button = (Button)findViewById(R.id.button03);
        progressBar = (ProgressBar)findViewById(R.id.progressBar02);
        textView = (TextView)findViewById(R.id.textView01);

        button.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                asyncTask = new AsyncTaskUpdateInfo(textView, progressBar);
                asyncTask.execute(1000);
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        asyncTask.onCancelled();//在activity的onDestroy()方法中取消asyncTask任务避免内存泄漏
    }
}

(2) AsyncTaskUpdateInfo .java

package comi.example.liy.mytestdemo;

import android.os.AsyncTask;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * Created by liy on 2019-11-07 13:13
 * AsyncTask是android提供的一个抽象类,需派生出子类来执行不同的异步任务
 * AsyncTask的三个参数(泛型)分别代表:耗时操作需传递的参数,进度条和返回结果
 * AsyncTask的五个方法:
 */
public class AsyncTaskUpdateInfo extends AsyncTask<Integer,Integer,String> {
    private TextView textView;
    private ProgressBar progressBar;

    public AsyncTaskUpdateInfo(TextView textView, ProgressBar progressBar) {
        this.textView = textView;
        this.progressBar = progressBar;
    }

    //(1)耗时操作执行前的UI更新
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        textView.setText("开始执行异步线程");
    }

    //(2)在子线程中执行耗时操作
    @Override
    protected String doInBackground(Integer... params) {
        int i;
        for (i = 10;  i<=100 ; i+=10) {
            publishProgress(i);//publishProgress()执行完成后就会调用onProgressUpdate()来更新进度条
        }
        return i + params[0].intValue() + "";
    }

    //耗时操作时的UI实时更新
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        int value = values[0];
        progressBar.setProgress(value);
    }

    //(3)耗时操作完成后的UI更新
    @Override
    protected void onPostExecute(String s) {//doInBackground()执行完毕后会执行onPostExecute(),并把返回结果传递给onPostExecute()
        super.onPostExecute(s);
        textView.setText("异步操作执行结束:" + result);
    }

    @Override
    protected void onCancelled(String s) {
        super.onCancelled(s);
    }

    @Override
    protected void onCancelled() {
        super.onCancelled();
    }
}

(3) activity_asynctask.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView
        android:id="@+id/textView01"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />
    <ProgressBar
        android:id="@+id/progressBar02"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"
        />
    <Button
        android:id="@+id/button03"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="更新progressbar"
        />
</LinearLayout>

2.4 AsyncTask注意事项

(1) 内存泄漏:AsyncTask引起的内存泄漏的原因和Handler一样。
说明:解决handler引起的内存泄漏可在activity的onDestory()生命周期函数中调用handler.removeCallbacks()方法,而解决AsyncTask引起的内存泄漏调用asyncTask.onCancelled()方法即可。
(2) 结果丢失:

(3) 并行 or 串行:AsyncTask虽然可以执行并行执行耗时操作,但是会导致线程池不稳定,AsyncTask仅适合执行耗时短的操作。

3. HandlerThread框架

3.1 HandlerThread介绍

(1)产生背景:耗时任务需要通过创建子线程来执行,执行完毕会自动销毁子线程;但线程的创建和销毁很消耗系统资源,当第一个耗时任务执行完毕后又有耗时任务,那么就需要重新再创建一个子线程,如此循环地创建和销毁线程很浪费系统资源。

为了解决这个问题,我们可以创建一个循环线程,在线程中创建Looper监听器来进行消息的轮询,即当有耗时任务投放到循环线程当中,线程就开始执行耗时任务,处理完成后循环线程处于阻塞等待状态,直到下一个耗时任务被投放在循环线程(通过阻塞等待来保证性能最优,避免频繁创建销毁线程消耗系统资源)。过程如下:

package comi.example.liy.mytestdemo;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

/**
 * Created by liy on 2019-11-07 14:40
 * 创建一个循环线程,在线程中创建Looper监听器来进行消息的轮询:HanderThread的原理
 */
public class HandlerTestActivity extends AppCompatActivity {

    private Handler handler1;
    private  HandlerTestActivity activity;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        activity = this;
    }

    @Override
    protected void onResume() {
        super.onResume();
        new MyThread().start();
    }

    //在子线程中调用Looper.prepare()为线程开启消息循环:默认情况下Android中除主线程外新建的子线程都没有开启消息循环,主线程系统会自动为其创建Looper对象并开启消息循环
    private class MyThread extends Thread{
        @Override
        public void run() {
            super.run();

            //(1)创建Looper对象
            Looper.prepare();
            handler1 = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    System.out.println("threadName:" + Thread.currentThread().getName() + ",messageWhat:" + msg.what);
                    Toast.makeText(activity,"threadName:" + Thread.currentThread().getName() + ",messageWhat:" + msg.what,Toast.LENGTH_SHORT).show();
                }
            };

            try{
                sleep(3000);
            }catch (Exception e){
                e.printStackTrace();
            }

            handler1.sendEmptyMessage(2);
            //注意:写在Looper.loop()之后的代码不会被执行,这个函数内部是一个死循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。
            Looper.loop();//(2)开始消息循环:Looper从消息队列取出消息并交给Handler处理
        }
    }

}

然而以上操作可以用已经封装好的HandlerThread类来帮我们完成。

(2) what is HandlerThread:HandlerThread本质上是一个内部建立了looper的特殊线程(继承Thread),可以进行looper循环,在HandlerThread内部创建Handler对象来处理消息。通过获取HandlerThread的looper对象传递消息给Handler,可以在handleMessage()方法中执行异步任务。

3.2 HandlerThread源码解析(内部机制)

/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.os;

/**
 * Handy class for starting a new thread that has a looper. The looper can then be 
 * used to create handler classes. Note that start() must still be called.
 */
public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    
    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread 
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    /**
     * Quits the handler thread's looper.
     * <p>
     * Causes the handler thread's looper to terminate without processing any
     * more messages in the message queue.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p class="note">
     * Using this method may be unsafe because some messages may not be delivered
     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
     * that all pending work is completed in an orderly manner.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     *
     * @see #quitSafely
     */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

    /**
     * Quits the handler thread's looper safely.
     * <p>
     * Causes the handler thread's looper to terminate as soon as all remaining messages
     * in the message queue that are already due to be delivered have been handled.
     * Pending delayed messages with due times in the future will not be delivered.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p>
     * If the thread has not been started or has finished (that is if
     * {@link #getLooper} returns null), then false is returned.
     * Otherwise the looper is asked to quit and true is returned.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     */
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}

3.3 HandlerThread使用

(1) HandlerThreadTestActivity .java

package comi.example.liy.mytestdemo;

import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

/**
 * Created by liy on 2019-11-08 9:15
 */
public class HandlerThreadTestActivity extends AppCompatActivity {
    private HandlerThread handlerThread;
    private Handler handler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        handlerThread = new HandlerThread("handler-thread");//创建一个线程(HandlerThread是一个具有Looper对象的特殊线程),名字是handler-thread
        handlerThread.start();//开启线程

        handler = new Handler(handlerThread.getLooper()){//在handler-thread线程中创建Handler对象
            @Override
            public void handleMessage(Message msg) {//该方法运行在handler-thread线程,可进行耗时操作
                super.handleMessage(msg);
                Log.v( "HandlerThread: " , "线程名:" + Thread.currentThread().getName() + ",消息标识:" + msg.what) ;
            }
        };

        handler.sendEmptyMessage(1);//在主线程给Handler发消息

        new Thread(new Runnable() {
            @Override
            public void run() {
                handler.sendEmptyMessage(2);//在子线程给Handler发消息
            }
        }).start();

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handlerThread.quit();//释放资源
    }
}

运行效果:

mytestdemo V/HandlerThread:线程名:handler-thread,消息标识:1
mytestdemo V/HandlerThread:线程名:handler-thread,消息标识:2

4. IntentService

4.1 what is IntentService

IntentService:IntentService本质上是一个内部封装了Handler和HandlerThread的特殊服务(继承Service),在 IntentService 内有一个工作线程来处理耗时操作。

4.2 IntentService源码解析(内部机制)

说明:IntentService本质上是一个封装了HandlerThread和Handler的异步框架,HandlerThread可以在线程中开启循环,利用Handler来发送消息。

/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.app;

import android.annotation.WorkerThread;
import android.annotation.Nullable;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;

/**
 * IntentService is a base class for {@link Service}s that handle asynchronous
 * requests (expressed as {@link Intent}s) on demand.  Clients send requests
 * through {@link android.content.Context#startService(Intent)} calls; the
 * service is started as needed, handles each Intent in turn using a worker
 * thread, and stops itself when it runs out of work.
 *
 * <p>This "work queue processor" pattern is commonly used to offload tasks
 * from an application's main thread.  The IntentService class exists to
 * simplify this pattern and take care of the mechanics.  To use it, extend
 * IntentService and implement {@link #onHandleIntent(Intent)}.  IntentService
 * will receive the Intents, launch a worker thread, and stop the service as
 * appropriate.
 *
 * <p>All requests are handled on a single worker thread -- they may take as
 * long as necessary (and will not block the application's main loop), but
 * only one request will be processed at a time.
 *
 * <div class="special reference">
 * <h3>Developer Guides</h3>
 * <p>For a detailed discussion about how to create services, read the
 * <a href="{@docRoot}guide/topics/fundamentals/services.html">Services</a> developer guide.</p>
 * </div>
 *
 * @see android.os.AsyncTask
 */
public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public IntentService(String name) {
        super();
        mName = name;
    }

    /**
     * Sets intent redelivery preferences.  Usually called from the constructor
     * with your preferred semantics.
     *
     * <p>If enabled is true,
     * {@link #onStartCommand(Intent, int, int)} will return
     * {@link Service#START_REDELIVER_INTENT}, so if this process dies before
     * {@link #onHandleIntent(Intent)} returns, the process will be restarted
     * and the intent redelivered.  If multiple Intents have been sent, only
     * the most recent one is guaranteed to be redelivered.
     *
     * <p>If enabled is false (the default),
     * {@link #onStartCommand(Intent, int, int)} will return
     * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
     * dies along with it.
     */
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    /**
     * You should not override this method for your IntentService. Instead,
     * override {@link #onHandleIntent}, which the system calls when the IntentService
     * receives a start request.
     * @see android.app.Service#onStartCommand
     */
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    /**
     * Unless you provide binding for your service, you don't need to implement this
     * method, because the default implementation returns null.
     * @see android.app.Service#onBind
     */
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     * This method is invoked on the worker thread with a request to process.
     * Only one Intent is processed at a time, but the processing happens on a
     * worker thread that runs independently from other application logic.
     * So, if this code takes a long time, it will hold up other requests to
     * the same IntentService, but it will not hold up anything else.
     * When all requests have been handled, the IntentService stops itself,
     * so you should not call {@link #stopSelf}.
     *
     * @param intent The value passed to {@link
     *               android.content.Context#startService(Intent)}.
     *               This may be null if the service is being restarted after
     *               its process has gone away; see
     *               {@link android.app.Service#onStartCommand}
     *               for details.
     */
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

4.3 IntentService使用方法

(1) IntentServiceTestActivity .java

package comi.example.liy.mytestdemo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * Created by liy on 2019-11-08 9:59
 */
public class IntentServiceTestActivity extends AppCompatActivity {

    private Button button;
    private ProgressBar progressBar;
    private TextView textView;

    private Intent intent;
    public final static String ACTION_TYPE_THREAD = "action.type.thread";

    private LocalBroadcastManager localBroadcastManager;
    private MyBroadcastReceiver myBroadcastReceiver;

    class MyBroadcastReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            if(intent.getAction()==ACTION_TYPE_THREAD){
                String status = intent.getStringExtra("status");
                int progress = intent.getIntExtra("progress",0);
                Log.v("IntentServiceUpdateInfo","status:" + status + ",progress:" + progress + "%");
                textView.setText("status:" + status + ",progress:" + progress + "%");
                progressBar.setProgress(progress);
            }
        }
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_asynctask);
        button = (Button)findViewById(R.id.button03);
        progressBar = (ProgressBar)findViewById(R.id.progressBar02);
        textView = (TextView)findViewById(R.id.textView01);

        button.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                intent = new Intent(IntentServiceTestActivity.this,IntentServiceUpdateInfo.class);
                startService(intent);
            }
        });

        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        myBroadcastReceiver = new MyBroadcastReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ACTION_TYPE_THREAD);
        localBroadcastManager.registerReceiver(myBroadcastReceiver,intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopService(intent);
        localBroadcastManager.unregisterReceiver(myBroadcastReceiver);
    }

}

(2) HandlerThreadTestActivity .java

package comi.example.liy.mytestdemo;

import android.app.IntentService;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

/**
 * Created by liy on 2019-11-08 10:10
 */
public class IntentServiceUpdateInfo extends IntentService {

    private boolean isRunning;
    private int count;

    private LocalBroadcastManager localBroadcastManager;

    public IntentServiceUpdateInfo(){
        super("IntentServiceUpdateInfo");
        Log.v("IntentServiceUpdateInfo","构造方法");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        Log.v("IntentServiceUpdateInfo","服务启动");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.v("IntentServiceUpdateInfo","重写onHandleIntent()方法处理耗时任务");
        try{
            Thread.sleep(1000);
            isRunning = true;
            count = 0;
            while (isRunning){
                count++;
                if (count>=100){
                    isRunning = false;
                    sendThreadStatus("线程结束",count);
                    return;
                }
                Thread.sleep(50);
                sendThreadStatus("线程运行中",count);
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    //发送进度消息:service通过broadcast向activity传递消息
    private void sendThreadStatus(String status, int progress){
        Intent intent = new Intent(IntentServiceTestActivity.ACTION_TYPE_THREAD);
        intent.putExtra("status",status);
        intent.putExtra("progress",progress);
        localBroadcastManager.sendBroadcast(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.v("IntentServiceUpdateInfo","服务结束" +count);
    }
    
}

(3) activity_asynctask.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView
        android:id="@+id/textView01"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />
    <ProgressBar
        android:id="@+id/progressBar02"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"
        />
    <Button
        android:id="@+id/button03"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="更新progressbar"
        />
</LinearLayout>

5. 拓展

(1) 更新UI可通过handler,也可以在runOnUiThread这个方法中。

//更新UI可以在runOnUiThread这个方法或通过handler
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Bitmap bitmap = BitmapFactory.decodeByteArray(data,0,data.length);
                        imageView.setImageBitmap(bitmap);
                    }
                });
上一篇下一篇

猜你喜欢

热点阅读