2022年2月28日 星期一

Android Studio - 每秒被重複調用的方法(五十四)

Android Studio - 每秒被重複調用的方法(五十四):

由於經常會使用 Timer 來做事情,所以將每秒被重複調用的方法記錄下來,方便將來使用。

Handler Count Down 每秒調用的程式
  • 操作系統:Windows 7 64-bit 版本
  • 開發環境:Android Studio 4.0.1 版本
  • 原程式:C:\Development\Development_Android\Android_Project\DIY-Android-009-18 Handler 1sTimer
  • 程式:C:\Development\Development_Android\Android_Project\ DIY-Android-009-18 Handler 1sTimer

Thread 線程每秒被重複調用範例:

        new Thread(new Runnable() {

            @Override

            public void run() {

                while (true) {

                    // write code

                    System.out.println(duration);

                    duration--;

                    try { Thread.sleep(1000);

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

            }

        }).start();


Timer,TimerTask 每秒被重複調用一次範例:

        Timer timer = new Timer();

        TimerTask t = new TimerTask() {

            @Override

            public void run() {

                System.out.println(duration);

                duration--;

            }

        };

        timer.scheduleAtFixedRate(t,1000,1000);


Count Down Timer 每秒被重複調用一次範例:

        new CountDownTimer(10000, 1000) {

            public void onTick(long millisUntilFinished) {

                // write code

                mtv_text.setText("Seconds remaining: " + millisUntilFinished / 1000);

            }

 

            public void onFinish() {

                mtv_text.setText("Counter Down Finished");

            }

        }.start();


Handler 每秒被重複調用一次範例:

public class MainActivity extends AppCompatActivity {

 

    private TextView mtv_text;

    private Handler countDownTimer;

    private Runnable runnable;

    private int duration = 10;

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        mtv_text = (TextView) findViewById(R.id.tv_text);

 

        countDownTimer = new Handler(Looper.getMainLooper());

        runnable=new Runnable() {

            @Override

            public void run() {

                mtv_text.setText("Count Down Timer Per Second \n" + duration +" second");

                duration--;

                countDownTimer.postDelayed(this,1000);

            }

        };

 

        countDownTimer.post(runnable);

    }

 

}


2022年 2月 28日(Mon)天氣報告
氣溫:49.0°F / 9.0°C @ 07:00
風速:每小時 11公里
降雨機會:100%
相對濕度:百分之 96%
天氣:雨

2022年2月27日 星期日

Android Studio - 解決 Handler Deprecated 棄用方法(五十三)

Android Studio - 解決 Handler Deprecated 棄用方法(五十三):

Handler 此構造函數在 Android 11 中已棄用(Handler() is deprecated),因為 Handler 可能會導致操作靜默丟失或崩潰的錯誤,但可以更改代碼,便可以繼續使用相同的功能,當然可以加入忽略警告代碼(@SuppressWarnings(“deprecation”);),但也可以重寫 Handler 代碼。

Android Studio Handler is Deprecated Message
使用以前的方法重寫 Handler 的 handleMessage 方法提示棄用:

Handler handler = new Handler(){

     // 提示deprecated

    @SuppressLint("HandlerLeak")

    @Override

    public void handleMessage(@NonNull Message msg) {

   

        super.handleMessage(msg);

        TextView tv_thread = findViewById(R.id.tv_thread);

        tv_thread.setText("" + msg.obj);

    }

};


改用新增 Looper.getMainLooper() 作解決:

Handler handler = new Handler(Looper.getMainLooper()){

     // 解决

    @SuppressLint("HandlerLeak")

    @Override

    public void handleMessage(@NonNull Message msg) {

   

        super.handleMessage(msg);

        TextView tv_thread = findViewById(R.id.tv_thread);

        tv_thread.setText("" + msg.obj);

    }

};


2022年 2月 27日(Sun)天氣報告
氣溫:47.0°F / 8.0°C @ 07:00
風速:每小時 21公里
降雨機會:50%
相對濕度:百分之 91%
天氣:多雲

2022年2月26日 星期六

Android Studio - ExecutorService(線程池)(五十二)

Android Studio - ExecutorService(線程池)(五十二):

當要使用大量的非同步任務,不想每次都創建子線程,可以使用 ExecutorService(線程池),令到減輕每個非同步任務的調用開銷和提高性能。

Android ExecutorService(線程池)
new Thread() 的缺點:
  • 每次 new Thread() 耗費性能
  • 調用 new Thread() 創建的線程缺乏管理,被稱為野線程,而且可以無限制創建,之間相互競爭,會導致過多佔用系統資源導致系統癱瘓
  • 不利於擴展,比如如定時執行、定期執行、線程中斷

ExecutorService 的優點:
  • 重用存在的線程,減少物件創建、消亡的開銷,性能佳
  • 可有效控制最大併發線程數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞,提供定時執行、定期執行、單線程、併發數控制等功能

通過 Executors 的靜態方法來創建,一般有三種:
  • 單線程:Executors.newSingleThreadExecutor()
  • 固定數量線程:Executors.newFixedThreadPool()
  • 動態線程:Executors.newCachedThreadPool()

ThreadPoolExecutor 類的典型構造:

public ThreadPoolExecutor(int corePoolSize,

                              int maximumPoolSize,

                              long keepAliveTime,

                              TimeUnit unit,

                              BlockingQueue<Runnable> workQueue) {

        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,

             Executors.defaultThreadFactory(), defaultHandler);

    }


ThreadPoolExecutor 的配置參數:

public ThreadPoolExecutor(int corePoolSize,

                      int maximumPoolSize,

                      long keepAliveTime,

                      TimeUnit unit,

                      BlockingQueue workQueue,

                      ThreadFactory threadFactory,

                      RejectedExecutionHandler handler) {

    this.corePoolSize = corePoolSize;

    this.maximumPoolSize = maximumPoolSize;

    this.workQueue = workQueue;

    this.keepAliveTime = unit.toNanos(keepAliveTime);

    this.threadFactory = threadFactory;

    this.handler = handler;

}


操作系統:Windows 7 64-bit 版本
開發環境:Android Studio 4.0.1 版本
原程式:C:\Development\Development_Android\Android_Project\DIY-Android-009-01D ExecutorService
程式:C:\Development\Development_Android\Android_Project\DIY-Android-009-01D ExecutorService

MainActivity.xml:

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import android.view.View;

import android.widget.TextView;

import java.util.concurrent.LinkedBlockingDeque;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

 

public class MainActivity extends AppCompatActivity {

 

    // Gets the number of available cores (not always the same as the maximum number of cores)

    private static int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();

    // Sets the amount of time an idle thread waits before terminating

    private static final int KEEP_ALIVE_TIME = 1000;

    // Sets the Time Unit to Milliseconds

    private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.MILLISECONDS;

    // Used to update UI with work progress

    private int count = 0;

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

    }

 

    // This is the runnable task that we will run 100 times

    private Runnable runnable = new Runnable() {

        @Override

        public void run() {

            // Do some work that takes 100 milliseconds

            try {

                Thread.sleep(100);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

 

            // Update the UI with progress

            runOnUiThread(new Runnable() {

                @Override

                public void run() {

                    count++;

                    String msg = count < 100 ? "Thread Working " : "Finished ... ";

                    updateStatus(msg + count);

                }

            });

        }

    };

 

    // Button click and performs work using a thread pool

    public void buttonClickThreadPool(View view) {

        count = 0;

        ThreadPoolExecutor mThreadPoolExecutor = new ThreadPoolExecutor(

                NUMBER_OF_CORES + 2,        // Initial pool size

                NUMBER_OF_CORES + 2,   // Max pool size

                KEEP_ALIVE_TIME,       // Time idle thread waits before terminating

                KEEP_ALIVE_TIME_UNIT,  // Sets the Time Unit for KEEP_ALIVE_TIME

                new LinkedBlockingDeque<Runnable>());  // Work Queue

        for (int i = 0; i < 100; i++) {

            mThreadPoolExecutor.execute(runnable);

        }

    }

 

    private void updateStatus(String msg) {

        ((TextView) findViewById(R.id.text)).setText(msg);

    }

}


active_main.xml:

<?xml version="1.0" encoding="utf-8"?>

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:fillViewport="false">

    <LinearLayout

        android:orientation="vertical"

        android:layout_width="match_parent"

        android:layout_height="match_parent">

 

        <TextView

            android:id="@+id/text"

            android:layout_width="match_parent"

            android:layout_height="53dp"

            android:text="TextView" />

 

        <Button

            android:id="@+id/poolButton"

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:onClick="buttonClickThreadPool"

            android:text="Thread Pool" />

    </LinearLayout>

</ScrollView>



Android Studio ExecutorService(線程池)程式
2022年 2月 26日(Sat)天氣報告
氣溫:37.0°F / 3.0°C @ 07:00
風速:每小時 5公里
降雨機會:1%
相對濕度:百分之 53%
天氣:晴

2022年2月25日 星期五

Android Studio - AsyncTask(非同步任務)(五十一)

Android Studio - AsyncTask(非同步任務)(五十一):

AsyncTask(非同步任務)允許我們的執行一個異步的任務在後台。我們可以將耗時的操作放在異步任務當中來執行,並隨時將任務執行的結果返回給我們的 UI 線程來更新我們的 UI 控件。通過 AsyncTask 我們可以輕鬆的解決多線程之間的通信問題。
※ AsyncTask 在 Android 11 (API level 30) 已經被廢棄 (deprecated) 了

Android Studio AsyncTask 過程
AsyncTask 的有五個步驟:
  • onPreExecute():處理前的最先動作,例如初始化某些參數或是顯示提示告訴使用者。
  • doInBackground(Void... arg0):實際背景處理的工作。
  • onProgressUpdate(Integer... values):處理過程中需要更新的動作,例如下載進度,在 doInBackground(Void... arg0) 中調用的方法為 publishProgress(values),其中參數 values 為整數陣列。
  • onPostExecute(String result):處理完的動作,例如提示使用者完成的訊息,或是更新介面。
  • onCancelled():當被取消時需要作的事,例如提示使用者任務取消,並更新介面。

AsyncTask 程式架構範例:

class GoodTask extends AsyncTask<Void, Integer, String> {

                // <傳入參數, 處理中更新介面參數, 處理後傳出參數>

    @Override

    protected String doInBackground(Void... arg0) {

        // TODO Auto-generated method stub

  

        // 再背景中處理的耗時工作

  

        return null; // 會傳給 onPostExecute(String result) String result

    }

 

    @Override

    protected void onPreExecute() {

        // TODO Auto-generated method stub

        super.onPreExecute();

 

        // 背景工作處理""需作的事

    }

 

    @Override

    protected void onProgressUpdate(Integer... values) {

        // TODO Auto-generated method stub

        super.onProgressUpdate(values);

  

        // 背景工作處理""更新的事

    }

 

    @Override

    protected void onPostExecute(String result) {

        // TODO Auto-generated method stub

        super.onPostExecute(result);

 

        // 背景工作處理完""需作的事

    }

 

    @Override

    protected void onCancelled() {

        // TODO Auto-generated method stub

        super.onCancelled();

  

        // 背景工作被"取消"時作的事,此時不作 onPostExecute(String result)

    }

}


AsyncTask 達成由  1 到 10 範例:

class GoodTask extends AsyncTask<Integer, Integer, String> {

                // <傳入參數, 處理中更新介面參數, 處理後傳出參數>

    int nowCount;

    @Override

    protected String doInBackground(Integer... countTo) {

        // TODO Auto-generated method stub

        // 再背景中處理的耗時工作

        try {

            for (int i = 0; i < countTo[0]; i++) {

                Thread.sleep(1000);

 

                nowCount = i + 1;

                publishProgress(nowCount);

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

        return "10";

    }

 

    @Override

    protected void onPreExecute() {

        // TODO Auto-generated method stub

        super.onPreExecute();

        // 背景工作處理""需作的事

        Toast.makeText(getApplicationContext(),

        "開始計時...", Toast.LENGTH_SHORT).show();

    }

 

    @Override

    protected void onProgressUpdate(Integer... values) {

        // TODO Auto-generated method stub

        super.onProgressUpdate(values);

        // 背景工作處理""更新的事

        txtCount.setText("目前計到 " + values[0] + " 秒。");

    }

 

    @Override

    protected void onPostExecute(String result) {

        // TODO Auto-generated method stub

        super.onPostExecute(result);

        // 背景工作處理完""需作的事

        Toast.makeText(getApplicationContext(),

        "接受到的完成參數為 "+ result + ",計時完成!!", Toast.LENGTH_SHORT).show();

    }

 

    @Override

    protected void onCancelled() {

        // TODO Auto-generated method stub

        super.onCancelled();

        // 背景工作被"取消"時作的事,此時不作 onPostExecute(String result)

        Toast.makeText(getApplicationContext(),

        "好可惜,計到 " + nowCount + " 秒!",  Toast.LENGTH_SHORT).show();

    }

}


AsyncTask 達成由 1 到 10 onCreate 範例:

@Override

public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

       

    txtCount = (TextView) findViewById(R.id.txtCount);

    btnCancel = (Button) findViewById(R.id.btnCancel);

    btnStart = (Button) findViewById(R.id.btnStart);

       

    btnStart.setOnClickListener(new OnClickListener() {

        public void onClick(View v) {

            // TODO Auto-generated method stub

            if (goodTask == null) {

                goodTask = new GoodTask();

                goodTask.execute(10);

            } else {

                if (goodTask.isCancelled()

                    || goodTask.getStatus().equals(AsyncTask.Status.FINISHED)) {

                    goodTask = new GoodTask();

                    goodTask.execute(10);

                }

            }

        }

   });

       

   btnCancel.setOnClickListener(new OnClickListener() {

       public void onClick(View v) {

           // TODO Auto-generated method stub

           if (goodTask != null) {

               if (!goodTask.isCancelled()

                   && goodTask.getStatus().equals(AsyncTask.Status.RUNNING)) {

                   goodTask.cancel(true);

               }

           }

       }

   });

}


AsyncTask 達成由 1 到 10 的 activity_main.xml 範例:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent" >

 

    <TextView

        android:id="@+id/txtCount"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerHorizontal="true"

        android:layout_centerVertical="true"

        android:padding="@dimen/padding_medium"

        android:text="@string/hello_world"

        tools:context=".MainActivity" />

 

    <Button

        android:id="@+id/btnCancel"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_below="@+id/txtCount"

        android:layout_centerHorizontal="true"

        android:layout_marginTop="60dp"

        android:text="取消" />

 

    <Button

        android:id="@+id/btnStart"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_above="@+id/txtCount"

        android:layout_centerHorizontal="true"

        android:layout_marginBottom="73dp"

        android:text="開始" />

 

</RelativeLayout>


2022年 2月 25日(Fri)天氣報告
氣溫:27.0°F / -3.0°C @ 07:00
風速:每小時 3公里
降雨機會:2%
相對濕度:百分之 78%
天氣:晴