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.