Android筆記——初用線程(純筆記,LOW,無太深個人理解)

三更燈火五更雞,正是男兒讀書時。黑發(fā)不知勤學早,白首方悔讀書遲?!佌媲?/p>

因為內(nèi)容還沒學太深,這篇就是已學到的知識做個總結(jié),還沒做太多個人的思考研究,做下記錄留給自己看以后好補充。

一、簡單方法在WORK線程中更新UI

學習一段時間了,漸漸知道網(wǎng)絡連接、IO操作之類應該放在線程中運行,而有時候這類操作過程中我們也許在某一步之后獲得數(shù)據(jù)希望將這個數(shù)據(jù)顯示到界面的時候,就會有些問題,你會發(fā)現(xiàn)若是在線程中使用setText這類影響UI的方法時程序就會產(chǎn)生異常,因為我們Android的UI是線程不安全的,所以我們要想更新UI就必須在主線程(UI線程)。但是正如上面舉例的情況,我們有時必須要在線程中根據(jù)其中運行獲得的數(shù)據(jù)更新UI,這時我們有幾種比較好的解決方案:Handler、AsyncTask.....但也許你的線程內(nèi)的操作并不復雜,或是只想簡單的把運行結(jié)果顯示到UI上,可以用下面的幾個簡單的方法。

Activity.runOnUiThread(Runnable action)

正如這段英文的意思“運行在UI線程”,不用多說啦,它的使用方法就是:

new Thread(new Runnable() {
    @Override
    public void run() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mTestTextView.setText("啦啦啦");
            }
        });
    }
}).start();

這樣就簡單的在線程中更新了TextView。當然你可以在runOnUiThread的前后進行其它操作。我們可以看看它的源碼:

/**
 * Runs the specified action on the UI thread. If the current thread is the UI
 * thread, then the action is executed immediately. If the current thread is
 * not the UI thread, the action is posted to the event queue of the UI thread.
 *
 * @param action the action to run on the UI thread
 */

public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

可知該方法會首先判斷現(xiàn)在的線程是不是UI線程,是則已,不是則實際使用了Handler的post方法來處理了我們寫在runOnUiThread的這段操作。

⑵View.post(Runnable action)

這個方法很尷尬的,我在做類似runOnUiThread那樣的操作的時候失敗了(因為我是在onCreate方法中寫的線程,下面會說為啥不行),這個方法是從網(wǎng)上看到的和runOnUiThread一起被列出的,為啥沒效果呢?我稍微研究了一下,記錄一下我的錯誤使用,先來看看它的源碼:

/**
 * <p>Causes the Runnable to be added to the message queue.
 * The runnable will be run on the user interface thread.</p>
 *
 * @param action The Runnable that will be executed.
 *
 * @return Returns true if the Runnable was successfully placed in to the
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 *
 * @see #postDelayed
 * @see #removeCallbacks
 */
public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.post(action);
    }
    // Assume that post will succeed later
    ViewRootImpl.getRunQueue().post(action);
    return true;
}

首先可以看到該方法會先判斷AttachInfo對象的內(nèi)容是否為空,若不為空,則同樣是用了Handler方法來處理我們的runnable,若為空,可以看到下面的注釋“假設post最后能成功”(什么鬼?)首先ViewRootImpl這個類與視圖的繪制有關——擴展閱讀ViewRootImpl,然后可以看到其利用了其內(nèi)部方法得到運行隊列,且其post方法是其靜態(tài)最終內(nèi)部類RunQueue中自定義的post方法,而其中也有postDelayed方法,參數(shù)為(Runnable action, long delayMillis) 第二個參數(shù)就是延遲毫秒數(shù)。總之看起來View.post()方法看起來就像Handler的post方法,但為什么我像runOnUiThread那樣使用卻無效呢,我百度了一下查到一句這樣的話“This method can be invoked from outside of the UI thread only when this View is attached to a window.”意思大概是該方法要想影響到UI線程必須等到視圖(view)附加到窗口(window)。什么鬼?最后參考了一百度了一下這個“attached to a window”了解到Activity中還有個onAttachedToWindow ()方法——擴展閱讀他人博客—onAttachedToWindow ()。我們可以知道我們要使用View.post()這個方法就必須等onAttachedToWindow ()這個方法執(zhí)行過了才行,且啟動程序時這個onAttachedToWindow ()運行在onResume()之后,而我在onCreate中開線程異步操作可能在TextView還沒attached to a window就post了,所以沒有產(chǎn)生絲毫的效果。有興趣的朋友可以看上面的大佬的博客多了解一下,我理解的可能不太對。(這個方法我最后實驗成功是把線程寫在一個按鈕的點擊監(jiān)聽器里,我點擊的時候自然已經(jīng)“attached to a window”了)

LOG

⑶View.postDelayed(Runnable action, long delayMillis)

這個就是上一個方法的延時方法,第二個參數(shù)寫要延時的毫秒數(shù)就行了,1000就是1秒。

二、利用線程進行簡單的下載(就是做個筆記)

直接上代碼吧:
  DownloadActivity.class

import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.net.URLConnection;

public class DownloadActivity extends AppCompatActivity {

    public static final String TAG = DownloadActivity.class.getSimpleName() + "-TAG";
    private Button mDownloadButton;
    private ProgressBar mDownloadProgressBar;
    private Handler mHandler = new DownloadHandler(this);
    private TextView mProgressTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_download);
        findViews();
        mDownloadButton.setOnClickListener(myClick);
    }

    private void findViews() {
        mDownloadButton = (Button) findViewById(R.id.btn_download);
        mDownloadProgressBar = (ProgressBar) findViewById(R.id.progressBar_download);
        mProgressTextView = (TextView) findViewById(R.id.textView_progress);
    }

    private View.OnClickListener myClick = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.btn_download:
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                URL url = new URL("http://download.qidian.com/epub/3666197.epub");//下載鏈接
                                URLConnection connection = url.openConnection();
                                InputStream downloadStream = connection.getInputStream();
                                int contentLength = connection.getContentLength();//獲得文件長度
                                Log.i(TAG, "contentLength: " + contentLength);
                                if (contentLength <= 0) {
                                    return;
                                }
                                //文件存放路徑
                                String downloadFolderName = Environment.getExternalStorageDirectory() + File.separator + "qdf" + File.separator;
                                Log.i(TAG, "下載路徑  " + downloadFolderName);
                                File file = new File(downloadFolderName);
                                if (!file.exists()) {
                                    //如果目錄不存在則創(chuàng)建目錄
                                    file.mkdir();
                                }
                                //文件目錄+文件名
                                String fileName = downloadFolderName + "test.txt";
                                File contentFile = new File(fileName);
                                if (contentFile.exists()) {
                                    contentFile.delete();
                                }

                                int downloadSize = 0;

                                byte[] bytes = new byte[1024];
                                int length;

                                //字節(jié)輸出流
                                OutputStream outputStream = new FileOutputStream(fileName);

                                while ((length = downloadStream.read(bytes)) != -1) {
                                    outputStream.write(bytes, 0, length);
                                    downloadSize += length;
                                    int progress = downloadSize * 100 / contentLength;
                                    //更新UI
                                    Message message = mHandler.obtainMessage();
                                    message.what = 0;
                                    message.obj = progress;
                                    mHandler.sendMessage(message);
                                    //ProgressBar可以在線程中操作
                                    mDownloadProgressBar.setProgress(progress);
                                }
                                Log.i(TAG, "download success");
                                downloadStream.close();
                                outputStream.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }

                        }
                    }).start();
                    break;
            }
        }
    };

    public TextView getProgressTextView() {
        return mProgressTextView;
    }


    public static class DownloadHandler extends Handler {

        public final WeakReference<DownloadActivity> mDownloadActivityWeakReference;

        public DownloadHandler(DownloadActivity downloadActivity) {
            mDownloadActivityWeakReference = new WeakReference<>(downloadActivity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            DownloadActivity activity = mDownloadActivityWeakReference.get();

            switch (msg.what) {
                case 0:
                    int progress = (int) msg.obj;
                    String progress2 = progress + "%";
                    activity.getProgressTextView().setText(progress2);
                    if (progress == 100) {
                        Toast.makeText(activity, "下載成功", Toast.LENGTH_SHORT).show();
                    }
                    break;
            }
        }
    }
}

網(wǎng)絡連接、IO操作自然是要在線程中啦,連接網(wǎng)絡后通過URLConnection 對象來獲得輸入流,并可以利用getContentLength()方法來獲得文件大小。最后利用字節(jié)輸出流輸出文件,其中根據(jù)已下載的大小與總大小之比來更新ProgressBar,因為是在線程中獲取數(shù)據(jù)后立馬更新UI所以使用了Handler。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容