Android - Cách tốt nhất và an toàn để dừng chuỗi


79

Tôi muốn biết cách tốt nhất để dừng một chuỗi trong Android. Tôi biết tôi có thể sử dụng AsyncTaskthay vì nó và có một cancel()phương pháp. Tôi phải sử dụng Threads trong tình huống của tôi. Đây là cách tôi đang sử dụng Thread:

Runnable runnable = new Runnable() {
        @Override
        public void run() {
            //doing some work
        }
    };
new Thread(runnable).start();

Vì vậy, có ai có bất kỳ ý tưởng về cách tốt nhất để ngăn chặn một chủ đề?

Câu trả lời:


88

Bạn nên làm cho việc hỗ trợ luồng của bạn bị gián đoạn. Về cơ bản, bạn có thể gọi yourThread.interrupt()để dừng chuỗi và trong phương thức run () của mình, bạn cần kiểm tra định kỳ trạng thái củaThread.interrupted()

Có một hướng dẫn tốt ở đây .


Cảm ơn, tôi đã tìm kiếm hủy () hoặc dừng () mọi lúc.
Miloš Černilovský

stop()Phương thức @ MilošČernilovský không được dùng nữa và bạn không nên sử dụng phương thức đó nữa.
Alston

29

Tình huống này không khác gì so với Java tiêu chuẩn. Bạn có thể sử dụng cách tiêu chuẩn để dừng một chuỗi:

class WorkerThread extends Thread {
    volatile boolean running = true;

    public void run() {
        // Do work...
        if (!running) return;
        //Continue doing the work
    }
}

Ý tưởng chính là kiểm tra giá trị của trường theo thời gian. Khi bạn cần dừng chuỗi của mình, bạn đặt runningthành false. Ngoài ra, như Chris đã chỉ ra, bạn có thể sử dụng cơ chế ngắt.

Nhân tiện, khi bạn sử dụng AsyncTask, apporach của bạn sẽ không khác nhiều. Sự khác biệt duy nhất là bạn sẽ phải gọi isCancel()phương thức từ nhiệm vụ của mình thay vì có một trường đặc biệt. Nếu bạn gọi cancel(true), nhưng không thực hiện cơ chế này, luồng vẫn không tự dừng lại, nó sẽ chạy đến cuối.


23

Trên Android áp dụng các quy tắc tương tự như trong môi trường Java bình thường. Trong Java, các luồng không bị giết, nhưng việc dừng một luồng được thực hiện theo cách hợp tác . Luồng được yêu cầu kết thúc và sau đó luồng có thể tắt một cách duyên dáng.

Thường thì một volatile booleantrường được sử dụng mà luồng kiểm tra định kỳ và kết thúc khi nó được đặt thành giá trị tương ứng.

Tôi sẽ không sử dụng một booleanđể kiểm tra xem chuỗi có nên kết thúc hay không . Nếu bạn sử dụng volatilelàm công cụ sửa đổi trường, điều này sẽ hoạt động đáng tin cậy, nhưng nếu mã của bạn trở nên phức tạp hơn, thay vào đó sử dụng các phương pháp chặn khác bên trong whilevòng lặp, thì điều đó có thể xảy ra, mã của bạn sẽ không kết thúc hoặc ít nhất là mất nhiều thời gian hơn khi bạn có thể muốn.

Một số phương pháp thư viện chặn hỗ trợ gián đoạn.

Mọi luồng đều có trạng thái bị gián đoạn cờ boolean và bạn nên tận dụng nó. Nó có thể được thực hiện như thế này:

public void run() {

   try {
      while(!Thread.currentThread().isInterrupted()) {
         // ...
      }
   } catch (InterruptedException consumed)
      /* Allow thread to exit */
   }

}

public void cancel() { interrupt(); }

Mã nguồn lấy từ Java Concurrency trong Thực tế . Vì cancel()phương thức là công khai, bạn có thể cho phép một luồng khác gọi phương thức này như bạn muốn.

Ngoài ra còn có một phương thức static được đặt tên kém interruptedđể xóa trạng thái bị gián đoạn của luồng hiện tại.


2
Trong Android Studio, nó cho biết, Exception 'java.lang.InterruptException' không bao giờ được ném vào khối thử tương ứng?
CraPo

1
@CraPo Thêm cái này trước vòng lặp while:if (Thread.interrupted()) { throw new InterruptedException();}
Kosso

16

Các Thread.stop()phương pháp có thể được sử dụng để ngăn chặn một chủ đề đã được tán thành; để biết thêm thông tin xem; Tại sao Thread.stop, Thread.suspend và Thread.resume không được dùng nữa? .

Đặt cược tốt nhất của bạn là có một biến mà chính chuỗi tham khảo và tự nguyện thoát ra nếu biến đó bằng một giá trị nhất định. Sau đó, bạn thao tác với biến bên trong mã của mình khi bạn muốn luồng thoát. Thay vào đó, tất nhiên, bạn có thể sử dụng một AsyncTaskthay thế.


4
AsyncTask thực sự không phải là một giải pháp thay thế cho cơ chế gián đoạn, nó là một công cụ để tương tác với UI. Như tôi đã chỉ ra trong câu trả lời của mình, bạn sẽ vẫn phải triển khai cơ chế tương tự trong AsyncTask như trong bất kỳ luồng thông thường nào, nếu bạn muốn có thể dừng nó trong khi thực thi.
Malcolm

2

Hiện tại và rất tiếc là chúng tôi không thể làm gì để dừng chuỗi ...

Thêm một cái gì đó vào câu trả lời của Matt, chúng ta có thể gọi interrupt()nhưng điều đó không dừng luồng ... Chỉ yêu cầu hệ thống dừng luồng khi hệ thống muốn giết một số luồng. Phần còn lại được thực hiện bởi hệ thống và chúng tôi có thể kiểm tra bằng cách gọi điện interrupted().

[ps: Nếu bạn thực sự đi cùng, interrupt()tôi sẽ yêu cầu bạn làm một số thí nghiệm với một giấc ngủ ngắn sau khi gọi điện interrupt()]


2

Hãy thử như thế này

Thread thread = new Thread() {
    @Override
    public void run() {
        Looper.prepare();   
        while(true){ 
            Log.d("Current Thread", "Running"); 
            try{
               Thread.sleep(1000);
            }catch(Exeption exception){ }
        }
    }
};

thread.start();
thread.interrupt();

1

Có 2 cách sau được ưu tiên để dừng một chuỗi.

  1. Tạo một biến boolean dễ bay hơi và thay đổi giá trị của nó thành false và kiểm tra bên trong luồng.

    volatile isRunning = false;
    
    public void run() {
    if(!isRunning) {return;}       
    
    }
    
  2. Hoặc bạn có thể sử dụng phương thức ngắt () có thể được nhận bên trong một luồng.

    SomeThread.interrupt();
    
    public void run() {
    if(Thread.currentThread.isInterrupted()) {return;}
     }
    

0

Tôi đã sử dụng phương pháp này.

Looper.myLooper().quit();

bạn co thể thử.


0

Điều này đã làm việc cho tôi như thế này. Giới thiệu một biến tĩnh trong hoạt động chính và thường xuyên kiểm tra xem tôi đã làm như thế nào bên dưới.

public class MainActivity extends AppCompatActivity {


//This is the static variable introduced in main activity
public static boolean stopThread =false;



  protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Thread thread = new Thread(new Thread1());
    thread.start();

    Button stp_thread= findViewById(R.id.button_stop);

    stp_thread.setOnClickListener(new Button.OnClickListener(){
           @Override
           public void onClick(View v){
              stopThread = true;
           }

     }

  }


  class Thread1 implements Runnable{

        public void run() {

        // YOU CAN DO IT ON BELOW WAY 
        while(!MainActivity.stopThread) {
             Do Something here
        }


        //OR YOU CAN CALL RETURN AFTER EVERY LINE LIKE BELOW

        process 1 goes here;

        //Below method also could be used
        if(stopThread==true){
            return ;
        }
        // use this after every line



        process 2 goes here;


        //Below method also could be used
        if(stopThread==true){
            return ;
        }
        // use this after every line


        process 3 goes here;


        //Below method also could be used
        if(stopThread==true){
            return ;
        }
        // use this after every line


        process 4 goes here;



       }
   }

}

0

Nếu có lớp luồng có trình xử lý trong dự án của bạn, khi bạn bắt đầu từ một trong các lớp phân mảnh, nếu bạn muốn dừng ở đây thì giải pháp là làm thế nào để dừng và tránh sự cố ứng dụng khi phân mảnh loại bỏ khỏi ngăn xếp.

Mã này bằng Kotlin . Nó hoạt động hoàn hảo.

class NewsFragment : Fragment() {

    private var mGetRSSFeedsThread: GetRSSFeedsThread? = null

    private val mHandler = object : Handler() {

        override fun handleMessage(msg: Message?) {


            if (msg?.what == GetRSSFeedsThread.GETRSSFEEDSTHREAD_SUCCESS) {
                val updateXMLdata = msg.obj as String
                if (!updateXMLdata.isNullOrEmpty())
                    parseUpdatePager(CommonUtils.getJSONObjectFromXML(updateXMLdata).toString())

            } else if (msg?.what == GetRSSFeedsThread.GETRSSFEEDSTHREAD_SUCCESS) {
                BaseActivity.make_toast(activity, resources.getString(R.string.pleaseTryAgain))
            }

        }
    }
    private var rootview: View? = null;
    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        rootview = inflater?.inflate(R.layout.fragment_news, container, false);

        news_listView = rootview?.findViewById(R.id.news_listView)
        mGetRSSFeedsThread = GetRSSFeedsThread(this.activity, mHandler)


        if (CommonUtils.isInternetAvailable(activity)) {
            mGetRSSFeedsThread?.start()
        }


        return rootview
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true);

    }



    override fun onAttach(context: Context?) {
        super.onAttach(context)
        println("onAttach")
    }

    override fun onPause() {
        super.onPause()
        println("onPause fragment may return to active state again")

        Thread.interrupted()

    }

    override fun onStart() {
        super.onStart()
        println("onStart")

    }

    override fun onResume() {
        super.onResume()
        println("onResume fragment may return to active state again")

    }

    override fun onDetach() {
        super.onDetach()
        println("onDetach fragment never return to active state again")

    }

    override fun onDestroy() {
        super.onDestroy()

        println("onDestroy fragment never return to active state again")
       //check the state of the task
        if (mGetRSSFeedsThread != null && mGetRSSFeedsThread?.isAlive!!) {
            mGetRSSFeedsThread?.interrupt();
        } else {

        }

    }

    override fun onDestroyView() {
        super.onDestroyView()
        println("onDestroyView fragment may return to active state again")



    }

    override fun onStop() {
        super.onStop()
        println("onStop fragment may return to active state again")



    }
}

Đoạn mã trên sẽ dừng chuỗi đang chạy khi bạn chuyển sang bất kỳ phân đoạn hoặc hoạt động nào khác từ phân đoạn hiện tại. cũng như nó tạo lại khi bạn quay lại phân đoạn hiện tại


0

Vấn đề là bạn cần kiểm tra xem luồng có chạy hay không !?

Cánh đồng:

private boolean runningThread = false;

Trong chuỗi:

new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep((long) Math.floor(speed));
                        if (!runningThread) {
                            return;
                        }
                        yourWork();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

Nếu bạn muốn dừng chuỗi, bạn nên tạo trường bên dưới

private boolean runningThread = false;

2
Điều này sẽ không ngăn chặn Thread, nó sẽ chỉ ngừng yourWork();được gọi. Để kiểm tra điều này, bạn có thể thêm vào Logbên dướipublic void run() {
KRK

@KRK Tôi biết điều này! và tôi đã đăng câu trả lời này vì nó rất hữu ích khi bạn chỉ muốn dừng điều gì đó và bắt đầu lại mà không cần làm bất cứ điều gì để phân luồng.
Hadi Note

@HadiNote KRK là chính xác. Bạn nêu câu trả lời của mình If you want to stop the thread you should make the below field: private boolean runningThread = false;. Điều này LAF không đúng.
pookie

0

Yêu cầu của tôi hơi khác so với câu hỏi, nhưng đây cũng là một cách hữu ích để dừng luồng thực thi các tác vụ của nó. Tất cả những gì tôi muốn làm là dừng luồng thoát khỏi màn hình và tiếp tục trong khi quay lại màn hình.

Theo tài liệu Android, đây sẽ là phương pháp thay thế được đề xuất cho phương pháp dừng đã không được dùng nữa từ API 15

Nhiều cách sử dụng stop nên được thay thế bằng mã chỉ đơn giản là sửa đổi một số biến để chỉ ra rằng luồng đích sẽ ngừng chạy. Luồng đích nên kiểm tra biến này thường xuyên và trả về từ phương thức chạy của nó một cách có trật tự nếu biến chỉ ra rằng nó sắp dừng chạy.

Lớp Thread của tôi

   class ThreadClass implements Runnable {
            ...
                  @Override
                        public void run() {
                            while (count < name.length()) {

                                if (!exited) // checks boolean  
                                  {
                                   // perform your task
                                                     }
            ...

OnStop và OnResume sẽ giống như thế này

  @Override
    protected void onStop() {
        super.onStop();
        exited = true;
    }

    @Override
    protected void onResume() {
        super.onResume();
        exited = false;
    }

0

Như chúng ta biết rằng Thread.stop () không được chấp nhận trong JAVA, ẩn chứa trong đó Thread.stop gọi phương thức ngắt () trên luồng để dừng nó, ngắt có nghĩa là được ném ra khỏi các phương thức giữ cho luồng chờ đợi một số luồng khác để thông báo sau khi quá trình thực thi hoàn tất. Ngắt sẽ không gây ra gì cho chuỗi nếu nó không được xử lý trong quá trình thực thi một chuỗi, như, if(Thread.interrupted())return; Vì vậy, về cơ bản chúng ta cần quản lý điểm bắt đầu và điểm dừng của chuỗi như gọi start()phương thức như Thread.start()started a while(true)bên trongrun() phương thức của luồng và kiểm tra trạng thái bị gián đoạn trong mỗi lần lặp và trả về từ luồng.

Xin lưu ý rằng một chuỗi sẽ không chết trong các trường hợp sau:

  1. Luồng vẫn chưa trở lại từ run ().
  2. Bất kỳ đối tượng nào thuộc sở hữu của luồng đều có thể truy cập được. (Điều này gợi ý để vô hiệu / loại bỏ các tham chiếu để GC thực hiện phần còn lại)

-2

Bên trong bất kỳ lớp Activity nào, bạn tạo một phương thức sẽ gán NULL cho cá thể luồng có thể được sử dụng thay thế cho phương thức stop () đã giảm giá trị để dừng thực thi luồng:

public class MyActivity extends Activity {

private Thread mThread;  

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


        mThread =  new Thread(){
        @Override
        public void run(){
            // Perform thread commands...
    for (int i=0; i < 5000; i++)
    {
      // do something...
    }

    // Call the stopThread() method.
            stopThread(this);
          }
        };

    // Start the thread.
        mThread.start(); 
}

private synchronized void stopThread(Thread theThread)
{
    if (theThread != null)
    {
        theThread = null;
    }
}
}

Điều này làm việc cho tôi mà không có vấn đề.


11
Đặt một Threadbiến thành nullsẽ không làm gì để dừng chuỗi. Đặt đối số vào nullbên trong stopThreadlà hoàn toàn lãng phí thời gian.
Ted Hopp,

2
Làm thế nào điều này có thể dừng chuỗi? Tại sao nơi có phiếu tích cực về điều này?
mvd

1
Điều này không dừng lại chủ đề. Thread vẫn chạy và chuyển nhượng này là null đến thể hiện của Chủ đề khiến không thể kiểm soát chủ đề đó, bởi vì bạn mất dụ (mà sẽ tiếp tục chạy cho đến khi quá trình được kết thúc)
Teocci
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.