Android线程学习笔记

UI线程要处理所有任务时,那些耗时很长的操作——诸如访问网络或查询数据库等——将会阻塞整个UI(线程)。一旦线程被阻塞,所有事件都不能被分发,包括屏幕绘图事件。从用户的角度看来,应用程序看上去像是挂起了。更糟糕的是,如果UI线程被阻塞超过一定时间(目前大约是5秒钟),用户就会被提示那个可恶的“应用程序没有响应”(ANR)对话框。

此外,Andoid的UI组件包并不是线程安全的。因此不允许从工作线程中操作UI——只能从UI线程中操作用户界面。于是,Andoid的单线程模式必须遵守两个规则:

  1. 不要阻塞UI线程。
  2. 不要在UI线程之外访问Andoid的UI组件包。

工作线程

根据以上对单线程模式的描述,要想保证程序界面的响应能力,关键是不能阻塞UI线程。如果操作不能很快完成,应该让它们在单独的线程中运行(“后台”或“工作”线程)。

例如:以下响应鼠标点击的代码实现了在单独线程中下载图片并在ImageView显示:

public void onClick(View v) { 
    new Thread(new Runnable() { 
        public void run() { 
            Bitmap b = loadImageFromNetwork("http://example.com/image.png"); 
            mImageView.setImageBitmap(b); 
        } 
    }).start(); 
}

乍看起来,这段代码似乎能运行得很好,因为创建了一个新的线程来处理访问网络的操作。可是它违反了单线程模式的第二条规则:不要在UI线程之外访问Andoid的UI组件包——以上例子在工作线程里而不是UI线程里修改了ImageView。这可能导致不明确、不可预见的后果,要跟踪这种情况也是很困难很耗时间的。

为了解决以上问题,Android提供了几种途径来从其它线程中访问UI线程。下面列出了有助于解决问题的几种方法:

比如说,可以使用View.post(Runnable)方法来修正上面的代码:

public void onClick(View v) { 
    new Thread(new Runnable() { 
        public void run() { 
            final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png"); 
            mImageView.post(new Runnable() { 
                public void run() { 
                    mImageView.setImageBitmap(bitmap); 
                } 
            }); 
        } 
    }).start(); 
}

以上代码的执行现在是线程安全的了:网络相关的操作在单独的线程里完成,而ImageView是在UI线程里操纵的。

不过,随着操作变得越来越复杂,这类代码也会变得很复杂很难维护。为了用工作线程完成更加复杂的交互处理,可以考虑在工作线程中用Handler来处理UI线程分发过来的消息。当然,最好的解决方案也许就是继承使用异步任务类AsyncTask,此类简化了一些工作线程和UI交互的操作。

使用异步任务

异步任务AsyncTask 允许以异步的方式对用户界面进行操作。它先阻塞工作线程,再在UI线程中呈现结果,在此过程中不需要对线程和handler进行人工干预。

要使用异步任务,必须继承AsyncTask类并实现doInBackground()回调方法,该对象将运行于一个后台线程池中。要更新UI时,须实现onPostExecute()方法来分发doInBackground()返回的结果,由于此方法运行在UI线程中,所以就能安全地更新UI了。然后就可以在UI线程中调用execute()来执行任务了。

例如,可以利用AsyncTask来实现上面的那个例子:

public void onClick(View v) { 
    new DownloadImageTask().execute("http://example.com/image.png"); 
} 
 
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { 
    /** The system calls this to perform work in a worker thread and 
      * delivers it the parameters given to AsyncTask.execute() */ 
    protected Bitmap doInBackground(String... urls) { 
        return loadImageFromNetwork(urls[0]); 
    } 
     
    /** The system calls this to perform work in the UI thread and delivers 
      * the result from doInBackground() */ 
    protected void onPostExecute(Bitmap result) { 
        mImageView.setImageBitmap(result); 
    } 
}

现在UI是安全的,代码也得到简化,因为任务分解成了工作线程内完成的部分和UI线程内完成的部分。

要全面理解这个类的使用,须阅读AsyncTask的参考文档。

一、Handler+Thread

public class HandlerActivity extends Activity {

    private TextView txtCount;

    private int mCount;

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 111:
                    int count = (int) msg.obj;
                    txtCount.setText("网络请求的数据:"+count);
                    break;
                default:
                    break;
            }
        }
    };

    private class MyThread extends Thread{

        @Override
        public void run() {

            while (true){
                //模拟网络请求
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                mCount ++;

                Message msg = mHandler.obtainMessage();
                msg.what = 111;
                msg.obj = mCount;
                mHandler.sendMessage(msg);
            }

        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        txtCount = (TextView) findViewById(R.id.txtCount);

        new MyThread().start();
    }


}


二、AsyncTask 从网络下载一张图片显示到UI界面,AsyncTask的几处回调都给了我们机会去中断任务,在任务状态的管理上较之Thread()方式更为灵活。值得注意的是AsyncTask的cancel()方法并不会终止任务的执行,开发者需要自己去检查cancel的状态值来决定是否中止任务。AsyncTask也有隐式的持有外部类对象引用的问题,需要特别注意防止出现意外的内存泄漏。AsyncTask由于在不同的系统版本上串行与并行的执行行为不一致,被不少开发者所诟病,这确实是硬伤,绝大部分的多线程场景都需要明确任务是串行还是并行。

线程优先级为background,对UI线程的执行影响极小。

public class GetInternetImgAsyncTask extends AsyncTask<String,Integer,Bitmap> {
    private ProgressBar mProgressBar;
    private TextView mTextView;
    private ImageView mImageView;

    public GetInternetImgAsyncTask(ProgressBar progressBar, TextView textView, ImageView imageView) {
        this.mProgressBar = progressBar;
        this.mTextView = textView;
        this.mImageView = imageView;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mTextView.setText("下载图片");
    }

    @Override
    protected Bitmap doInBackground(String... params) {

            URL myFileUrl = null;
            Bitmap bitmap = null;
            InputStream is = null;
            HttpURLConnection conn = null;
            try {
                myFileUrl = new URL(params[0]);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
            try {
                conn = (HttpURLConnection)myFileUrl
                        .openConnection();
                conn.setDoInput(true);
                conn.connect();
                is =conn.getInputStream();
                bitmap = BitmapFactory.decodeStream(is);

                byte[] data = new byte[1024];
                int seg = 0;
                long total = conn.getContentLength();
                long current = 0;
                while (!isCancelled()&&(seg = is.read(data)) != -1){
                    current += seg;
                    int progress = (int) ((long)current/total*100);

                    publishProgress(progress);
                }

                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }finally{
                try {
                    if(is != null){
                        is.close();
                    }
                    if( conn != null){
                        conn.disconnect();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return bitmap;

    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        mProgressBar.setProgress(values[0]);
        mTextView.setText(values[0] + "%");
        super.onProgressUpdate(values);

    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (bitmap != null) {
            mImageView.setImageBitmap(bitmap);
        }else {
            mTextView.setText("下载失败");
        }
        mProgressBar.setVisibility(View.GONE);
        mTextView.setText("下载完成");
        super.onPostExecute(bitmap);

    }


    @Override
    protected void onCancelled() {
        super.onCancelled();
        mTextView.setText("任务取消");
    }
}


执行AsyncTask

public class AsyncTaskActivity extends Activity{
    private Button btn_get_img;
    private ProgressBar progressBar;
    private TextView textView;
    private ImageView img;
    private GetInternetImgAsyncTask getInternetImgAsyncTask;

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

        progressBar = (ProgressBar)findViewById(R.id.progressBar02);
        textView = (TextView)findViewById(R.id.textView01);
        img = (ImageView) findViewById(R.id.img_show);
        btn_get_img = (Button) findViewById(R.id.btn_get_img);

        getInternetImgAsyncTask = new GetInternetImgAsyncTask(progressBar,textView,img);

        btn_get_img.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //开启异步线程下载图片
                Log.e("AsyncTaskActivity", "任务是否被取消 :"+getInternetImgAsyncTask.isCancelled());
                if(!getInternetImgAsyncTask.isCancelled()){
                    //为了防止task二次执行导致程序崩溃,需要new一个task
                    getInternetImgAsyncTask = new GetInternetImgAsyncTask(progressBar, textView, img);
                    getInternetImgAsyncTask.execute("http://c.hiphotos.baidu.com/image/pic/item/d439b6003af33a876bcce3f7c35c10385243b5be.jpg");
                }
            }
        });

    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        //取消异步任务
        if(getInternetImgAsyncTask != null && getInternetImgAsyncTask.getStatus() != AsyncTask.Status.FINISHED){
            getInternetImgAsyncTask.cancel(true);
        }

    }
}

三、HandlerThread 定时更新UI界面,在需要对多任务做更精细控制,线程切换更频繁的场景之下,Thread()和AsyncTask都会显得力不从心。HandlerThread却能胜任这些需求甚至更多。HandlerThread将Handler,Thread,Looper,MessageQueue几个概念相结合。Handler是线程对外的接口,所有新的message或者runnable都通过handler post到工作线程。Looper在MessageQueue取到新的任务就切换到工作线程去执行。不同的post方法可以让我们对任务做精细的控制,什么时候执行,执行的顺序都可以控制。HandlerThread最大的优势在于引入MessageQueue概念,可以进行多任务队列管理。HandlerThread背后只有一个线程,所以任务是串行执行的。串行相对于并行来说更安全,各任务之间不会存在多线程安全问题。HandlerThread所产生的线程会一直存活,Looper会在该线程中持续的检查MessageQueue。这一点和Thread(),AsyncTask都不同,thread实例的重用可以避免线程相关的对象的频繁重建和销毁。HandlerThread较之Thread(),AsyncTask需要写更多的代码,但在实用性,灵活度,安全性上都有更好的表现。


public class HandlerThreadActivity extends Activity {

    private TextView txtCount;

    private HandlerThread mHandlerThread;
    private Handler mHandler;

    //处理UI显示的handler
    private Handler mUiHandler = new Handler();

    private boolean isUpdateInfo = false;

    private static final int MSG_UPADATE_INFO = 0x110;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handlerthread);
        txtCount = (TextView) findViewById(R.id.txtCount);

        initHandlerThread();
    }

    private void initHandlerThread() {
        mHandlerThread = new HandlerThread("updatethreadhandler");
        mHandlerThread.start();

        mHandler = new Handler(mHandlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {

                checkForUpdate();

                if(isUpdateInfo){
                    mHandler.sendEmptyMessageDelayed(MSG_UPADATE_INFO, 1000);
                }
            }
        };
    }

    private void checkForUpdate() {
        try {
            Thread.sleep(1000);
            mUiHandler.post(new Runnable() {
                @Override
                public void run() {
                    String result = "实时更新中,当前大盘指数:<font color='red'>%d</font>";
                    result = String.format(result, (int) (Math.random() * 3000 + 1000));
                    txtCount.setText(Html.fromHtml(result));
                }
            });
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    @Override
    protected void onResume() {
        super.onResume();
        isUpdateInfo = true;
        mHandler.sendEmptyMessage(MSG_UPADATE_INFO);
    }

    @Override
    protected void onPause() {
        super.onPause();
        isUpdateInfo = false;
        mHandler.removeMessages(MSG_UPADATE_INFO);

    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandlerThread.quit();
    }
}


理解handler.post(runnable)可以参考  深入理解Looper、Handler、Message三者之间的关系  


四、IntentService,和AsyncTask不同,没有和UI线程的交互,也不像HandlerThread的工作线程会一直存活。IntentService背后其实也有一个HandlerThread来串行的处理Message Queue。只不过在所有的Message处理完毕之后,工作线程会自动结束。所以可以把IntentService看做是Service和HandlerThread的结合体,适合需要在工作线程处理UI无关任务的场景。

public class IntentSer extends IntentService {

    public IntentSer(){
        super("IntentSer");
    }


    private static final String TAG = "IntentSer";
    private String url_path="http://ww2.sinaimg.cn/bmiddle/9dc6852bjw1e8gk397jt9j20c8085dg6.jpg";

    @Override
    public void onCreate() {
        Log.i(TAG, "Service is Created");
        super.onCreate();
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "Service is Destroyed");
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return super.onBind(intent);
    }


    @Override
    protected void onHandleIntent(Intent intent) {
        Log.i(TAG, "HandleIntent is execute");

        try {
            //在设备应用目录创建一个文件
            File file = new File(this.getFilesDir(),"weibo.jpg");
            Log.i(TAG,"图片存储路径:" + file.getAbsolutePath());
            FileOutputStream fos = new FileOutputStream(file);

            //获取网络图片的输入流
            InputStream inputStream = new URL(url_path).openStream();
            //把网络图片输入流写入到文件输出流
            byte[] date = new byte[1024];
            int len = -1;
            while ((len = inputStream.read(date))!=-1){
                fos.write(date,0,len);
            }
            fos.close();
            inputStream.close();
            Log.i(TAG, "The file download is complete");

        } catch (IOException e){
            e.printStackTrace();
        }

    }
}

启动IntentService

Intent service = new Intent(getApplicationContext(),IntentSer.class);
startService(service);


注意,Service需要在manifest.xml文件中注册。






  • 5
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
作为AI语言模型,我只能提供一些Android开发的基础知识和常用概念,希望对您有所帮助。 1. Android组成部分 Android操作系统由四个主要组成部分组成:应用程序、应用程序框架、系统运行库和硬件抽象层。 2. Activity Activity是Android应用程序中的一个组件,用于表示应用程序中的一个屏幕。Activity可以包含用户界面元素,如文本框、按钮和图像。 3. Intent Intent是Android应用程序中的一个基本组件,用于在不同的组件之间传递数据。Intent可以用于启动Activity、启动服务、发送广播等。 4. 布局 布局是Android应用程序中用于定义用户界面的XML文件。布局文件定义了应用程序中的视图层次结构,包括文本框、按钮、图像等。 5. AndroidManifest.xml AndroidManifest.xml是Android应用程序中的一个必需文件,用于描述应用程序的基本信息。该文件包含应用程序的包名、应用程序的入口Activity、应用程序需要的权限等信息。 6. 生命周期 Android应用程序中的每个组件都有自己的生命周期,包括创建、启动、暂停和销毁。了解组件的生命周期可以帮助开发者更好地管理应用程序的资源。 7. 线Android应用程序中的线程用于执行长时间运行的操作,如下载文件或执行计算。但是,在主线程中执行长时间运行的操作会导致应用程序变得缓慢或停止响应,因此必须使用异步线程。 8. Service Service是Android应用程序中的一种组件,用于在后台执行长时间运行的操作。与Activity不同,Service没有用户界面,可以在后台执行。 9. 广播 广播是Android应用程序中的一种机制,用于在不同组件之间传递消息。广播可以用于通知应用程序中的其他组件,例如当设备电池电量低时,应用程序可以发送广播通知其他组件。 10. 内存管理 内存管理是Android应用程序中的一个重要方面。Android应用程序必须管理内存以确保应用程序能够正常运行,并避免出现内存泄漏和内存溢出等问题。可以使用垃圾回收器、使用合适的数据结构、避免创建不必要的对象等方法来管理内存。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值