## 進程管理 ##
- 頂部佈局和軟件管理同樣是寫好的 組合控件java
- 寫一個類 ProcessInfoProvider提供進程信息數據android
- 獲取正在運行進程數安全
public static int getRunningProcess(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> runningAppProcesses = am.getRunningAppProcesses();
return runningAppProcesses.size();
}app
- android:process 屬性ide
>每一個應用啓動起來至少有一個進程, 進程名稱默認和包名同樣, 四大組件默認都在這個進程中運行.
系統提供了一個屬性, android:process, 這個屬性能夠配置在application節點或者四大組件節點中,表示當前組件運行在指定進程中,
>例如在清單文件中作以下配置: 注意寫法 要加 : 這個冒號
<activity android:name=".activity.SettingActivity"
android:process=":setting"/>函數
當啓動這個Activity的時候, 這個Activity會運行在 com.itheima.mobilesafe:setting 這個進程中.
這種狀況比較少見, 可是仍是有必定的應用場景的, 好比能夠擴大內存使用量, 或者多進程守護等等.佈局
- 得到全部的進程數動畫
public static int getAllProcess(Context context) {
PackageManager pm = context.getPackageManager();
// 這裏由於有process標籤的緣由 因此要添加flag 獲取全部組件的進程
List<PackageInfo> installedPackages = pm.getInstalledPackages(PackageManager.GET_ACTIVITIES
| PackageManager.GET_SERVICES | PackageManager.GET_RECEIVERS
| PackageManager.GET_PROVIDERS);
// 用HashSet取出重複的值
HashSet<String> set = new HashSet<String>();
for (PackageInfo packageInfo : installedPackages) {
String appProcessName = packageInfo.applicationInfo.processName;
set.add(appProcessName);
ActivityInfo[] activities = packageInfo.activities;
if (activities != null) {
for (ActivityInfo activityInfo : activities) {
String pn = activityInfo.processName;
set.add(pn);
}
}
ServiceInfo[] services = packageInfo.services;
if (services != null) {
for (ServiceInfo serviceInfo : services) {
String pn = serviceInfo.processName;
set.add(pn);
}
}
ProviderInfo[] providers = packageInfo.providers;
if (providers != null) {
for (ProviderInfo providerInfo : providers) {
String pn = providerInfo.processName;
set.add(pn);
}
}
ActivityInfo[] receivers = packageInfo.receivers;
if (receivers != null) {
for (ActivityInfo receiverInfo : receivers) {
String pn = receiverInfo.processName;
set.add(pn);
}
}
}
return set.size();
}ui
## 內存信息獲取 ram ##this
- 獲取可用(剩餘)內存, 使用ActivityManager獲取便可
/**
* 獲取可用的內存, 單位 byte
* @param context
* @return
*/
public static long getFreeMemory(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
MemoryInfo outInfo = new MemoryInfo();
// 賦值函數(輸出函數), 傳入一個對象, 給對象賦值
am.getMemoryInfo(outInfo);
long availMem = outInfo.availMem;
return availMem;
}
- 獲取總內存, 這個也但是使用ActivityManager獲取, 可是會有版本兼容問題
/**
* 得到全部的內存, 單位 byte
* @param context
* @return
*/
@SuppressLint("NewApi")//去掉警告
public static long getTotalFreeMemory(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
MemoryInfo outInfo = new MemoryInfo();
am.getMemoryInfo(outInfo);
long totalMem = 0;
判斷版本號 重點重點重點重點重點重點重點重點
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
totalMem = outInfo.totalMem;
} else {
totalMem = getLowTotalMem();
}
return totalMem;
}
/**
* 低版本獲取全部內存 /proc/meminfo 文件讀取
* @return
*/
private static long getLowTotalMem() {
// MemTotal: 513492 kB
BufferedReader br = null;
String line = "";
try {
File file = new File("/proc/meminfo");
br = new BufferedReader(new FileReader(file));
line = br.readLine();
line = line.replaceAll("MemTotal:", "");
line = line.replaceAll("kB:", "");
line = line.trim();
br.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return Long.parseLong(line) * 1024;
}
- listivew先簡單實現 convertView, 和viewholder寫法
對應的 bean
public class ProcessInfo {
public String name;
public long memory;
public Drawable icon;
public boolean isSystem;
public boolean isChecked;
public String packageName;
}
- ### 獲取全部的內存信息 ###
/**
* 獲取所有正在運行的進程信息
*
* @return
*/
public static ArrayList<ProcessInfo> getProcessInfos(Context context) {
ArrayList<ProcessInfo> infos = new ArrayList<ProcessInfo>();
// 獲取活動管理器
ActivityManager activityManager = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
// 獲取正在運行的全部的進程
List<RunningAppProcessInfo> runningAppProcesses = activityManager
.getRunningAppProcesses();
// 獲取包管理器
PackageManager packageManager = context.getPackageManager();
// 遍歷 全部的進程
for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses) {
// runningAppProcessInfo.pid;//進程的惟一標識
String processName = runningAppProcessInfo.processName;// 進程名字 默認
// 和包名同樣
// 圖標
Drawable icon = null;
// 名字
String name = null;
// 是否是系統進程
boolean isSys = false;
try {
// 獲取應用信息對象
ApplicationInfo applicationInfo = packageManager
.getApplicationInfo(processName, 0);
// 圖標
icon = applicationInfo.loadIcon(packageManager);
// 名字
name = applicationInfo.loadLabel(packageManager).toString();
if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM) {
// 是系統進程
isSys = true;
} else {
// 用戶進程
isSys = false;
}
} catch (NameNotFoundException e) {
e.printStackTrace();
// 出異常的清空 1 本身添加了進程 :aa 2 系統進程
// 設置出異常的默認值
name = processName;
icon = context.getResources().getDrawable(
R.drawable.ic_launcher);
isSys = true;
}
int[] pids = new int[] { runningAppProcessInfo.pid };
// 獲取指定pid的進程的 內存信息
android.os.Debug.MemoryInfo[] processMemoryInfo = activityManager
.getProcessMemoryInfo(pids);
// 獲取內存大小
long totalPss = processMemoryInfo[0].getTotalPss() * 1024;
ProcessInfo info = new ProcessInfo(name, icon, totalPss, isSys,
false, processName);
infos.add(info);
}
return infos;
}
## StickyListHeaders的使用 ##
- 導入開源項目 StickyListHeaders 設置爲依賴項目. 注意 使用的工程和依賴的工程要在一個盤符下
-
到Github上搜索 StickyListHeaders, 下載整個zip, 導入到Eclipse中, 最好拷貝到工做空間.
將佈局文件中的ListView換成
<se.emilsjolander.stickylistheaders.StickyListHeadersListView
android:id="@+id/lv_pm"
android:layout_width="match_parent"
android:layout_height="match_parent" />
- 讓Adapter實現StickyListHeadersAdapter接口 實現多出的兩個方法
/**
* 返回每一個條目對應的分隔條目view 根據每一個條目的信息進行判斷 這裏是有兩種
*/
@Override
public View getHeaderView(int position, View convertView, ViewGroup parent) {
TextView tv;
if (convertView == null) {
convertView = new TextView(getApplicationContext());
tv = (TextView) convertView;
} else {
tv = (TextView) convertView;
}
tv.setBackgroundColor(Color.parseColor("#aaaaaa"));
tv.setTextSize(15);
tv.setTextColor(Color.BLACK);
tv.setPadding(4, 4, 4, 4);
ProcessInfo info = (ProcessInfo) getItem(position);
if (info.isSysPro) {
tv.setText("系統進程" + sysProInfos.size() + "個");
} else {
tv.setText("用戶進程" + userProInfos.size() + "個");
}
return convertView;
}
/**
* 獲取每個分隔條目的id, 有多少個分隔條目, 這個方法就應該返回多少種不一樣的值
*/
@Override
public long getHeaderId(int position) {
ProcessInfo info = (ProcessInfo) getItem(position);
if (info.isSysPro) {
return 0;
} else {
return 1;
}
}
###勾選, 全選, 反選###
- 若是ListView條目中有 Button, ImageButton, CheckBox等默承認點擊的控件,
這些控件會搶走條目的點擊事件. 能夠給條目最外層佈局設置一個屬性:
android:descendantFocusability="blocksDescendants"
這樣設置的話就是點在哪一個控件上, 哪一個控件響應點擊事件.
- 注意 注意 注意 注意 , 在咱們這個頁面中, CheckBox應該設置設置爲不可點擊, 而後給ListView條目設置點擊事件,
CheckBox的勾選狀態應由當前條目對應的JavaBean決定.
<CheckBox
android:id="@+id/cb_itempm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:clickable="false"
android:focusable="false"
android:layout_marginRight="10dp"
android:focusableInTouchMode="false" />
- 設置listview的單條點擊 進行CheckBox選中狀態的切換
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
System.out.println("item");
// 獲取點擊條目的數據信息
ProcessInfo info = mInfos.get(position);
if (TextUtils.equals(info.packageName, getPackageName())) {
return;
// 更改數據選中狀態,而後刷新界面
info.isChecked = !info.isChecked;
mAdapter.notifyDataSetChanged();
}
- 在adapter的getview方法裏
// 根據數據設置是否選中
viewHolder.cbSelect.setChecked(info.isCheck);
- 在頁面中添加全選, 反選按鈕
>這裏有一個小技巧:在 LinearLayout 中, 若是一個控件高度設置爲 0dp, weight爲1,
其餘控件都的不配置weight屬性, 則weight爲1的控件能夠佔滿 LinearLayout 除了其餘控件的剩餘空間.
- 全選反選的點擊事件
case R.id.btn_pm_all:
// 更改數據源裏的選中狀態 而後刷新頁面
for (ProcessInfo info : mInfos) {
info.isChecked = true;
}
mAdapter.notifyDataSetChanged();
break;
case R.id.btn_pm_reverse:
// 更改數據源裏的選中狀態 而後刷新頁面
for (ProcessInfo info : mInfos) {
info.isChecked = !info.isChecked;
}
mAdapter.notifyDataSetChanged();
break;
##進程清理功能##
- ActivityManager提供了殺死進程的方法:
/**
* 殺死後臺進程, 須要權限: KILL_BACKGROUND_PROCESSES
* @param context
* @param packageName
*/
public static void killBackgroundProcess(Context context, String packageName) {
ActivityManager actManager = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
// 只能殺死後臺進程
actManager.killBackgroundProcesses(packageName);
// 只有系統才能調用下面這個方法, 對應的權限也只能系統申請
// actManager.forceStopPackage("");
}
- 點擊清理按鈕的時候, 遍歷數據集合,若是當前對象是被選中的, 殺死這個進程,
殺死進程以後應該從數據集合裏移除對象, 因此要使用 ListIterator, 最後刷新ListView
case R.id.ib_process_manager_clean:
// 殺死進程
ListIterator<ProcessInfo> listIterator = mInfos.listIterator();
while (listIterator.hasNext()) {
ProcessInfo processInfo = (ProcessInfo) listIterator.next();
if (processInfo.isCheckd) {
ProcessInfoProvider.killPro(getApplicationContext(), info.packageName);
// 從集合裏移除
listIterator.remove();
}
}
// 刷新ListView
mAdapter.notifyDataSetChanged();
break;
有一些系統進程是殺不死的, 這裏咱們只是欺騙一下用戶.體驗好一些
- 若是點擊是咱們本身的應用,不去執行選中效果 , 過濾掉 以下:
在 onItemClick 裏:
// 不勾選本身
if(TextUtils.equals(processInfo.mPackageName, getPackageName())) {
return;
}
在全選和反選的for循環裏:
// 不勾選本身
if(TextUtils.equals(processInfo.mPackageName, getPackageName())) {
continue;
}
- 清理進程後給用戶提示
int killCount = 0;//記錄清理數量
long killSize = 0;//記錄清理內存大小
while (listIterator.hasNext()) {
ProcessInfo info = listIterator.next();
if (info.isChecked) {
ProcessInfoProvider.killPro(getApplicationContext(), info.packageName);
killCount++;
killSize += info.memory;
listIterator.remove();
}
}
ToastUtils.Show(
getApplicationContext(),
"清理進程: " + killCount + "個, 釋放內存: "
+ Formatter.formatFileSize(getApplicationContext(), killSize));
##SlidingDrawer的使用##
- 在佈局裏的 FrameLayout 中添加一個 SlidingDrawer, 用法能夠看這個類的註釋, 注意id的寫法 id爲content對應的內容佈局 id爲handle對應的爲拖動的手柄
<SlidingDrawer
android:id="@+id/sd_pm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:content="@+id/content"
android:handle="@+id/handle" >
<RelativeLayout
android:id="@id/handle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/drawer_bg" >
<ImageView
android:id="@+id/iv_pm_arrow1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:src="@drawable/drawer_arrow_up" />
<ImageView
android:id="@+id/iv_pm_arrow2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/iv_pm_arrow1"
android:layout_centerHorizontal="true"
android:src="@drawable/drawer_arrow_up" />
</RelativeLayout>
<LinearLayout
android:id="@id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#fff"
android:clickable="true"
android:orientation="vertical" >
<TextView
style="@style/NormalTextStyle"
android:layout_margin="10dp"
android:text="進程管理設置" />
<cn.itcast.mobliesafe05.view.SettingView
android:id="@+id/sv_pm_showsys"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@drawable/selector_setting_item_top"
itcast:istoggle="true"
itcast:title="顯示系統進程" >
</cn.itcast.mobliesafe05.view.SettingView>
<cn.itcast.mobliesafe05.view.SettingView
android:id="@+id/sv_pm_lockclean"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/selector_setting_item_bottom"
itcast:istoggle="true"
itcast:title="鎖屏自動清理" >
</cn.itcast.mobliesafe05.view.SettingView>
</LinearLayout>
</SlidingDrawer>
- 三角箭頭的動畫
/**
* 開啓小抽屜上的 箭頭動畫
*/
private void startArrowAnimation() {
AlphaAnimation aa = new AlphaAnimation(0.2f, 1.0f);
aa.setDuration(500);// 動畫時間
aa.setRepeatCount(Animation.INFINITE);// 動畫次數爲 無限次
aa.setRepeatMode(Animation.REVERSE);// 動畫模式
imgArrow1.setImageResource(R.drawable.drawer_arrow_up);// 設置圖片是向上箭頭
imgArrow1.startAnimation(aa);
AlphaAnimation aa1 = new AlphaAnimation(1.0f, 0.2f);
aa1.setDuration(500);// 動畫時間
aa1.setRepeatCount(Animation.INFINITE);// 動畫次數爲 無限次
aa1.setRepeatMode(Animation.REVERSE);// 動畫模式
imgArrow2.setImageResource(R.drawable.drawer_arrow_up);// 設置圖片是向上箭頭
imgArrow2.startAnimation(aa1);
}
/**
* 中止箭頭動畫
*/
protected void stopArrowAnimation() {
// 中止箭頭動畫
imgArrow1.clearAnimation();
imgArrow2.clearAnimation();
// 更換圖片爲向下箭頭
imgArrow1.setImageResource(R.drawable.drawer_arrow_down);
imgArrow2.setImageResource(R.drawable.drawer_arrow_down);
}
- SlidingDrawer的打開和關閉
sdPm.setOnDrawerOpenListener(new OnDrawerOpenListener() {
@Override
public void onDrawerOpened() {
stopArrowAnimation();
}
});
sdPm.setOnDrawerCloseListener(new OnDrawerCloseListener() {
@Override
public void onDrawerClosed() {
// TODO Auto-generated method stub
startArrowAnimation();
}
});
- 是否顯示系統進程
case R.id.sv_pm_showsys:
// 顯示系統進程
svShowSys.toggle();
// 保存是否顯示系統進程的狀態
SharedPreferencesUtils.saveBoolean(getApplicationContext(),
Constants.IS_SHOW_SYS, svShowSys.isToggle());
// 記錄當前開關的狀態 刷新頁面時用
isShowSys = svShowSys.isToggle();
// 刷新頁面
mAdapter.notifyDataSetChanged();
break;
- 回顯數據
isSysShow = PreferencesUtil.getBoolean(getApplicationContext(), Constants.PRO_SHOW_SYS,
true);
sivShowSys.setToggleOn(isSysShow);
> adapter裏要作處理
@Override
public int getCount() {
System.out.println("刷新");
if (isSysShow) {
return mInfos.size();
} else {
return mUserInfos.size();
}
}
- 系統進程是否顯示後的細節處理
1.更改多選和反選時循環遍歷的數據源
// 建立一個集合 用來指向不用的數據
ArrayList<ProcessInfo> selectProcess;
if (isShowSys) {
// 若是顯示系統進程 那麼就遍歷所有數據
selectProcess = mInfos;
} else {
// 若是不顯示系統進程 那麼就遍歷用戶數據
selectProcess = mUserInfos;
}
2. 清理進程時也要修改 遍歷的數據源 而且清理後刪除數據源時要注意:
// 根據系統進程是否顯示 判斷移除另外一個數據源數據 保證顯示正確
mUserInfos.remove(info);
mSysInfos.remove(info);
- 鎖屏殺死進程 ,鎖屏廣播必須動態註冊 ,並且功能在退出應用後也能夠執行,因此要開啓一個後臺服務
sivAutoClean.toggle();
Intent intent = new Intent(this, LockCleanService.class);
if (ServiceStateUtils.isRunning(getApplicationContext(), LockCleanService.class)) {
stopService(intent);
} else {
startService(intent);
}
- 在onstart裏回顯數據 因爲進程信息實時變化,因此初始化數據也放在了 裏面
@Override
protected void onStart() {
super.onStart();
initTopData();
initListData();
// 回顯 鎖屏清理服務的狀態
boolean isAutoCleanRun = ServiceStateUtils.isRunning(
getApplicationContext(), LockAutoCleanService.class);
svLockClean.setToggleOn(isAutoCleanRun);
}
- 服務裏監聽鎖屏
@Override
public void onCreate() {
super.onCreate();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);// 鎖屏的action
// 動態註冊 鎖屏的廣播接收者 鎖屏的廣播只能動態註冊起做用
registerReceiver(receiver, filter);
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//清除全部進程
ProcessInfoProvider.killAllPro(context);
}
};
## 前臺服務 ##
- 360, 金山, 騰訊的應用是殺不死的, 這時由於他們開啓了前臺服務, 系統認爲他們是前臺進程.
而 actManager.killBackgroundProcesses只能殺死後臺進程.
要讓一個應用成爲前臺進程, 能夠在使用前臺服務. 前臺服務也叫守護服務, 會在通知欄裏顯示一個通知,
通知欄所在的應用叫 SystemUI, 這個應用是不會掛掉的, 若是一個應用有前臺服務, 在通知欄裏有通知,
系統也會認爲這個進程是前臺進程, 不會殺死它.
- 寫一個服務, ProtectService, 在開機廣播裏, 和 SplashActivity 的onCreate 方法裏
開啓這個服務. 接下來就要把它變成一個前臺服務了. 在 onCreate 方法裏調用以下代碼:
@Override
public void onCreate() {
super.onCreate();
// 開啓前臺服務
System.out.println("開啓前臺服務");
Notification notification = new Notification();
// 指定狀態欄裏的小圖標
notification.icon = R.drawable.ic_launcher;
// 指定通知欄裏顯示的View
notification.contentView = new RemoteViews(getPackageName(), R.layout.view_notification);
// 必需要有一個id, 代表是本應用哪個通知. 隨便寫一個便可
startForeground(mId, notification);
}
- 對應的佈局文件:
-
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ImageView android:layout_width="64dp"
android:layout_height="64dp"
android:src="@drawable/ic_launcher"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="黑馬手機衛士保護您的安全"/>
</LinearLayout>
應用成了前臺應用, 即便退出全部的Activity, 依然是前臺進程, 不會被殺死
---------------------------------10 13號-----------------------------------------------------------------------------------------------------
# widget #
- 長按桌面, 或者在全部應用列表裏向右滑動, 能夠添加窗口小部件.
- 寫一個類, 繼承 AppWidgetProvider, 而後在清單文件裏配置, 按照文檔來就好了.
主要是 meta-data 標籤裏配置的 xml:
<!-- <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="40dp" 最小寬高
android:minHeight="40dp"
android:updatePeriodMillis="1800000" 多久更新一下Widget, 單位是毫秒, 最短是半個小時 1800000
android:previewImage="@drawable/preview" 預覽圖片
android:initialLayout="@layout/example_appwidget" 佈局文件
android:configure="com.example.android.ExampleAppWidgetConfigure" 配置頁面
android:resizeMode="horizontal|vertical" 縮放模式
android:widgetCategory="home_screen"> 類型, 顯示在桌面上, 仍是顯示在鎖屏界面上, API 17
</appwidget-provider> -->
- 修改佈局, 反編譯金山的apk, 反編譯以後再清單文件裏搜索, 把須要的文件所有拷貝過來
該拷的拷, 該刪的刪, 該改的改. 改完了別忘了修改清單文件
- Widget的生命週期
public class WidgetReceiver extends AppWidgetProvider{
/**
* 接收到事件
*/
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
System.out.println("onReceive");
}
/**
* 第一次添加
*/
@Override
public void onEnabled(Context context) {
super.onEnabled(context);
System.out.println("onEnabled");
}
/**
* 被添加(第一次, 或者再次添加)/更新
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
/**
* 被刪除
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
}
/**
* 最後一個被刪除
*/
@Override
public void onDisabled(Context context) {
super.onDisabled(context);
}
}
- 定時更新Widget
看一下日誌, 發現金山每隔一會就更新一下Widget, 它在配置文件裏配的 android:updatePeriodMillis="0",
不依賴系統的更新, 它實際上是啓動了一個服務, 在服務裏作定時操做.
咱們也寫一個服務, 定時更新Widget, 在 onEnabled 方法裏啓動, 在 onDisable 方法裏中止
另外在 onUpdate 方法裏也要起一下服務, 確保服務正在運行. 這是爲了不桌面上已經有Widget,
而後直接在項目上右鍵 run as 不走 onEnabled 方法.
/**
* 被添加(第一次, 或者再次添加)/更新
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
// onUpdate 方法裏也要起一下服務, 確保服務正在運行. 避免桌面上已經有Widget,
// 而後直接在項目上右鍵 run as 不走 onEnabled 方法.
if (!ServiceStateUtil.isServiceRunning(context, UpdateWidgetService.class)) {
context.startService(new Intent(context, UpdateWidgetService.class));
}
}
## 定時器 ##
java提供的Timer類
private void timer1() {
Timer timer = new Timer();
參1 定時任務 參2 第一次執行的延時時間 參3 間隔
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("定時器: Timer");
}
}, 0, 3000);
// 中止
// timer.cancel();
}
- 在相應的服務裏: 在oncreate方法中
// 鬧鐘管理器 能夠執行定時任務 而且應用退出也起做用 可是timer就不行
am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent();
intent.setAction("com.itheima.mobilesafe.action.upwidget");
// PendingIntent 延時的intent 對應的動做不會當即執行 須要有一個觸發的事件
// 這裏是用定時器來觸發發送一個廣播
operation = PendingIntent.getBroadcast(getApplicationContext(), 100, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// 參1 計時方式,參2 第一次執行的延時時間,參3執行間隔 參4 延時的intent
am.setRepeating(AlarmManager.RTC, 2000, 5000, operation);
//註冊接受更新的廣播
IntentFilter filter = new IntentFilter();
filter.addAction("com.itheima.mobilesafe.action.upwidget");
registerReceiver(receiver, filter);
- 關於setRepeating的參數1計時方式 瞭解一下
public static final int ELAPSED_REALTIME
// 當系統進入睡眠狀態時,這種類型的鬧鈴不會喚醒系統。直到系統下次被喚醒才傳遞它,該鬧鈴所用的時間是相對時間,是從系統啓動後開始計時的,包括睡眠時 間,能夠經過調用SystemClock.elapsedRealtime()得到。系統值是3 (0x00000003)。
public static final int ELAPSED_REALTIME_WAKEUP
//能喚醒系統,用法同ELAPSED_REALTIME,系統值是2 (0x00000002) 。
public static final int RTC
//當系統進入睡眠狀態時,這種類型的鬧鈴不會喚醒系統。直到系統下次被喚醒才傳遞它,該鬧鈴所用的時間是絕對時間,所用時間是UTC時間,能夠經過調用 System.currentTimeMillis()得到。系統值是1 (0x00000001) 。
public static final int RTC_WAKEUP
//能喚醒系統,用法同RTC類型,系統值爲 0 (0x00000000) 。
Public static final int POWER_OFF_WAKEUP
//能喚醒系統,它是一種關機鬧鈴,就是說設備在關機狀態下也能夠喚醒系統,因此咱們把它稱之爲關機鬧鈴。受SDK版本影響,某些版本並不支持,使用方法同RTC類型,系統值爲4(0x00000004)。
- 在註冊的receiver裏
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (TextUtils.equals(action, "com.itheima.mobilesafe.action.upwidget")) {
updateWidget();
}
}
};
//更新Widget內容, 要使用 AppWidgetManager 這個類
private void updateWidget() {
// 獲取 AppWidgetManager
mWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
// 初始化widget組件
ComponentName provider = new ComponentName(this, WidgetReceiver.class);
// 初始化遠程view對象, 這個View不在咱們的應用進程裏
RemoteViews remoteView = new RemoteViews(getPackageName(), R.layout.process_widget);
remoteView.setTextViewText(R.id.tv_running_num,
"正在運行的軟件:" + ProcessInfoProvider.getRunningProcessCount(this));
remoteView.setTextViewText(R.id.tv_avail_mem,
"可用內存:" + Formatter.formatFileSize(this, ProcessInfoProvider.getAvailMemory(this)));
// 更新遠程view
mWidgetManager.updateAppWidget(provider, remoteView);
}
- AppWidgetManager 和 ComponentName 初始化能夠放在 onCreate 方法裏.
- 在ondestroy裏
@Override
public void onDestroy() {
super.onDestroy();
//服務結束取消定時器 和取消註冊
am.cancel(operation);
unregisterReceiver(receiver);
}
- 設置Widget的點擊事件 仍是以前的 RemoteViews那裏 增長點擊事件就好
Intent intent = new Intent();
intent.setAction("com.itheima.mobilesafe.action.widgetclean");
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 101,intent, PendingIntent.FLAG_UPDATE_CURRENT);
//點擊發送一個廣播 去殺死進程
views.setOnClickPendingIntent(R.id.btn_clear, pendingIntent);
widgetManager.updateAppWidget(component, views);
> 以前 註冊的廣播增長一個action
IntentFilter filter = new IntentFilter();
filter.addAction("com.itheima.mobilesafe.action.upwidget");
filter.addAction("com.itheima.mobilesafe.action.widgetclean");
registerReceiver(receiver, filter);
> 廣播接收者裏也增長判斷
private BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (TextUtils.equals(action, "com.itheima.mobilesafe.action.upwidget")) { updateWidget(); } else if (TextUtils.equals(action, "com.itheima.mobilesafe.action.widgetclean")) { ProcessInfoProvider.killAllPro(getApplicationContext()); } } };