从单独的外部类更新多个线程的多个视图

问题描述 投票:0回答:1

我正在开发一个游戏项目。我想将我的游戏的每个视图关联到其各自的线程,然后根据该线程中运行的逻辑更新视图。

为了简化,我发布了一个示例:

这是Main Activity类,它将实现UI:

public class Main extends Activity{

    private View root;
    private boolean ready = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        init();
    }

    private void init() {
        setContentView(R.layout.s_main);
        root = findViewById(R.id.root);
        ViewTreeObserver vto = root.getViewTreeObserver();
        vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                root.getViewTreeObserver().removeOnPreDrawListener(this);
                ready = true;
                return true;
            }
        });
    }

    public void start(View view) {
        try {
            if (ready && !Threads.run) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                    new AsyncTasks(this, R.id.txv1).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
                    new AsyncTasks(this, R.id.txv2).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
                } else {
                    new AsyncTasks(this, R.id.txv1).execute();
                    new AsyncTasks(this, R.id.txv2).execute();
                }
            } else {
                Threads.run = false;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

这是AsyncTask扩展类更新View:

public class AsyncTasks extends AsyncTask<Void, Void, String> {

    private TextView view;
    private boolean breakMove;
    private String updateError;

    public AsyncTasks(Activity activity, int viewId) {
        breakMove = false;
        updateError = null;
        view = activity.findViewById(viewId);
    }

    @Override
    protected String doInBackground(Void... voids) {
        String message;
        Threads.run = true;
        try {
            while (!breakMove) {
                publishProgress();
                Thread.sleep(100);
            }
            message = updateError != null ? updateError : "Thread Ends";
        } catch (Exception ex) {
            ex.printStackTrace();
            message = ex.getMessage();
        }
        return message;
    }

    @Override
    protected void onProgressUpdate(Void... values) {
        super.onProgressUpdate(values);
        try {
            breakMove = !Threads.run;
            if (view != null)
                view.setText(String.valueOf(Math.random() * 100));
        } catch (Exception ex) {
            breakMove = true;
            ex.printStackTrace();
            updateError = ex.getMessage();
        }
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
        Threads.run = false;
    }
}

这很好用。但是有一些限制:

  • 对于短期线程,建议使用AsyncTask,而不是Game或Long Running Thread项目。
  • 在最新的android框架中,只有5个AsyncTask线程可以同时运行,其余的将在等待队列中。因此,如果我的项目需要同时更新5个以上的视图,它将无法工作。

我尝试过的:

  • 其他其他Thread实现(如Runnable,Handler,Service等)不允许更新视图。请记住,我的线程是在单独的外部文件或类中编码的。
  • 不建议使用runOnUiThread,因为它在UI线程上运行,因此它会使主线程一直忙,并且在调用它的线程结束后它的输出也很明显。

我正在寻找一个简单的干净解决方案,就像我上面编写的那样,通过多线程实现多个视图的更新。

提前致谢

android multithreading view
1个回答
0
投票

我找到了解决方案。简单干净:


public class Main extends Activity{

    private View root;
    private Runs runs;
    private boolean ready = false;
    private final Context context = this;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        init();
    }

    private void init() {
        setContentView(R.layout.s_main);
        runs = new Runs(this);
        root = findViewById(R.id.root);
        //
        ViewTreeObserver vto = root.getViewTreeObserver();
        vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                root.getViewTreeObserver().removeOnPreDrawListener(this);
                ready = true;
                return true;
            }
        });
    }

    private void startRuns() {
        try {
            runs.run();
            Threads.run = true;
        } catch (Exception ex) {
            Alerts.alert(context, ex.getMessage());
        }
    }

    public void start(View view) {
        try {
            if (ready && !Threads.run) {
                startRuns();
            } else {
                Threads.pause = !Threads.pause;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            Alerts.alert(context, ex.getMessage());
        }
    }
}

public class Runs implements Runnable {

    private int count;
    private Handler handler;
    private TextView view1, view2;

    public Runs(Activity activity) {
        count = 0;
        handler = new Handler();
        view1 = activity.findViewById(R.id.txv1);
        view2 = activity.findViewById(R.id.txv2);
    }

    @Override
    public void run() {
        if (!Threads.pause) {
            update();
        }
        handler.postDelayed(this, Threads.sleep);
    }

    private void update() {
        view1.setText(String.valueOf(count++));
        view2.setText(String.valueOf(Math.random() * 100));
    }


}

© www.soinside.com 2019 - 2024. All rights reserved.