JavascriptProva

sabato 9 gennaio 2016

Ristudio dell'AsyncTask in Android


Ripassiamo l'asyncTask.
Riscrivere il codice che carica in una ImageView un'immagine scalata mediante InJustDecodeBounds, quindi farla elaborare nel contesto di un AsyncTask.
Riscrivo il codice che carica un'immagine scalata, scelta fra le immagini in memoria, in un ImageView:
public class MainActivity extends Activity {
 ImageView immagine;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  
  setContentView(R.layout.activity_main);
  immagine=(ImageView)findViewById(R.id.imageView1);
  
  Intent intent=new Intent();
  intent.setAction(Intent.ACTION_PICK);
  intent.setData(Uri.parse("content://media/external/images/media"));
  startActivityForResult(intent, 0);
 }
 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data){
  if(resultCode==RESULT_OK){
   BitmapFactory.Options opzioni=new BitmapFactory.Options();
   opzioni.inJustDecodeBounds=true;
   BitmapFactory.decodeFile(getPathFromUri(data.getData()),opzioni);
   int fattore=1;
   while(opzioni.outWidth/fattore>200 && opzioni.outHeight/fattore>200){
    fattore*=2;
   }
   opzioni.inSampleSize=fattore;
   opzioni.inJustDecodeBounds=false;
   Bitmap bitmap=BitmapFactory.decodeFile(getPathFromUri(data.getData()),opzioni);
   immagine.setImageBitmap(bitmap);
  }
 }
 
 private String getPathFromUri(Uri uri){
  Cursor cursor=getContentResolver().query(uri, null, null, null, null);
  cursor.moveToFirst();
  int indice=cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
  String s=cursor.getString(indice);
  return s;
  
 }


 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  // Handle action bar item clicks here. The action bar will
  // automatically handle clicks on the Home/Up button, so long
  // as you specify a parent activity in AndroidManifest.xml.
  int id = item.getItemId();
  if (id == R.id.action_settings) {
   return true;
  }
  return super.onOptionsItemSelected(item);
 }
}
Bene: tutto il codice in rosso l'ho scritto a memoria, commettendo solo un errore di distrazione (una maiuscola al posto di una minuscola).
Adesso riaffrontiamo l'AsyncTask...

Come si estende AsyncTask?
Ecco: scrivo
private class asyncTask extends AsyncTask{
ed Eclipse mi dà automaticamente l'indicazione di scrivere i metodi non implementati.
Li aggiungo automaticamente e ottengo questo:
 private class asyncTask extends AsyncTask{

  @Override
  protected Object doInBackground(Object... params) {
   // TODO Auto-generated method stub
   return null;
  }
  
 }
ottengo il solo metodo Object doInBackground, che dovrebbe riferirsi all'attività da svolgere in modo asincrono, al di fuori dell'UI.

Quell'Object come tipo del metodo doInBackground e quell'Object riferito ai parametri dello stesso metodo mi ricordano qualcosa: quando ho usato AsyncTask in precedenza, se non usavo i Generics nella dichiarazione della classe ottenevo Object automaticamente.
Vado a rivedere il codice di quando ho usato questa classe in precedenza:
 class MyAsync extends AsyncTask<String,Void,Bitmap>{
  
  
  private WeakReference<ImageView> riferimento;
  public MyAsync(ImageView immagine){
   riferimento=new WeakReference<ImageView>(immagine);
   
   
   
   
  }
  @Override
  protected Bitmap doInBackground(String... params) {
   BitmapFactory.Options opzioni=new BitmapFactory.Options();
   opzioni.inJustDecodeBounds=true;
   BitmapFactory.decodeFile(params[0],opzioni);
   int imageHeight=opzioni.outHeight;
   int imageWidth=opzioni.outWidth;
   opzioni.inJustDecodeBounds=false;
   int altezza=imageHeight/2;
   int larghezza=imageWidth/2;
   int reqHeight=riferimento.get().getLayoutParams().height;
   int reqWidth=riferimento.get().getLayoutParams().width;
   int inSampleSize=1;
   while(altezza/inSampleSize>reqHeight || larghezza/inSampleSize>reqWidth){
    inSampleSize*=2;
   }
   opzioni.inSampleSize=inSampleSize;
   Bitmap bmp=BitmapFactory.decodeFile(params[0],opzioni);
   
   return bmp;
  }
  @Override 
  protected void onPostExecute(Bitmap bmp){
   if(riferimento!=null && bmp!=null){
    ImageView immagine=(ImageView)riferimento.get();
    if(immagine!=null){
     immagine.setImageBitmap(bmp);
    }
   }
   
   
  }
 } 
Ricordo che quello di mezzo era spesso impostato a Void. Il primo corrisponde al tipo dei parametri mentre il secondo corrisponde al tipo del metodo doInBackground.

Proviamo... il metodo dovrebbe restituire una Bitmap da inserire nell'immagine, mentre dovrebbe "ingoiare" una String che sarebbe il Path, quindi i generics dovrebbero essere <String, Void, Bitmap>, ossia prima quello che si deve dare in pasto al metodo doInBackground, quindi come ultimo il risultato dello stesso metodo.
Se metto questo, il tipo e i parametri del metodo dovrebbero generarmisi automaticamente con l'addizione automatica dei metodi non implementati:
 private class asyncTask extends AsyncTask<String,Void,Bitmap>{

  @Override
  protected Bitmap doInBackground(String... params) {
   // TODO Auto-generated method stub
   return null;
  }
 } 
Ottimo!

Poi nel corpo del metodo ho inserito tutto il codice per l'elaborazione dell'immagine scalata...
Su questo non ci sono problemi. Il metodo "ingoia" il Path dell'immagine e "sputa fuori" l'immagine scalata e impacchettata da mettere nell'ImageView.
C'è poi il metodo onPostExecute, che dovrebbe fare ciò che viene dopo l'attività eseguita fuori dalla UI.
Cosa fa questo metodo?
Ecco! Ottenuto il Path e restituita l'immagine, la deve mettere nell'ImageView.
E l'ImageView viene ricevuta in pasto dal costruttore.
@Override 
  protected void onPostExecute(Bitmap bmp){
   if(riferimento!=null && bmp!=null){
    ImageView immagine=(ImageView)riferimento.get();
    if(immagine!=null){
     immagine.setImageBitmap(bmp);
    }
   }
   
   
  }
Ecco.
Il costruttore ha "impacchettato" il controllo ImageView in una variabile.
Qui lo restituisce come metodo get() della variabile, per attaccarci la bitmap, dopo essersi assicurato che la bitmap esista e che la variabile non sia nulla e che l'oggetto restituito dalla variabile non sia nullo.
Quante paranoie!

Il costruttore prende come argomento l'ImageView.
Prima dichiara la variabile di tipo WeakReference.
  private WeakReference riferimento;
  public MyAsync(ImageView immagine){
   riferimento=new WeakReference(immagine);
  }
In effetti questa variabile è un po' particolare: va istanziata con il Generic del tipo ImageView, e quindi vi va conservata l'ImageView fornita come parametro.
A questo punto mi riscrivo tutto: ricomponiamo il mandala
public class MainActivity extends Activity {
 ImageView immagine;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  
  setContentView(R.layout.activity_main);
  immagine=(ImageView)findViewById(R.id.imageView1);
  
  Intent intent=new Intent();
  intent.setAction(Intent.ACTION_PICK);
  intent.setData(Uri.parse("content://media/external/images/media"));
  startActivityForResult(intent,0);
  
 }
 
 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data){
  if(resultCode==RESULT_OK){
   asyncTask async=new asyncTask(immagine);
   async.execute(getPathFromUri(data.getData()));
   Log.d("uri",data.getData()+" ");
  }
 }
 

 private String getPathFromUri(Uri uri){
  Cursor cursor=getContentResolver().query(uri, null, null, null, null);
  cursor.moveToFirst();
  int index=cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
  String s=cursor.getString(index);
  return s;
 }
 

 class asyncTask extends AsyncTask{
  
  WeakReference riferimento;
  public asyncTask(ImageView i){
   riferimento=new WeakReference(i);
  }
  @Override
  protected Bitmap doInBackground(String... params) {
   BitmapFactory.Options opzioni=new BitmapFactory.Options();
   opzioni.inJustDecodeBounds=true;
   BitmapFactory.decodeFile(params[0],opzioni);
   int fattore=1;
   while(opzioni.outWidth/fattore>200 && opzioni.outHeight>200){
    fattore*=2;
   }
   opzioni.inJustDecodeBounds=false;
   opzioni.inSampleSize=fattore;
   Bitmap bitmap=BitmapFactory.decodeFile(params[0],opzioni);
   return bitmap;
  }
  @Override
  protected void onPostExecute(Bitmap bitmap){
   if(bitmap!=null && riferimento!=null){
    if(bitmap!=null){
     ImageView i=(ImageView)riferimento.get();
     i.setImageBitmap(bitmap);
    }
   }
  }
  
 }
Scritto tutto il codice marcato. Piccolo errore di distrazione: un true al posto di un false, corretto il quale funziona perfettamente!!!

Nessun commento:

Posta un commento