三更燈火五更雞,正是男兒讀書時道宅。黑發(fā)不知勤學(xué)早食听,白首方悔讀書遲胸蛛。——顏真卿
因為內(nèi)容還沒學(xué)太深樱报,這篇就是已學(xué)到的知識做個總結(jié)葬项,還沒做太多個人的思考研究,做下記錄留給自己看以后好補充迹蛤。
一民珍、簡單方法在WORK線程中更新UI
學(xué)習一段時間了,漸漸知道網(wǎng)絡(luò)連接盗飒、IO操作之類應(yīng)該放在線程中運行嚷量,而有時候這類操作過程中我們也許在某一步之后獲得數(shù)據(jù)希望將這個數(shù)據(jù)顯示到界面的時候,就會有些問題逆趣,你會發(fā)現(xiàn)若是在線程中使用setText這類影響UI的方法時程序就會產(chǎn)生異常蝶溶,因為我們Android的UI是線程不安全的,所以我們要想更新UI就必須在主線程(UI線程)宣渗。但是正如上面舉例的情況身坐,我們有時必須要在線程中根據(jù)其中運行獲得的數(shù)據(jù)更新UI,這時我們有幾種比較好的解決方案:Handler落包、AsyncTask.....但也許你的線程內(nèi)的操作并不復(fù)雜部蛇,或是只想簡單的把運行結(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将饺,若為空,可以看到下面的注釋“假設(shè)post最后能成功”(什么鬼?)首先ViewRootImpl這個類與視圖的繪制有關(guān)——擴展閱讀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”了)
⑶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)絡(luò)連接、IO操作自然是要在線程中啦擎淤,連接網(wǎng)絡(luò)后通過URLConnection 對象來獲得輸入流奢啥,并可以利用getContentLength()方法來獲得文件大小。最后利用字節(jié)輸出流輸出文件嘴拢,其中根據(jù)已下載的大小與總大小之比來更新ProgressBar桩盲,因為是在線程中獲取數(shù)據(jù)后立馬更新UI所以使用了Handler。