Фоновая задача Android для пользовательского интерфейса
Я разрабатываю приложение под Android, которое обрабатывает растровые изображения и применяет фильтры GPU к растровым изображениям. Поскольку это может быть медленно, я решил установить "изображение по умолчанию", например, поддельное изображение, которое будет отображаться до тех пор, пока изображение, которое я хочу отфильтровать, не будет готово.
До этого я ожидал, что растровое изображение будет изменено перед отображением, но оно не предлагает приятного пользовательского опыта.
Проблема в том, что у меня есть предупреждение на системном уровне:
System.err﹕ android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Я видел несколько тем, которые указывают, что я должен использовать runOnUiThread
, но я не знаю, как заставить это работать.. вот код моих приложений
Активность FilterActivity
, получает URI, который содержит путь к файлу. FilterPreview
Метод обрабатывает класс, которому удастся поставить в очередь действие, которое я хочу сделать.
public class FiltersActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_filters);
mFiltersView = (ListView) findViewById(R.id.filters_place_holder);
ViewGroup.LayoutParams params = mFiltersView.getLayoutParams();
Intent intent = getIntent();
mSnapShotUri = intent.getData();
getBitmapFromURI(mSnapShotUri);
....
private void getBitmapFromURI(Uri uri) {
try {
mSnapShot = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
}
catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "File not found");
}
finally {
mPreviewBitmap = Bitmap.createScaledBitmap(mBitmap, PreviewSizeWidth, PreviewSizeHeight, false);
FiltersPreview();
}
}
private void FiltersPreview() {
boolean mPreview = true;
mFiltersView.setAdapter(new CameraImageFiltersAdapter(this, mSnapShot, mPreviewBitmap, mPreview));
mFiltersView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
CameraFiltersFactory effect = (CameraFiltersFactory) v.findViewById(R.id.filteredImage).getTag();
effect.save(new GPUImage.OnPictureSavedListener() {
@Override public void onPictureSaved(Uri uri) {
Intent previewIntent = new Intent(FiltersActivity.this, PreviewActivity.class);
previewIntent.setData(uri);
startActivityForResult(previewIntent, 0);
}
});
}
});
}
В моем приложении часть ниже замедляет пользовательский интерфейс, поскольку я не использую фоновую задачу / поток для обновления пользовательского интерфейса.
mFiltersView.setAdapter(new CameraImageFiltersAdapter(this, mSnapShot, mPreviewBitmap, mPreview));
CameraImageFiltersAdapter
учебный класс (stub_id
это поддельное изображение, отображаемое в процессе применения фильтров)
public CameraImageFiltersAdapter(Context c, Bitmap original, Bitmap current, boolean isPreview) {
....
mExecutorService = Executors.newFixedThreadPool(5);
}
@SuppressLint("InflateParams")
public View getView(int position, View convertView, ViewGroup parent) {
ImageView filteredImage;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.list_item_filter, null);
filteredImage = (ImageView) convertView.findViewById(R.id.filteredImage);
filteredImage.setImageResource(stub_id);
queueFiltered(filteredImage, position);
}
return convertView;
}
private void queueFiltered(ImageView imageView, int position) {
CameraFiltersFactory holder = new CameraFiltersFactory(mContext, imageView);
mExecutorService.submit(new FilterLoader(holder, position));
}
class FilterLoader implements Runnable {
CameraFiltersFactory FilterToLoad;
int FilterPosition;
FilterLoader(CameraFiltersFactory FilterToLoad, int pos) {
this.FilterToLoad = FilterToLoad;
this.FilterPosition = pos;
}
@Override
public void run() {
try {
FilterToLoad.setResources(mOriginalBitmap, mCurrentBitmap, mFilterIds[FilterPosition], mPreview);
} catch (Throwable th) {
th.printStackTrace();
}
}
}
Любая идея?
2 ответа
Передать активность в качестве параметра для адаптера и затем вызвать:
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
//change the image here
}
});
Вы не можете вносить изменения в представления в потоках вашего ThreadPool, использовать обработчик основного потока, чтобы опубликовать сообщение и обработать его в основном потоке. Или используя AsyncTask, как эта Обработка растровых изображений вне потока пользовательского интерфейса