Menadżer zadań (Android Studio) * Uwaga od Android 5.0 Lollipop możliwość pobierania uruchomionych procesów została ograniczona tylko do bieżącej aplikacji Należy utworzyć wirtualne urządzenie z Android 4.0.3 IceCreamSandwich Aby można je było połączyć: Tools, Android, wyłączyć Enable ADB Integration (Android Debug Bridge) [Na bazie projektu Pawła Kruzy] 1. Tworzymy projekt typu Android Project: Nazwa = MenedzerZadan Package name = pl.umk.fizyka Minimum SDK: API 15 (Android 4.0.3) Activity: Empty Activity o nazwie "Menedzer" Uwaga! Poza pobieraniem pamięci używanej przez procesy, wszystko jest zgodne z API 4 (Android 1.6) !!! 2. Interfejs będzie oparty o listę ListView Przechodzimy do edycji pliku res\layout\activity_menedzer.xml 3. W pliku res/values/strings.xml zmieniamy app_name na "Działające procesy:" (wartość wykorzystywana w nagłówku) Działające procesy: 4. W pliku MenedzerActivity.java a) tworzymy pole odpowiadające liście (kontrolce) i liście procesów (pole będzie potrzebne w klasach wewnętrznych): ListView lista; List listaProcesow; Konieczny jest import java.util.List, android.widget.ListView i android.app.ActivityManager. b) W metodzie MenedzerZadanActivity.onCreate wiążemy je z listą w pliku XML lista=(ListView)findViewById(R.id.listView1); c) Tworzymy metodę pobierającą listę procesów i metody pomocnicze: Importy: import java.util.List; import android.app.ActivityManager; //tu mozna zdobyc wiecej informacji np. wolna pamiec import android.app.ActivityManager.RunningAppProcessInfo; import android.widget.ArrayAdapter; Metoda: private List pobierzListeProcesow() { ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE); return am.getRunningAppProcesses(); } d) Metoda wyświetlająca listę procesów w ListView: private void wypelnijListeNazwamiProcesow(ListView lista) { //wczesniej trzeba bedzie wywolac: listaProcesow=pobierzListeProcesow(); if(listaProcesow==null || listaProcesow.size()==0) return; int liczbaProcesow=listaProcesow.size(); String[] nazwyProcesow = new String[liczbaProcesow]; for (int i = 0; i < liczbaProcesow; i++) { RunningAppProcessInfo proces=listaProcesow.get(i); nazwyProcesow[i] = proces.processName+" ("+proces.pid+")"; } lista.setAdapter( new ArrayAdapter( this, android.R.layout.simple_list_item_1, nazwyProcesow)); } e) Wywołanie obu powyższych metod dodajemy do onCreate: listaProcesow = pobierzListeProcesow(); lista=(ListView)findViewById(R.id.listView1); wypelnijListeNazwamiProcesow(lista, listaProcesow); ** Uruchom i przetestuj W Android 5 zobaczymy tylko bieżący proces, podczas gdy we wcześniejszych - więcej procesów --> KONKURS: Przygotować metodę pobierzListeProcesow (+1 do oceny), StackOverflow 4. Kliknięcie elementu listy -> informacja o zajmowanej pamięci: Metoda pomocnicza: private int pamiecZajmowanaPrzezProces(int pid) { ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); int tablica_numerow_pid[] = {pid}; android.os.Debug.MemoryInfo[] memoryInfoArray = am.getProcessMemoryInfo(tablica_numerow_pid); //min API: 5 return memoryInfoArray[0].getTotalSharedDirty(); //kB, min API: 5 } Zmiana w metodzie MenedzerActivity.onCreate: @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); lista=(ListView)findViewById(R.id.listView1); wypelnijListeNazwamiProcesow(lista, pobierzListeProcesow()); lista.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView parent, View view, int position, long id) { RunningAppProcessInfo proces=listaProcesow.get(position); int pid = proces.pid; String tekst = "Informacje o procesie\n"; tekst+="PID: " + pid + "\n"; tekst+="Ilość zajmowanej pamięci: "+pamiecZajmowanaPrzezProces(pid)+" kb\n"; tekst+="Priorytet podstawowy: "+proces.importance; Toast toast = Toast.makeText(getApplicationContext(), tekst, Toast.LENGTH_LONG); toast.show(); } }); } --------------------------------------------------------------------------------------------- Zamiast toastów z opisem procesu, informacje o procesie dołączymy do listy (własne ułożenie/layout listy) 1. W res/layout tworzymy plik wiersz.xml z własnym ułożeniem wiersza listy: Prawym klawiszem na pozycję res\layout i z menu kontekstowego wybieramy New, XML, Layout XML File W oknie New Android Component wpisujemy Layout File Name: wiersz (bez .xml) Root Tag: LinearLayout Klikamy Finish Kod pliku uzupełniamy: Zapisz do pliku! 2. W pliku MenedzerZadanActivity.java zastępujemy metodę wypelnijListeNazwamiProcesow przez private class ProcesInfo { public String nazwa; public String opis; } private class ProcesyZOpisamiAdapter extends ArrayAdapter { private ArrayList items; public ProcesyZOpisamiAdapter(Context context, int textViewResourceId, ArrayList items) { super(context, textViewResourceId, items); this.items = items; } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; if (v == null) { LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = vi.inflate(R.layout.wiersz, null); } ProcesInfo o = items.get(position); if (o != null) { TextView nazwa = (TextView)v.findViewById(R.id.nazwa); if (nazwa != null) nazwa.setText(o.nazwa); TextView opis = (TextView)v.findViewById(R.id.opis); if(opis != null) opis.setText(o.opis); } return v; } } private String opisPriorytetu(int priorytet) { switch(priorytet) { case RunningAppProcessInfo.IMPORTANCE_FOREGROUND: return "Pierwszy plan"; case RunningAppProcessInfo.IMPORTANCE_VISIBLE: return "Widoczny"; case RunningAppProcessInfo.IMPORTANCE_SERVICE: return "Usługa"; case RunningAppProcessInfo.IMPORTANCE_BACKGROUND: return "W tle"; case RunningAppProcessInfo.IMPORTANCE_EMPTY: return "Pusty"; default: return ""; } } private void wypelnijListeNazwamiProcesowZOpisem(ListView lista) { int liczbaProcesow=listaProcesow.size(); ArrayList opisyProcesow=new ArrayList(liczbaProcesow); //for (int i = 0; i < liczbaProcesow; i++) //{ // RunningAppProcessInfo proces=listaProcesow.get(i); for(RunningAppProcessInfo proces : listaProcesow) { ProcesInfo opisProcesu=new ProcesInfo(); opisProcesu.nazwa = proces.processName; int pid = proces.pid; String tekst = ""; tekst+="PID: " + pid + ", "; tekst+="Pamięć: "+pamiecZajmowanaPrzezProces(pid)+" kb, "; tekst+="Priorytet: "+opisPriorytetu(proces.importance)+" ("+proces.importance+")"; opisProcesu.opis = tekst; opisyProcesow.add(opisProcesu); } ProcesyZOpisamiAdapter a=new ProcesyZOpisamiAdapter(this,R.layout.wiersz,opisyProcesow); lista.setAdapter(a); } private void odswiezListe() { listaProcesow=pobierzListeProcesow(); lista.setAdapter(null); wypelnijListeNazwamiProcesowZOpisem(lista); } 3. Wyświetl nową postać listy i zwiąż z jej elementami z oknem dialogowym (AlertDialog) pytającym o zabicie procesu. Uwaga! Zabijanie innego procesu nie jest czymś, co aplikacja w systemie Android powinna robić. @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); listaProcesow=pobierzListeProcesow(); lista=(ListView)findViewById(R.id.listView1); //wypelnijListeNazwamiProcesow(lista); wypelnijListeNazwamiProcesowZOpisem(lista); lista.setOnItemClickListener(new AdapterView.OnItemClickListener() { public void onItemClick(AdapterView parent, View view, int position, long id) { RunningAppProcessInfo proces=listaProcesow.get(position); final int pid = proces.pid; final String[] nazwyPakietow= proces.pkgList; AlertDialog ad=new AlertDialog.Builder(parent.getContext()).create(); ad.setTitle("Menedżer zadań"); ad.setMessage("Czy zamknąć proces "+((ProcesInfo)lista.getItemAtPosition(position)).nazwa+"?"); //alertDialog.setIcon(R.drawable.icon); ad.setButton(Dialog.BUTTON_POSITIVE, "Tak", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { //tak mozna zabic tylko wlasny proces android.os.Process.killProcess(pid); if (pid!=android.os.Process.myPid()) Toast.makeText(getApplicationContext(), "Można zabić tylko własny proces!", Toast.LENGTH_LONG).show(); /* ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); for(String nazwaPakietu : nazwyPakietow) { am.restartPackage(nazwaPakietu); //deprecated //am.killBackgroundProcesses(nazwaPakietu); } */ odswiezListe(); return; } }); ad.setButton(Dialog.BUTTON_NEGATIVE, "Nie", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { return; } }); ad.show(); } }); } * Aby działało zamykanie zadań umieszczone w komentarzach należy do pliku manifestu dodać uprawnienie (za application): Ale uwaga: This constant was deprecated in API level 8. The restartPackage(String) API is no longer supported. ** Nowsze jest: am.killBackgroundProcesses(nazwaPakietu); ------------------------------------------------------------- Zmiana nazwy bieżącej aktywności: 1. W drzewie klas projektu (widok Android) odnajdujemy klasę MenedzerActivity 2. Z menu kontekstowego: Refactor, Rename lub Shift+F6 3. Zmieniamy nazwe na ProcesyActivity (zmieni się również nazwa pliku) Dodawanie aktywności do aplikacji 1. W drzewie projektu przejdź do pliku manifestu AndroidManifest.xml. 2. Z menu kontekstowego: New, Activity, Empty Activity. 3. W oknie New Android Activity Activity name: ZadaniaActivity Kliknij Finish Powstanie nowy plik o zawartości: package pl.umk.fizyka.menedzerzadan; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class ZadaniaActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_zadania); } } Zmiana domyślnej aktywności: 1. W pliku manifestu wystarczy przenieść element intent-filter do elementu nowej aktywności. Uruchomiona zostanie pierwsza aktywność z odpowiednią intencją. Można również zmienić uruchamianą aktywność z poziomu Android Studio: 1. Z menu Run wybierz Edit Configurations... 2. W sekcji Launch Options, z rozwijanej listy Launch wybierz "Specified Activity". 3. W polu Activity ustaw ZadaniaActivity Wskazana aktywność musi mieć intent-filter Zmiana tytułu: Tytuł okna wyświetlany w TitleBar jest ustawiony w manifeście w elemencie application: android:label="@string/app_name" Można go nadpisać na poziomie aktywności: Tytuł można zmienić też z kodu: this.setTitle("Zadania:"); (w onCreate odpowiedniej aktywności) 1. Zasadniczy interfejs nowej aktywności będzie opierał się na tym samym layoucie (tj. res/layout/activity_menedzer.xml). Uzyskamy to dodając do metody onCreate polecenie: setContentView(R.layout.activity_menedzer); 2. Zmienimy format wiersza. Do res/layout dodajmy kolejny plik typu Android XML Layout File: wiersz_zadania.xml ze wskazaniem na LinearLayout. 3. W klasie ZadaniaActivity definiujemy metody pobierajace zadania: package pl.umk.menedzerzadan; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; public class ZadaniaActivity extends Activity { List listaZadan; ListView lista; private List pobierzListeZadan() { ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE); return am.getRunningTasks(Integer.MAX_VALUE); //deprecated, wymaga uprawnienia GET_TASKS } private class ZadanieInfo { public Drawable ikona; public String nazwa; public String opis; } private class ZadaniaAdapter extends ArrayAdapter { private ArrayList items; public ZadaniaAdapter(Context context, int textViewResourceId, ArrayList items) { super(context, textViewResourceId, items); this.items = items; } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; if (v == null) { LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = vi.inflate(R.layout.wiersz_zadania, null); } ZadanieInfo o = items.get(position); if (o != null) { ImageView ikona = (ImageView)v.findViewById(R.id.ikona); if (ikona != null) ikona.setImageDrawable(o.ikona); TextView nazwa = (TextView)v.findViewById(R.id.nazwa); if (nazwa != null) nazwa.setText(o.nazwa); TextView opis = (TextView)v.findViewById(R.id.opis); if(opis != null) opis.setText(o.opis); } return v; } } private void wypelnijListeNazwamiZadan(ListView lista) { PackageManager pm=this.getPackageManager(); int liczbaZadan=listaZadan.size(); ArrayList opisyZadan=new ArrayList(liczbaZadan); for (int i = 0; i < liczbaZadan; i++) { try { RunningTaskInfo zadanie=listaZadan.get(i); ZadanieInfo opisZadania=new ZadanieInfo(); ApplicationInfo ai=pm.getApplicationInfo(zadanie.baseActivity.getPackageName(), PackageManager.GET_META_DATA); opisZadania.ikona=pm.getApplicationIcon(ai); opisZadania.nazwa=pm.getApplicationLabel(ai).toString(); opisZadania.opis=Integer.toString(ai.uid)+", "+ai.packageName+", "+ai.sourceDir; opisyZadan.add(opisZadania); } catch(Exception exc) { } ZadaniaAdapter a=new ZadaniaAdapter(this,R.layout.wiersz_zadania,opisyZadan); lista.setAdapter(a); } } private void odswiezListe() { listaZadan=pobierzListeZadan(); lista.setAdapter(null); wypelnijListeNazwamiZadan(lista); } /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.activity_zadania); setContentView(R.layout.activity_menedzer); } } 4. Metoda ActivityManager.getRunningTasks wymaga uprawnienia GET_TASKS: a. przechodzimy do AndroidManifest.xml b. uzupełniamy listę uprawnień: * W nowszych wersjach Androida działa bez tego uprawnienia, a uprawnienie jest deprecated! 4. Następnie do metody onCreate dodajemy kod, który wykorzysta powyższe metody. I tym razem użyjemy okna dialogowego AlertDialog. @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_menedzer); listaZadan=pobierzListeZadan(); lista=(ListView)findViewById(R.id.listView1); wypelnijListeNazwamiZadan(lista); lista.setOnItemClickListener(new AdapterView.OnItemClickListener() { public void onItemClick(AdapterView parent, View view, int position, long id) { RunningTaskInfo zadanie=listaZadan.get(position); final String nazwaPakietu= zadanie.baseActivity.getPackageName(); AlertDialog ad=new AlertDialog.Builder(parent.getContext()).create(); ad.setTitle("Menedżer zadań"); ad.setMessage("Czy zamknąć zadanie "+((ZadanieInfo)lista.getItemAtPosition(position)).nazwa+"?"); //alertDialog.setIcon(R.drawable.icon); ad.setButton(Dialog.BUTTON_POSITIVE, "Tak", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); //am.restartPackage(nazwaPakietu); am.killBackgroundProcesses(nazwaPakietu); odswiezListe(); return; } }); ad.setButton(Dialog.BUTTON_NEGATIVE, "Nie", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { return; } }); ad.show(); } }); } ************************************ W domu: postępując podobnie przygotować aktywność UslugiActivity wyświetlającą listę usług.