如何在Android的后台线程上运行代码?


131

我希望一些代码在后台连续运行。我不想在服务中这样做。还有其他办法吗?

我曾尝试Thread在我的班级中调用该类,Activity但我Activity仍然在后台停留一段时间,然后停止。该Thread班也停止工作。

class testThread implements Runnable {
        @Override
        public void run() {
            File file = new File( Environment.getExternalStorageDirectory(), "/BPCLTracker/gpsdata.txt" );
            int i = 0;

            RandomAccessFile in = null;

            try {
                in = new RandomAccessFile( file, "rw" );
            } catch (FileNotFoundException e) {
// TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
// TODO Auto-generated catch block
                e.printStackTrace();
            }
//String line =null;
            while ( true ) {
                HttpEntity entity = null;
                try {
                    if ( isInternetOn() ) {
                        while ( ( line = in.readLine() ) != null ) {

                            HttpClient client = new DefaultHttpClient();
                            String url = "some url";
                            HttpPost request = new HttpPost( url );
                            StringEntity se = new StringEntity( line );
                            se.setContentEncoding( "UTF-8" );
                            se.setContentEncoding( new BasicHeader( HTTP.CONTENT_TYPE, "application/json" ) );
                            entity = se;
                            request.setEntity( entity );
                            HttpResponse response = client.execute( request );
                            entity = response.getEntity();
                            i++;
                        }
                        if ( ( line = in.readLine() ) == null && entity != null ) {
                            file.delete();
                            testThread t = new testThread();
                            Thread t1 = new Thread( t );
                            t1.start();
                        }


                    } else {
                        Thread.sleep( 60000 );
                    } // end of else

                } catch (NullPointerException e1) {
                    e1.printStackTrace();
                } catch (InterruptedException e2) {
                    e2.printStackTrace();
                } catch (IOException e1) {
// TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }// end of while
        }// end of run

    }

1
你好 您可以张贴一些代码作为您尝试过的示例吗?有多种方法可以在后台线程上运行代码,但是必须记住,如果要访问任何UI组件,则必须使用runOnUiThread()方法在UI线程上访问它们。
Alex Wiese 2013年

3
是否有不使用服务的任何特别的理由
DeltaCap019

不建议这样做。连续运行会耗尽设备电池的电量。
HelmiB

我已经发送了代码。
user2082987 2013年

我尝试在服务中使用它,但是它多次发送相同的记录。
user2082987 2013年

Answers:


379

如果你需要:

  1. 在后台线程上执行代码

  2. 执行不触摸/不更新用户界面的代码

  3. 执行(简短)代码,最多需要几秒钟才能完成

然后使用以下使用AsyncTask的干净高效的模式:

AsyncTask.execute(new Runnable() {
   @Override
   public void run() {
      //TODO your background code
   }
});

10
注意:需要API级别11
friederbluemle 2015年

9
您为什么不使用以下更轻量的代码:new Thread(new Runnable(){@Override public void run(){//后台代码}})。start();
雅乐轩'17

3
有可能导致内存泄漏吗?
Hilfritz

5
要注意的一件事是AsyncTasks已排队。如果将此用于一堆服务器api调用,则它们将不会并行运行。
Chase Roberts


45

记住运行后台,连续运行是两个不同的任务。

对于长期的后台进程,线程并不是Android的最佳选择。但是,这是代码,需要您自担风险...

记住Service或Thread将在后台运行,但是我们的任务需要触发(一次又一次地调用)来获取更新,即,一旦任务完成,我们就需要调用该函数以进行下一次更新。

计时器(定期触发),警报(时基触发),广播(事件基触发),递归将唤醒我们的功能。

public static boolean isRecursionEnable = true;

void runInBackground() {
    if (!isRecursionEnable)
        // Handle not to start multiple parallel threads
        return;

    // isRecursionEnable = false; when u want to stop
    // on exception on thread make it true again  
    new Thread(new Runnable() {
        @Override
        public void run() {
            // DO your work here
            // get the data
            if (activity_is_not_in_background) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // update UI
                        runInBackground();
                    }
                });
            } else {
                runInBackground();
            }
        }
    }).start();
}

使用服务: 如果启动服务,它将启动,它将执行任务,并终止自身。任务执行后。终止也可能是由异常引起的,或者用户从设置中手动将其终止。START_STICKY(粘性服务)是android给出的选项,如果服务终止,服务将自行重启。

还记得多处理和多线程之间的问题区别吗? 服务是一个后台进程(就像没有UI的活动一样),与在活动中启动线程以避免在主线程(活动线程)上加载的方式相同,与在服务上启动线程(或异步任务)所需的方式相同避免增加服务负荷。

在单个语句中,如果要运行后台继续任务,则需要启动StickyService并基于事件在服务中运行线程


您是否建议使用此方法在后台平均运行20分钟的自定义SignalStrengthListener,同时每秒从监听器获取一次值以更新UI线程?这里是更多上下文:stackoverflow.com/questions/36167719/…–
JParks

如果设置为“ isRecursionEnable = true”,该过程如何再次运行?然后,您在'runInBackground()'的'run'方法中调用,它如何不在函数开头输入if语句?
米奇'18

23

我希望一些代码在后台连续运行。我不想在服务中这样做。还有其他办法吗?

您正在寻找的最可能的机械是AsyncTask。它直接指定用于在后台线程上执行后台进程。它的主要好处还在于,它提供了一些在Main(UI)线程上运行的方法,如果您想让用户了解任务的某些进展或使用从后台进程检索的数据来更新UI,则可以更新您的UI。

如果您不知道如何开始,这里是个不错的教程:

注意:也有可能IntentService与之配合使用ResultReceiver


@ user2082987所以您尝试了吗?
Simon Dorociak

asyncTask的这些参数令人困惑
user2082987 2013年

无论如何,AsyncTask只是线程的包装,因此它无法解决Android在后台杀死应用程序的问题
Lassi Kinnunen

嗨@LassiKinnunen我5年前就写了这个答案。现在一切都改变了,我目前不使用AsyncTask,因为它的内存泄漏不安全。
Simon Dorociak

是的,我的确意识到这一点,但是人们仍然会通过Google找到它,而且我看到很多人建议使用Google包装器,即使它几乎没有用处,对不起。至于最初的问题,如果有人想要在2018年寻找答案,请创建一个服务并启动它,并通过通知将其标记为前台,如果您希望最大程度地避免它被杀死。实际的长时间运行过程可以使用处理程序或单独的线程进行处理。没意识到他们设法使内存泄漏不安全吗?
Lassi Kinnunen

16

简单的三线

@awardak布兰登·鲁德(Brandon Rude)的答案中以评论的方式找到了一种简单的方法:

new Thread( new Runnable() { @Override public void run() { 
  // Run whatever background code you want here.
} } ).start();

我不确定这是否或如何比使用更好,AsyncTask.execute但似乎对我们有用。关于差异的任何评论将不胜感激。

谢谢,@ awardak


我们使用handler.post()方法将其发布回Thread的run方法内的主线程。
androidXP

2

Robospice是AsyncTask的替代方法。https://github.com/octo-online/robospice

robospice的某些功能。

1.异步执行(在后台AndroidService中)网络请求(例如:使用Spring Android的REST请求)。准备好结果后,在UI线程上通知您的应用。

2.是强类型的!您使用POJO发出请求,并获得POJO作为请求结果。

3.不对用于请求的POJO或项目中使用的Activity类施加任何约束。

4.缓存结果(在Json中同时包含Jackson和Gson,或者Xml,或者纯文本文件,或者二进制文件,甚至使用ORM Lite)。

5.当且仅当活动仍然存在时,才将网络请求的结果通知您的活动(或任何其他上下文)

6,完全没有内存泄漏,就像Android Loaders一样,与Android AsyncTasks在其UI线程上通知您的活动无关。

7.使用简单但健壮的异常处理模型。

样品开始。https://github.com/octo-online/RoboSpice-samples

https://play.google.com/store/apps/details?id=com.octo.android.robospice.motivations&feature=search_result上的机器人机器人示例。


如何将从网络获取的数组存储到SQLite?基本上,我想:从网络获取数据并将其保存到我的SQLite中-都在后台线程中,然后通知我的UI刷新ListView。我通常使用IntentService进行此操作,但RoboSpice的键入较少。
2016年

@yar,如果长期运行,则可以使用意图服务。此外,您也可以使用asynctask,但前提是使用时很短。您可以创建一个线程并在那里做所有的工作。我已经很久没有使用robospice了。还有其他网络库,例如volley ...
Raghunandan

好,谢谢。我曾考虑过使用Robospice,但看起来对我的需求来说不太好(灵活)。我长期使用IntentService成功。
2016年

2
class Background implements Runnable {
    private CountDownLatch latch = new CountDownLatch(1);
    private  Handler handler;

    Background() {
        Thread thread = new Thread(this);
        thread.start();
        try {
            latch.await();
        } catch (InterruptedException e) {
           /// e.printStackTrace();
        }
    }

    @Override
    public void run() {
        Looper.prepare();
        handler = new Handler();
        latch.countDown();
        Looper.loop();
    }

    public Handler getHandler() {
        return handler;
    }
}

5
欢迎使用StackOverflow。其中仅包含代码的答案往往被标记为删除,因为它们是“低质量”的。请阅读有关回答问题的帮助部分,然后考虑在您的答案中添加一些注释。
格雷厄姆(Graham)

0

如果您需要使用不同的代码方便地运行线程,请参见以下示例:

听众:

public interface ESLThreadListener {

    public List onBackground();

    public void onPostExecute(List list);

}

螺纹类

public class ESLThread extends AsyncTask<Void, Void, List> {


    private ESLThreadListener mListener;

    public ESLThread() {

        if(mListener != null){

            mListener.onBackground();
        }
    }

    @Override
    protected List doInBackground(Void... params) {

        if(mListener != null){

            List list = mListener.onBackground();

            return list;
        }

        return null;
    }

    @Override
    protected void onPostExecute(List t) {
        if(mListener != null){

            if ( t != null) {
                mListener.onPostExecute(t);
            }
        }

    }


    public void setListener(ESLThreadListener mListener){

        this.mListener = mListener;
    }
}

运行不同的代码:

  ESLThread thread = new ESLThread();
                        thread.setListener(new ESLThreadListener() {
                            @Override
                            public List onBackground() {
                                List<EntityShoppingListItems>  data = RoomDB.getDatabase(context).sliDAO().getSL(fId);

                                return data;

                            }

                            @Override
                            public void onPostExecute(List t) {

                                List<EntityShoppingListItems> data = (List<EntityShoppingListItems>)t;
                                adapter.setList(data);
                            }
                        });

                        thread.execute();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.