android 廣播+服務+Application結合案例:獲取所有安裝應用的資訊並展示到listview上,結合SearchView進行檢索
阿新 • • 發佈:2018-11-25
效果圖:
思路:
- 1、建立自定義Application,在其onCreate()回撥方法中啟動一個Service,在Service中開啟一個Thread執行緒,在該執行緒中,使用PackageManger類(主要職責是管理應用程式包)的getInstalledApplications()方法獲取已安裝的應用程式資訊,並將獲取的資料集合(型別為List<ApplicationInfo>)通過Intent傳送給自定義廣播。
- 2、在廣播的回撥方法中,接收來自1中傳來的資料,並將其作為資料來源設定給ListView的介面卡。
- 3、在SearchView的回撥方法中, 迴圈使用loadLabel(PackageManager pm) 獲得當前應用程式的label與使用者輸入的字串進行包含比較,再建立List<ApplicationInfo>集合,如果包含就新增到此集合。最後將該集合重新作為資料來源繫結ListView的介面卡。
佈局很簡單:
1。main_acitivity.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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" android:orientation="vertical" tools:context=".MainActivity"> <android.support.v7.widget.SearchView android:layout_margin="10dp" android:id="@+id/main_sv" android:layout_width="match_parent" android:layout_height="wrap_content" app:searchHintIcon="@drawable/ic_action_action_search" android:background="@drawable/sv_bg" app:iconifiedByDefault="false" app:queryHint="請輸入..."> </android.support.v7.widget.SearchView> <ListView android:id="@+id/main_lv" android:layout_width="match_parent" android:layout_height="match_parent"> </ListView> </LinearLayout>
2。list_item.xml 介面卡每一個item的佈局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/iv_icon" android:layout_width="40dp" android:layout_height="40dp" android:layout_margin="10dp" tools:background="@mipmap/ic_launcher" /> <TextView android:id="@+id/tv_title" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="10dp" android:layout_weight="1" android:textSize="20dp" tools:text="測試測試" /> </LinearLayout>
程式碼:
bean類 封裝你想要app的資訊類
AppInfos.java 因為涉及intent傳輸需實現Parcelable方法
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
/**
* <p>檔案描述:<p>
* <p>作者:Mr-Donkey<p>
* <p>建立時間:2018/11/24 18:14<p>
* <p>更改時間:2018/11/24 18:14<p>
* <p>版本號:1<p>
*/
/**
* app應用資訊封裝類
*/
public class AppInfos implements Parcelable {
private CharSequence label;
private Drawable img;
public AppInfos(CharSequence label, Drawable img) {
this.label = label;
this.img = img;
}
public CharSequence getLabel() {
return label;
}
public void setLabel(CharSequence label) {
this.label = label;
}
public Drawable getImg() {
return img;
}
public void setImg(Drawable img) {
this.img = img;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
}
protected AppInfos(Parcel in) {
}
public static final Creator<AppInfos> CREATOR = new Creator<AppInfos>() {
@Override
public AppInfos createFromParcel(Parcel in) {
return new AppInfos(in);
}
@Override
public AppInfos[] newArray(int size) {
return new AppInfos[size];
}
};
}
MyApliacation.java類 全域性的進行開啟服務
import android.app.Application;
import android.content.Intent;
/**
* <p>檔案描述:<p>
* <p>作者:Mr-Donkey<p>
* <p>建立時間:2018/11/24 14:51<p>
* <p>更改時間:2018/11/24 14:51<p>
* <p>版本號:1<p>
*/
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//1.開啟服務
startService(new Intent(this, MyService.class));
}
}
MyService.java 進行服務的一系列操作 看註釋
主要是註冊,登出,傳送廣播
import android.app.Service;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.util.Log;
import com.demo.showapp.config.Config;
import java.util.ArrayList;
import java.util.List;
/**
* <p>檔案描述:<p>
* <p>作者:Mr-Donkey<p>
* <p>建立時間:2018/11/24 14:52<p>
* <p>更改時間:2018/11/24 14:52<p>
* <p>版本號:1<p>
*/
public class MyService extends Service {
private static final String TAG = "MyService";
//1.建立服務
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: ");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: ");
//開啟執行緒
new Thread() {
@Override
public void run() {
super.run();
Log.d(TAG, "run: 開啟獲取app資訊的程序");
//1.獲取app資訊
PackageManager pm = getPackageManager();
List<ApplicationInfo> mLists = pm.getInstalledApplications(0);
Log.d(TAG, "run: " + mLists.get(1).loadLabel(pm));
Intent i = new Intent();
i.setAction(Config.BROADCAST_ACTION);
i.putParcelableArrayListExtra(Config.APP_INFO, (ArrayList<? extends Parcelable>) mLists);
//2.傳送廣播
sendBroadcast(i);
}
}.start();
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: ");
return null;
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind: ");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy: ");
super.onDestroy();
}
}
Config.java 全域性配置類,配置一些共有的資訊
public class Config {
public static final String SEARCH_TEXT = "search_text";
public static final String APP_INFO = "app_Info";
public static final String BROADCAST_ACTION = "com.demo.action";
}
ListAdapter.java 基礎BaseAdapter
在裡面實現了SearchView的檢索
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.demo.showapp.R;
import com.demo.showapp.bean.AppInfos;
import com.squareup.picasso.Picasso;
import java.util.ArrayList;
import java.util.List;
/**
* <p>檔案描述:<p>
* <p>作者:Mr-Donkey<p>
* <p>建立時間:2018/11/24 16:18<p>
* <p>更改時間:2018/11/24 16:18<p>
* <p>版本號:1<p>
*/
/**
* 裡面實現的思路是:
* 1.新建一個List new_number這個物件用來儲存過濾後符合條件的值
* 2.判斷constraint是否為空,是否有值.滿足條件的話遍歷List,並且判斷List的值是否有包含過濾的條件.
* 3.如果是的話把值存到new_number.如果一個符合的都沒有把temp_number的值(也就是最開始傳進來的List)賦值給new_number
* 4.新建一個FilterResults物件分別把new_number的size和value賦值給FilterResults.value和FilterResults.count,然後返回FilterResults物件
* 5.接著在publishResults()方法中取出過濾後滿足條件的值也就是results.values
* 6.判斷results.count的過濾後結果的個數是否大於0
* 7.大於0的話把值賦值給number然後重新整理介面,不滿足的話把temp_number(也就是最開始傳進來的值)賦值給number,然後重新整理介面
*/
public class ListAdapter extends BaseAdapter implements Filterable {
private Context mContext;
private List<AppInfos> mAppInfos, temp_Infos;
private LayoutInflater mLayoutInflater;
private PackageManager pm;
private TestFilter myFilter;
public ListAdapter(Context context, List<AppInfos> appInfos) {
mAppInfos = appInfos;
temp_Infos = appInfos;
mContext = context;
mLayoutInflater = LayoutInflater.from(context);
pm = context.getPackageManager();
}
@Override
public int getCount() {
return mAppInfos.size();
}
@Override
public Object getItem(int position) {
return mAppInfos.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder vh;
if (convertView == null) {
vh = new ViewHolder();
convertView = mLayoutInflater.inflate(R.layout.list_item, null, false);
vh.icon = convertView.findViewById(R.id.iv_icon);
vh.title = convertView.findViewById(R.id.tv_title);
convertView.setTag(vh);
} else {
vh = (ViewHolder) convertView.getTag();
}
vh.icon.setBackground(mAppInfos.get(position).getImg());
vh.title.setText(mAppInfos.get(position).getLabel());
return convertView;
}
/**
* 實現了Filterable介面,這個方法需要覆寫getFilter()方法.
*
* @return
*/
@Override
public Filter getFilter() {
if (myFilter == null) {
myFilter = new TestFilter();
}
return myFilter;
}
private class ViewHolder {
private ImageView icon;
private TextView title;
}
class TestFilter extends Filter {
/**
* 該方法主要完成對資料進行過濾的工作
*
* @param constraint
* @return
*/
@Override
protected FilterResults performFiltering(CharSequence constraint) {
List<AppInfos> new_number = new ArrayList();
if (constraint != null && constraint.toString().trim().length() > 0) {
for (int i = 0; i < temp_Infos.size(); i++) {
String content = (String) temp_Infos.get(i).getLabel();
Drawable img = temp_Infos.get(i).getImg();
if (content.contains(constraint)) {
new_number.add(new AppInfos(content, img));
}
}
} else {
new_number = temp_Infos;
Log.e("MainActivity","adapter");
}
FilterResults filterResults = new FilterResults();
filterResults.count = new_number.size();
filterResults.values = new_number;
return filterResults;
}
/**
* 接收過濾後資料
* 在該方法裡面進行介面的重新整理工作
*/
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mAppInfos = (List<AppInfos>) results.values;
if (results.count > 0) {
Toast.makeText(mContext, "查詢成功", Toast.LENGTH_SHORT).show();
notifyDataSetChanged();
} else {
Toast.makeText(mContext, "暫無應用", Toast.LENGTH_SHORT).show();
notifyDataSetChanged();
}
}
}
}
MainActivity.java
1.註冊廣播,接受傳來的資訊
2.橫豎屏的狀態儲存
3.按返回鍵後重新回來恢復狀態
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.graphics.drawable.Drawable;
import android.icu.util.LocaleData;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.Filter;
import android.widget.ListView;
import android.widget.TextView;
import com.demo.showapp.adapter.ListAdapter;
import com.demo.showapp.bean.AppInfos;
import com.demo.showapp.config.Config;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private ListAdapter mAadpter;
private ListView lv;
private SearchView sv;
private List<AppInfos> mInfos = new ArrayList<>();
private String search_text = "";
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive: 拿到broadcast傳送來的資訊");
if (intent.getAction().equals(Config.BROADCAST_ACTION)) {
List<ApplicationInfo> infos = intent.getParcelableArrayListExtra(Config.APP_INFO);
//將應用資訊裝到新的集合中
for (ApplicationInfo info : infos) {
CharSequence label = info.loadLabel(getPackageManager());
Drawable icon = info.loadIcon(getPackageManager());
mInfos.add(new AppInfos(label, icon));
}
initView();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate: ");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* 初始化檢視
*/
private void initView() {
lv = findViewById(R.id.main_lv);
sv = findViewById(R.id.main_sv);
mAadpter = new ListAdapter(this, mInfos);
lv.setAdapter(mAadpter);
searchViewListener();
}
/**
* searchView的監聽事件
*/
public void searchViewListener() {
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
//提交的text
@Override
public boolean onQueryTextSubmit(String s) {
Log.d(TAG, "onQueryTextSubmit: " + s);
//Adapter的內容進行過濾.
mAadpter.getFilter().filter(s);
return false;
}
//改變的text
@Override
public boolean onQueryTextChange(String s) {
Log.d(TAG, "onQueryTextChange: " + s);
//儲存全域性變數
search_text = s;
//Adapter的內容進行過濾.
mAadpter.getFilter().filter(s);
return false;
}
});
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume: ");
//註冊廣播
registerReceiver();
}
/**
* 動態註冊廣播
*/
private void registerReceiver() {
//1.意圖過濾
Log.d(TAG, "registerReceiver: ");
IntentFilter mFilter = new IntentFilter();
mFilter.addAction(Config.BROADCAST_ACTION);
//2.註冊廣播
registerReceiver(mBroadcastReceiver, mFilter);
}
/**
* 登出廣播
*/
@Override
protected void onPause() {
Log.d(TAG, "onPause: ");
super.onPause();
unregisterReceiver(mBroadcastReceiver);
}
@Override
protected void onDestroy() {
Log.d(TAG, "onDestroy: ");
super.onDestroy();
}
/**
* 返回時儲存狀態
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
try {
if (keyCode == KeyEvent.KEYCODE_BACK) {
Intent home = new Intent(Intent.ACTION_MAIN);
home.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
home.addCategory(Intent.CATEGORY_HOME);
startActivity(home);
return true;
}
} catch (Exception e) {
//ignore
}
return super.onKeyDown(keyCode, event);
}
/**
* 橫豎屏切換儲存值
* 原理:
* 1.橫屏切換豎屏實際上是先把當前的橫屏的Activity殺掉,然後重新建立一個豎屏的Activity
* 2.我們可以使用onSaveInstanceState()方法儲存資料
* 3.它是在橫屏Activity將殺死前呼叫,可以將須要儲存的資料放入Bundle封裝在系統中
* 4.切換豎屏後這個Activity又重新被建立 這樣可以在onCreate(Bundle)
* 5.或者onRestoreInstanceState(Bundle)方法中來回復之前儲存在Bundle中的資料
* 6.這樣就可以實現橫豎屏介面切換資料的儲存與讀取
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
Log.d(TAG, "onSaveInstanceState: ");
super.onSaveInstanceState(outState);
outState.putString(Config.SEARCH_TEXT, search_text);
outState.putParcelableArrayList(Config.APP_INFO, (ArrayList<? extends Parcelable>) mInfos);
}
/**
* 恢復值
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
Log.d(TAG, "onRestoreInstanceState: ");
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null || !savedInstanceState.getParcelableArrayList(Config.APP_INFO).isEmpty()) {
mInfos = savedInstanceState.getParcelableArrayList(Config.APP_INFO);
search_text = savedInstanceState.getString(Config.SEARCH_TEXT);
Log.d(TAG, "onRestoreInstanceState: 恢復資料");
if (mInfos != null || !mInfos.isEmpty()) {
initView();
//設定預設的查詢值為上次輸入的值
sv.setQuery(search_text, true);
}
}
}
}
完成。需要demo留下郵箱看到後回覆