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 <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="pl.umk.fizyka.menedzerzadan.MenedzerActivity"> <ListView android:id="@+id/listView1" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </android.support.constraint.ConstraintLayout> 3. W pliku res/values/strings.xml zmieniamy app_name na "Dzia�aj�ce procesy:" (warto�� wykorzystywana w nag��wku) <resources> <string name="app_name">Dzia�aj�ce procesy:</string> </resources> 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<ActivityManager.RunningAppProcessInfo> 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<RunningAppProcessInfo> 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<String>( 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: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:id="@+id/nazwa" android:textSize="18sp" android:textColor="#000000" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="3" /> <TextView android:id="@+id/opis" android:textSize="10sp" android:textColor="#000000" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="2" android:ellipsize="marquee" android:maxLines="1" /> </LinearLayout> 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<ProcesInfo> { private ArrayList<ProcesInfo> items; public ProcesyZOpisamiAdapter(Context context, int textViewResourceId, ArrayList<ProcesInfo> 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<ProcesInfo> opisyProcesow=new ArrayList<ProcesInfo>(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): <uses-permission android:name="android.permission.RESTART_PACKAGES" /> Ale uwaga: This constant was deprecated in API level 8. The restartPackage(String) API is no longer supported. ** Nowsze jest: am.killBackgroundProcesses(nazwaPakietu); <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" /> ------------------------------------------------------------- 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. <activity android:name=".ProcesyActivity"> </activity> <activity android:name=".ZadaniaActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> 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: <activity android:name=".ProcesyActivity"> </activity> <activity android:name=".ZadaniaActivity" android:label="Zadania:"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> 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. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#FFFFFF" android:orientation="horizontal" > <ImageView android:id="@+id/ikona" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher" /> <LinearLayout android:id="@+id/linearLayout1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <TextView android:id="@+id/nazwa" android:textSize="18sp" android:textColor="#000000" android:text="Nazwa" android:layout_width="fill_parent" android:layout_height="24sp" /> <TextView android:id="@+id/opis" android:textSize="10sp" android:textColor="#000000" android:text="Opis" android:layout_width="fill_parent" android:layout_height="14sp" android:maxLines="1" android:ellipsize="marquee" /> </LinearLayout> </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<RunningTaskInfo> listaZadan; ListView lista; private List<RunningTaskInfo> 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<ZadanieInfo> { private ArrayList<ZadanieInfo> items; public ZadaniaAdapter(Context context, int textViewResourceId, ArrayList<ZadanieInfo> 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<ZadanieInfo> opisyZadan=new ArrayList<ZadanieInfo>(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�: <uses-permission android:name="android.permission.RESTART_PACKAGES" /> <uses-permission android:name="android.permission.GET_TASKS" /> * 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.