Android:同步线程和处理程序

Posted

技术标签:

【中文标题】Android:同步线程和处理程序【英文标题】:Android: Synchronizing Threads and Handler 【发布时间】:2013-04-07 17:40:12 【问题描述】:

我的应用程序出现问题。我对 android 开发非常陌生,所以我希望这是一个简单的错误。 所以我正在尝试编写一个应用程序,通过 Wifi(第一个线程)获取一些原始数据,进行一些计算(第二个线程),从计算的数据(第三个线程)创建一个位图并更新显示(处理程序)。 一些参数由 SeekBars 调整。一切正常。但有时当我更改 seekBarValues 时,图片不再更新。线程和处理程序仍在运行,所以我没有收到错误。

这里有一些代码:

主活动:

public class MainActivity extends Activity implements OnClickListener 
    private Handler outputHandler = new Handler();
    private SeekBar  seekBarBrightness,
    seekBarContrast,
    seekBarGamma;

    public static volatile double gamma = 1;
    public static volatile double brightness = 500;
    public static volatile double contrast = 500; 
    public static volatile double min = 0;
    public static volatile double max = 0;

    public static volatile boolean isRunning = false;

    public static volatile int r = 0;
    public static volatile int g = 0;
    public static volatile int b = 0;
    public static volatile int width = 400;
    public static volatile int height = 250;

    public volatile double inputExchangeFrame[][ ]= new double[400][250], outputExchangeFrame[][]= new double[400][250];

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        seekBarBrightness = (SeekBar) findViewById(R.id.seekBarBrightness); 
        seekBarContrast = (SeekBar) findViewById(R.id.seekBarContrast);
        SeekBarGamma = (SeekBar) findViewById(R.id.seekBarGamma);

        newImage = Bitmap.createBitmap(width, height, BitmapConfig.ARGB_8888);              
        picture.setImageBitmap(newImage);


        

    @Override   
    public void onClick(View v)  
        if(v==toggleButtonStartStop)
            if(toggleButtonStartStop.isChecked()==true) 
                isRunning=true; 
                getFrame();
                calculateFrame();
                showFrame();
            
            else 
                isRunning = false;
            
        
                

还有 MainACtivity 中声明的其他方法: getFrame():

private synchronized void getFrame()
     inputThread = new Runnable()
          public void run()
               while(isRunning == true)

                   //inputFrame fron WIFI

                    inputExchangeFrameLock.lock();
                    //synchronized(inputExchangeFrame)
                         for(int x_1=0 ; x_1<width ; x_1++)
                              for(int y_1=0 ; y_1<height ; y_1++)
                                   inputExchangeFrame[x_1][y_1]=inputFrame[x_1][y_1];
                              
                         
                    //
                   inputExchangeFrameLock.unlock();

                   try                         
                        Thread.sleep(120);
                    
                   catch (InterruptedException e) 
                        e.printStackTrace();
                   
              
         
     ;new Thread(inputThread).start();
;

计算帧:

private synchronized void calculateFrame()
     calculationThread = new Runnable()
          public void run()
               while(isRunning == true)    
               //get Data from InputThread to applicate several Filter
               inputExchangeFrameLock.lock();
               //synchronized(inputExchangeFrame)
                    for(int x_2=0 ; x_2<width ; x_2++)
                         for(int y_2=0 ; y_2<height ; y_2++)
                              calculationFrame[x_2][y_2]=inputExchangeFrame[x_2][y_2];
                         
                    
               //
               inputExchangeFrameLock.unlock();

                 //Do some calculations on calculationFrame  

               outputExchangeFrameLock.lock();
               //synchronized(outputExchangeFrame) 
                    for(int x_3=0 ; x_3<width ; x_3++)
                         for(int y_3=0 ; y_3<height ; y_3++)
                              outputExchangeFrame[x_3][y_3] = calculationFrame[x_3][y_3];
                         
                    
               //
               outputExchangeFrameLock.unlock();
               
          
     ; new Thread(calculationThread).start();
;

showFrame():

private synchronized void showFrame()
     outputThread = new Runnable()
          public void 
               while(isRunning == true)
                    contrast =  seekBarContrast.getProgress() +1;
                    brightness = seekBarBrightness.getProgress() + 1;
                    gamma = seekBarGamma.getProgress() + 1;

                    outputExchangeFrameLock.lock();
                    //synchronized(outputExchangeFrame)
                    for(int x_4=0 ; x_4<width ; x_4++)
                         for(int y_4=0 ; y_4<height ; y_4++)
                              outputFrame[x_4][y_4] = outputExchangeFrame[x_4][y_4];
                          
                       
                     //
                     outputExchangeFrameLock.unlock();

                     for (int x_14=0 ; x_14<width ; x_14++)
                          for(int y_14=0; y_14<height; y_14++)

                             //Calculation of r,g,b  using gamma, brightness and contrast
                          
                          synchronized(newImage)
                               newImage.setPixel(x_14, y_14, Color.rgb(r,g,b));
                          
                     

                     outputHandler.removeCallbacks(pictureUpdate);
                     outputHandler.post(pictureUpdate);

                     try 
                          Thread.sleep(50);
                      
                     catch (InterruptedException e) 
                          e.printStackTrace();
                     
                
           
     ; new Thread(outputThread).start();   
;

和处理程序:

私有 Runnable 图片Update = new Runnable() 公共无效运行() 同步(新图像) 图片.setImageBitmap(newImage); ;

我知道那是很多文本。但我真的不知道从哪里开始搜索以及我可以做些什么来找到错误。

任何建议都会很棒!

谢谢

迈克尔

【问题讨论】:

【参考方案1】:

您正在实现几个线程,而您尝试执行的操作的顺序是不确定的。

例如,可以在 getFrame.Runable().run() 之前执行“showFrame.Runable().run()”。

    “同步”关键字没有帮助,因为当工作在单独的线程中停止时。同步函数调用将返回,锁将被释放。 “锁定”也无助于确保正确的顺序。

我的建议是使用 AsyncTask,http://developer.android.com/reference/android/os/AsyncTask.html。你可以这样做:

private class ProcessFrameTask extends AsyncTask 

 protected void doInBackground(Object... objs) 
     getFrame(); // Copy the frame
     calculateFrame(); // Do your magic
 

 protected void onPostExecute(Long result) 
     showFrame(); // Post the result back to main thread
 

没有多线程和协作,只有一个 Worker 线程完成所有工作,并将结果发布回主线程。

【讨论】:

感谢您的快速回复。我已经想到了 AsynkTask 解决方案。但问题是计算部分非常占用 CPU,Wifi 数据速率非常慢。我的概念是始终从 Wifi 获取新的 rawData,以便我始终在计算线程中拥有实际数据。只有一个工作线程会减慢速度还是我错了?是否可以在 Asynktask 中执行一段时间(true)或者我应该执行“new ProcessFrameTask.execute();”在 onPostExecute 中? 1.多线程通常不会增加吞吐量。如果你有多个核心,也许它会这样做,你可以做一些测试来看看。 2. 如果瓶颈是 wifi,并且您希望用户看到传入的数据,请使用 java.util.concurrent 进行多线程处理,而不是自己实现。一种简单的方法是让一个线程等待数据,并在有足够数据时发布到另一个线程进行处理。 所以现在我尝试了您的解决方案。不幸的是同样的问题。一切正常,但有时在我更改任何 seekBarValue 后图片不再更新。我尝试了一些 USB 调试,现在我得到了这个错误:“OpenGLRenderer 无法从位图生成纹理”。错误是否可能是,我的图片的像素比 ImageView 少?我希望 android 自动插值。但似乎我必须自己做这件事。或者有没有其他问题。谢谢 我不确定是什么问题。堆栈跟踪会很好。并且,还要尝试通过确保您的代码在没有任何线程代码的情况下工作来分离关注点。 我想我解决了这个问题。我不确定,但我认为该应用程序希望从标记为 GC 销毁的对象中创建位图。所以该对象仍然存在,但不能用于更新显示。这就是为什么没有错误消息的原因。现在我创建一个新的位图对象,每个循环都计算位图,它似乎可以工作。我将采用您推荐的 AsyncTasak 解决方案。同步要容易得多。所以非常感谢。帮了我很多!

以上是关于Android:同步线程和处理程序的主要内容,如果未能解决你的问题,请参考以下文章

android 上的 SQLite 数据库、多线程、锁和帐户同步

Android 线程处理

Android:处理同步网络调用

Android 线程和处理程序没有停止

Android C++系列:Linux线程线程同步

Android多线程研究——线程同步和相互排斥及死锁